MAATRIX / Блог / SonarQube на сервере: частые ошибки и решения

SonarQube на сервере: частые ошибки и решения

MAATRIX

SonarQube — не просто линтер: под капотом у него встроенный Elasticsearch, отдельная PostgreSQL и своя JVM с жёсткими требованиями к лимитам ОС. Поэтому даже опытные админы спотыкаются на первом же запуске: сервис падает сразу после старта, не хватает памяти, CI зависает на анализе. Ниже — конкретные ошибки, которые чаще всего встречаются на VPS, и рабочие решения без танцев с бубном.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Архитектура SonarQube и почему она спотыкается на сервере

SonarQube с версии 7.x — это три внутренних процесса в одной JVM-обвязке: app (супервизор), web (интерфейс на Tomcat) и search node — встроенный Elasticsearch, куда пишутся результаты анализа. Отдельно живёт база данных: SonarQube официально поддерживает только PostgreSQL (начиная с 9.x-веток H2 и MySQL выпилены совсем).

Из этой связки вытекают почти все проблемы на сервере:

  • Elasticsearch внутри SonarQube требует те же системные лимиты, что и обычный ES-кластер — vm.max_map_count, file descriptors, ulimit по процессам.
  • Три JVM-процесса (или минимум два — web и search) съедают память отдельно друг от друга, и OOM killer чаще всего убивает именно search node, а не web.
  • PostgreSQL должен быть доступен на момент старта web-процесса — если соединение не установилось за таймаут, SonarQube уходит в CrashLoop.

Официальный минимум — 2 ядра CPU и 4 ГБ RAM для маленькой команды; для реальной работы с несколькими проектами и параллельным CI закладывайте от 4 ГБ на саму SonarQube плюс отдельно ресурсы под PostgreSQL и раннеры. На 2 ГБ RAM SonarQube стартует нестабильно и падает при первом же крупном скане.

Ошибка vm.max_map_count: Elasticsearch не запускается

Самая частая причина, по которой SonarQube вообще не поднимается — сообщение в логах:

Elasticsearch did not exit normally - check the logs at ...
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

Это лимит ядра на количество memory-mapped областей на процесс — стандартное требование Elasticsearch, и SonarQube здесь не исключение. Фикс временный (до перезагрузки):

sudo sysctl -w vm.max_map_count=262144

И постоянный — добавить в /etc/sysctl.conf (или отдельный файл /etc/sysctl.d/99-sonarqube.conf):

vm.max_map_count=262144
fs.file-max=131072

Применить без перезагрузки:

sudo sysctl --system

Вторая по частоте ошибка из той же серии — про лимиты процессов и открытых файлов:

The maximum number of processes for user is too low
The SonarQube search node hasn't been able to start...

Правится в /etc/security/limits.conf:

sonarqube   -   nofile   65536
sonarqube   -   nproc    4096

Если SonarQube запущена как systemd-сервис — этого может быть недостаточно, systemd игнорирует limits.conf по умолчанию. Добавьте лимиты прямо в unit-файл:

[Service]
LimitNOFILE=65536
LimitNPROC=4096

и перечитайте конфигурацию: sudo systemctl daemon-reload && sudo systemctl restart sonarqube.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нехватка памяти: OOM killer и настройка heap

Если в dmesg или journalctl -k находите строку вроде Out of memory: Killed process ... (java), значит ядро прибило одну из JVM SonarQube по нехватке RAM. Проверить это можно быстро:

dmesg -T | grep -i "killed process"

По умолчанию SonarQube выделяет под каждый процесс фиксированный heap, который можно и нужно подстраивать под реальный объём сервера — правки вносятся в sonar.properties (обычно /opt/sonarqube/conf/sonar.properties или примонтированный конфиг в Docker):

sonar.web.javaOpts=-Xmx1G -Xms256m
sonar.ce.javaOpts=-Xmx1G -Xms256m
sonar.search.javaOpts=-Xmx1G -Xms1G -XX:MaxDirectMemorySize=256m

Ориентир по памяти (это именно ориентир — у вас будет зависеть от числа проектов и языков в анализе):

КомпонентМин. heapКомфортный heap
web512 МБ1 ГБ
compute engine (ce)512 МБ1–2 ГБ
search (Elasticsearch)512 МБ1–2 ГБ
PostgreSQL512 МБ – 1 ГБ

Итого на сервер с запасом под ОС и CI-раннер стоит закладывать от 6–8 ГБ RAM, если SonarQube анализирует несколько проектов параллельно. Если бюджет ограничен и памяти категорически не хватает — временно спасает своп, но это костыль: под нагрузкой своп резко просаживает скорость индексации в Elasticsearch, лучше сразу проверить, чего не хватает серверу и вырасти по тарифу.

PostgreSQL: подключение, кодировка и права

Вторая по частоте причина падений — база данных. Типичная ошибка в логах web процесса:

org.postgresql.util.PSQLException: FATAL: password authentication failed for user "sonar"

или

Caused by: java.net.ConnectException: Connection refused

Порядок действий, который закрывает 90% таких случаев. Создайте базу и пользователя с нужной кодировкой (SonarQube требует UTF8 и локаль, поддерживающую C collation для части таблиц):

CREATE ROLE sonar WITH LOGIN PASSWORD 'сложный_пароль';
CREATE DATABASE sonarqube OWNER sonar ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0;
GRANT ALL PRIVILEGES ON DATABASE sonarqube TO sonar;

Пропишите доступ в sonar.properties:

sonar.jdbc.username=sonar
sonar.jdbc.password=сложный_пароль
sonar.jdbc.url=jdbc:postgresql://127.0.0.1:5432/sonarqube

