はじめに
プロジェクトのソースコード解析を自動化するため、OCI上に SonarQube を Docker で構築し、systemd で管理しようとしました。
しかし、いざサービスを起動してみると、コンテナが数秒おきに再作成され続ける「無限ループ」が発生。ログすら出ない沈黙の停止に悩まされた、落とし穴とその解決策を共有します。
また、本記事では実際に使用した環境構築用の設定や、Mavenを用いた解析の実行方法についても触れます。
実行環境
- OS: Ubuntu 24.04.3 LTS
- Memory: 16 GB
- Arch: x86_64
- Docker Compose: v2.39.2
1. SonarQubeの環境構築
まずはベースとなる環境を構築します。今回は、公式の docker-sonarqube をベースに、PostgreSQLをデータベースとして使用する構成を採用しました。
docker-compose.yml
services:
sonarqube:
image: sonarqube:community
hostname: sonarqube
container_name: sonarqube
read_only: true
depends_on:
db:
condition: service_healthy
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_temp:/opt/sonarqube/temp
ports:
- "9000:9000"
networks:
- ${NETWORK_TYPE:-ipv4}
db:
image: postgres:17
healthcheck:
test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
interval: 10s
timeout: 5s
retries: 5
hostname: postgresql
container_name: postgresql
environment:
POSTGRES_USER: sonar
POSTGRES_PASSWORD: sonar
POSTGRES_DB: sonar
volumes:
- postgresql:/var/lib/postgresql
networks:
- ${NETWORK_TYPE:-ipv4}
volumes:
sonarqube_data:
sonarqube_temp:
sonarqube_extensions:
sonarqube_logs:
postgresql:
networks:
ipv4:
driver: bridge
enable_ipv6: false
dual:
driver: bridge
enable_ipv6: true
ipam:
config:
- subnet: "192.168.2.0/24"
gateway: "192.168.2.1"
- subnet: "2001:db8:2::/64"
gateway: "2001:db8:2::1"
Mavenを用いた解析の実行
環境が整えば、Mavenプロジェクトから以下のコマンドを叩くだけで、簡単に静的解析結果をSonarQubeへ送信できます。
mvn sonar:sonar \
-Dsonar.projectKey=my-project \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=your-sonarqube-token
これにより、コードの複雑度や潜在的なバグがダッシュボード上で可視化されます。
2. systemd の親切心が招いた「無限再起動ループ」
systemd でサービス化し、Restart=always を設定したところ、コンテナが数秒おきに壊されては作られる地獄のループに突入しました。
問題の設定:
[Service]
ExecStart=/usr/bin/docker compose up -d
Restart=always
なぜループしたのか?:
- systemd が
docker compose up -dを実行する。 -
-d(バックグラウンド実行)なので、コンテナを起動した直後にコマンド自体は「正常終了」して終了する。 - systemd は「おっと、プログラムが終了したぞ! でも
Restart=alwaysだから、すぐにもう一度動かさなきゃ!」と判断。 - すぐに再び
ExecStartが走る。 -
docker composeが既存のコンテナを「再作成(Recreate)」して起動し直す。 - 以下、無限ループへ。
解決策:
systemd に「コマンドを叩いたら仕事は終わりだよ」と教え、終了後も「起動中」という状態だけを維持させる Type=oneshot 設定に変更します。
修正後の設定(/etc/systemd/system/sonarqube-docker.service):
[Service]
Type=oneshot # 一回叩いて終わり
RemainAfterExit=yes # 終了後も「起動中」とみなす
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
Restart=no # systemdによる勝手な再起動を禁止
💡 コラム:systemdでDockerを管理する「2つの流派」
「そもそも -d を使うのは一般的なのか?」という疑問への回答として、よく使われる2つのパターンを比較します。
| 管理方法 | 設定タイプ | メリット | デメリット |
|---|---|---|---|
A. フォアグラウンド (-dなし) |
Type=simple |
systemdがプロセスを直接監視。ログも journalctl で一元管理。 |
docker stop 等の外部操作を「異常終了」と誤認しやすい。 |
B. デタッチモード (-dあり) |
Type=oneshot |
普段の docker compose コマンドと同じ感覚で扱える。 |
中のコンテナが死んでいても、systemd上は active と表示され続ける。 |
今回は、運用に慣れている Docker 側での操作感を優先し、Bのデタッチモードを採用しました。systemdを「OS起動時にコンテナを立ち上げるトリガー」として割り切る構成です。
4. まとめ
systemdをよくわからないまま使っていて思わぬ動作をしたので記事にしました。
同じことに悩む誰かの助けになれば幸いです。
おわりに
次回は、この SonarQube 環境をフル活用し、Jenkins Pipelineで400以上のプロジェクトを環境再起動に強い構成で解析した方法について書きたいと思います。