AWSのCodePipelineを使ってECSサービスにデプロイをする為に行った調査の雑多メモ

このブログis何?

  • CodePipeline や CodeBuild などを使って ECS のデプロイをしたい
  • その為にIAMを最小権限に絞ったりビルド用のCodeBuildを用意したりが必要なので下調べをしていった
  • ざっくりこの辺読めばよかったみたいなのをまとめたので、参考になれば。

CI環境からECR へ プッシュ をする事を実現する為の最小限のIAMポリシー

大体こういう時はクラスメソッド様の記事を漁れば出てくる。

...という事であった。ありがたい、流石のクラスメソッド様。

dev.classmethod.jp

以下のJSONに対してリソースを指定してあげる事で、特定のECRへのプッシュだけを実現する最小限のIAMポリシーが実現出来ます。 これだけだと学びが小さいのでそれぞれどういうものかを調べてみました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "GetAuthorizationToken",
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken"
            ],
            "Resource": "*"
        },
        {
            "Sid": "PushImageOnly",
            "Effect": "Allow",
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:PutImage"
            ],
            "Resource": "arn:aws:ecr:ap-northeast-1:<AWSアカウントID>:repository/<リポジトリ名>"
        }
    ]
}

GetAuthorizationToken って何?

ecr:GetAuthorizationToken は Resource: * で設定

認証Tokenを取得する権限 ってのが直訳から伝わってくる感じだけど、何の為にTokenが必要なのか。

今回のECRは プライベートリポジトリ だけど、プライベートリポジトリへアクセスするためにはTokenが必要って事なのか?と当たりをつけてAWSのドキュメントを見ていった。 大体以下のドキュメントをたどっていけばなんとなく把握出来た。

docs.aws.amazon.com

ECRへイメージをプッシュするには S3などの他のサービスのように IAM の ACCESS_KEY / SECRET で認証する事はできなくて、 AWSユーザーとして ECRへの Push する為のTokenを発行する、って手順を踏む必要がある。

その認証トークンを取得するには IAM に ecr:GetAuthorizationToken の権限が付与されていないとできなくて、認証トークン取得時には、 ECRのリポジトリ名は明らかにしてないので、 Resource: * で無いだめって感じっぽい。

認証トークン
クライアントがイメージをプッシュおよびプルするには、AWS ユーザーとして Amazon ECR レジストリに対して認証する必要があります。詳細については、「プライベートレジストリの認証」を参照してください。
認可トークンの許可スコープは、認証トークンの取得に使用される IAM プリンシパルの許可スコープと一致します。
認証トークンは、IAM プリンシパルからアクセス可能で 12 時間有効な Amazon ECR レジストリにアクセスするために使用されます。
認可トークンを取得するには、GetAuthorizationToken API オペレーションを使用して、ユーザー名 AWS とエンコードされたパスワードを含む base64 エンコード認可トークンを取得します。
AWS CLI の get-login-password コマンドでは、よりシンプルな方法で、認証トークンを取得し、デコードして、認証のために docker login コマンドにパイプできます。

PutImage以外の権限

PutImageが無いと、ECRへイメージをPushするのはできないというのは直感的に分かる。
でも、その他にも最小権限として、 次の4つが必要なのはなぜだろう?って思ったので調べてみる。

  • "ecr:BatchCheckLayerAvailability"
  • "ecr:InitiateLayerUpload"
  • "ecr:UploadLayerPart"
  • "ecr:CompleteLayerUpload"

で、調べた結果を以下にざっくりまとめてみた。大体、全部 AWSのSDKなどが管理しているので開発側はそこまで意識しなくても良いんだけど、内部的に権限が必要、という話だった

  • ecr:BatchCheckLayerAvailability
    • リポジトリに設定されている 1つ以上のイメージレイヤーに対してそれが有効なのかどうかチェックする権限を付与する
    • これがあることで、イメージをリポジトリにプッシュする際に、以前にアップロードされた事があればスキップするようなことも出来る
  • ecr:InitiateLayerUpload
    • ECRにイメージレイヤのアップロードイベントの開始を知らせる権限
    • 通知はイメージレイヤーがすでにアップロードされている場合は行われない事になっていて、すでにアップロードされているかどうかは ecr:BatchCheckLayerAvailability によって、判断される
  • ecr:UploadLayerPart
    • 各イメージレイヤーをアップロードする権限
  • ecr:CompleteLayerUpload
    • 各イメージレイヤー単位でアップロードが完了したことをECRに通知する権限

イメージレイヤーとかは以下のQiitaとかを参考にして理解を深めた。

qiita.com

