目次
16 章

ECR — イメージレジストリ

ECS と Lambda が取りに行くコンテナイメージをどこに保管するかを整理します。Amazon ECR の private / public の違い、IAM 認証、docker push / pull、イメージスキャン、タグ戦略、ライフサイクルポリシー、マルチアーキテクチャ(linux/amd64 + arm64)、VPC Endpoint とクロスアカウントアクセスまでを扱います。

直前の 第15章 ECS と Fargate でコンテナ運用の絵を固めました。ここで欠けているのは、その ECS / Fargate が取りに行くイメージがどこにあるのかです。Docker Hub のような外部レジストリも可能ですが、AWS の中では標準が Amazon ECR (Elastic Container Registry) です。

本章は ECR の構造をまとめて整理します — private / public の違い、IAM 認証、イメージのプッシュ / プル、セキュリティ(スキャン、タグの不変性)、運用(ライフサイクルポリシー、マルチアーキテクチャ)です。ここで固める push / pull の流れとライフサイクルポリシーは、4部 第22章 ECS Fargate デプロイの骨格第24章 CI/CD パイプライン で毎回登場します。

イメージレジストリの役割 #

Docker の流れを思い出してみます。

イメージのライフサイクル
docker build → ローカルイメージ
docker push → レジストリ (リモート)
docker pull (別のマシン) → そのイメージを取得して docker run

レジストリはその中間地点です。誰が、どこで、どのバージョンのイメージを受け取れるかを決定します。

オプション比較 #

レジストリ用途
Docker Hub最も有名。無料だが pull 制限、public がデフォルト
GHCR (GitHub Container Registry)GitHub アカウントと連携。private の無料枠が大きい
Amazon ECR PrivateAWS の中で IAM で認証。ECS / Lambda / EKS と自然に連携
Amazon ECR PublicOSS 配布用。誰でも anonymous pull
GCR / Azure ACR他クラウド用

ECS / Lambda / EKS が AWS の中にあるなら ECR が標準です。

  • IAM で認証します — 別途のパスワード管理がありません。
  • VPC Endpoint でインターネットを通らずに pull します (NAT コスト節約)。
  • 同じリージョンなので pull が速いです。
  • イメージスキャン (脆弱性の自動分析) が統合されます。

Private vs Public #

ECR は2種類です。

Private (ほとんどの場合) #

自分のアカウントのユーザー / ロールだけがアクセスできます。会社 / 運用ワークロードはほぼすべてここです。

  • リージョン単位 (イメージごとにリージョンが刻まれます)
  • IAM ポリシーでアクセス制御
  • コスト: GB 保管 + Data Transfer

Public (OSS 配布 / 学習資料) #

世界中の誰でも anonymous pull が可能です。AWS が運用する Public Gallery に露出します。

  • 常に us-east-1 にホスティング (グローバル)
  • Push は IAM 認証、Pull は無認証
  • コスト: GB 保管 + Data Transfer (push 側)

本章は Private を基準に進めます。

Repository を作る #

ECR の単位は Repository です。1つの repo の中に同じアプリの複数のバージョン (タグ) を保管します。

repo を作る
aws ecr create-repository \
  --repository-name myapp \
  --region ap-northeast-2 \
  --image-scanning-configuration scanOnPush=true \
  --encryption-configuration encryptionType=AES256

成功すると URI です。

123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp

これがすべての push / pull のアドレスです。形は次のとおりです。

ECR URI の形
<アカウントID>.dkr.ecr.<リージョン>.amazonaws.com/<repo>:<タグ>

オプション整理 #

オプション意味
image-scanning-configuration scanOnPush=truepush 時に自動で脆弱性スキャン
image-tag-mutability IMMUTABLE同じタグの上書き禁止 — 運用推奨
encryption-configuration encryptionType=KMSユーザー管理 KMS キーで暗号化

コンソールで GUI でも同じように作れます。

認証 — aws ecr get-login-password #

