Amazon ECR は、コンテナイメージを保管する AWS のレジストリで、使い方は「リポジトリを作る → docker login する → タグを付けて push する → 増えたイメージをライフサイクルポリシーで整理する」の4段階です。 イメージの置き場所は「アカウントID.dkr.ecr.リージョン.amazonaws.com/リポジトリ名:タグ」という URI で表します。

この記事では、aws ecr の主要コマンドを返り値の JSON つきで早見表にし、push の手順とレジストリ URI の組み立て方、イメージの一覧・削除、タグの上書き禁止、リポジトリポリシーを順に扱います。後半は、用途別にそのまま使えるライフサイクルポリシーの JSON 集と rulePriority の評価順、書き間違いで返るエラー文、エラーと原因の対応表です。

確認環境:Ubuntu 24.04 / AWS CLI 1.46.1 / Python 3.11.15。AWS の API をローカルで再現するツール(moto 5.2.3)で実行しています。掲載コマンドは接続先の指定を外してあり、実際の AWS でそのまま使える形です。アカウント ID は 123456789012、リージョンは ap-northeast-1 で統一し、イメージの一覧に出てくるイメージは検証用の小さなマニフェストを登録したものです。docker コマンドと、ライフサイクルポリシーの評価の決まりは AWS 公式ドキュメントの記載に沿っています。

aws ecr コマンドの早見表

やりたいこと コマンド 主な返り値
リポジトリを作る aws ecr create-repository --repository-name 名前 repository(repositoryUri を含む)
リポジトリを一覧・確認する aws ecr describe-repositories repositories の配列
docker login 用のパスワードを得る aws ecr get-login-password パスワード文字列(標準出力)
イメージを一覧する aws ecr list-images --repository-name 名前 imageIds(ダイジェストとタグ)
イメージの詳細を見る aws ecr describe-images --repository-name 名前 imageDetails(タグ・サイズ・push 日時)
イメージを消す aws ecr batch-delete-image --repository-name 名前 --image-ids imageTag=タグ imageIds(消えたもの)と failures
ライフサイクルポリシーを設定する aws ecr put-lifecycle-policy --repository-name 名前 --lifecycle-policy-text file://policy.json lifecyclePolicyText
ライフサイクルポリシーを見る・消す aws ecr get-lifecycle-policy / delete-lifecycle-policy lifecyclePolicyText、lastEvaluatedAt
タグの上書きを禁止する aws ecr put-image-tag-mutability --repository-name 名前 --image-tag-mutability IMMUTABLE imageTagMutability
push 時のスキャンを設定する aws ecr put-image-scanning-configuration --repository-name 名前 --image-scanning-configuration scanOnPush=true imageScanningConfiguration
リポジトリポリシーを設定する aws ecr set-repository-policy --repository-name 名前 --policy-text file://policy.json policyText
リポジトリポリシーを見る・消す aws ecr get-repository-policy / delete-repository-policy policyText
リポジトリを消す aws ecr delete-repository --repository-name 名前(中身ごとなら --force) repository

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Amazon ECR の使い方 リポジトリ作成とライフサイクルポリシー」

リポジトリを作る(create-repository)

aws ecr create-repository --repository-name myapp/web
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:123456789012:repository/myapp/web",
        "registryId": "123456789012",
        "repositoryName": "myapp/web",
        "repositoryUri": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/web",
        "createdAt": 1791182141.57,
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

repositoryUri が、docker tag と docker push で使う値です。 何も指定しないと、タグは上書きできる設定(MUTABLE)、push 時のスキャンは無効、暗号化は AES256 になります。

リポジトリ名はスラッシュで区切って myapp/web のように階層を付けられます。使えるのは英小文字・数字と、区切りのハイフン・アンダースコア・ピリオド・スラッシュです。大文字を入れると次のメッセージが返ります。

