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: 2Gi − 1536m heap = ~512m на ОС и non-heap.
Настройка GitLab OIDC
Перед деплоем создайте GitLab Application:
- GitLab → Admin Area → Applications → New Application
- Название:
SonarQube - Redirect URI:
https://sonarqube.example.com/oauth2/callback/gitlab - Scopes:
read_user,api - Сохранить 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:
- SonarQube → Quality Gates → Create
- Название:
Infrastructure - Добавить условия: только Bugs и Security Hotspots
- Назначить на инфраструктурные проекты
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. Туда стоит смотреть в первую очередь, если задача сканера завершилась успешно, а результаты не появились.