本連載では、以下のイメージに沿って「CodeBuild」「SonarQube」を使った継続的インテグレーション(CI:Continuous integration)環境を実際に構築していきます。
今回は、Amazon RDS、アプリケーションロードバランサ(ALB)を設定して、ECSクラスタ上にオープンソースの静的解析ツールである「SonarQubeServer」をECSコンテナとして構築します。
SonarQubeの概要
SonarQubeは、SonarSourceによって提供されているオープンソースの静的解析プラットフォームで、形式的なソースコードのバグや不適切な記法のチェック、カバレッジの計測などを行い、品質の可視化を可能にするものです。
解析できるプログラミング言語として、Java(Android含む)、C#、PHP、JavaScript、TypeScript、C/C++、Ruby、Kotlin、Go、COBOL、PL/SQL、PL/I、ABAP、VB.NET、VB6、Python、RPG、Flex、Objective-C、Swift、CSS、HTML、and XMLなど、25を超える言語をサポートし、それらのコードのバグやCode Smell(深刻な問題が発生する可能性があるコード)、セキュリティ脆弱性を検出することができます。
また、SonarQubeはプラグインにより拡張が可能になっており、「IntelliJ IDEA」や「Eclipse」などの統合開発環境と連携するSonarlintプラグイン、「Maven」や「Gradle」などのライブラリ管理ツール向けのScannerプラグインに加え、GitHubやGitLab、JIRAやJenkinsなどの各開発ツールとの統合プラグイン、チェックルールを追加する場合のFindBugsプラグインなどさまざまなものが提供されています。
SonarQubeを構成する代表的なコンポーネントや機能、その特徴は以下の通りです。
コンポーネント | 機能 | 特徴 |
---|---|---|
SonarQubeServer | ダッシュボード | SonarQubeの中核となるWebアプリケーション。解析対象ごとにプロジェクトを作成し、解析結果のサマリやIssue、解析のアクティビティが可視化できる |
QualityProfile | 解析する言語のチェックルールの定義、適用するプロジェクトへの設定を行う。ソースコードを実装する開発端末で、QualityProfileが設定されたSonarQubeServerプロジェクトを指定することで、複数の端末に同一のルールを適用できる | |
QualityGate | 開発端末でソースコードをコミット・プッシュする場合やSonarScannnerでビルドする際に、プロジェクトに設定した静的チェックルール・メトリクスをパスしたか検証し、またその検証ルールを定義する機能 | |
MarketPlace | FindBugsなどの追加の解析ルールや他のサードパーティ製ツールとの統合プラグインなどのマーケットプレイスで検索・インストールを行う機能。プラグインの一覧はこちら | |
Sonarlint | ソースコード検証・サジェスト | IntelliJ IDEAやEclipseなどの統合開発環境にプラグインをインストールし、コーディングする際に検証やサジェスト、チェックルールの詳細表示などを行う機能 |
SonarScanner | ソースコード解析・結果送信 | MavenやGladle、Jenkinsなどを使ってアプリケーションをビルドする際に、プラグインを組み込み、ソースコードの解析・検証、カバレッジ測定などを実行して、SonarQubeServerへ結果を連携する機能 |
SonarQubeには、上記の機能を無料で使用可能なCommunity Editionや、ソースコードのブランチやプルリクエスト解析などの機能を追加したDeveloper Edtion、ポートフェリオ管理機能や高度なテクニカルサポートがあるEnterprize Edtion、複数のSonarQubeServerをクラスタとして構築し、高可用性な運用ができるDataCenter Edtionがあります。以降では、Community Editionを使って環境構築を進めていきます。
Amazon RDSを使ったSonarQubeServer用データベースの構築
SonarQubeServerは、サーバの設定やプロジェクトの解析結果、組み込んだプラグイン、セキュリティ情報などを保存しておくリレーショナルデータベースが必要になります。データベースはOracle、PostgreSQL、MySQL、MS SQLServerなど主要なデータベースエンジンをサポートしていますが、ここでは自動バックアップなど高い可用性で運用が可能なAmazon RDS(PostgreSQL)を使ってデータベースを構築します。
RDS構築の要領は、筆者が執筆している別連載「AWSで作るクラウドネイティブアプリケーションの基本」の第11回「Amazon RDSにアクセスするSpringアプリケーション(1)」を参照してください。設定内容は、同記事の手順とほぼ同一でかまいませんが、「データベースの名前」だけ、「sonar」を設定するようにしてください。
構築後は、sonarデータベースにアクセスするユーザーを作成しましょう。
セキュリティグループでRDSのインバウンド接続が許可されたIPアドレスを持つEC2インスタンスなどで、PSQLクライアントなどをインストールし、作成したRDSのエンドポイント、データベース、ユーザー名を指定してRDSに接続後、ユーザー名「sonar」、パスワード「sonar」とするユーザーを作成します。
[centos@ip-XXXX-XXX-XXX-XXX ~]$ psql -U username -d sonar -h mynavi-sample-db.xxxxxxx.ap-northeast-1.rds.amazonaws.com
Password for user username:
psql (9.4.0, server 10.6)
sonar=> create role sonar with LOGIN password 'sonar';
なお、CentOSのPSQLインストール方法ですが、EC2でCentOS7を実行した場合は、以下のコマンドでPSQLをインストールできます。
[centos@ip-XXXX-XXX-XXX-XXX ~]$ sudo yum update -y
// omit
[centos@ip-XXX-XXX-XXX-XXX ~]$ sudo yum install -y postgresql
SonarQubeServer向けのアプリケーションロードバランサの構築
続いて、SonarQubeServerへ接続するためにALBを経由して接続するように構築します。ALBと接続するターゲットグループにECSコンテナを登録することにより、ヘルスチェック機能が有効になり、サーバが何らかの理由で落ちた場合も再起動が実行され、SonarQubeServerの可用性を向上させることができます。
ALBの構築は、連載「AWSで作るクラウドネイティブアプリケーションの基本」の第5回「AWS ECS上に構築するSpringアプリケーション(2)」を適宜参考にしてください。
今回、設定内容でポイントとなる項目は以下の通りです。
設定箇所 | 設定内容 | 説明 |
---|---|---|
基本設定:スキーム | インターネット向け(internet-facing) | 開発端末からSonarQubeServerへインターネット経由でアクセスするため、パブリックサブネット向けのALBを作成します |
セキュリティグループの設定:セキュリティグループの割り当て | 新しいセキュリティグループを作成する | ポート80番の任意のIPからのアクセスを許可するセキュリティグループを新しく作成します |
ルーティングの設定:ターゲットグループ | 新しいターゲットグループ | ターゲットグループを新規作成します |
ルーティングの設定:ターゲットグループ | [ターゲットの種類]インスタンス [プロトコル]HTTP [ポート]80 | HTTPプロトコルでターゲットグループにアクセスします |
ルーティングの設定:ヘルスチェック | [プロトコル]HTTP [パス]/ [間隔]300秒 | SonarQubeServerの初回起動はデータベースの構築など時間を要するため、300[s]ほどにチェック間隔を設定してください |
ターゲットの登録 | [登録済みターゲット]特に設定しない | 後述のECSサービス作成時にターゲットの登録を行うので、ここでは何も設定せずに登録します |
SonarQubeを構築するDockerfile
次に、SonarQubeServerをECSで構築するためのDockerfileを作成します。SonarQubeServerでデフォルトで提供されている起動スクリプトである「sonar.sh」は、SonarQubeアプリケーションをスクリプト中で別プロセスでデーモンとして起動する形式になっているため、Dockerfileイメージでそのまま実行するとDockerプロセスとしてアプリケーションを起動することができません。そこで、SonarSourceから提供されているDockerfileを参考に、CentOSでSonarQubeServerを構築するDockerfileを以下のように作成します。
# Dockerfile for sonarqube server
# (A)
FROM docker.io/centos:latest
# (B)
MAINTAINER debugroom
# (C)
RUN yum install -y java-1.8.0-openjdk zip unzip wget
# (D)
ENV SONAR_VERSION=7.7 \
SONARQUBE_HOME=/usr/local/sonarqube \
SONARQUBE_JDBC_USERNAME=sonar \
SONARQUBE_JDBC_PASSWORD=sonar \
SONARQUBE_JDBC_URL=jdbc:postgresql://mynavi-sample-sonarqube-db.xxxxxxxx.ap-northeast-1.rds.amazonaws.com/sonar
# (E)
EXPOSE 9000
# (F)
RUN groupadd -r sonarqube && useradd -r -g sonarqube sonarqube
# (G)
RUN set -x \
&& (gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys F1182E81C792928921DBCAB4CFCA4A29D26468DE \
|| gpg --batch --keyserver ipv4.pool.sks-keyservers.net --recv-keys F1182E81C792928921DBCAB4CFCA4A29D26468DE) \
&& curl -o sonarqube.zip -fSL https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-$SONAR_VERSION.zip \
&& curl -o sonarqube.zip.asc -fSL https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-$SONAR_VERSION.zip.asc \
&& gpg --batch --verify sonarqube.zip.asc sonarqube.zip \
&& unzip sonarqube.zip \
&& mv sonarqube-$SONAR_VERSION $SONARQUBE_HOME \
&& rm sonarqube.zip* \
&& rm -rf $SONARQUBE_HOME/bin/*
# (H)
WORKDIR $SONARQUBE_HOME
# (I)
COPY run.sh $SONARQUBE_HOME/bin/
# (J)
RUN chmod 744 $SONARQUBE_HOME/bin/run.sh
# (K)
RUN chown -R sonarqube:sonarqube $SONARQUBE_HOME
# (L)
USER sonarqube
# (M)
CMD ["./bin/run.sh"]
Dockerfileの実行コマンドは、次の通りです。
項番 | コマンド | 説明 |
---|---|---|
A. | FROM centos:centos7 | コンテナイメージのベースとなるOSイメージを指定します。 |
B. | MAINTAINER debugroom | メンテナンスしている組織や個人の名称を指定します。省略可能です |
C. | RUN yum install -y java-1.8.0-openjdk zip unzip wget | OSにJDK、zipコマンド、unzipコマンド、wgetコマンドをインストールしてます |
D. | ENV SONAR_VERSION=7.7 omit… | コンテナの環境変数にSonarQubeのバージョンやインストールするホームディレクトリ、ユーザー名、パスワード、RDSのエンドポイントを設定します |
E. | EXPOSE 9000 | コンテナのポート9000をオープンします |
F. | RUN groupadd -r sonarqube && useradd -r -g sonarqube sonarqube | sonarqubeグループとsonarqubeユーザーを作成します |
G. | RUN set -x omit… | SonarQubeServerをダウンロードして、$SONARQUBE_HOME配下へ展開します |
H. | WORKDIR $SONARQUBE_HOME | コマンド実行する起点となるディレクトリを$SONARQUBE_HOMEへ移します |
I. | COPY run.sh $SONARQUBE_HOME/bin/ | SonarQubeServerアプリケーションを実行するスクリプトrun.shを$SONARQUBE_HOME/binへコピーします |
J. | RUN chmod 744 $SONARQUBE_HOME/bin/run.sh | run.shに実行権限を付与します |
K. | RUN chown -R sonarqube:sonarqube $SONARQUBE_HOME | $SONARQUBE_HOME配下にある全てのディレクトリ/ファイルの所有者権限をグループsonarqube、ユーザーsonarqubeへ変更します |
L. | USER sonarqube | sonarqubeユーザーへスイッチします |
M. | CMD ["./bin/run.sh"] | SonarQubeServer実行スクリプトを起動します |
また、SonarQubeServerを実行するrun.shスクリプトは以下の通りです。起動スクリプトはSonarSourceから提供されているものをほぼそのまま利用しますが、javaコマンドでアプリケーションを起動するプロセスをフォアグラウンドで実行しているのがポイントです。
#!/usr/bin/env bash
set -e
if [ "${1:0:1}" != '-' ]; then
exec "$@"
fi
declare -a sq_opts
while IFS='=' read -r envvar_key envvar_value
do
if [[ "$envvar_key" =~ sonar.* ]]; then
sq_opts+=("-D${envvar_key}=${envvar_value}")
fi
done <
こちらも、連載「AWSで作るクラウドネイティブアプリケーションの基本」の第7回「AWS ECS上に構築するSpringアプリケーション(4)」と同様の手順で、Dockerfile作成後にDockerがインストールされた適当な端末で、イメージを作成します。
[centos@ip-XXX-XXX-XXX-XXX]$ docker build . -t debugroom/sonarqube-server:latest
// omit
[centos@ip-XXX-XXX-XXX-XXX ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
debugroom/sonarqube-server latest 97e608f45e20 2 minites ago 973MB
Dockerイメージが作成されたら、Docker Hubへプッシュします。
[centos@ip-XXX-XXX-XXX-XXX ~]$ docker login
// omit - enter USERID and password
[centos@ip-XXX-XXX-XXX-XXX ~]$ docker push debugroom/sonarqube-server:latest
ECSクラスタ/タスク定義、サービスの実行
SonarQubeServerのDockerfileイメージをDockerHubへプッシュした後は、EC2起動型のECSクラスタおよびECSタスク定義を行い、コンテナをサービスとして実行します。ECSクラスタの構築手順は連載「AWSで作るクラウドネイティブアプリケーションの基本」の第10回に記載してある内容を参考に進めてください。ただし、今回のポイントになる設定項目は以下の通りです。
設定箇所 | 設定内容 | 説明 |
---|---|---|
ECSクラスタの作成:インスタンスの設定 | 1 | 今回はターゲットグループに1つのECSコンテナだけ起動するように設定します。複数のECSクラスタを実行場合はDataCenterEditionが必要になるので複数には設定しないようにしましょう |
ECSクラスタの作成:ネットワーキング:セキュリティグループ | 新規作成:[プロトコル]HTTP、SSH [ポート]80、22 [ソース]ALBのセキュリティグループ、0.0.0.0/0 | ECSクラスタにはALBからの通信をポート80番で静的マッピングするため、ソースを上記で作成したALBのセキュリティグループに設定します。またトラブルシューティング用にSSHのポートも開けておきます |
ECSタスクの定義:タスクのサイズ:タスクメモリ | 2048MiB | SonarQubeでは、ハードウェア要求スペックとして2GB程度のメモリを推奨しているため、相応のメモリを確保しましょう |
ECSタスクの定義:タスクのサイズ:タスクCPU | 1 vcpu | 0.5 vcpu以下にすると、サービス起動時に時間がかかりすぎてヘルスチェックに引っかかる場合があるので、1vcpu程度取っておいた方がベターです |
ECSタスクの定義:コンテナの追加:ポートマッピング | [ホストポート]80 [コンテナポート]9000 | SonarQubeは9000番ポートでアプリケーションを起動するので、ALBからくるリクエストを9000番へ転送するように設定します |
ECSサービスの実行:サービスの設定:サービスタイプ | REPLICA | 今回はターゲットグループに1つのECSコンテナだけ起動するようにサービスタイプをREPLICAでタスク数を1に設定しますが、DAEMONでも可です |
ECSサービスの実行:サービスの設定:タスクの数 | 1 | 今回はターゲットグループに1つのECSコンテナだけ起動するように設定します。複数のサービスを実行する場合はDataCenterEditionが必要になるので複数には設定しないようにしましょう |
ECSサービスの実行:ネットワーク構成:ヘルスチェックの猶予期間 | 300 | 上記のタスクサイズの設定で、アプリケーションの起動に60秒ほど要するため、余裕を持った値を設定しておきます |
ECSサービスの実行:サービスの検出:サービスの検出の統合の有効化 | チェックしない | 今回はDNSでALBのエンドポイントを利用するので、設定をオフにしておきます |
ECSサービスを実行した後、ALBのDNS名を使ってブラウザからアクセスできれば起動は成功です。
なお、正しくコンテナが実行されない場合、ECSクラスタにSSHログインし、「docker ps -a」を実行することでコンテナの実行を確認できます。また、コンテナ実行時のログは/var/log/ecs/ecs-agent.log_XXXXで、SonarQubeServerの起動のログはコンテナの/usr/local/sonarqube/logsに出力されるよう設定されています。トラブルシューティングをする際は、それらのログを参照してください。
* * *
次回はSonarQubeServerでプロジェクトを作成し、QualityProfileを設定して開発端末のIDEで環境構築してみます。
著者紹介
川畑 光平(KAWABATA Kohei)
某システムインテグレータにて、金融機関システム業務アプリケーション開発・システム基盤担当を経て、現在はソフトウェア開発自動化関連の研究開発・推進に従事。
Red Hat Certified Engineer、Pivotal Certified Spring Professional、AWS Certified Solutions Architect Professionalなどの資格を持ち、アプリケーション基盤・クラウドなどさまざまな開発プロジェクト支援にも携わる。
本連載の内容に対するご意見・ご質問は Facebook まで。