aws ecr create-repository --repository-name MyApp
An error occurred (InvalidParameterException) when calling the CreateRepository operation: Invalid parameter at 'repositoryName' failed to satisfy constraint: 'must satisfy regular expression '(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*'

作成時にまとめて設定する

タグの上書き禁止、push 時のスキャン、タグ(リソースに付ける目印)は、作成時に指定できます。

aws ecr create-repository \
  --repository-name myapp/api \
  --image-tag-mutability IMMUTABLE \
  --image-scanning-configuration scanOnPush=true \
  --tags Key=team,Value=web \
  --query 'repository.[repositoryUri,imageTagMutability,imageScanningConfiguration.scanOnPush]'
[
    "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/api",
    "IMMUTABLE",
    true
]

リポジトリを一覧する(describe-repositories)

aws ecr describe-repositories \
  --query 'repositories[].[repositoryName,imageTagMutability,repositoryUri]' \
  --output table
-------------------------------------------------------------------------------------------
|                                  DescribeRepositories                                   |
+-----------+------------+----------------------------------------------------------------+
|  myapp/web|  MUTABLE   |  123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/web   |
|  myapp/api|  IMMUTABLE |  123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/api   |
+-----------+------------+----------------------------------------------------------------+

--query を付けずに実行すると、create-repository と同じ形の repository が配列で返ります。URI だけ欲しいときは次のようにします。

aws ecr describe-repositories --repository-names myapp/web \
  --query 'repositories[0].repositoryUri' --output text
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/web

docker push の手順とレジストリ URI の組み立て方

URI の形

部分 例 何を表すか
レジストリ 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com アカウント ID とリージョンで決まる。docker login の宛先
リポジトリ myapp/web create-repository で付けた名前
タグ v1.2.0 イメージの版を表す名前。省くと latest
イメージ URI 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/web:v1.2.0 docker tag・push・pull で使う全体

レジストリの部分は「アカウント ID」と「リージョン」の2つから機械的に組み立てられます。 次のスクリプトは、アカウント ID を sts get-caller-identity で取り出して URI を作り、リポジトリ側の repositoryUri と突き合わせます。

#!/usr/bin/env bash
set -euo pipefail

REPO="myapp/web"
TAG="v1.2.0"
REGION="ap-northeast-1"

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGISTRY="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com"
IMAGE_URI="${REGISTRY}/${REPO}:${TAG}"

echo "registry : ${REGISTRY}"
echo "image uri: ${IMAGE_URI}"

# リポジトリ側の値と一致することを確かめる
aws ecr describe-repositories --repository-names "${REPO}" --region "${REGION}" \
  --query 'repositories[0].repositoryUri' --output text
registry : 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com
image uri: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/web:v1.2.0
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/web

push までのコマンド列

AWS 公式ドキュメント「Docker イメージを Amazon ECR プライベートリポジトリにプッシュする」に載っている流れを、上の変数を使って並べると次のようになります。

# 1. レジストリにログインする(パスワードをパイプで docker login に渡す)
aws ecr get-login-password --region "${REGION}" \
  | docker login --username AWS --password-stdin "${REGISTRY}"

# 2. 手元のイメージに ECR の URI でタグを付ける
docker tag myapp-web:latest "${IMAGE_URI}"

# 3. push する
docker push "${IMAGE_URI}"

押さえておきたい点は4つです。

項目 内容(公式ドキュメントより)
ユーザー名 常に AWS という固定の文字列
ログインの有効期間 認証トークンは 12 時間有効。期限が過ぎたら手順1をもう一度実行する
ログインの単位 レジストリ(アカウントとリージョンの組)ごとに必要
リポジトリ push の前にリポジトリが存在している必要がある(リポジトリ作成テンプレートを定義している場合を除く)

get-login-password は、パスワードを標準出力に1行出すだけのコマンドです。画面やログに残さないよう、パイプで --password-stdin に渡します。

イメージを一覧・確認する(list-images / describe-images)

ここからは、myapp/web に v1.0.0、v1.1.0、v1.2.0 の3つを登録し、v1.2.0 と同じイメージに latest も付けた状態で実行します。

list-images:ダイジェストとタグの組を見る

aws ecr list-images --repository-name myapp/web
{
    "imageIds": [
        {
            "imageDigest": "sha256:526f8c25ba6a4a29743ca6e95413fa31516639b97af0cea17b99acd1a0b34c8e",
            "imageTag": "v1.0.0"
        },
        {
            "imageDigest": "sha256:922d78db02ccd70df74c20e6c1294251234bfc229e034948cddc7957ac53cd30",
            "imageTag": "v1.1.0"
        },
        {
            "imageDigest": "sha256:074f0c53d3b0b4c3d91365271a478734d42b327581c7cf7625cd6fa12fa293d3",
            "imageTag": "v1.2.0"
        },
        {
            "imageDigest": "sha256:074f0c53d3b0b4c3d91365271a478734d42b327581c7cf7625cd6fa12fa293d3",
            "imageTag": "latest"
        }
    ]
}

v1.2.0 と latest は imageDigest が同じです。 ダイジェストはイメージの中身から決まる識別子で、タグはそれに付けた名前です。1つのイメージに複数のタグを付けると、list-images ではタグごとに1行ずつ出ます。

タグだけを並べたいときは --query を使います。

aws ecr list-images --repository-name myapp/web \
  --query 'imageIds[].imageTag' --output text
v1.0.0  v1.1.0  v1.2.0  latest

describe-images:サイズと push 日時を見る

aws ecr describe-images --repository-name myapp/web --image-ids imageTag=latest
{
    "imageDetails": [
        {
            "registryId": "123456789012",
            "repositoryName": "myapp/web",
            "imageDigest": "sha256:074f0c53d3b0b4c3d91365271a478734d42b327581c7cf7625cd6fa12fa293d3",
            "imageTags": [
                "v1.2.0",
                "latest"
            ],
            "imageSizeInBytes": 1472,
            "imagePushedAt": 1791182588,
            "imageManifestMediaType": "application/vnd.docker.distribution.manifest.v2+json"
        }
    ]
}

describe-images はイメージ1つにつき1件で、タグは imageTags にまとまります。imagePushedAt は UNIX 時間(秒)です。

push の早い順に並べて、日時・サイズ・タグを1行ずつ出すには、sort_by を使います。

aws ecr describe-images --repository-name myapp/web \
  --query 'sort_by(imageDetails,&imagePushedAt)[].[imagePushedAt,imageSizeInBytes,join(`,`,imageTags)]' \
  --output text
1791182582  1472    v1.0.0
1791182585  1472    v1.1.0
1791182588  1472    v1.2.0,latest

末尾の要素を取れば、最後に push されたイメージのタグが分かります。

aws ecr describe-images --repository-name myapp/web \
  --query 'sort_by(imageDetails,&imagePushedAt)[-1].imageTags'
[
    "v1.2.0",
    "latest"
]

リポジトリに無いタグを指定すると、次のメッセージが返ります。

An error occurred (ImageNotFoundException) when calling the DescribeImages operation: The image with imageId {imageDigest:'null', imageTag:'v9'} does not exist within the repository with name 'myapp/web' in the registry with id '123456789012'

イメージを消す(batch-delete-image)

タグを指定して消す

aws ecr batch-delete-image --repository-name myapp/web --image-ids imageTag=v1.0.0
{
    "imageIds": [
        {
            "imageDigest": "sha256:526f8c25ba6a4a29743ca6e95413fa31516639b97af0cea17b99acd1a0b34c8e",
            "imageTag": "v1.0.0"
        }
    ],
    "failures": []
}

複数まとめて消す:結果は failures も見る

--image-ids は空白区切りで複数並べられます。存在するタグと存在しないタグを混ぜて実行すると、次のようになりました。

aws ecr batch-delete-image --repository-name myapp/web \
  --image-ids imageTag=v1.1.0 imageTag=v9.9.9
{
    "imageIds": [
        {
            "imageDigest": "sha256:922d78db02ccd70df74c20e6c1294251234bfc229e034948cddc7957ac53cd30",
            "imageTag": "v1.1.0"
        }
    ],
    "failures": [
        {
            "imageId": {
                "imageTag": "v9.9.9"
            },
            "failureCode": "ImageNotFound",
            "failureReason": "Requested image not found"
        }
    ]
}

消せなかったものがあってもコマンド自体は終了コード 0 で終わり、内容は failures に入ります。 スクリプトで使うときは、failures が空かどうかを確かめます。

タグだけ外す・イメージごと消す

複数のタグが付いたイメージで、タグを1つ指定して消すと、そのタグだけが外れます。

aws ecr batch-delete-image --repository-name myapp/web --image-ids imageTag=latest
aws ecr list-images --repository-name myapp/web
{
    "imageIds": [
        {
            "imageDigest": "sha256:074f0c53d3b0b4c3d91365271a478734d42b327581c7cf7625cd6fa12fa293d3",
            "imageTag": "v1.2.0"
        }
    ]
}

latest が外れ、v1.2.0 のイメージは残りました。ダイジェストを指定すると、タグの数に関係なくイメージそのものが消えます。

aws ecr batch-delete-image --repository-name myapp/web \
  --image-ids imageDigest=sha256:074f0c53d3b0b4c3d91365271a478734d42b327581c7cf7625cd6fa12fa293d3
aws ecr list-images --repository-name myapp/web
{
    "imageIds": []
}
指定のしかた 動き
imageTag=タグ(ほかにもタグがある) そのタグだけ外れる
imageTag=タグ(最後の1つ) イメージが消える
imageDigest=sha256:... タグの数に関係なくイメージが消える

タグの上書きを禁止する(put-image-tag-mutability)

初期設定の MUTABLE では、同じタグで push し直すと、タグが新しいイメージに付け替わります。リリース用のタグが別の中身に入れ替わらないようにするには IMMUTABLE にします。

aws ecr put-image-tag-mutability \
  --repository-name myapp/web \
  --image-tag-mutability IMMUTABLE
{
    "registryId": "123456789012",
    "repositoryName": "myapp/web",
    "imageTagMutability": "IMMUTABLE"
}

AWS 公式ドキュメント「Amazon ECR でイメージタグの上書きを防ぐ」によると、IMMUTABLE のリポジトリにすでにあるタグで push すると ImageTagAlreadyExistsException が返ります。また、IMMUTABLE_WITH_EXCLUSION を選ぶと、ワイルドカードで指定したタグだけを上書きできる例外にできます。公式ドキュメントに載っている例は次のとおりです(latest だけ上書きを許す設定)。

aws ecr put-image-tag-mutability \
  --repository-name myapp/web \
  --image-tag-mutability IMMUTABLE_WITH_EXCLUSION \
  --image-tag-mutability-exclusion-filters filterType=WILDCARD,filter=latest

指定できる値は、存在しない値を渡したときのメッセージからも確かめられます。

An error occurred (InvalidParameterException) when calling the PutImageTagMutability operation: Invalid parameter at 'imageTagMutability' failed to satisfy constraint: 'Member must satisfy enum value set: [IMMUTABLE, MUTABLE, MUTABLE_WITH_EXCLUSION, IMMUTABLE_WITH_EXCLUSION]'
設定 同じタグでの push 向いている使い方
MUTABLE タグが新しいイメージに付け替わる latest や dev のような、動かして使うタグが中心のリポジトリ
IMMUTABLE 受け付けない v1.2.0 のように版を固定したいリポジトリ
IMMUTABLE_WITH_EXCLUSION 例外に指定したタグだけ付け替わる 版のタグは固定し、latest だけ動かしたいリポジトリ

リポジトリポリシーを設定する(set-repository-policy)

リポジトリポリシーは、「誰がこのリポジトリを使えるか」をリポジトリ側に書く JSON です。別の AWS アカウントからの pull を許可する場合などに使います。次の例は、アカウント 210987654321 に pull に必要な3つの操作を許可します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPullFromOtherAccount",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::210987654321:root" },
      "Action": [
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "ecr:BatchCheckLayerAvailability"
      ]
    }
  ]
}
aws ecr set-repository-policy \
  --repository-name myapp/web \
  --policy-text file://repo-policy.json
{
    "registryId": "123456789012",
    "repositoryName": "myapp/web",
    "policyText": "{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"AllowPullFromOtherAccount\",\n      \"Effect\": \"Allow\",\n      \"Principal\": { \"AWS\": \"arn:aws:iam::210987654321:root\" },\n      …(省略)…\n}\n"
}

