Help us understand the problem. What is going on with this article?

AWS ECR上のDockerイメージをpullせずにDockerタグをコピーする

More than 1 year has passed since last update.

やりたいことはタイトルどおり、
AWS ECR上のDockerイメージをpullせずにDockerタグをコピーしたい。

なぜかというと、CIからECSにデプロイするときに、 hoge:current を一旦 hoge:old に退避して、 hoge:latesthoge:current に上書きしたいみたいなことがあるんだけど、イメージがそこそこサイズがでかいと、一旦 docker pull して docker tag して docker push し直すみたいなのが時間がかかる。

AWSの公式ドキュメント見てたら、やりたいことはdockerコマンドを使わずにaws-cliでできそうだったのでやってみたんだけど、

参考: AWS CLI を使用してイメージにもう一度タグを付ける
https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/retag-aws-cli.html

CIに組み込もうとすると+αで若干の工夫が必要だったので、そのへんの補足。

ミニマム

例として、hoge:latest => hoge:current にコピーする場合は、こんなかんじ。

MANIFEST=$(aws ecr batch-get-image --repository-name hoge --image-ids imageTag=latest --query 'images[].imageManifest' --output text)
aws ecr put-image --repository-name hoge --image-tag current --image-manifest "$MANIFEST"

これは、まぁAWSの公式ドキュメントに書いてある。

CIに組み込むための若干の工夫

はまりポイント1

デプロイが途中で失敗して再ランした場合など、同じイメージダイジェストで同じタグを重複pushすると
ImageAlreadyExistsExceptionで失敗するので、ダイジェストが既に同じでないか事前にチェックしておく必要がある。

つまり

FROM_DIGEST=$(aws ecr describe-images --repository-name hoge --image-ids imageTag=latest --query "imageDetails[0].imageDigest")
TO_DIGEST=$(aws ecr describe-images --repository-name hoge --image-ids imageTag=current --query "imageDetails[0].imageDigest")
if [[ "$FROM_DIGEST" = "$TO_DIGEST" ]]
then
  # 既にコピー元とコピー先が同じダイジェストの場合は何もしない
  echo "Image already exists. Skipped."
else
  # リモートでタグをコピー
  ()
fi

はまりポイント2

aws ecr describe-images だと指定したタグが存在しない場合にImageNotFoundExceptionで失敗するので
aws ecr list-images を使う。
実装してるときに手元からawsコマンドでタグ作ったりしちゃってたから、気づかなかったんだよ。

つまり

FROM_DIGEST=$(aws ecr list-images --repository-name hoge --query "imageIds[?imageTag=='latest'] | [0].imageDigest")
TO_DIGEST=$(aws ecr list-images --repository-name hoge --query "imageIds[?imageTag=='current'] | [0].imageDigest")
()

完成品

hoge:latest => hoge:current にコピーする場合は、こんなかんじ。

# デプロイが途中で失敗して再ランした場合など、同じイメージダイジェストで同じタグを重複pushすると
# ImageAlreadyExistsExceptionで失敗するので、ダイジェストが既に同じでないか事前にチェックしておく
# aws ecr describe-images だと指定したタグが存在しない場合にImageNotFoundExceptionで失敗するので
# aws ecr list-images を使う
# https://docs.aws.amazon.com/cli/latest/reference/ecr/list-images.html
FROM_DIGEST=$(aws ecr list-images --repository-name hoge --query "imageIds[?imageTag=='latest'] | [0].imageDigest")
TO_DIGEST=$(aws ecr list-images --repository-name hoge --query "imageIds[?imageTag=='current'] | [0].imageDigest")
if [[ "$FROM_DIGEST" = "$TO_DIGEST" ]]
then
  # 既にコピー元とコピー先が同じダイジェストの場合は何もしない
  echo "Image already exists. Skipped."
else
  # リモートでタグをコピー
  echo "docker tag copy: latest => current"
  # https://docs.aws.amazon.com/cli/latest/reference/ecr/batch-get-image.html
  MANIFEST=$(aws ecr batch-get-image --repository-name hoge --image-ids imageTag=latest --query 'images[].imageManifest' --output text)
  # https://docs.aws.amazon.com/cli/latest/reference/ecr/put-image.html
  aws ecr put-image --repository-name hoge --image-tag current --image-manifest "$MANIFEST"
fi

できたー。

余談

本題と関係ないけど、Dockerで動的タグを使うなとか条件反射でツッコミたくなった人は aws ecs update-service --force-new-deployment とか ECS_IMAGE_PULL_BEHAVIOR でググってくれ。

crowdworks
21世紀の新しいワークスタイルを提供する日本最大級のクラウドソーシング「クラウドワークス」のエンジニアチームです!
https://crowdworks.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away