AWS CodePipelineを使用してgit pushでAmazon ECSをデプロイする | SEEDS Creators' Blog | 株式会社シーズ

AWS CodePipelineを使用してgit pushでAmazon ECSをデプロイする

クラウド事業部エンジニアの川勝です。

今回は、
「AWS CDKを(途中まで)使用してAmazon ECSの Blue/Greenデプロイ環境を構築する」
で作成したデプロイ環境をAWS CodePipelineを使用してgit pushしたらデプロイされるようにしていきます。

全体の流れ

  1. ローカル環境でgithubへgit pushする
  2. githubのwebhookでAWSへ通知、AWS CodePipelineが実行される。
  3. SourceセクションでGithubからリソースを取得
  4. BuildセクションでAWS CodeBuildを使用してdockerコンテナをbuild、Amazon ECRへpushする。
  5. DeployセクションでAWS CodeDeployを実行する
  6. デプロイ完了!

Dockerコンテナ設定

Amazon ECRのレジストリは作成されているものとします。

アプリケーション
取り敢えず「Hello, world!」と出力するだけのもの
https://github.com/kawakattsun/codepipeline-sample/blob/master/main.go

docker buid
こちらのDockerfileをbuildするのみ
https://github.com/kawakattsun/codepipeline-sample/blob/master/Dockerfile
ちなみにつかってみたかったのでdistrolessを使いました。alpineよりサイズは大きくなりますがアプリケーションの実行に不要なものが一切入ってないそうです。

あとbuildspec.yml、appspec.yaml、taskdef.jsonを用意する必要がありますが、詳細は後述いたします。

それではAWS CodePipeline作成してきます。

Source

AWS CodePipelieの画面の「パイプラインを作成する」からはじめていきます。

パイプライン名をきめて、サービスロールは「新らしいサービスロール」を選択すると自動で作成されます。
ちなみにAWS CodePipelineの実行ロール以外にwebhookを実行?受ける?ロールも作成されるようです。

次にソースプロバイダーを選択します。
Githubへのpushで実行したいのでGithubを選択します。

選択すると「Githubに接続する」というボタンが表示されるのでクリックします。
そうするとGithub認証の画面に進むので許可して認証を完了させます。

Gihutbに接続する

Githubの認証が成功すると、認証したアカウントに権限のあるリポジトリが選択できます。
ブランチはpushされたときにwebhookを動作させたいブランチを選択します。
検出オプションはデフォルトのGithubウェブフックで進めます。

ちなみGithubに認証したアカウントがGithubのwebhookに対する権限がないと作成が完了できません。
AWS CodePipelineの作成完了時にGithubのwebhookを作成しに行くようです。
またGithubのwebhookは同一リポジトリに複数AWS CodePipelineを設定すると、その数だけ作成されるようです。
つまりGithub的にはブランチは関係なくpushがあればwebhookが動作し、
受けたAWS側でブランチに応じたAWS CodePipelineを実行する、、という動作になるようです。
(test, staging, productionとか複数あると若干無駄にwebhook動く感じがしますね….)

AWS CodePipelineの作成が完了するとGithubにwebhookが登録される

Build

次はビルドステージの設定です。
プロバイダーはAWS CodeBuildを選択し、
今回はAWS CodeBuildは新規に作成しますので「プロジェクトを作成する」を選択します。 

プロジェクトを作成する

ちなみに既存のプロジェクトを選択できますが、AWS CodeBuild側からはじめるとソースプロバイダーにAWS CodePipelineが選択できません。
したがってここから作成する必要があるようです。

AWS CodeBuildの作成画面に遷移するので進めていきます。

プロジェクト名

ビルドする環境(コンテナ)も設定です。
Amazon Linux 2とUbuntuから選択できましたがAmazon Linux 2 で進めます。
あとのイメージとかバージョンは最新のものを選択しました。
「特権付与」に関しては今回はdocker buildを実行する必要がありますのでチェックを入れておきます。

サービスロールは新しいサービスロールを選択しておけば自動で作成されます。
ただ、ビルドで実行するコマンドにAmazon ECRへのpushを行っていますのでその権限が必要です。
作成が完了してからIAMから編集して権限を追加します。

とりあえずecrの全部許可でテスト。
プロダクション運用では適切な権限を設定してください。

{
  "Effect": "Allow",
  "Action": [
    "ecr:*"
  ],
  "Resource": [
    "*"
  ]
}

続いてBuildSpecの設定です。
ファイルを使用する場合、リポジトリに含めておくことで使用できます。
CloudWatch Logsのチェックは入れておかないとビルドの実行ログが見れなくなるのでほぼ必須です。
グループ名等は未入力で問題ありません。

buildspec.yml

buildspec.ymlの中身を少し説明しておきます。
https://github.com/kawakattsun/codepipeline-sample/blob/master/buildspec.yml
やっていることは

  • Amazon ECRの認証情報でdocker login
  • gitのcommitハッシュからdocker image tagを生成
  • docker buildして tagづけ
  • docker push
  • imageDetail.jsonとtaskdef.jsonの生成

です。
最後の imageDetail.jsonとtaskdef.jsonの生成 ですが、これは後続のAWS CodeDeployでAmazon ECS FargateのBlue/Greenを実行するために必要なファイルです。