policyText は JSON を文字列にしたものなので、改行が \n で入ります。読みやすく取り出すには --query policyText --output text を付けます。

aws ecr get-repository-policy --repository-name myapp/web \
  --query policyText --output text
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPullFromOtherAccount",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::210987654321:root" },
      "Action": [
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "ecr:BatchCheckLayerAvailability"
      ]
    }
  ]
}

set-repository-policy は、渡した JSON でポリシー全体を置き換えます。 Statement を足すときは、今のポリシーを取り出して編集し、全体を渡します。

ポリシーとして成り立たない JSON を渡すと、次のメッセージが返ります。

An error occurred (InvalidParameterException) when calling the SetRepositoryPolicy operation: Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided'

ライフサイクルポリシーの基本と設定コマンド

ライフサイクルポリシーは、「どのイメージを、どの条件で自動的に削除するか」をリポジトリごとに書く JSON です。push を続けるとイメージは増える一方なので、残す数や日数を決めておくと保管量を一定に保てます。

1つのルールの形

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "タグなしイメージを 14 日で削除",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 14
      },
      "action": { "type": "expire" }
    }
  ]
}
項目 必須 値 意味
rulePriority 必須 整数。ルールごとに別の値 適用の順番。小さいほど先
description 任意 文字列 ルールの説明
tagStatus 必須 tagged / untagged / any 対象にするイメージ
tagPrefixList tagged のとき、どちらか必須 文字列の配列 タグの接頭辞で選ぶ
tagPatternList tagged のとき、どちらか必須 文字列の配列 ワイルドカードつきのパターンで選ぶ
countType 必須 imageCountMoreThan / sinceImagePushed など 数で数えるか、日数で数えるか
countUnit 日数で数えるとき必須 days 単位
countNumber 必須 1 以上の整数 残す数、または日数
action.type 必須 expire(削除) 条件に合ったイメージに行うこと

