Memo

メモ > サーバ > サービス: AWS > ECR+ECS+Codeシリーズで自動デプロイ: CodePipelineでイメージを自動デプロイ

■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::123456789012: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 が参照されていないようにも思う)

Advertisement