コネヒト開発者ブログ

コネヒト開発者ブログ

CodeBuildを使ったECSへのコンテナデプロイ

こんにちは。インフラエンジニアの永井(shnagai)です。

今回は、CodeBuildでのECSデプロイについて書いてみました。 普段、TravisCIを使ってメインサービスのECSのデプロイを行っているのですが、新規開発するにあたりCodeBuildを使ったECSのデプロイを組んでみたのでその内容をまとめています。

内容はざっくり下記4項目について書いてます。

  • CodeBuildのみで行うECSへのコンテナデプロイの構成図
  • 他のCIサービスと比べた時のCodeBuildの良さ
  • CodeBuildの設定でハマった点
  • デプロイフロー

CodeBuildのみで行うECSへのコンテナデプロイの構成図

CodePipelineを使ったECSへのデプロイをする選択肢もあるのですが、これだと運用フローに大きく変更が入るので今回は、現在使っているecs-deploy を使ったECSデプロイを移植する形にしました。 全体のデプロイフローとしては下記のような形になっています

f:id:nagais:20190617171151j:plain

他のCIサービスと比べた時のCodeBuildの良さ

個人的に感じた、CodeBuildによるコンテナビルドの良さについてまとめてみました。

プロジェクトの並列度を考えなくて良い

一般的なCIサービスだと契約プランによって並列度には制限あると思います。 従量課金で何プロジェクトでも並列で動かすことが出来るのはCodeBuildならではだと思います!

Dockerイメージビルド時のローカルキャッシュが設定一つで有効化出来る

コンテナをフルビルドするするとめちゃくちゃ遅いですよね。。 もちろんCIサービスでもレイヤキャッシュをバックアップしてごにょごにょすることでキャッシュすることは出来るのですが、CodeBuildだと設定一つですごく簡単に、DockerBuild時のキャッシュ設定で出来るます。 これは、革命的かと思います。

プロジェクトのアーティファクトから下記設定をすることで、Dockerのレイヤキャッシュが有効になります。

項目
キャッシュタイプ LOCAL
ローカルキャッシュオプション LOCAL_DOCKER_LAYER_CACHE

※ただ、ローカルキャッシュというだけあって、ビルドによりキャッシュが効いたり効かなかったりまちまちだなという印象です。裏側のアーキテクチャがわからないですが、これはビルドを多数繰り返すことで複数ホストにキャッシュが出来て解決されるものなのか、また別の理由なのかは運用しながら探っていければと思っています。

参考までに、検証時に試したキャッシュ利用有無でのビルド速度の違いです。 f:id:nagais:20190617171539p:plain

DockerのBUILDKITも使える

Dockerビルド高速化における切り札とも言える、BUILDKIT もランタイムのDockerが18.06以上なのでサポートされています。 ローカルのビルドはBUILDKIT前提なのだけど、CIサービスが対応してないからデプロイにかかるビルドは遅いみたいな悩みともおさらば出来ます!

buildspec.ymlに下記を記述することで利用可能になります。

DOCKER_BUILDKIT: "1"

IAM Roleで権限をコントロール出来るので、鍵の管理が不要に

IAM Roleでプロジェクト毎に必要な権限を管理出来ます。 CIのプロジェクト毎に鍵の登録をするような運用がなくなるのがgoodだなと思います。

CloudWatchEventsのトリガにCodeBuildのイベントを指定出来るので通知も簡単に書ける

TravisCIでは、エラーハンドリングをしながらSlackを呼び出すことで通知を実装していました。

CodeBuildだと、下記のようなカスタムイベントパターンを指定することで、CodeBuildのステータスをトリガにLambdaを呼び出せます。 プロジェクトの状態検知はこちらに寄せることが出来るので、簡単に通知をすることが出来ます。

このイベントを受け取りLambdaをキックすることで、Slackに飛ばす運用をしています。

{
  "source": [
    "aws.codebuild"
  ],
  "detail-type": [
    "CodeBuild Build State Change"
  ],
  "detail": {
    "build-status": [
      "FAILED",
      "IN_PROGRESS",
      "SUCCEEDED"
    ],
    "project-name": [
      "トリガに使いたいproject-name",
    ]
  }
}

イメージ

f:id:nagais:20190617175251p:plain

CodeBuildの設定でハマった点

CodeBuildは、s3でホストしている静的コンテンツ配信サイトの自動更新には使っていたのですが、久々に触ったら下記の点が変わっていたので少しハマりしました。

ランタイムの指定方法が変わっていた

これまでだと、CodeBuildのランタイム環境は、例えばjsのビルドに使う環境だとaws/codebuild/nodejs:10.1.0 というようにプロジェクト側に指定する形だったのですが、今回プロジェクトを作る中で、 aws/codebuild/standard:2.0-1.10.0 といったような standard イメージしか選択出来ないようになっていました。

トラブルシューティングにあるくらいメジャーなエラーだったのですが、、、下記サンプルのように、buildspec.ymlの中でランタイムを指定することに気づかずハマりました。

Troubleshooting CodeBuild - AWS CodeBuild

phases:
  install:
    runtime-versions:
      docker: 18
  build:
    commands:
      - echo docker tag replace started
      - ...

プロジェクトの発火条件になるGitHubのイベント設定

CodeBuildはイベントタイプ+細かな条件を指定してプロジェクトを発火出来るのですが、下記の設定にたどりつくまで色々と試行錯誤しました。

これはCodePipelineだとブランチ指定した設定しか出来なかったのを自分の中で引きずっていたというのが大きいのですが、CodeBuildではもう少し細かい発火条件が出来るのを知るまでに少し時間がかかりました。

  • masterへのpush時にプロジェクトを発火させたい場合は下記のような設定をします。

f:id:nagais:20190617172341p:plain

  • tag push時にプロジェクトを発火させたい場合は下記のような設定をします。

f:id:nagais:20190617172401p:plain

デプロイフロー

参考までに現在のこのプロジェクトのデプロイフローは下記のようになっています。

① GithubのPRベースで開発

② masterマージ時に、code buildの[StgProject]が発火して下記要領でステージングへのデプロイを実施

  • Dockerイメージのビルド
  • ECRの該当リポジトリに対して、latest というタグでpush
  • ecs-deploy(shellで書かれたツール)により、対象サービスの更新を行う

③ ステージング環境にてデプロイした変更に問題がないことを確認

④ 本番デプロイするために現在のコードに対してtagを打つ

⑤githubのtag push時に、code buildの[prdProject]が発火して下記要領で本番へのデプロイを実施

  • ECRにあるイメージをタグ名リプレイスしてコピー
    • latestrelease, releaserelease-1gen,release-1genrelease-2gen
    • aws cliのecr batch-get-image, ecr put-image を利用
  • ecs-deploy(shellで書かれたツール)により、対象サービスの更新を行う

⑥ 本番にて動作確認のテストを行う

⑦ やばいとなった時は、 ecs-emergency-rollback.sh にて緊急ロールバック(release-1genイメージ使ったロールバック)を行う

コネヒトでは一緒に成長中のサービスを支えるために働く仲間を探しています。 少しでも興味もたれた方は、是非気軽にオフィスに遊びにきていただけるとうれしいです。

AWSやDockerを駆使してサービスの信頼性を向上させるエンジニア募集 - コネヒト株式会社のインフラエンジニア中途の求人 - Wantedly