Docker Hub と違い ECR は AWS IAM で認証します。パスワードが別途ありません。代わりに一時トークンを受け取って docker login します。

ECR ログイン (12 時間有効)
aws ecr get-login-password --region ap-northeast-2 \
  | docker login --username AWS --password-stdin \
    123456789012.dkr.ecr.ap-northeast-2.amazonaws.com

トークンは 12時間有効です。CI では毎回新しく受け取って使うのが自然です (CI ジョブの開始時に一度)。

必要な権限 #

push するユーザー / ロールのポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:InitiateLayerUpload",
        "ecr:UploadLayerPart",
        "ecr:CompleteLayerUpload",
        "ecr:PutImage",
        "ecr:BatchGetImage"
      ],
      "Resource": "arn:aws:ecr:ap-northeast-2:123456789012:repository/myapp"
    }
  ]
}

GetAuthorizationToken だけが * 資源で、残りは特定の repo に制限します (第6章 セキュリティ基本 の最小権限)。

Pull だけする権限 #

ECS Task の Execution Role のように pull だけすればよいです。AWS マネージドポリシー AmazonECSTaskExecutionRolePolicy が ECR pull 権限を自動で含みます。

Push / Pull #

Push #

最初の push
# ビルド
docker build -t myapp:v1 .

# タグ (ECR URI に)
docker tag myapp:v1 \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1

# ログイン (上を参照)
aws ecr get-login-password --region ap-northeast-2 | docker login ...

# push
docker push \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1

イメージが 100MB なら最初の push は 100MB のアップロードです。次からはレイヤー単位のキャッシングなので変更された部分だけが上がり、通常は数 MB です。

Pull #

pull
docker pull 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1

ECS / Lambda は自動で pull します。コンソールで直接やることはほとんどありませんが、デバッグ時に有用です。

タグ戦略 #

ECR repo の中で同じイメージの複数のバージョンをどう呼ぶかに関するルールです。よく見るパターンです。

1) Semver #

myapp:1.4.2
myapp:1.4
myapp:1
myapp:latest

ライブラリ / ツールのように外部に配布する用途に自然です。運用では latest が危険です (どの時点の latest なのか曖昧)。

2) Git SHA #

myapp:abc1234        ← short sha
myapp:abc1234567...  ← full sha

CI でビルドした commit と 1:1 でマッチします。運用に最もおすすめする方式です — どの commit が運用にあるかを即座に追跡できます。

3) 環境 + シーケンス #

myapp:prod-2025-04-01.001
myapp:staging-2025-04-01.005

リリースを日付ごとに数える用途で使います。

4) マルチタグ #

運用推奨パターンは immutable + alias です。

イメージを一度ビルド → 2つのタグ
docker tag myapp:abc1234 myapp:abc1234           # 不変 (永遠にそのまま)
docker tag myapp:abc1234 myapp:prod-current      # 可変 (現在の運用を指す)

ECR repo 自体は IMMUTABLE (一度 push したタグを上書きできない) にしておき、alias が必要なら別のツール (deployment system) が管理します。

イメージスキャン #

ECR は push したイメージの脆弱性を自動でスキャンしてくれます。scanOnPush=true オプションです (上で設定)。

2種類 #

種類コスト
Basic Scanningopen source CVE DB (CoreOS Clair) ベースの単発スキャン無料
Enhanced ScanningInspector 統合。OS レイヤー + 言語ライブラリ (npm、pip など) まで。継続モニタリング (イメージ push 後にも新しい CVE 発見時にアラート)repo あたり時間 / イメージあたり

運用ワークロードは Enhanced を検討します。Basic も最初の出発には十分です。

結果を見る #

スキャン結果
aws ecr describe-image-scan-findings \
  --repository-name myapp \
  --image-id imageTag=v1

コンソールでは repo → イメージ → “Vulnerabilities” タブで CRITICAL / HIGH / MEDIUM / LOW の数がひと目で見えます。

ビルド段階で遮断する #