この表は AWS 公式ドキュメント「Amazon ECR のライフサイクルポリシーのプロパティ」の内容をまとめたものです。同じページには、countType に sinceImagePulled と sinceImageTransitioned、action.type に transition(アーカイブ用の保管先へ移す)も載っています。この記事では削除(expire)に絞って扱います。

設定・確認・削除

aws ecr put-lifecycle-policy \
  --repository-name myapp/web \
  --lifecycle-policy-text file://lc-untagged.json
{
    "registryId": "123456789012",
    "repositoryName": "myapp/web",
    "lifecyclePolicyText": "{\n  \"rules\": [\n    {\n      \"rulePriority\": 1,\n      \"description\": \"タグなしイメージを 14 日で削除\",\n      \"selection\": {\n        \"tagStatus\": \"untagged\",\n        \"countType\": \"sinceImagePushed\",\n        \"countUnit\": \"days\",\n        \"countNumber\": 14\n      },\n      \"action\": { \"type\": \"expire\" }\n    }\n  ]\n}\n"
}

1つのリポジトリに設定できるポリシーは1つで、put-lifecycle-policy を実行するたびに全体が置き換わります。 ルールを足すときは、rules の配列に追記したファイルを渡し直します。

# 今のポリシーを読みやすく表示する
aws ecr get-lifecycle-policy --repository-name myapp/web \
  --query lifecyclePolicyText --output text

