Docker with ECS × Railsを実現させるために考えたこと(CI編)
こんにちは。エンジニアの志村です。
前回・前々回とRailsをDocker運用する記事を書いておりますが、今回はCI編です。
過去の記事は下記になります。
導入編でも書いた通り、CIはJenkinsを使用することにしました。 Dockerでの運用前はCircleCIを使用しておりました。
現在の構成
構成
MasterとSlaveの2段構成にしています。
Masterにジョブを置くとジョブの管理が面倒になるのと、ジョブの組み合わせ次第では不具合が起きる可能性があるので、Masterにはジョブを持たせず、テストやlint等はslaveで行うように設定しています。
Master, Slave共にDocker + ECSで構築しています。
ECSでSlave用のクラスタを用意し、
Amazon EC2 Container Service Plugin - Jenkins - Jenkins Wiki
を使用してSlaveを管理しています。
- Pipelineを使用。Pipelineは全てJenkinsfileにて記述し、Gitリポジトリで管理
- Docker+ECSでMaster & Slaveを管理
- docker-composeでテスト・lintを走らせる
上記のように、Jenkinsコンテナ内でさらにRailsのDockerコンテナを起動している状態となります。所謂Docker in Dockerです。
調べたところ、Docker in Dockerには賛否両論あるようです。
ホストマシンのsocketをマウントし各コンテナで使用するように設定してあるので、今回の例だと、RailsのコンテナからJenkinsのコンテナが見えてしまうという問題があります。
しかし、
- Dockerfileに記述すれば誰でもJenkinsの設定を変えることが出来る
- ECSを使用して簡単にSlaveを管理出来る
という利点から今回はこのDocker in Dockerの方式を採用しました。
フロー
- Githubのpush or Jenkinsからの操作
- GithubのStatusをPendingに変更
- docker-compose build
- rubocop
- scss-lint
- slim-lint
- RSpec
- (Deployモードなら)docker build
- (Deployモードなら)ECRへのプッシュ
- (Deployモードなら)harmonik*1を使用したデプロイ
というフローになっております。
フローは4種類となり、それぞれJenkinsfileで管理するようにしています。
環境 | モード |
---|---|
production | deploy |
production | test |
staging | deploy |
staging | test |
モードはtest・deployの2種類です。
testモードはRSpecまで(master, developブランチならば当該サーバに自動デプロイ)、deployモードはdocker build→デプロイまでを行うように設定しています。
こうすることによって、テスト無しでドッグフーディング環境にデプロイすることが出来、ビジネスサイドのチェックも素早く行うことが出来ます。
Jenkinsfile
Jenkinsfileはフェーズごとにファイルを分けました。
Jenkinsfile Jenkinsfile.test Jenkinsfile.build Jenkinsfile.deploy
と言った具合です。
JenkinsfileはGroovyで記述します。
Jenkinsfile
環境やモードによってどの処理をするか、クレデンシャルの読み込み、またGithubのStatusを替えるといった処理を記述しています。
- Jenkinsのパラメータから環境・モードを判別
- Slackに開始を通知
- mode == ‘test’の場合、GithubのStatusをPendingに変更
- それぞれに必要なCredentialをS3からダウンロード
- deployモードならbuild, deployファイルの処理を行う
- testモードならtestファイルの処理を行う。master, developブランチならさらにbuild, deployファイルの処理を行う
- 終了通知をslackに送る
Jenkinsfile.test
testモードで使用するファイルです。主にテストやlintを回します。
- テストで使用するENVをS3からダウンロード
- docker-compose build
- bundle install
- Rubocop
- scss-lint
- slim-lint
- rake db:create && rake db:schema:load
- RSpec
Jenkinsfile.build
deployモード、またmaster・developブランチのtest終了後に使用します。 コンテナをビルドし、ECRにプッシュするところを担当しています。
- 各コンテナに使用するクレデンシャルをS3からダウンロード
- 各コンテナをビルド(タグはブランチ名+コミットハッシュ)
- 各コンテナをECRの各レポジトリにプッシュ
とこちらも至ってシンプルです。
Jenkinsfile.deploy
こちらは前回の記事で記述したデプロイツールのharmonik*2を使用します。
- バッチコンテナのデプロイ
- メンテナンスコンテナのデプロイ
- Rails関連コンテナのデプロイ
とこちらも比較的シンプルです。 Jenkinsfileを分割することにより、共通するコードをまとめることが出来、非常に見通しが良くなりました。
と弊社では上記のようにJenkinsを運用しています。 最初の設定は大変ですが、Dockerを利用することにより管理コストの削減や興味がある人間が気軽にJenkinsを設定出来るようになりました。