imageDetail.jsonはこのファイル名でImageURIというキーに使用するイメージのURIを値に作成すると、taskdef.jsonのプレイスフォルダ(<IMAGE1_NAME>)を実行時に置き換えてくれるというものです。
AWS CodeBuildで実行した値をAWS CodeDeployにわたすために必要な感じですね。
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html
こちらがドキュメントですが
– Amazon ECS 標準デプロイアクションの imagedefinitions.json ファイル
– Amazon ECS Blue/Green デプロイアクション用 imageDetail.json ファイル
の後者のほうです。
jsonの構造とか微妙に違うので注意です。

taskdef.jsonは今回はtaskdef.templateというのを用意しておいて、
envsubstで環境変数の埋め込みをしています。
https://github.com/kawakattsun/codepipeline-sample/blob/master/taskdef.template
環境変数から渡すサンプルとしてAWS_ACCOUNT_IDを設定しています。
IMAGE1_NAMEは置換してくれるのですが、他の値は置換できなさそうなでこの手法を使用しています。
taskdef自体はパラメータストアから読み込みができたりもしますので、そちらを使ってもいいかと思います。
(そちらのほうが正攻法かも?)

buildspec.ymlの最後のartifactsにファイルを明記しておくと、そのファイルだけがビルドステージの出力としてS3に保存され、そのファイルが次のデプロイステージで使用できます。

artifacts:
  files:
    - imageDetail.json
    - appspec.yaml
    - taskdef.json

artifactsを指定していない場合はソースすべてがs3に保存されるようですが容量制限(3GB)があるので必要なファイルだけ指定したほうがいいでしょう。
ビルドのためにパケージをDLしたりしていると制限が超える可能性があります。
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/limits.html

appspec.yamlは前回の記事ではコンソールで直接入力していた値です。
https://github.com/kawakattsun/codepipeline-sample/blob/master/appspec.yaml
ことなるのはTaskDefinitionを<TASK_DEFINITION>というプレイスホルダにしておくことで、自動であたらしいタスク定義のリビジョンARNが使用されます。
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-ecs-ecr-codedeploy.html#tutorials-ecs-ecr-codedeploy-taskdefinition

環境変数

AWS CodePipeline上でAWS CodeBuildで使用する環境変数が設定できます。
今回はAWS_ACCOUNT_IDを設定していました。
実運用にあたっては一点注意が必要で設定できる文字数に制限がありようです。
大量の環境変数を設定していると以下のエラーに遭遇しました。

ValidationException
1 validation error detected: Value at 'pipeline.stages.2.member.actions.1.member.configuration' failed to satisfy constraint: Map value must satisfy constraint: [Member must have length less than or equal to 1000, Member must have length greater than or equal to 1]

制限をみていると以下の文字数制限でしょうか。
おそらく最終的に内部で持っているjsonの文字数に制限がありそうでした。
やはりパラメータストアを活用するなどしたほうが正攻法そうです。

アクション設定値の最大長 (たとえば、CodeCommit アクション設定の RepositoryName 設定の値は 1000 文字未満にする必要があります。

作成完了するとAWS CodePipelineにもどります。

AWS CodeBuild作成完了!

Deploy

続いてDeployステージです。
こちらは前回作成したAWS CodeDeployのアプリケーションを選択しましょう。
さきにAWS CodePipelineから作っていた場合はスキップすることも可能です。

デプロイを設定しなかった場合

アクションプロバイダーはAWS CodeDeployではなくAmazon ECS(ブルー/グリーン)を選択します。
次に入力アーティファクトではBuildArtifactを選択します。
これでAWS CodeBuildの最後でS3に保存したファイルが読み込まれます。
タスク定義とAppSpecファイルのファイル名はデフォルト値のママにしていますが、例えばファイルの配置を任意のディレクトリ配下にしている場合などは変更可能です。
タスク定義のプレースホルダー文字は<IMAGE1_NAME>を入力します。
これはちょっとドキュメントみても任意の名称でいいのかどうかがはっきりしませんでした…
以下を参照しています。
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-ecs-ecr-codedeploy.html#tutorials-ecs-ecr-codedeploy-taskdefinition

以上で設定は完了です!
あとはローカルからgit commitしてpushすれば実行されます!

すべての実行が完了した様子。

slackを見てみるととちゃんと通知がきていますね!
(失敗してるのもいっぱい出てますが…)

slack通知の様子

まとめ

AWS CodePipelineでgit pushでデプロイする方法でした。
ちょっと構築完了までにはドキュメントを読み漁ったりするのが大変だったり、
(AWS CodeBuild, AWS CodeDeploy, AWS CodePipelineのそれぞれのチュートリアルをみつつ完全マッチするものがなかった…)
AWS CodeBuildの用意されている環境変数でAWS CodePipelineから実行すると使えないもの(CODEBUIDL_WEBHOOKプレフィック関係。こちら参照)もあったりしましたが、
1つのCI環境でいろいろやるのではなく、複数のサービスを組み合わせてつなげて実行できるのは使いこなすと強力なツールだと思いました。
例えばビルドステージで複数のbuildを並列で実行したり、デプロイがおわってからビルドステージであと処理したり、、、とか組み合わせしだいで自由度が高そうです。
詳しくは一度チュートリアルをみると理解が深まると思います。

またいい使い方ができたときにはブログにしたいと思います。

以上、川勝でした。