AWS上級 #2 ECR — イメージレジストリ

読了 9分

#1 ECS と Fargate でコンテナ運用の全体像を掴みました。1 つ — その ECS / Fargate が持っていく イメージ がどこに住むかが空白です。Docker Hub のような外部レジストリも可能ですが、AWS の中での標準は Amazon ECR (Elastic Container Registry)

今回は ECR の構造 — private / public の違い、IAM 認証、イメージのプッシュ / プル、セキュリティ (スキャン、タグ不変性)、運用 (ライフサイクルポリシー、マルチアーキテクチャ) を一気に整理します。

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

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 で認証 — 別途のパスワード管理 X
  • 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 ジョブ開始時に 1 回)。

必要な権限 #

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 だけすれば OK。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

イメージを 1 度ビルド → 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

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

イメージが溜まると 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 に統一 #

#1 Fargate で task definition の runtimePlatform.cpuArchitecture: ARM64 にしておけば ARM 専用イメージだけ push しても OK。単価が約 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 コストを大きく削減 — 運用トラフィックの大きい環境ではほぼ必須です。

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

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 に 1 度だけ置き、他のアカウントは pull。同じイメージを環境別アカウントに重複ビルドしません。

コスト #

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

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

よく出会う落とし穴 #

1) denied: User: ... 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 を作るときにライフサイクル も一緒に。

まとめ #

今回つかんだもの:

  • ECR の役割 — AWS の中のイメージレジストリ。ECS / Lambda / EKS と IAM で自然に接続
  • Private vs Public — 運用は Private。Public は OSS 配布用
  • Repository = 1 つのアプリのイメージ集合。URI の形 <アカウント>.dkr.ecr.<リージョン>.amazonaws.com/<repo>:<タグ>
  • 認証aws ecr get-login-password → docker login。12 時間トークン
  • 権限 — push 権限と pull 権限 (AmazonECSTaskExecutionRolePolicy) を分離
  • タグ戦略 — Semver / Git SHA / 環境+シーケンス。運用は Git SHA + IMMUTABLE
  • イメージスキャン — Basic (無料) / Enhanced (Inspector 統合、継続モニタリング)。CI ゲートで CRITICAL ブロック
  • ライフサイクルポリシー — untagged 7 日、最新 N 個維持のようなルールで自動整理
  • マルチアーキテクチャ — buildx で amd64 + arm64。または Fargate ARM に統一 (20% 安い)
  • VPC Endpoint (api / dkr / s3) — NAT Gateway コスト回避。運用ではほぼ必須
  • クロスアカウント — Repository Policy で prod ↔ dev pull を許可
  • 落とし穴 — 権限不足、URI タイポ、IMMUTABLE 衝突、マルチアーキテクチャ忘れ、NAT コスト、イメージ累積

次回 — Lambda #

ECS / ECR は コンテナが常に立ち上がっているモデル です。今回はその反対側 — リクエストが来たときだけ関数が目を覚ます サーバーレスです。

#3 Lambda 基礎 では Lambda の動作 (vs ECS / EC2)、runtime / handler / イベントモデル、コールドスタート、並行性、ロギングまで — AWS のサーバーレスの最初の一歩を一気に整理します。

X