CRITICAL があればデプロイを止めます — CI ジョブで次を置きます。

CI ゲート
CRITICAL=$(aws ecr describe-image-scan-findings \
  --repository-name myapp --image-id imageTag=$SHA \
  --query 'imageScanFindings.findingSeverityCounts.CRITICAL' \
  --output text)

if [ "$CRITICAL" != "None" ] && [ "$CRITICAL" -gt 0 ]; then
  echo "🚨 CRITICAL CVE 発見。デプロイ中断。"
  exit 1
fi

このゲートは 第24章 CI/CD パイプライン でビルド段階にそのまま挟み込みます。

ライフサイクルポリシー — 自動整理 #

イメージが積もると ECR コストが来ます。ライフサイクルポリシー で自動整理します。

lifecycle.json
{
  "rules": [
    {
      "rulePriority": 1,
      "description": "untagged イメージを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" }
    }
  ]
}
適用
aws ecr put-lifecycle-policy \
  --repository-name myapp \
  --lifecycle-policy-text file://lifecycle.json

よくあるパターンは次のとおりです。

  • untagged を 7 ~ 14日後に削除 (失敗したビルドの残骸)
  • タグ prefix pr- のものを 30日後に削除 (PR プレビューイメージ)
  • タグ prefix release- は永久保存

運用ワークロードは1年分のイメージが累積すると GB 単位のコストがかかります。ライフサイクルを最初に固めておくことが運用衛生の核心です。

マルチアーキテクチャイメージ #

Apple Silicon Mac (arm64) でビルドしたイメージをそのまま運用 (通常は amd64) に push するとブートしません。2つの道があります。

1) buildx で多プラットフォーム push #

buildx multi-arch
docker buildx create --use
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myapp:v1 \
  --push .

ECR の1つのタグ (v1) の中に2つのアーキテクチャが一緒に入る manifest list です。pull 側が自分のアーキテクチャに合うものを自動選択します。

2) Fargate ARM で統一 #

第15章 Fargate で task definition の runtimePlatform.cpuArchitecture: ARM64 にしておくと、ARM 専用イメージだけ push すればよくなります。単価が約 20% 安いボーナスがあります。

Task Definition (ARM)
{
  "runtimePlatform": {
    "cpuArchitecture": "ARM64",
    "operatingSystemFamily": "LINUX"
  }
}

小 / 中トラフィックの新規プロジェクトは最初から ARM をおすすめします。

VPC Endpoint — NAT なしで pull #

private subnet の ECS Task が ECR から pull すると、基本は NAT Gateway 経由なので GB あたりで課金されます。

ECR は VPC Endpoint をサポートして NAT を迂回します。

ECR VPC Endpoint 2個
# api 呼び出し用
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-xxx \
  --service-name com.amazonaws.ap-northeast-2.ecr.api \
  --vpc-endpoint-type Interface \
  --subnet-ids subnet-aaa subnet-bbb

# イメージ layer ダウンロード用
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-xxx \
  --service-name com.amazonaws.ap-northeast-2.ecr.dkr \
  --vpc-endpoint-type Interface \
  --subnet-ids subnet-aaa subnet-bbb

# イメージ layer が S3 に生きているので S3 endpoint も一緒に
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-xxx \
  --service-name com.amazonaws.ap-northeast-2.s3 \
  --vpc-endpoint-type Gateway \
  --route-table-ids rtb-xxx

3つ (api、dkr、s3) で1セットです。NAT Gateway コストを大きく減らしてくれます — 運用トラフィックが大きい環境ではほぼ必須です。VPC と endpoint の深さは 第28章 VPC 深掘り でさらに扱います。

クロスアカウントアクセス #

prod アカウントの ECR repo を dev アカウントから pull したいなら Repository Policy で許可します。

repo-policy.json
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "AllowDevAccountPull",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::222222222222:root"
      },
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ]
    }
  ]
}
aws ecr set-repository-policy \
  --repository-name myapp \
  --policy-text file://repo-policy.json