Если PostgreSQL стоит на отдельном сервере или в соседнем контейнере — проверьте pg_hba.conf: строка host sonarqube sonar 0.0.0.0/0 scram-sha-256 (замените маску на реальную подсеть) и что listen_addresses в postgresql.conf не ограничен localhost. Общие грабли с самим PostgreSQL и его тюнингом на сервере разобраны отдельно в статье про настройку PostgreSQL на VPS — там же про частые ошибки подключения и права ролей.

Отдельно стоит следить за ростом базы: SonarQube хранит историю всех анализов, и на активных проектах таблица issues растёт быстро. Настройте retention (Administration → Configuration → General → Housekeeping) — по умолчанию история хранится дольше, чем реально нужно для дашбордов.

Развёртывание в Docker Compose и reverse proxy через nginx

На практике проще всего поднимать SonarQube и PostgreSQL вместе через Docker Compose — так меньше проблем с системными лимитами (их выставляете один раз на хосте, а не на каждом процессе). Рабочий минимальный docker-compose.yml:

services:
  sonarqube:
    image: sonarqube:lts-community
    depends_on:
      - db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonarqube
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: сложный_пароль
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
      nproc: 4096
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs
    ports:
      - "127.0.0.1:9000:9000"
    restart: unless-stopped

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: сложный_пароль
      POSTGRES_DB: sonarqube
    volumes:
      - postgresql_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  postgresql_data:

vm.max_map_count из раздела выше всё равно нужно выставить на хосте — контейнер этот лимит не переопределяет, он общий для ядра. Порт 9000 намеренно проброшен только на 127.0.0.1: наружу отдаём через nginx с SSL, а не голый HTTP.

Конфиг reverse proxy:

server {
    listen 443 ssl http2;
    server_name sonar.example.com;

    ssl_certificate     /etc/letsencrypt/live/sonar.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sonar.example.com/privkey.pem;

    client_max_body_size 50m;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}

proxy_read_timeout увеличен намеренно — крупные проекты долго отдают результаты анализа, и дефолтные 60 секунд нередко режут соединение ошибкой таймаута. Общие грабли с настройкой nginx как прокси разобраны в статье nginx как reverse proxy: частые ошибки.

Интеграция с CI: токены, вебхуки и зависшие quality gate

Когда сама SonarQube поднята и работает, следующая волна ошибок — уже на стороне CI. Самая частая — раннер падает с 401 Unauthorized или You're not authorized to run analysis. Причина почти всегда одна: токен сгенерирован под пользователем, у которого нет прав Execute Analysis на конкретный проект, либо токен истёк (в новых версиях можно ставить срок жизни токена).

Токен создаётся в My Account → Security и передаётся в CI как секретная переменная, а не хардкодится в .gitlab-ci.yml:

sonarqube-check:
  stage: test
  image: sonarsource/sonar-scanner-cli:latest
  variables:
    SONAR_HOST_URL: "https://sonar.example.com"
    SONAR_TOKEN: "$SONAR_TOKEN"
  script:
    - sonar-scanner -Dsonar.projectKey=my-project
  only:
    - merge_requests
    - main

Если сборка виснет на этапе Waiting for the analysis report to be processed и в итоге падает по таймауту — почти всегда это либо перегруженный Compute Engine (не хватает heap sonar.ce.javaOpts, см. раздел про память), либо не настроен вебхук для quality gate: сканер ждёт ответа от SonarQube через sonar.qualitygate.wait=true, а вебхук не долетает, потому что SonarQube стоит за прокси и не может достучаться до раннера, или наоборот. Проверяйте доступность в обе стороны: раннер должен видеть SONAR_HOST_URL, а сама SonarQube — уметь достучаться до CI по вебхуку (Administration → Configuration → Webhooks), если вы используете sonar.qualitygate.wait.

Общие принципы настройки CI-раннера на сервере — в статье про GitLab CI Runner: частые ошибки и решения; если у вас Jenkins вместо GitLab — логика та же, разница только в том, где хранится токен.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Сколько реально нужно RAM для SonarQube на сервере?

Официальный минимум — 4 ГБ, но это тесно уже для одного среднего проекта. Для стабильной работы с несколькими репозиториями и параллельным CI закладывайте 6–8 ГБ: 2–3 ГБ под heap процессов SonarQube и остаток под PostgreSQL, ОС и CI-раннер.

Можно ли использовать вместо PostgreSQL встроенную H2?

Нет, начиная с современных веток SonarQube H2 поддерживается только для тестового локального запуска разработчика и явно не рекомендуется для сервера — данные не переживут перезапуск в промышленной эксплуатации, а сама СУБД не рассчитана на нагрузку.

Почему после обновления версии SonarQube перестал стартовать?

Чаще всего — несовместимая версия Java (проверьте требуемую JVM в release notes конкретной версии) или незавершённая миграция базы: посмотрите logs/ce.log и logs/web.log, там обычно явно написано, на каком шаге миграции застряло.

Нужен ли SonarQube отдельный сервер или можно на том же, где крутится CI?

Можно на одном, если ресурсов хватает с запасом, но лучше разносить — Compute Engine и Elasticsearch создают заметную нагрузку на CPU в моменты анализа, и она будет конкурировать с самими сборками CI за ресурсы.

Как понять, что упало — web, ce или search?

Смотрите отдельные логи в logs/: sonar.log — общий супервизор, web.log, ce.log, es.log. Именно es.log чаще всего содержит причину при проблемах с vm.max_map_count и памятью.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →