SonarQube в Kubernetes: деплой и интеграция с GitLab CI

Published: 2026-02-19

SonarQube работает на infra-кластере и сканирует код из всех GitLab-проектов. Доступ через OIDC (GitLab), история анализов хранится в PostgreSQL, результаты поступают от sonar-scanner-cli в GitLab CI.


HelmRelease

yamlapiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: sonarqube
  namespace: infra
spec:
  chart:
    spec:
      chart: sonarqube
      version: "10.4.*"
      sourceRef:
        kind: HelmRepository
        name: sonarqube
        namespace: flux-system
  values:
    edition: "community"
    jdbcOverwrite:
      enable: true
      jdbcUrl: "jdbc:postgresql://postgres.infra.svc.cluster.local:5432/sonarqube"
      jdbcUsername: sonarqube
      jdbcPassword: "${SONAR_DB_PASS}"
    sonarProperties:
      sonar.forceAuthentication: "true"
      sonar.auth.gitlab.enabled: "true"
      sonar.auth.gitlab.url: "https://gitlab.example.com"
      sonar.auth.gitlab.applicationId: "${SONAR_GITLAB_APP_ID}"
      sonar.auth.gitlab.secret: "${SONAR_GITLAB_SECRET}"
      sonar.auth.gitlab.allowUsersToSignUp: "true"
    persistence:
      enabled: true
      storageClass: local-path
      size: 20Gi
    resources:
      requests:
        cpu: 400m
        memory: 1Gi
      limits:
        cpu: 2
        memory: 2Gi
    jvmOpts: "-Xms512m -Xmx1536m"

jdbcOverwrite.enable: true отключает встроенный H2/Postgres-контейнер и переключает SonarQube на внешний PostgreSQL. Без этого используется встроенная база — для продакшена она не годится: данные пропадают при перезапуске пода.

jvmOpts: "-Xms512m -Xmx1536m" — минимальный и максимальный размер JVM heap. Максимум должен оставлять запас на накладные расходы JVM в пределах лимита памяти контейнера: limits.memory: 2Gi1536m heap = ~512m на ОС и non-heap.

Настройка GitLab OIDC

Перед деплоем создайте GitLab Application:

  1. GitLab → Admin Area → Applications → New Application
  2. Название: SonarQube
  3. Redirect URI: https://sonarqube.example.com/oauth2/callback/gitlab
  4. Scopes: read_user, api
  5. Сохранить Application ID и Secret в Vault:
bashvault kv put secret/infra/sonarqube \
  gitlab_app_id=YOUR_APP_ID \
  gitlab_secret=YOUR_SECRET \
  db_password=POSTGRES_PASS

PostgreSQL для SonarQube

SonarQube нужна внешняя база данных. На infra-кластере небольшой PostgreSQL запущен через оператор CloudNativePG:

yamlapiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: postgres
  namespace: infra
spec:
  instances: 1
  storage:
    size: 10Gi
    storageClass: local-path
  bootstrap:
    initdb:
      database: sonarqube
      owner: sonarqube
      secret:
        name: postgres-sonarqube-creds

secret.name: postgres-sonarqube-creds указывает на Kubernetes Secret с ключами username и password. CloudNativePG создаёт базу данных и пользователя при первом запуске.


ESO для учётных данных

yamlapiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: sonarqube-creds
  namespace: infra
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: sonarqube-creds
  data:
    - secretKey: SONAR_DB_PASS
      remoteRef:
        key: secret/infra/sonarqube
        property: db_password
    - secretKey: SONAR_GITLAB_APP_ID
      remoteRef:
        key: secret/infra/sonarqube
        property: gitlab_app_id
    - secretKey: SONAR_GITLAB_SECRET
      remoteRef:
        key: secret/infra/sonarqube
        property: gitlab_secret

GitLab CI scanner job

yamlsonarqube:
  stage: quality
  image: registry.example.com/sonarsource/sonar-scanner-cli:5.0.1
  variables:
    SONAR_HOST_URL: "https://sonarqube.infra.test.antonnovikov.com"
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
    GIT_DEPTH: "0"
    GIT_STRATEGY: fetch
  script:
    - sonar-scanner
        -Dsonar.projectKey=${CI_PROJECT_PATH_SLUG}
        -Dsonar.sources=.
        -Dsonar.host.url=${SONAR_HOST_URL}
        -Dsonar.login=${SONAR_TOKEN}
        -Dsonar.gitlab.project_id=${CI_PROJECT_ID}
        -Dsonar.gitlab.commit_sha=${CI_COMMIT_SHA}
        -Dsonar.gitlab.ref_name=${CI_COMMIT_REF_NAME}
  allow_failure: true
  only:
    - merge_requests
    - master

GIT_DEPTH: "0" необходим для blame в SonarQube: чтобы привязать найденные проблемы к авторам, нужна полная история git. С GIT_DEPTH: "50" по умолчанию blame работает только для последних коммитов.

GIT_STRATEGY: fetch — GitLab-раннер делает fetch вместо clone и сохраняет историю между запусками пайплайна.

SONAR_TOKEN — переменная CI уровня проекта (masked), генерируется в SonarQube: My Account → Security → Generate Tokens.

allow_failure: true — анализ SonarQube носит информационный характер. Пайплайн не должен блокировать деплой из-за качества кода, если quality gate не сделан блокирующим намеренно.


Quality gate

Стандартный Sonar Way проверяет новый код по условиям:

  • покрытие тестами ≥ 80%
  • дублирование строк ≤ 3%
  • нет новых багов уровня blocker/critical
  • нет новых security hotspots

Для инфраструктурных репозиториев (YAML, shell, Python без тестов) покрытие неактуально. Создайте собственный quality gate:

  1. SonarQube → Quality Gates → Create
  2. Название: Infrastructure
  3. Добавить условия: только Bugs и Security Hotspots
  4. Назначить на инфраструктурные проекты

sonar-project.properties

inisonar.projectName=My Service
sonar.sources=src/
sonar.tests=tests/
sonar.python.coverage.reportPaths=coverage.xml
sonar.exclusions=**/vendor/**,**/migrations/**
sonar.coverage.exclusions=**/test*/**,**/*test*

Закоммитьте в корень репозитория — сканер подхватит автоматически.


Управление объёмом данных анализов

Если SonarQube обслуживает только инфраструктурные репозитории, PVC на 20 ГБ хватает на годы. Старые данные периодически очищайте:

bashcurl -X POST \
  -u admin:password \
  "https://sonarqube.example.com/api/projects/delete_analyses?project=my-project&from=2024-01-01&to=2024-06-01"

Или настройте через Administration → Configuration → Housekeeping.

Мониторинг состояния SonarQube

bashcurl https://sonarqube.example.com/api/system/status | jq .status
# Должен вернуть "UP"

Упавшие фоновые задачи видны в Administration → Background Tasks вместе с полными stack trace. Туда стоит смотреть в первую очередь, если задача сканера завершилась успешно, а результаты не появились.