Amazon ECRのコンテナイメージを更新した一方でAmazon ECSのサービスを更新しなかった場合、タスク定義の設定によっては新規タスク起動時にCannotPullContainerErrorで失敗する
CannotPullContainerErrorでAmazon ECS新規タスク起動に失敗する
おのやんです。
みなさん、Amazon ECS(以下、ECS)が新規タスクを起動しようとしたときに、CannotPullContainerError
で新規タスク起動に失敗したことはありますか?私はあります。
原因はタイトルの通りで、 Amazon ECR(以下、ECR)のコンテナイメージを更新した一方でECSのサービスを更新しないため なのですが、ネットの検索でも意外と引っかからなかったので、ブログに情報をまとめてみます。
前提条件
今回のECSの環境情報は以下の通りです。過去のさまざまな要件を経て、このような運用に落ち着いています。
- 1つのECSサービスで、1つのECSタスクを管理している
- ECSサービスのDesiredCountは1に設定している
- 頻繁なタスク定義更新は控える
- タスク定義で参照するECRイメージのタグは、常に
001
- ECRにコンテナイメージをプッシュする場合は、最新のコンテナイメージに
001
のタグをつけている(いわゆるlatest運用) - CI/CDでコンテナイメージpushとAWS Serverless Application Model (SAM) デプロイを自動化している
- ECSサービスの更新は、手動で実行している
CannotPullContainerErrorが発生した経緯
開発環境で作業を行う際は、コードリポジトリにGitのコミットをプッシュしてでCI/CDを実行しています。この際、コンテナイメージビルド & pushは必ず実行されるようになっていました。そのため、ECSで実行されるプログラム内容が同じであってもECR上のコンテナイメージはコミットがpushされるたびに別のものになっていました。コードの内容はまったく同じであるということで、ECSサービスはコンテナイメージpush後に更新していませんでした。
すなわち、ECSタスク定義で参照しているECRコンテナイメージと、ECR上の最新のコンテナイメージで、実体が違う状態でした。
この状態で、何らかの原因でECSタスクが停止した場合、ECSサービスのDesiredCount:1
をおかげでタスクを自動で起動してくれるのですが、以下のエラーが発生してタスクの再起動に延々と失敗していました。
CannotPullContainerError: pull image manifest has been retried 1 time(s): failed to resolve ref xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/aws-test-ecr:001@sha256:yyyyyyyyyyyyyyyy: xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/aws-test-ecr:001@sha256:yyyyyyyyyyyyyyyy: not found
ログの内容を読んでみると、ECSタスク定義にて特定のダイジェストを参照している一方で、そのダイジェストを持つコンテナイメージが存在しない、ということがわかります。ECRに保存するコンテナイメージのタグは一緒(いわゆるlatest運用)なので、「参照しているARNは一緒だが中身が違う」という状態になっています。
こちらの挙動は、次の記事でも詳しく紹介されています。
解決策
ECSサービスを更新すれば、コンテナイメージのダイジェストが最新のECRコンテナイメージのものになるため、ECSタスク自動起動の際にエラーが出なくなるはずです。
そもそもECRのコンテナイメージは、可能であれば、latest運用ではなくコンテナイメージごとにIDなど付ける運用方式が望ましいです。
今回のエラーは、仮にlatest運用している状態であってもECSのサービス更新自動化により防げるかと思いますので、CI/CDでECSサービス時更新の処理追加も現実的な解決策になります。
ただ、CI/CDで毎回ECSサービスを更新していると、ECSへの構成変更が頻繁に発生することになるため、AWS Config(以下、Config)の課金が増加するなどの可能性があります。運用コスト・人的ミス発生時の影響度合いと、Configなどによる料金増加のバランスを考えながら、CI/CDを設定しましょう。
タスク定義の設定でもこの問題は回避できる
今回の前提条件として、「頻繁なタスク定義の更新は抑える」という要件がありましたので、メインの解決策としては扱わなかったのですが、本来であればイメージダイジェスト関係の挙動は、タスク定義のversionConsistency
というパラメータで制御できます。
タスク定義を編集できる状態であれば、"versionConsistency": "disabled"
のキーバリューを追加しましょう。この項目を追加していれば、タスク起動時にイメージダイジェストの変換を行わないので、デプロイを実施せずとも大丈夫です。
{
"family": "nginx-samples",
"containerDefinitions": [
{
+ "versionConsistency": "disabled",
"name": "nginx",
latest運用しているECRコンテナイメージを更新したら、要件によってはECRサービスも更新する必要があるかも
タスクのオーケストレーション動作に影響するため、タスク定義をあまり修正できないような要件だと、ECSの再デプロイも選択肢に入ってきます。