はじめに
ECSのローリングデプロイの手順をまとめました。
流れ
①CodeBuild作成
②CodeBuildで作成されたIAMロールに、ECRの権限付与
③buildspec.ymlの作成
④CodePipeline作成
⑤CodePipeline実行時のエラー集
デプロイまでのイメージ図
前提
-
下記の記事でFargateを作成済み
(記事を参考にしなくても、ECSが作成されていればよい。Fargateタイプ,EC2タイプどちらでも可)
Fargate+WordPress構築 ①~Fargate+WordPress構築 ③ -
GitHubにリポジトリ作成済み
-
もしくは、Laravelをfargateで構築した記事も参考になるかと思います。
CodeBuild作成
プロジェクトの設定
ソース
ソースプロバイダ:GitHub
リポジトリ:OAuth を使用して接続する
GitHubに接続
をクリックします
リポジトリの URL:[GitHubのURL]
環境
- 環境イメージ:
マネージド型イメージ
- オペレーティングシステム:
Amazon Linux 2
- ランタイム:
Standard
- イメージ:
最新のバージョン
- イメージのバージョン:
最新のバージョン
- 特権付与:
チェックする!
- 環境タイプ:
linux
- サービスロール:
新しいサービスロール
ロールは後で、修正します。
サービスロールのIAM
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Effect": "Allow",
"Resource": [
"arn:aws:logs:ap-northeast-1:xxxxxxxxxx:log-group:/aws/codebuild/*:*",
"arn:aws:logs:ap-northeast-1:xxxxxxxxxx:log-group:/aws/codebuild/*:*"
],
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
},
{
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::codepipeline-ap-northeast-1-*"
],
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectVersion",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
]
},
{
"Effect": "Allow",
"Action": [
"codebuild:CreateReportGroup",
"codebuild:CreateReport",
"codebuild:UpdateReport",
"codebuild:BatchPutTestCases",
"codebuild:BatchPutCodeCoverages"
],
"Resource": [
"arn:aws:codebuild:ap-northeast-1:xxxxxxxxxx:report-group/*"
]
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": "ssm:GetParameters",
"Resource": "arn:aws:ssm:*:xxxxxxxxxx:parameter/*"
}
]
}
追加設定
環境変数(コンテナ1つの場合)
名前 | 値 | 意味 |
---|---|---|
AWS_DEFAULT_REGION | ap-northeast-1 | awsリージョン |
IMAGE_REPO_NAME | wordpress | ECRのリポジトリ名 |
CONTAINER_NAME | sample-wordpress | タスク定義で設定したコンテナ名 |
IMAGE_TAG | prod | ECRに保管されたイメージタグ |
AWS_ACCOUNT_ID | 12345678 | AWSアカウントID |
環境変数(コンテナ2つの場合)
下記は、Laravelをfargateで構築した記事になります。
タスク定義で設定したコンテナが2つの場合、 下記のようにIMAGE_REPO_NAME
とCONTAINER_NAME
をそれぞれ2つ設定します。
1つのタスクにコンテナが2つある場合があるかと思います。
例えば、Laravelの場合、php-fpmのコンテナとnginxのコンテナを使用します。
名前 | 値 | 意味 |
---|---|---|
AWS_DEFAULT_REGION | ap-northeast-1 | awsリージョン |
IMAGE_REPO_NAME_1 | laravel-php | ECRのリポジトリ名 |
CONTAINER_NAME_1 | app | タスク定義で設定したコンテナ名 |
IMAGE_REPO_NAME_2 | laravel-nginx | ECRのリポジトリ名 |
CONTAINER_NAME_2 | nginx | タスク定義で設定したコンテナ名 |
IMAGE_TAG | prod | ECRに保管されたイメージタグ |
AWS_ACCOUNT_ID | 12345678 | AWSアカウントID |
Buildspec
ビルド仕様:buildspec ファイルを使用する
builspec.yml
ファイルは、後ほど作成して、GitHubにプッシュします。
バッチ設定
アーティファクト
アーティファクトなし
Docker イメージを Amazon ECR にプッシュする場合は、[アーティファクトなし] を選択できます。
キャッシュタイプ
:ローカル
DockerLayerCache
:チェックする
SourceCache
:チェックする
ローカルキャッシュについて
概要
DockerイメージのレイヤやGitのメタデータをCodeBuildの実行ホスト環境内にキャッシュする事で実行時間短縮が期待できます。
CodeBuildのローカルキャッシュには以下3つの種類が存在します。
-
DockerLayerCache
Buildフェーズなどで実行するDockerイメージのbuild/pull時の各レイヤをキャッシュしてイメージのビルドやDLを高速化します。
Dockerイメージのbuildやpullは主にBuildフェーズなどで行う内容の為、実行時間が短縮されるフェーズも同様です。 -
SourceCache
ビルドに使用するソースコードを保存してあるGitリポジトリからDLしたメタデータをキャッシュして、次回以降のDLに前回DL時からの差分のみをDLできるようになります。
GitリポジトリからDLしたメタデータをキャッシュするので、DOWNLOAD_SOURCEフェーズの実行時間が短縮されます。 -
CustomCache
buidspec.ymlのcache/pathsで指定したディレクトリ配下のパスに存在するファイル群がキャッシュされるようになります。
キャッシュについて、詳しくはこちら↓
ログ
CloudWatch Logsがチェックされていますので、そのままにします。
ビルド出力ログが CloudWatch にアップロードされます。
ビルドプロジェクトを作成
をクリックします。
CodeBuildで作成されたIAMロールに、ECRの権限付与
CodeBuildを実行する際、ロールにECRを操作する権限が必要になります。
IAMのロールから、codebuildで検索すると、先程名付けたプロジェクト名のポリシーが見つかります。
ECRの実行権限を追加します。
**上記の画像の`ここに加える!!`の位置に以下のコードを追加します。** {
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
],
"Resource": "*",
"Effect": "Allow"
},
buildspec.ymlの作成
GitHubにプッシュするファイルは以下の2つです。
Dockerfile
buildspec.yml
ファイルの置き場所は全てルートディレクトリ配下です。
./
├── Dockerfile
└── buildspec.yml
CodeBuildで必須ファイルは、以下の2つになります。
buildspec.yml
buildspec.ymlの作成
buildspec.ymlは、以下をコピペし、一部編集します。
buildspec.ymlファイルは、ソースコードのトップディレクトリに設置しておくことで、CodeBuildがbuildspec.ymlの内容を読み込み実行します。
DockerHubのアカウント作成
その前に、DockerHubのアカウント作成する必要があります。理由を含めて下記の記事を見てください
コンテナが1つの場合
version: 0.2
env:
parameter-store:
DOCKER_USER: DOCKER_HUB_ID
DOCKER_PASSWORD: DOCKER_HUB_PASSWORD
phases:
pre_build:
commands:
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- echo $DOCKER_PASSWORD | docker login -u $DOCKER_USER --password-stdin
build:
commands:
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- printf '[{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
imagedefinitions.jsonについて
CodeBuildステージで、ビルド情報がimagedefinitions.json
に記載され、デプロイステージで、その情報を元にタスク定義の新しいリビジョンを作成し、タスク定義を反映したタスクがデプロイされます
詳細はこちら↓
コンテナが2つの場合
version: 0.2
env:
parameter-store:
DOCKER_USER: DOCKER_HUB_ID
DOCKER_PASSWORD: DOCKER_HUB_PASSWORD
phases:
pre_build:
commands:
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- echo $DOCKER_PASSWORD | docker login -u $DOCKER_USER --password-stdin
build:
commands:
- docker build -t $IMAGE_REPO_NAME_1:$IMAGE_TAG -f docker/php/Dockerfile .
- docker build -t $IMAGE_REPO_NAME_2:$IMAGE_TAG -f docker/nginx/Dockerfile .
- docker tag $IMAGE_REPO_NAME_1:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME_1:$IMAGE_TAG
- docker tag $IMAGE_REPO_NAME_2:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME_2:$IMAGE_TAG
post_build:
commands:
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME_1:$IMAGE_TAG
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME_2:$IMAGE_TAG
- printf '[{"name":"%s","imageUri":"%s"},{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME_1 $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME_1:$IMAGE_TAG $CONTAINER_NAME_2 $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME_2:$IMAGE_TAG > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
buildspec.ymlのコマンド集
設定 | 意味 | 備考 |
---|---|---|
version | ビルドスペックのバージョン | 現時点2021/10/1の最新バージョンは0.2です。 |
phases | ビルドの各フェーズでどういったコマンドを実行するか定義する大本のキー | 設定可能なフェーズは install / pre_build / build / post_build の4つです。 |
install | インストールフェーズで実行するコマンドを定義 | ビルドする前に必要なライブラリなどをここで用意します。 |
pre_build | ビルド前に実行するコマンドを定義 | 設定ファイルを書き換えたりといったビルド前に実施するちょっとした処理内容を記述する箇所です。 |
build | ビルドする際に実行するコマンドを定義 | dockerのイメージをビルドしたり、タグ付けします。 |
post_build | ビルド後に実行するコマンドを定義 | 例えばGitHubリリースへアップロードするといったビルド後の処理を記述します。 |
artifacts | ビルド後に生成した成果物(アーティファクト)の設置場所 | type はzipのみ指定可能。 files でCodeBuildでビルドしたアーティファクトのパスを指定します。ワイルドカードも可能です。 |
そしてGitHubにプッシュしましょう。
CodePipeline作成
CodePipeline作成をクリックします。
パイプラインの設定
- パイプライン名:
適切に
- サービスロール:
新しいサービスロール
ソースステージを追加する
- ソースプロバイダー:
GitHub(バージョン2)
- 接続:
GitHubに接続する
をクリック
- 接続名:
リポジトリ名
GitHubに接続する
をクリック
Authorize AWS Connector for GitHub
をクリックすると前の画面に戻る
- GitHubアプリの
新しいアプリをインストールする
をクリックします
接続
をクリックします
- リポジトリ名:
選択肢として表示されます
- ブランチ名:
選択肢として表示されます
ビルドステージを追加する
- プロバイダーを構築する:
AWS CodeBuild
- プロジェクト名:
先程作成したCodeBuild名
- 環境変数:
無記入
- ビルドタイプ:
単一ビルド
デプロイステージを追加する
- デプロイプロバイダ:
Amazon ECS
- クラスター名:
ECSで作成済みのクラスター
- サービス:
ECSで作成済みのサービス
- イメージ定義ファイル - オプショナル:
無記入
イメージ定義ファイル(image definitions file)は、CodeBuildする時に作成されるので、書かなくても大丈夫です。
次に進み問題なければ、パイプライン作成する
をクリックします。
CodePipelineが動きます。
CodePipeline実行時のエラー
Code Buildステージエラー①
Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command:
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin
xxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com. Reason: exit status 1
原因は、ビルドを実行するロールにECRを操作する権限がない
ことが考えられます。
先程説明した手順通り、権限が付与されているか確認しましょう。
そして、ECRの実行権限のポリシーを追加する必要があります。
CodePipelineを再実行
してみましょう。
CodePipelineの変更をリリースする
をクリックすると、再実行されます。
それでもだめなら、アクセスキーとシークレットキーが正しく設定されているか確認します
$ cat ~/.aws/credentials
Code Buildステージエラー②
Phase context status code: YAML_FILE_ERROR Message: YAML file does not exist
buildspec.yml
がないと言われていますので、buildspec.yml
を加え、GitHubにプッシュすると解決します。
Code Buildステージエラー③
Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: docker push xxxxxxxxx.dkr.ecr.p-northeast-1.amazonaws.com/wordpress:latest. Reason: exit status 1
p-northeast-1
となっております。正しくは、ap-northeast-1
です。
ECS Deployステージエラー①
The AWS ECS container wordpres does not exist
コンテナ名が間違えている可能性が大です。この場合、正しくはwordpress
です。
ECS Deployステージエラー②
imagedefinitions.json
が無効なフォーマットになっています。
The image definition file imagedefinitions.json contains invalid JSON format
buildspec.yml
に以下の記載がない場合に、このエラーになります。
こちらの工程ですね
コンテナが1つの場合
printf '[{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG > imagedefinitions.json
コンテナが2つの場合
printf '[{"name":"%s","imageUri":"%s"},{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME_1 $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME_1:$IMAGE_TAG $CONTAINER_NAME_2 $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME_2:$IMAGE_TAG > imagedefinitions.json
デプロイまで成功すると、ECSのタスクが実行されているはずです!
1回のビルドで2つのサービスのタスクをデプロイする
1回のビルドで、2つのサービス(2つのタスク定義をデプロイ)にそれぞれタスクをデプロイしたい場合があるかと思います。
その場合、Deployステージにアクションを追加
クリックし、先程設定したものとは別に、デプロイしたいサービスを指定します。
また、アクション名
と変数の名前空間
は、先程作成したDeployと名前を被らないように設定すると、完了です。
ビルド完了後、各サービスに設定されたタスクがそれぞれデプロイされます。
CodeBuildの環境を編集する場合
codebuildの環境を修正し、更新するとき、以下のエラーになります。
codebuild.amazonaws.com did not create the default version (v4) of the CodeBuildBasePolicy-xxxxxxx-ap-northeast-1 policy that is attached to the codebuild-xxxxxxx-role role.
To continue, detach the policy from any other identities and then delete the policy and the role.
「AWS CodeBuild にこのサービスロールの編集を許可し、このビルドプロジェクトでの使用を可能にする」
のチェックを外すと、更新できます。
DockerHubアカウントを設定
Code Buildステージエラーで以下のエラーが起きることがあるため、DockerHubの無料プランに入ることをおすすめします。
toomanyrequests: You have reached your pull rate limit.
You may increase the limit by authenticating and upgrading:
https://www.docker.com/increase-rate-limit
下記の記事通りに行ってください。
CodePipelineの通知設定
Slackに通知設定は下記の記事通りにするとできます。
CloudFrontのキャッシュを削除するステージ追加
CloudFrontを使用している場合、デプロイごとにCloudFrontのキャッシュを削除したいケースがあるかと思います。
以下の記事通り行うと実現できます。
参考記事
- CodePipelineでECSにデプロイする(Ver.rolling update)
- CodeBuildでECRビルドエラーから得た4つの知見
- AWS CodeCommitからCodePipelineでECRへDockerイメージをビルド
- チュートリアルを通してCodeBuildを理解する