Sonarqube 구축 (On Kubernetes)

NOHHYEONGJUN·2025년 6월 26일

CI/CD

목록 보기
14/15
post-thumbnail

Sonarqube

SonarQube코드 품질과 보안 취약점을 분석하고 지속적으로 관리하는 오픈소스 플랫폼이다.

주로 CI/CD 파이프라인과 연동하여 코드 변경 시 자동으로 분석을 수행하며, 버그, 코드 스멜(Smell), 보안 이슈 등을 탐지한다.

 

  • 코드 품질과 보안 취약점을 자동으로 검사하는 정적 코드 분석 도구이다.

  • 지속적인 코드 품질 모니터링을 통해 더 안정적이고 유지보수가 용이한 코드를 작성할 수 있다.

 

간단하게 Kubernetes 위에 Sonarqube를 배포하고 사용하는 방법을 정리해보았다.

추가로, keycloak OpenID Connect 연동도 함께 정리하였다.


 

 


1. 배포를 위한 yamls

Sonarqube는 비교적 배포가 쉬워, 간단하게 deploy를 사용해 배포한다.

deploy.yaml

oidc를 사용하기 위해 initcontainer로 플러그인 다운로드 및 공유한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sonarqube
  namespace: sonarqube
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sonarqube
  template:
    metadata:
      labels:
        app: sonarqube
    spec:
      securityContext:
        fsGroup: 1000
        runAsUser: 1000
      initContainers:
        - name: setup-plugins
          image: curlimages/curl
          command: ['sh', '-c', 'mkdir -p /opt/sonarqube/extensions/plugins && curl -L -o /opt/sonarqube/extensions/plugins/sonar-auth-oidc-plugin-2.1.1.jar https://github.com/vaulttec/sonar-auth-oidc/releases/download/v2.1.1/sonar-auth-oidc-plugin-2.1.1.jar']
          volumeMounts:
            - name: extensions
              mountPath: /opt/sonarqube/extensions
      containers:
        - name: sonarqube
          image: sonarqube:9.9-community
          env:
            - name: SONAR_JDBC_URL
              value: "jdbc:postgresql://sonarqube-postgresql:5432/sonarqube?socketTimeout=60&connectTimeout=30"
            - name: SONAR_JDBC_USERNAME
              value: sonarqube
            - name: SONAR_JDBC_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: sonarqube-secrets
                  key: SONAR_JDBC_PASSWORD
            - name: SONAR_ES_BOOTSTRAP_CHECKS_DISABLE
              value: "true"
          ports:
            - containerPort: 9000
          startupProbe:
            httpGet:
              path: /api/system/status
              port: 9000
            failureThreshold: 30
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /api/system/status
              port: 9000
            initialDelaySeconds: 60
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /api/system/status
              port: 9000
          resources:
            limits:
              cpu: "8"
              memory: "32Gi"
            requests:
              cpu: "4"
              memory: "8Gi"
          volumeMounts:
            - name: data
              mountPath: /opt/sonarqube/data
            - name: extensions
              mountPath: /opt/sonarqube/extensions
            - name: logs
              mountPath: /opt/sonarqube/logs
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: sonarqube-data
        - name: extensions
          persistentVolumeClaim:
            claimName: sonarqube-extensions
        - name: logs
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: sonarqube
  namespace: sonarqube
spec:
  type: ClusterIP
  ports:
    - port: 9000
      targetPort: 9000
  selector:

 

ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sonarqube-ingress
  namespace: sonarqube
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-buffer-size: "128k"
    nginx.ingress.kubernetes.io/proxy-busy-buffers-size: "256k"
    nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
spec:
  ingressClassName: nginx
  rules:
  - host: ex)www.mysonarqube.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: sonarqube
            port:
              number: 9000

 

postgre.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sonarqube-postgresql
  namespace: sonarqube
spec:
  serviceName: sonarqube-postgresql
  replicas: 1
  selector:
    matchLabels:
      app: sonarqube-postgresql
  template:
    metadata:
      labels:
        app: sonarqube-postgresql
    spec:
      securityContext:
        fsGroup: 1001
      containers:
        - name: postgresql
          image: bitnami/postgresql:14
          env:
            - name: POSTGRESQL_USERNAME
              value: sonarqube
            - name: POSTGRESQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: sonarqube-secrets
                  key: POSTGRESQL_PASSWORD
            - name: POSTGRESQL_DATABASE
              value: sonarqube
            - name: POSTGRESQL_POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: sonarqube-secrets
                  key: POSTGRESQL_POSTGRES_PASSWORD
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: data
              mountPath: /bitnami/postgresql
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: [ "ReadWriteOnce" ]
        storageClassName: longhorn
        resources:
          requests:
            storage: 30Gi
---
apiVersion: v1
kind: Service
metadata:
  name: sonarqube-postgresql
  namespace: sonarqube
spec:
  type: ClusterIP
  ports:
    - port: 5432
      targetPort: 5432
  selector:
    app: sonarqube-postgresql

 

pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sonarqube-data
  namespace: sonarqube
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn
  resources:
    requests:
      storage: 70Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sonarqube-extensions
  namespace: sonarqube
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn
  resources:
    requests:
      storage: 20Gi

 

secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: sonarqube-secrets
  namespace: sonarqube
type: Opaque
stringData:
  SONAR_JDBC_PASSWORD: ex)base64
  POSTGRESQL_PASSWORD: ex)base64
  POSTGRESQL_POSTGRES_PASSWORD: ex)base64

 

 


2. Keycloak OpenID Connect 설정


 

 


3. 추가 설정

General

  • Server URL도 설정해야 한다.

  • Logo를 설정해 줄 수 있다.

 

User

  • 관리자 및 일반 사용자의 권한을 설정한다.

 

Webhooks

  • Jenkins 등 필요한 웹훅을 설정한다.

 

Permission Template

  • 각 사용자마다 자동으로 Permission Template이 생성되도록 설정한다.


 

 


결론

Sonarqube는 CI/CD 파이프라인에서 꼭 사용하는 것이 좋다고 생각한다.

Quality Gate를 사용할 것인지도 중요하지만, 지속적으로 코드 스캔 결과를 직접 확인하는게 운영에 도움이 되는 것 같다. (깨달음)

profile
Cloud/DevOps & Network Virtualization에 관심 있는 대학생입니다. 🐳

0개의 댓글