PutImage権限は、あくまで新規タグを発行したり既存タグを更新するような権限で、イメージをアップロードする為に各イメージレイヤに対してアップロード権限を付与するような仕組みと捉える事で理解が深まった。

CodeBuildからECRへPushする際に参考にしたもの

codebuild docer ecr ぐらいで雑に調べた所、一番最初にあった公式ドキュメントを読む事で大体基本のサンプルが載っててよかった。

docs.aws.amazon.com

buildspec.ymlの環境変数の使い方みたいなのも載ってて参考になりました。

ちなみに、 buildspec.yml というのが CodeBuildにおいて ビルド仕様ファイル という CodeBuild がビルドを実行するために使用するオペレーティングシステム、プログラミング言語ランタイム、およびツールの組み合わせ を記述するものです。
通例 buildspec.yml という名前でルートディレクトリに保存をします。

CodePipelineから ECS へデプロイをする際に参考にしたもの

docs.aws.amazon.com

これもAWSのドキュメントを読みました。

ざっくりまとめると、先程書いてた Docker イメージのビルド => ECRへPush という CodeBuild から imagedefinition.json というファイルを生成して CodeBuildの出力ソースとします。

CodePipeline を作って、 ソースとして GitHubなどを main ブランチへのPushイベントを指定、 その次のビルドステージとして 先程のCodeBuild を指定、 最後に デプロイステージとして AWSが用意したECSのデプロイ(これはウィザードを使って、くCluster , Service ) を指定する という事をすると、ECSの指定のサービスにデプロイが行われます。

imagedefinition.json って何? ってのが気がかりだったのですが、以下のドキュメントを見ると把握出来ました。

docs.aws.amazon.com

こんな感じで コンテナ名 と ECRのイメージのURI を指定します。

[
  {
    "name": "sample-app",
    "imageUri": "11111EXAMPLE.dkr.ecr.us-west-2.amazonaws.com/ecs-repo:latest"
  }
]

この imagedefinition.json をビルドステージの成果物(出力)として作成して、CodePipeline で 次のデプロイステージのECSのデプロイ定義にわたす事で 指定のECSのサービスが依存してる タスク定義のリビジョンが新規に作成されて、ECSのデプロイが行われる事が確認出来ました。

imagedefinition.json はデフォルト名なので、 imagedefinition_hoge.json みたいなのをビルドステージで生成して、ECSのデプロイ定義で imagedefinition_hoge.json を読み込むように設定すると別のファイルを読み込ませたりすることも可能だそうです。

まとめ

これまで GitHub Actions でばっかりやってたんですけど、初めてCodePipelineを使ってみました。

ECSのデプロイ体験が良すぎて、これは良いものですね・・!

GitHub Actions でのECSのデプロイでは、ECSのタスク定義のJSONをGit管理していて イメージをビルド&プッシュ して タスク定義の新規リビジョンを作って ECSサービスにそのリビジョンを紐付ける、ということをやってました。

反面、CodePipelineでのデプロイでは、コンテナ名とイメージURIだけを作るだけで後はよしなにやってくれます。アプリケーションエンジニアとしては、要は最新のデプロイ定義がECSのサービスにデプロイされればいいわけでタスク定義については管理しなくてもな〜、ってのは思ってたりしたのでそこがAWSに一任出来るのは アプリケーション・インフラの分界点として出来るの良いな、って感じがしました。

銀の弾丸か?というとそうでもなくってマネジメントコンソールでみただけだと、Code Pipeline における ソースステージのトリガー( GitHubのmainブランチのPushがあれば...みたいなの )はやっぱり、GitHub Actionsの方が豊富だな、って思ったりします。

聞いた話だと、GitHub のリリースタグによってPipelineを発火させるには Lambda だったりなにか別ので リリースタグの検知 + Pipelineをキックするのをやらせる必要がある、などがあるそうです。

後は、 CodeBuild の runtime version とかは、Node.js は まだ 14 までしか対応してなかったり、みたいなのもありますかね。この辺はサードパーティが充実してる GitHubActionsとかが便利そうな点だな、とは思いました。

docs.aws.amazon.com

この辺を考慮すると GitHub 側で ECRへのプッシュをやる => CodePipeline で ECRのプッシュを検知して imagedefinition.json を作る => ECS へデプロイ というような各得意な所を分けちゃってもいいかな?って思ったりしました。

でも、 imagedefinition.json を作る CodeBuild の buildspec.yml は アプリケーション側で持ってたいよな・・? とか思ったり。。。ここらは詳しい人教えて下さい・・!( まあ、コンテナ名が複数無い場合とかは構成として取れそうかもしれない)