# ポリシーを外す
aws ecr delete-lifecycle-policy --repository-name myapp/web

ポリシーが無いリポジトリで get-lifecycle-policy を実行すると、次のメッセージになります。

An error occurred (LifecyclePolicyNotFoundException) when calling the GetLifecyclePolicy operation: Lifecycle policy does not exist for the repository with name 'myapp/api' in the registry with id '123456789012'

用途別ライフサイクルポリシー JSON 集

以下の JSON は、どれも put-lifecycle-policy で登録できることを確かめたものです。数字(日数・世代数)は運用に合わせて変えてください。

タグなしイメージを N 日で削除する

タグが付いていないイメージを、push からの日数で片付けるルールです。

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "タグなしイメージを 14 日で削除",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 14
      },
      "action": { "type": "expire" }
    }
  ]
}

最新 N 世代を残す

タグの有無に関係なく、push が新しい順に 10 個を残し、それより前のものを削除します。

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "最新 10 世代を残す",
      "selection": {
        "tagStatus": "any",
        "countType": "imageCountMoreThan",
        "countNumber": 10
      },
      "action": { "type": "expire" }
    }
  ]
}

imageCountMoreThan は「この数を超えた分を対象にする」という意味なので、countNumber は「残したい数」を書きます。countUnit は付けません。