原則は次のとおりです。運用イメージは運用アカウントの ECR に一度だけ置き、他のアカウントは pull します。同じイメージを環境ごとのアカウントに重複ビルドしません。マルチアカウントガバナンスは 第29章 セキュリティガバナンス で扱います。

コスト #

項目価格 (ソウルリージョン)
StorageGB / 月 $0.10
Data Transfer Out (インターネット)GB $0.126 (1GB 無料)
Data Transfer Out (同じリージョン)無料
Enhanced Scanningイメージあたり + repo あたり時間

同じリージョンの中の ECS が pull すれば無料です。インターネットに出る (CI / 外部ツール) pull だけがコストです。ライフサイクルポリシーで storage だけ抑えればコストはほぼ 0 に近いです。

よく出会う落とし穴 #

1) denied: ... is not authorized to perform: ecr:... #

権限漏れです。両方を確認します。

  • ユーザー / ロールのポリシーに ECR action が入っているか
  • Repository Policy が遮断していないか (通常は空 — 空なら IAM ポリシーだけで十分)

2) manifest unknown または repository ... not found #

99% はリージョン / アカウント ID の打ち間違いです。URI の ap-northeast-2 の位置、123456789012 の位置をもう一度確認します。

3) IMMUTABLE repo に同じタグを push #

同じタグを2回 push しようとすると拒否されます。意図された動作で運用推奨です。CI ジョブで commit SHA でタグを付ける形で回避します。

4) マルチアーキテクチャの抜け #

Mac (ARM) でビルド → push → x86_64 Fargate で exec format error が出ます。buildx で multi-arch するか、task definition を ARM で統一します。

5) NAT Gateway コストの爆発 #

ECR pull が NAT を通ると GB あたりで課金されます。VPC Endpoint の3つ (api / dkr / s3) を追加します。

6) イメージの累積 #

ライフサイクルポリシーなしで1年運用すると数千個のイメージが GB 単位のコストを作ります。最初に repo を作るときにライフサイクルも一緒に置きます。

練習問題 #

  1. 自分のアプリのイメージにどのタグ戦略を使うか §「タグ戦略」の4つのパターンから1つを選び、運用で latest を使わない理由を1行で書いておきましょう。第24章 CI/CD パイプライン で CI が commit SHA でタグを付けるとき、この決定が基準になります。
  2. private subnet の ECS Task が ECR から pull するとき、NAT Gateway を通る経路と VPC Endpoint(api / dkr / s3)を通る経路のコスト差を §「VPC Endpoint」と §「コスト」を根拠に1段落で説明してみましょう(第15章 ECS と Fargate の NAT コストの落とし穴とまとめてみます)。
  3. ライフサイクルポリシーで untagged イメージを7日で削除 + 最新30個を維持するルールを適用すると、1年間毎日1回ずつデプロイするとき repo に残るイメージ数がおおよそいくつになるかを §「ライフサイクルポリシー」を根拠に推定してみましょう。

一行まとめ: ECR は AWS の中のコンテナイメージレジストリで、ECS / Lambda / EKS と IAM で連携し、運用では Private repo を使います。認証は 12時間のトークンを受け取る aws ecr get-login-password で、運用推奨タグは Git SHA + IMMUTABLE。push 時にイメージスキャンで CRITICAL を CI で遮断し、ライフサイクルポリシーでイメージを自動整理します。マルチアーキテクチャは buildx または Fargate ARM で解決し、VPC Endpoint(api · dkr · s3)で NAT コストを回避します。

次の章 #

ECS と ECR はコンテナが常に立ち上がっているモデルです。次の 第17章 Lambda 基礎 では反対側 — リクエストが来たときだけ関数が目覚めるサーバーレス — を扱います。Lambda の動作方式、runtime / handler / イベントモデル、コールドスタート、同時実行、ロギングまで AWS サーバーレスの最初のボタンを整理します。

X