先日、今まではオンプレのJenkinsでCIを回していたのをAWS Codeシリーズに移行しました。
移行の背景
GitLab + JenkinsでCIを運用していました。
構成はこんな感じです。
この構成の問題
- Jenkinsサーバーのスペックが限界
- 別ver開発のビルドが横で動くと、リリース用ビルド時間が通常の1h30minから4h程度まで増加
- 開発者が好き勝手ジョブを追加するので、異常に重くなる場合がある(ひどい時はJenkins自体から応答が返ってこない)
- ビルドノードを追加しようにも、物理マシンを追加する予算はない
- Jenkinsのメンテナンスが面倒
- 定期的にディスクフルになる
- GitLabが部門共有なので、GitLab CIやwebhookを設定できない
これらを解決するために、並列でいくらでも実行できるCodeBuildへの移行の検討を開始しました。
また、今後のデプロイ自動化やコンテナの利用も見据えて、AWS上にコード管理/CIを持って行った方がよいという理由もあります。
※ちなみに、CircleCI等のSaaSを利用する選択肢は弊社では採れません。
現状と移行時の選択肢
太字が採用した選択肢です。
ソースコードリポジトリ
- 現状: GitLab on 部門AWS
- 重い
- たまに落ちる
- webhook禁止
-
CodeCommit
- フルマネージドサービス
- CloudWatch Eventsが使える
- GitHub Enterprise on AWS
- 高機能
- 高い
CI環境
ビルド制御とビルドノードの2点で考える
- 現状: Jenkins on 物理マシン + Jinekinsノード on 物理マシン
- スペックの限界
- 並列ビルドが発生するとすぐ死ぬ
- メンテナンスコスト高
-
CodePipeline + CodeBuild
- フルマネージド
- Jenkinsより柔軟性は下がる
- お金さえあればいくらでも並列実行可能
- Jenkins on AWS + CodeBuild
- Jenkinsの豊富なプラグインが利用可能
- メンテナンスコストはCodePipelineより高くなる
- ビルドの並列実行は問題なさそう
アーティファクトリポジトリ
- 現状: NEXUS on Local Machine
- ディスクフル問題
-
S3
- マネージドなので、メンテナンスコスト小
- NEXUS on AWS
- CodeBuildからのアクセス経路確保に難あり
- メンテナンスコスト高
Codeシリーズの課題
コードシリーズに移行し、ビルドのパイプライン等を組んでみてわかってきた課題がいくつかありました。
CodeBuildの実行結果が成功/失敗の2値しかない
Jenkinsの場合、ビルド結果はSUCCESS, UNSTABLE, FAILUREの3値で表されます。
しかし、CodeBuildの場合はSuccessとFailureの2値しかありません。
Jenkinsの場合は、ビルドには成功したが、テストには失敗した場合、UNSTABLEとして運用していましたが、これを期にテスト失敗もFailureとする運用に変えました。
テストが通らないコードをmavenリポジトリに登録するの、ダメ、ゼッタイ!
静的解析の結果が閲覧し辛い
CodeBuildには静的解析やテスト結果、カバレッジなどをS3にArtifactとして保存する機能があります。
checkstyleやFindBugs、JUnitの結果はhtml形式で出力できるので、S3側の設定で静的サイトホスティングを使えば、結果をそのままS3から閲覧することができます。
ただ、これらの静的解析結果はgradleのサブプロジェクトごとに出力されるため、複数のリポジトリ、複数のサブプロジェクトがある場合、閲覧性が悪いです。
また、ビルド毎にS3への出力パスを変えて、各ビルド分の結果を保持しようとすると、URLがビルド毎に変わってしまうため、テスト結果等のページを開くのにも一苦労です。
そこで、今回は、ビルド後のアーティファクト格納時に、アーティファクトに加えてこれらのファイルパスを出力し、このファイルパスのみは固定のS3パスに保存するようにしました。そして、静的サイトとして作成し、S3上に保存したページから、このファイルパスが書かれたファイルを読み込み、各種リンクを表示する仕組みを作りました。
ビルド -> ビルドのパイプラインが組めない
CodePipelineを使用すると、push -> build -> deployのようなパイプラインを組むことができ、簡単にCDを実現することができます。
ただ、少し使いづらいのが、ビルド -> ビルドといった設定ができない点です。
今担当している商品では、あるリポジトリのgradleプロジェクトをビルド -> それに依存するプロジェクトをビルドという感じの依存関係が多数あるため、この機能は必須でした。
リポジトリAのビルドが終わったら、リポジトリBとCのビルドを実行し、BとCの両方のビルドが終わったら、リポジトリDをビルドする、といった並列でのビルドを実現したかったので、今回はCloudWatch Events、SNS、Lambdaを利用し、独自にパイプラインを作成することにしました。
完成したパイプラインは以下のような仕組みで動いています。
まとめ
- Lambda使えばなんとでもなる
- プライベートなプロジェクトならGitHub + TravisCIでいいや
- お金があるならGitHub Enterprise + CircleCIでいいや