タグなしは日数で、全体は世代数で整理する

最初に入れるポリシーとして使いやすい組み合わせです。

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "タグなしは 7 日で削除",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 7
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 2,
      "description": "タグ付きも含め最新 30 世代を残す",
      "selection": {
        "tagStatus": "any",
        "countType": "imageCountMoreThan",
        "countNumber": 30
      },
      "action": { "type": "expire" }
    }
  ]
}

タグの接頭辞別に残し方を変える

prod で始まるタグは多めに残し、dev で始まるタグは日数で削除し、どれにも当てはまらないものは世代数で整理します。

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "prod で始まるタグは 30 世代を残す",
      "selection": {
        "tagStatus": "tagged",
        "tagPrefixList": ["prod"],
        "countType": "imageCountMoreThan",
        "countNumber": 30
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 2,
      "description": "dev で始まるタグは 7 日で削除",
      "selection": {
        "tagStatus": "tagged",
        "tagPrefixList": ["dev"],
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 7
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 3,
      "description": "タグなしは 1 日で削除",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 1
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 10,
      "description": "残りは最新 50 世代を残す",
      "selection": {
        "tagStatus": "any",
        "countType": "imageCountMoreThan",
        "countNumber": 50
      },
      "action": { "type": "expire" }
    }
  ]
}

rulePriority は連番でなくてもかまいません。最後の any のルールを 10 にしておくと、あとから 4〜9 にルールを差し込めます。

ワイルドカードのパターンで選ぶ

tagPatternList を使うと、* を含むパターンでタグを選べます。

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "release- で始まるタグは最新 20 世代を残す",
      "selection": {
        "tagStatus": "tagged",
        "tagPatternList": ["release-*"],
        "countType": "imageCountMoreThan",
        "countNumber": 20
      },
      "action": { "type": "expire" }
    }
  ]
}

rulePriority の評価順と書き方の決まり

AWS 公式ドキュメント「Amazon ECR のライフサイクルポリシーを使用してイメージのクリーンアップを自動化する」と、プロパティのページに書かれている決まりです。

決まり 内容
評価と適用 すべてのルールは優先度に関係なく同時に評価され、そのあと rulePriority の順に適用される
数字と優先度 rulePriority の数字が小さいほど優先される(1 は 2 より先)
1つのイメージに効くルール 1つのイメージを削除対象にするルールは、多くても1つ
先のルールとの関係 優先度の高いルールのタグ条件に合ったイメージを、優先度の低いルールが削除対象にすることはない
any の位置 tagStatus が any のルールは、rulePriority を最も大きい値にする(最後に評価される)
tagged の条件 tagStatus が tagged なら、tagPrefixList か tagPatternList のどちらかを指定する。両方を同時には書けない
untagged の条件 tagStatus が untagged なら、tagPrefixList も tagPatternList も書かない
接頭辞を複数書いたとき tagPrefixList に複数書くと、「すべてに当てはまるイメージ」だけが対象になる
ワイルドカードの数 tagPatternList の * は、1つの文字列につき4つまで
数え方 imageCountMoreThan は push が新しい順に並べ、指定した数を超えた分を対象にする。sinceImagePushed は push からの日数で数える
削除までの時間 条件を満たしたイメージは、24 時間以内に削除される見込み

「接頭辞別」のポリシーで世代数が合わないときは、「1つのイメージに効くルールは多くても1つ」を思い出すと整理できます。 prod のルールで残すことになったイメージは、後ろの any のルールでは削除されません。ただし any のルールが数を数えるときには、そのイメージも勘定に入ります。

公式ドキュメントは、リポジトリに適用する前にライフサイクルポリシーのプレビューで削除対象を確かめることを勧めています。CLI では aws ecr start-lifecycle-policy-preview と aws ecr get-lifecycle-policy-preview がこれにあたります。

