SonarQubeとは
オープンソースのコード品質管理ツールです。
下記のようなことを行ってくれます。
- 重複しているコードの検出
- クラスや関数の複雑度の計測
- バグや脆弱性のチェック
より詳しい説明や具体的な使い方は 公式サイト やこちらの記事などを参考に・・・。
SonarQubeいいじゃん!うちのチームでも使いたい!!ってなった方は こちら へどうぞ。
以下 docker-compose.yaml
が出来上がるまでの過程です。
背景
公式が用意してくれている docker-composeのサンプル 、普通はこれで十分事足ります。
ただこれだと通信がHTTPで丸腰です・・・。
社内や閉じたネットワーク内で使うならまだしも、お外に置くならHTTPSで通信させたいところ。
ということで、HTTPSで利用できるSonarQubeをサクッと立ち上げるための設定を作りたい!と思った次第です。
##事前に必要な作業
今回利用するサービスの制限として、ドメインを持っていたり外から通信できることが条件となっているため、下記の作業は必須となります。
なお「爆速で構築する」にこれらの作業は含みませんので、事前に準備しておいてください。
- ホストにDockerとDocker Composeをインストールする(必要に応じてカーネルも調整する)。
- ドメインを取得し、DNSの設定を行う。
- ホストに対して外部からアクセスできるようにする(80番と443番ポートを開放する)。
Docker Composeの設定
まずは雛形を調達
公式が用意してくれているサンプルがあるので、有り難く使わせてもらいましょう!
この内容をベースにあれこれいじっていきます。
version: "2"
services:
sonarqube:
image: sonarqube:7.9.1-community
ports:
- "9000:9000"
networks:
- sonarnet
environment:
- sonar.jdbc.url=jdbc:postgresql://db:5432/sonar
volumes:
- sonarqube_conf:/opt/sonarqube/conf
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
db:
image: postgres
networks:
- sonarnet
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
volumes:
- postgresql:/var/lib/postgresql
- postgresql_data:/var/lib/postgresql/data
networks:
sonarnet:
driver: bridge
volumes:
sonarqube_conf:
sonarqube_data:
sonarqube_extensions:
postgresql:
postgresql_data:
とりあえずV3へ移行
公式のサンプルはバージョンが2系です・・・。
せっかくなので新しい3系に書き換えます。
加えて、サービスの依存関係や起動の設定、名前など、個人的に気になる部分をちょっと修正します。
version: "3"
services:
sonarqube-web:
container_name: sonarqube-web
image: sonarqube:7.9.1-community
restart: always
ports:
- "9000:9000"
environment:
- sonar.jdbc.url=jdbc:postgresql://sonarqube-db:5432/sonar
volumes:
- sonarqube-conf:/opt/sonarqube/conf
- sonarqube-data:/opt/sonarqube/data
- sonarqube-extensions:/opt/sonarqube/extensions
depends_on:
- sonarqube-db
networks:
- sonarqube
sonarqube-db:
container_name: sonarqube-db
image: postgres:12.1
restart: always
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
volumes:
- postgresql-root:/var/lib/postgresql
- postgresql-data:/var/lib/postgresql/data
networks:
- sonarqube
networks:
sonarqube:
driver: bridge
volumes:
sonarqube-conf:
sonarqube-data:
sonarqube-extensions:
postgresql-root:
postgresql-data:
HTTPSで通信させるには
公式 いわくnginxとか使ってリバースプロキシを構成せいとのことです。
To run the SonarQube server over HTTPS, you must build a standard reverse proxy infrastructure.
リバースプロキシ用意するの面倒くさい・・・。
nginxとか名前しか知らないし、SonarQube使いたいだけなのに何でnginxの勉強しなきゃならないのん・・・。
リバースプロキシしてくれるのDockerイメージとかないのかなぁ・・・、ありました!
jwilder/nginx-proxy というイメージがそのまま使えそうです。
リバースプロキシを設定
nginx-proxyを組み込みます。
通常は、サーバの VIRTUAL_HOST
にアクセス時のドメイン名を設定するだけで、勝手にリバースプロキシしてくれるとのこと(すげぇ・・・)。
ただ ドキュメント にある通り、デフォルトで見に行くのは80番ポートらしいので、今回はSonarQubeの9000番ポートも VIRTUAL_PORT
に設定します。
If your container exposes multiple ports, nginx-proxy will default to the service running on port
80
. If you need to specify a different port, you can set aVIRTUAL_PORT
env var to select a different one. If your container only exposes one port and it has aVIRTUAL_HOST
env var set, that port will be selected.
合わせて9000番ポートのホストへのバインドが不要になるので解除します。
version: "3"
services:
sonarqube-web:
container_name: sonarqube-web
image: sonarqube:7.9.1-community
restart: always
environment:
- sonar.jdbc.url=jdbc:postgresql://sonarqube-db:5432/sonar
# Settings for nginx reverse proxy.
- VIRTUAL_HOST=your.sonarqube.domain.jp # 変えてね!
- VIRTUAL_PORT=9000
volumes:
- sonarqube-conf:/opt/sonarqube/conf
- sonarqube-data:/opt/sonarqube/data
- sonarqube-extensions:/opt/sonarqube/extensions
depends_on:
- sonarqube-db
networks:
- sonarqube
sonarqube-db:
container_name: sonarqube-db
image: postgres:12.1
restart: always
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
volumes:
- postgresql-root:/var/lib/postgresql
- postgresql-data:/var/lib/postgresql/data
networks:
- sonarqube
sonarqube-reverse-proxy:
container_name: sonarqube-reverse-proxy
image: jwilder/nginx-proxy:latest
restart: always
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
depends_on:
- sonarqube-web
networks:
- sonarqube
networks:
sonarqube:
driver: bridge
volumes:
sonarqube-conf:
sonarqube-data:
sonarqube-extensions:
postgresql-root:
postgresql-data:
証明書を発行/更新するには
HTTPSで通信する際に必須の証明書についてですが、別件で Let's Encrypt を使った経験はあるので、適当にスクリプト組んで自分でDockerイメージ作れるだろうと思っていました。
が、なんとnginx-proxyの説明を読んでいくと jrcs/letsencrypt-nginx-proxy-companion というイメージが使えるとあるじゃないですか!
証明書の発行/更新を自動で行ってくれるので、こちらの作業も不要になりました。
証明書管理機能の追加
letsencrypt-nginx-proxy-companionを組み込みます。
基本的な改修ポイントはドキュメントにある通りです。
- nginxプロキシコンテナの443番ポートを、ホストにバインドする。
-
/etc/nginx/certs
、/etc/nginx/vhost.d
、/usr/share/nginx/html
をnginxプロキシコンテナと共有する。 - SonarQubeコンテナの
LETSENCRYPT_HOST
に、証明書発行のためのドメイン名を設定する(VIRTUAL_HOST
と同じでOK)。 - SonarQubeコンテナの
LETSENCRYPT_EMAIL
に、Let's Encryptからの通知を受け取れるメールアドレスを設定する(証明書の有効期限が切れそうになると通知がきます)。
一点ドキュメントには特に書いてなく手間取ったのが、 NGINX_PROXY_CONTAINER
と NGINX_DOCKER_GEN_CONTAINER
の設定です。
実行時にnginxプロキシコンテナが見つからないと怒られたので焦りましたが、言われた通りに設定したらちゃんと見つけてくれました(言われたとおり NGINX_PROXY_CONTAINER
を設定すると、次は NGINX_DOCKER_GEN_CONTAINER
を設定しろと言ってきます・・・)。
sonarqube-lets-encrypt | Error: can't get nginx-proxy container ID !
sonarqube-lets-encrypt | Check that you are doing one of the following :
sonarqube-lets-encrypt | - Use the --volumes-from option to mount volumes from the nginx-proxy container.
sonarqube-lets-encrypt | - Set the NGINX_PROXY_CONTAINER env var on the letsencrypt-companion container to the name of the nginx-proxy container.
sonarqube-lets-encrypt | - Label the nginx-proxy container to use with 'com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy'.
また、Let's Encryptを使ったことある方はご存知かと思いますが、無料で証明書発行できるからと言ってやりたい放題というわけではありません。
例えば レート制限 というものがあり、テストや検証段階でトライアンドエラーを繰り返していると引っかかる可能性があります・・・。
Let's Encryptにはそのためのテスト環境も用意されており、 テスト環境の制限 は本番環境に比べてゆるゆるなので、まずはテスト環境を利用してきちんと動作することを確認しましょう。
こちら にある通りテスト用の証明書発行設定も用意されているので、まずは設定しておきます。
The
LETSENCRYPT_TEST
environment variable, when set totrue
on a proxyed application container, will create a test certificates that don't have the 5 certs/week/domain limits and are signed by an untrusted intermediate (they won't be trusted by browsers).If you want to do this globally for all containers, set
ACME_CA_URI
as described in Container configuration .
version: "3"
services:
sonarqube-web:
container_name: sonarqube-web
image: sonarqube:7.9.1-community
restart: always
environment:
- sonar.jdbc.url=jdbc:postgresql://sonarqube-db:5432/sonar
# Settings for nginx reverse proxy.
- VIRTUAL_HOST=your.sonarqube.domain.jp # 変えてね!
- VIRTUAL_PORT=9000
# Settings for Let's Encrypt.
- LETSENCRYPT_HOST=your.sonarqube.domain.jp # 変えてね!
- LETSENCRYPT_EMAIL=your.email.address@gmail.com # 変えてね!
# Settings for test.
- LETSENCRYPT_TEST=true
volumes:
- sonarqube-conf:/opt/sonarqube/conf
- sonarqube-data:/opt/sonarqube/data
- sonarqube-extensions:/opt/sonarqube/extensions
depends_on:
- sonarqube-db
networks:
- sonarqube
sonarqube-db:
container_name: sonarqube-db
image: postgres:12.1
restart: always
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
volumes:
- postgresql-root:/var/lib/postgresql
- postgresql-data:/var/lib/postgresql/data
networks:
- sonarqube
sonarqube-reverse-proxy:
container_name: sonarqube-reverse-proxy
image: jwilder/nginx-proxy:latest
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- nginx-certs:/etc/nginx/certs:ro
- nginx-vhost:/etc/nginx/vhost.d
- nginx-html:/usr/share/nginx/html
depends_on:
- sonarqube-web
networks:
- sonarqube
sonarqube-lets-encrypt:
container_name: sonarqube-lets-encrypt
image: jrcs/letsencrypt-nginx-proxy-companion:latest
restart: always
environment:
# Find nginx proxy container.
- NGINX_PROXY_CONTAINER=sonarqube-reverse-proxy
- NGINX_DOCKER_GEN_CONTAINER=sonarqube-reverse-proxy
# Settings for test.
- ACME_CA_URI=https://acme-staging-v02.api.letsencrypt.org/directory
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- nginx-certs:/etc/nginx/certs:rw
- nginx-vhost:/etc/nginx/vhost.d
- nginx-html:/usr/share/nginx/html
depends_on:
- sonarqube-reverse-proxy
networks:
- sonarqube
networks:
sonarqube:
driver: bridge
volumes:
sonarqube-conf:
sonarqube-data:
sonarqube-extensions:
postgresql-root:
postgresql-data:
nginx-certs:
nginx-vhost:
nginx-html:
HTTPSで通信できるSonarQubeを構築するための設定
動作確認が済んだらテスト用の設定を削除します。
docker-compose up -d
だけで、HTTPSで通信できるSonarQubeが爆誕します。
version: "3"
services:
sonarqube-web:
container_name: sonarqube-web
image: sonarqube:7.9.1-community
restart: always
environment:
- sonar.jdbc.url=jdbc:postgresql://sonarqube-db:5432/sonar
# Settings for nginx reverse proxy.
- VIRTUAL_HOST=your.sonarqube.domain.jp # 変えてね!
- VIRTUAL_PORT=9000
# Settings for Let's Encrypt.
- LETSENCRYPT_HOST=your.sonarqube.domain.jp # 変えてね!
- LETSENCRYPT_EMAIL=your.email.address@gmail.com # 変えてね!
volumes:
- sonarqube-conf:/opt/sonarqube/conf
- sonarqube-data:/opt/sonarqube/data
- sonarqube-extensions:/opt/sonarqube/extensions
depends_on:
- sonarqube-db
networks:
- sonarqube
sonarqube-db:
container_name: sonarqube-db
image: postgres:12.1
restart: always
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
volumes:
- postgresql-root:/var/lib/postgresql
- postgresql-data:/var/lib/postgresql/data
networks:
- sonarqube
sonarqube-reverse-proxy:
container_name: sonarqube-reverse-proxy
image: jwilder/nginx-proxy:latest
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- nginx-certs:/etc/nginx/certs:ro
- nginx-vhost:/etc/nginx/vhost.d
- nginx-html:/usr/share/nginx/html
depends_on:
- sonarqube-web
networks:
- sonarqube
sonarqube-lets-encrypt:
container_name: sonarqube-lets-encrypt
image: jrcs/letsencrypt-nginx-proxy-companion:latest
restart: always
environment:
# Find nginx proxy container.
- NGINX_PROXY_CONTAINER=sonarqube-reverse-proxy
- NGINX_DOCKER_GEN_CONTAINER=sonarqube-reverse-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- nginx-certs:/etc/nginx/certs:rw
- nginx-vhost:/etc/nginx/vhost.d
- nginx-html:/usr/share/nginx/html
depends_on:
- sonarqube-reverse-proxy
networks:
- sonarqube
networks:
sonarqube:
driver: bridge
volumes:
sonarqube-conf:
sonarqube-data:
sonarqube-extensions:
postgresql-root:
postgresql-data:
nginx-certs:
nginx-vhost:
nginx-html:
上のYAMLが全てですが GitHub にも一応置いておきます。
気が向いたらSonarQubeのバージョンアップに合わせて、バージョン上げていこうと思います。
さいごに
シンプルにグローバルIPを割り当てたGCE上で動かしていますが、 GCP使うならGCEの前にロードバランサ立てて、そこHTTPSの終端にすればよくね? と悟りを開いたところです。