■ECR+ECS+Codeシリーズで自動デプロイ: CodePipelineでイメージを自動デプロイ
■タスク定義ファイル作成
※この「タスク定義ファイル作成」という手順自体、丸ごと不要だったりするか
アプリケーションのリポジトリに taskdef.json を含めても、参照されている気配が無い
そもそもこの内容は、プログラムからでは無くAWSコンソールから設定すべき(アプリケーション開発者に触らせるべきものではない)もののような
環境変数の値も、リポジトリ内のコードに含めると変数化している意味が無いような
アプリケーションのリポジトリに含めることができたとして、緊急でAWSコンソールから編集した場合のことを考える必要がある
※以下によると「AWSコンソールから管理できるが、必要ならコードでも管理できる。それぞれ一長一短」みたい。ひとまず、タスク定義ファイルは作成しない方針でいいような
ECSタスク定義をコンソールから作って後悔した後、コード管理するため最速でJSON登録可能にする超愚直な方法 | DevelopersIO
https://dev.classmethod.jp/articles/ecs-task-definition-to-json/
※引き続き要検証
★Fargateで再検証するにあたって、ひとまずこの「タスク定義ファイル作成」という手順を丸ごと飛ばして以降の検証を進めている
Elastic Container Service → Amazon ECS → タスク定義 → Container-Test-Task
最新のリビジョンのページ内で「JSON」タブに表示される内容をもとに、taskdef.json というファイルを作る
その際、イメージの指定部分を「"image": "<IMAGE1_NAME>",」に調整し、さらに以下の項目を削除する
・taskDefinitionArn
・revision
・status
・requiresAttributes
・compatibilities
FargateではなくEC2の場合、動的ポートマッピングのために、hostPortは「0」にしておく(もとからそうなっているなら問題無い)
"portMappings": [
{
"hostPort": 80,
↓
"hostPort": 0,
describe で出力した ECS タスク定義をさくっと登録可能な形に整形する | Stuck inside
https://blog.msysh.me/posts/2020/12/transform_task_definition_by_describe_to_be_able_to_register.htm...
describe-task-definitionで取得したJSONはそのままではregister-task-definitionで登録できないお話 | DevelopersIO
https://dev.classmethod.jp/articles/describe-task-definition-to-register-task-definition/
内容は異なるが、以下は参考までにファイル内容の具体例
sbcntr-resources/taskdef.json at main - uma-arai/sbcntr-resources
https://github.com/uma-arai/sbcntr-resources/blob/main/cicd/taskdef.json
このファイルはコミット&プッシュし、アプリケーションのリポジトリに含めておく
リポジトリ直下に配置するのが一般的かもしれないが、今回は以下に配置してみる(Dockerに関するファイルは docker フォルダにまとめる)
値がnullの項目は削除できるかもしれないが、そのままでも支障はないはず
参考までに、以下は実際に登録した内容
※まだ不要な情報を削れるかもしれない
docker\ecr_php81_apache\taskdef.json
{
"containerDefinitions": [
{
"name": "ecr_php81_apache",
"image": "<IMAGE1_NAME>",
"cpu": 0,
"portMappings": [
{
"name": "ecr_php81_apache-80-tcp",
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp",
"appProtocol": "http"
}
],
"essential": true,
"environment": [],
"environmentFiles": [],
"mountPoints": [],
"volumesFrom": [],
"ulimits": [],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/Container-Test-Task",
"awslogs-create-group": "true",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
}
}
],
"family": "Container-Test-Task",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"networkMode": "awsvpc",
"volumes": [],
"placementConstraints": [],
"requiresCompatibilities": [
"FARGATE"
],
"cpu": "1024",
"memory": "3072",
"runtimePlatform": {
"cpuArchitecture": "X86_64",
"operatingSystemFamily": "LINUX"
},
"registeredAt": "2023-05-12T05:05:28.498Z",
"registeredBy": "arn:aws:iam::949004901725:user/refirio-user",
"tags": []
}
■CodePipelineを作成
※「Container-Test」というパイプライン名は、実際は案件名と環境をもとに付けると良さそう
CodePipeline → パイプライン → パイプラインを作成する
パイプラインの設定を選択する
パイプライン名: Container-Test
サービスロール: 新しいサービスロール(以前作成したものがあれば「既存のサービスロール」を選択する)
ロール名: AWSCodePipelineServiceRole-ap-northeast-1-Container-Test(パイプライン名を入力すると、自動で入力された / 以前作成したものがあれば選択する)
「次に」ボタンをクリック
ソースステージを追加する
ソースプロバイダー: Bitbucket
接続: Bitbucketに接続(パイプラインを再作成する場合、以前作成したものを選択した)
接続名: Container-Test ※この接続名は、本番・検収・開発で共通して使う想定で良さそう
「Bitbucketに接続」ボタンをクリック
Bitbucketへのログインを求められるのでログイン
以下の許可を求められるので許可する
AWS CodeStar is requesting access to the following:
・アカウント情報の読み取り
・あなたの所属するチーム情報を読み取る
「接続」ボタンをクリック
Bitbucketアプリ: 「新しいアプリをインストールする」ボタンをクリック
アクセスの許可を求められるので許可
以下の許可を求められるので許可する
AWS CodeStar requests access
・アカウント情報の読み取り
・リポジトリとそのプルリクエストを確認する
・自分のリポジトリの管理
・リポジトリを確認し修正する
Authorize for workspace: refirio(対象のリポジトリがあるプロジェクトを選択する)
「アクセスを許可する」ボタンをクリック
「接続」ボタンをクリック
もとの画面に戻って「接続する準備が完了しました」と表示されることを確認する
リポジトリ名: refirio/ecr_php81_apache
ブランチ名: master
検出オプションを変更する: 「ソースコードの変更時にパイプラインを開始する」にチェックを入れる
出力アーティファクト形式: CodePipeline のデフォルト
「次に」ボタンをクリック
ビルドステージを追加する
プロバイダーを構築する: AWSCodeBuild
リージョン: アジアパシフィック(東京)
プロジェクト名: Container-Test(選択対象に表示されるので選択する)
ビルドタイプ: 単一ビルド
「次に」ボタンをクリック
デプロイステージを追加する
デプロイプロバイダー: Amazon ECS
リージョン: アジアパシフィック(東京)
クラスター名: Container-Test-Cluster
サービス名: Container-Test-Service
イメージ定義ファイル: imagedefinitions.json
「次に」ボタンをクリック
レビュー
「パイプラインを作成する」ボタンをクリック
「成功」と表示されるのを確認する
引き続き、各処理がそれぞれ実行されていくのを確認する
「Source」はすぐに「成功しました」となった
その2〜3分後くらいに、「Build」が「成功しました」となった
その4〜5分後くらいに、「Deploy」が「成功しました」となった
合計10分ほどでデプロイが完了したが、本番反映自体は3〜4分ほどでされていた(以降は不要なタスクやターゲットグループの削除)
どんなにイメージのビルド時間を短くしても、デプロイ完了までは5〜6分程度かかるかもしれない(デプロイ時間については、後述の「デプロイ時間の短縮」も参照)
以降は、プログラムの修正をmasterブランチにコミット&プッシュするだけで、処理が開始されデプロイされる
(developブランチの内容をmasterにマージ&プッシュとしても、同様に処理される)
プッシュすると、すぐにCodePipelineの「Source」処理が始まる
なお、本番反映時に30秒程度、新旧が混在する状態が続く
これは恐らく標準デプロイ(ローリングアップデート)の仕様で、避けるためにはBlue/Greenデプロイにする必要があると思われる
■問題の修正1
「Build」で「失敗しました」となった
詳細から「実行の詳細へのリンク」を表示すると、以下が表示される
[Container] 2022/02/02 10:37:42 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: docker image push ${REPOSITORY_URI}:${IMAGE_TAG}. Reason: exit status 1
イメージのプッシュができなかったみたい
すでに指定のタグ名でイメージがビルド&プッシュ済みになっているからだった
(タグの上書きを禁止している)
リポジトリでプログラムを微調整してコミット&プッシュしてみる
パイプラインが「進行中」になった
CodeBuildの画面でも「進行中」になっているので、詳細を見つつ見守る
今度はビルドが成功した
■問題の修正3
「Deploy」で「失敗しました」となった
詳細を表示すると、以下が表示される
無効なアクション設定
The image definition file imagedefinitions.json contains invalid JSON format
docker/buildspec.yml に記述ミスがあったので修正して再実行したところ、デプロイも進むようになった
「詳細」をクリックすると Container-Test-Service に遷移した
「デプロイメント」を確認すると、すぐに完了していた
「イベント」を確認すると「service Container-Test-Service was unable to place a task because no container instance met all of its requirements. The closest matching container-instance 653f223309c54e67b32b21e6ea8e8408 is already using a port required by your task. For more information, see the Troubleshooting section.」と表示されていた
しばらくして「タスク定義」と「サービス」を確認すると、更新されていた
ただし、いつまで待っても「タスク」が置き換わらなかった
■問題の修正4
15分以上待っても「タスク」が置き換わらなかったので「停止して中止」にした
CodePipelineなしでも発生していたものと同じ現象だったので、動的ポートマッピングを設定すると完了できた
動的ポートマッピングに対応した taskdef.json をコミットしてもデプロイできない場合、
あらかじめAWSコンソール上で動的ポートマッピングに対応させてデプロイを行っておいてから再度試す
(デプロイ前の時点で動的ポートマッピングに対応している必要があるのかもしれない。そもそも今回は taskdef.json が参照されていないようにも思う)