ライフサイクルポリシーの書き間違いで返るエラー文

lc-untagged.json を1か所ずつ変えて put-lifecycle-policy を実行し、返ったメッセージです。

変えたところ 返ったメッセージ(要点)
末尾に余分なカンマを入れた(JSON として読めない) Lifecycle policy validation failure: Could not map policyString into LifecyclePolicy.
rulePriority を消した Lifecycle policy validation failure: object has missing required properties (["rulePriority"])
tagStatus を none にした Lifecycle policy validation failure: instance value (none) not found in enum :(possible values: ["any", "tagged", "untagged"])
countNumber を 0 にした Lifecycle policy validation failure: numeric instance is lower than the required minimum (minimum: 1, found: 0)
file:// を付けずにファイル名だけ渡した Parameter validation failed: Invalid length for parameter lifecyclePolicyText, value: 12, valid min length: 100
ファイル名を間違えた Error parsing parameter '--lifecycle-policy-text': Unable to load paramfile file://nofile.json: [Errno 2] No such file or directory: 'nofile.json'

上の4つは、全文では次の形で返ります。

An error occurred (InvalidParameterException) when calling the PutLifecyclePolicy operation: Invalid parameter at 'LifecyclePolicyText' failed to satisfy constraint: 'Lifecycle policy validation failure: object has missing required properties (["rulePriority"])'

「valid min length: 100」は、file:// の付け忘れで出やすいメッセージです。 --lifecycle-policy-text lc-keep.json と書くと、ファイルの中身ではなく「lc-keep.json」という 12 文字そのものがポリシーとして送られます。

リポジトリを消す(delete-repository)

イメージが残っているリポジトリは、そのままでは消せません。

aws ecr delete-repository --repository-name myapp/web
An error occurred (RepositoryNotEmptyException) when calling the DeleteRepository operation: The repository with name 'myapp/web' in registry with id '123456789012' cannot be deleted because it still contains images

中のイメージごと消すなら --force を付けます。

aws ecr delete-repository --repository-name myapp/web --force
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:123456789012:repository/myapp/web",
        "registryId": "123456789012",
        "repositoryName": "myapp/web",
        "repositoryUri": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/web",
        "createdAt": 1791182141.57,
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": true
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

--force は確認なしで全イメージを消すので、リポジトリ名を見直してから実行します。

エラー文と原因の対応表

aws ecr コマンドで返るもの

出たメッセージ 原因 直し方
RepositoryAlreadyExistsException ... The repository with name 'myapp/web' already exists in the registry with id '123456789012' 同じ名前のリポジトリがすでにある そのまま使う。設定を変えるなら put- 系のコマンドを使う
RepositoryNotFoundException ... The repository with name 'myapp/web' does not exist in the registry with id '123456789012' リポジトリ名の間違い、またはリージョンが違う --region を確かめる。aws ecr describe-repositories で一覧する
InvalidParameterException ... 'repositoryName' failed to satisfy constraint リポジトリ名に大文字など使えない文字がある 英小文字・数字・区切り記号だけにする
ImageNotFoundException ... does not exist within the repository 指定したタグやダイジェストのイメージが無い aws ecr list-images でタグを確かめる
RepositoryNotEmptyException ... cannot be deleted because it still contains images イメージが残っているリポジトリを消そうとした 先にイメージを消すか、--force を付ける
LifecyclePolicyNotFoundException / RepositoryPolicyNotFoundException ポリシーを設定していないリポジトリで get を実行した 設定してから実行する。スクリプトでは「未設定」として扱う
Parameter validation failed: Invalid length for parameter imageIds, value: 0, valid min length: 1 batch-delete-image に空の一覧を渡した 対象が0件のときは実行しないよう、件数を先に確かめる

リポジトリはリージョンごとに別々です。ap-northeast-1 に作った myapp/web を us-east-1 で探すと、同じ RepositoryNotFoundException になりました。

aws ecr describe-repositories --repository-names myapp/web --region us-east-1
An error occurred (RepositoryNotFoundException) when calling the DescribeRepositories operation: The repository with name 'myapp/web' does not exist in the registry with id '123456789012'

docker コマンドで出るもの

AWS 公式ドキュメント「Amazon ECR 使用時の Docker コマンドのエラーのトラブルシューティング」に載っている内容です。

出たメッセージ 公式ドキュメントが挙げる原因 直し方
no basic auth credentials または HTTP 403 (Forbidden) 別のリージョンに対して認証している(トークンはリージョンごと) リポジトリと同じリージョンで get-login-password を実行し、同じリージョンのレジストリに docker login する
同上 トークンの期限が切れている(有効期間は 12 時間) get-login-password と docker login をもう一度実行する
同上 リポジトリに push する権限が足りない IAM のポリシーとリポジトリポリシーを確かめる
Filesystem verification failed 手元のディスクの空きが足りない、またはネットワークの不調 使っていないイメージを消して空きを作る。Amazon S3 への通信が通ることを確かめる

docker push が通らないときは、「URI のリージョン」「docker login した宛先のリージョン」「get-login-password の --region」の3つが同じかどうかを最初に確かめます。 3つとも、この記事のスクリプトのように1つの変数から作っておくと、ずれを防げます。

よくある質問

Q. リポジトリは push すれば自動で作られますか? A. 公式ドキュメントでは、push の前にリポジトリが存在している必要があるとされています(リポジトリ作成テンプレートを定義している場合を除きます)。先に aws ecr create-repository を実行してください。

Q. docker login は毎回必要ですか? A. 認証トークンの有効期間は 12 時間です。期限内は同じレジストリに対してログインし直す必要はありません。CI のように毎回新しい環境で動かす場合は、push の前に毎回実行します。

Q. ライフサイクルポリシーを設定したのに、すぐにはイメージが減りません。 A. 公式ドキュメントでは、条件を満たしたイメージは 24 時間以内に削除される見込みとされています。設定直後に減らなくても、ポリシーの内容が合っていれば時間をおいて反映されます。

Q. latest タグと版のタグ、どちらを使えばよいですか? A. デプロイ先で「どの中身が動いているか」を確かめやすいのは、v1.2.0 のような版のタグやコミットのハッシュです。latest は同じタグで中身が入れ替わるので、動作確認用に限ると扱いやすくなります。版のタグを固定したい場合は IMMUTABLE か IMMUTABLE_WITH_EXCLUSION を使います。

まとめ

  • イメージ URI は「アカウントID.dkr.ecr.リージョン.amazonaws.com/リポジトリ名:タグ」。レジストリ部分はアカウント ID とリージョンから組み立てられる
  • push は「get-login-password を docker login に渡す → docker tag → docker push」。ユーザー名は AWS、トークンは 12 時間有効
  • list-images はタグごと、describe-images はイメージごとに返る。日時順は sort_by で並べる
  • batch-delete-image は failures も確かめる。タグ指定はタグを外し、ダイジェスト指定はイメージごと消す
  • ライフサイクルポリシーは1リポジトリに1つで、全体置き換え。rulePriority は小さいほど先、any のルールは最も大きい値にする
  • file:// の付け忘れは「valid min length: 100」で気づける
  • RepositoryNotFoundException や no basic auth credentials は、まずリージョンをそろえる

参考・出典

  • Amazon ECR ユーザーガイド「Docker イメージを Amazon ECR プライベートリポジトリにプッシュする」 https://docs.aws.amazon.com/AmazonECR/latest/userguide/docker-push-ecr-image.html
  • Amazon ECR ユーザーガイド「ライフサイクルポリシーを使用してイメージのクリーンアップを自動化する」 https://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html
  • Amazon ECR ユーザーガイド「ライフサイクルポリシーのプロパティ」 https://docs.aws.amazon.com/AmazonECR/latest/userguide/lifecycle_policy_parameters.html
  • Amazon ECR ユーザーガイド「イメージタグの上書きを防ぐ」 https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-tag-mutability.html
  • Amazon ECR ユーザーガイド「Docker コマンドのエラーのトラブルシューティング」 https://docs.aws.amazon.com/AmazonECR/latest/userguide/common-errors-docker.html

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Amazon ECR の使い方 リポジトリ作成とライフサイクルポリシー」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?