こしあん
2025-02-12

ECRのクロスリージョン/クロスアカウントレプリケーションを試す


84{icon} {views}


同一アカウント内でのECRクロスリージョン複製なら簡単だが、クロスアカウントではレジストリポリシーが必須でレポジトリポリシーとの混同に注意が必要。Terraformで実装する場合も“aws_ecr_repository_policy”と“aws_ecr_registry_policy”を正しく使い分けることで、エラーなく自動複製が可能になる。

はじめに

  • ECRのリポジトリは簡単にクロスリージョンにレプリケートできる。
  • クロスアカウントもレプリケートできるが、コピー先のレジストリポリシーが必要。レジストリポリシーとレポジトリポリシーが紛らわしいので要注意
  • 同一アカウントの場合は、レジストリポリシーの設定は不要で簡単。

レジストリポリシーとレポジトリポリシー(クロスアカウントのみ)

レジストリとレポジトリ

クロスアカウントの場合のみ必要。こちらの記事が詳しい

まず、ECR にはレジストリとリポジトリの概念があり、混乱しがちなので整理しておきます。
レジストリは各 AWS アカウントに提供されるコンポーネントで、その中に 1 つ以上のリポジトリを作成できます。
リポジトリは Docker イメージ、OCI 互換イメージを保存出来るコレクションです。単一の ECR レジストリの中で複数のリポジトリを使用してコンテナイメージを整理出来ます。

このレジストリとリポジトリですが、どちらもリソースベースポリシーを設定することが出来ます。レジストリポリシーの場合は全てのリポジトリに自動適用されます。一方でリポジトリポリシーの場合は個別に設定が必要です。

[アップデート] ECR プライベートレジストリのポリシースコープが更新され、複数のリポジトリポリシーを管理しなくてもよくなりました

必要なレジストリポリシー

クロスアカウントにレプリケートする場合は、レジストリポリシーに以下のポリシーが必要。複製だけならecr:ReplicateImageだけでも良く、ecr:CreateRepositoryを入れるとない場合は自動でリポジトリを作ってくれる。

{
    "Version":"2012-10-17",
    "Statement":[
        {
            "Sid":"ReplicationAccessCrossAccount",
            "Effect":"Allow",
            "Principal":{
                "AWS":"arn:aws:iam::source_account_id:root"
            },
            "Action":[
                "ecr:CreateRepository",
                "ecr:ReplicateImage"
            ],
            "Resource": [
                "arn:aws:ecr:us-west-2:your_account_id:repository/*"
            ]
        }
    ]
}

Private registry policy examples for Amazon ECR

レジストリとレポジトリを間違えると

もしこれをレポジトリポリシーとして適用すると、Resourceの設定ができなく以下のようなエラーが出るはずだ。

putting ECR Repository Policy (my-repo): operation error ECR: SetRepositoryPolicy, https response error StatusCode: 400, RequestID: xxx, InvalidParameterException: Invalid parameter at ‘PolicyText’ failed to satisfy constraint: ‘Invalid repository policy provided’

これはレポジトリにポリシーを設定するのだから、Resourceは設定できなくて当たり前である。なら、Resourceを切ってレポジトリポリシーを設定すればいいのではないかと思うかもしれないが、複製元側で以下のようなエラーが出る(CloudTrailのログで確認)

“User: arn:aws:sts::{source_account}:assumed-role/AWSServiceRoleForECRReplication/ECRReplication is not authorized to perform: ecr:ReplicateImage on resource: arn:aws:ecr:ap-northeast-1:{dest_account}:repository/my-repo because no resource-based policy allows the ecr:ReplicateImage action”

レジストリとレポジトリを間違えて2時間ぐらいハマっていた

Terraformだと余計紛らわしい

  • レポジトリポリシー:aws_ecr_repository_policy
  • レジストリポリシー:aws_ecr_registry_policy

ちなみにこの間違いはGPTも気づかなかった。o3-mini-highもo1-highもずっと自分と一緒にアホみたいな間違えをしていた。

マネジメントコンソールでの見分け方

Features→Permissionsにあるのがレジストリポリシー(こっちをいじる)。

Repositories以下にあるのがレポジトリポリシー

結果

ローカルからnginxをクローンしてソース(develop)のアカウントにPush

docker pull nginx:latest

aws ecr get-login-password --region ap-northeast-1 --profile develop | docker login --username AWS --password-stdin <source_id>.dkr.ecr.ap-northeast-1.amazonaws.com

docker tag <source_id>.dkr.ecr.ap-northeast-1.amazonaws.com/my-repo:nginx

docker push docker tag <source_id>.dkr.ecr.ap-northeast-1.amazonaws.com/my-repo:nginx

同一アカウント内

東京リージョンにPushしたもの

北カリフォルニアリージョンに複製。時刻が古いが以前のレプリケーションが残っていたため

クロスアカウント

develop側の東京より若干遅れている(10秒程度)。ほぼリアルタイムで複製されている

コード

前置きが長くなってしまったが、Terraformのコードを示す。

  • 複製元のアカウント:developプロファイル
  • クロスアカウント複製先:managementプロファイル

これを以下のような複製を行う

  • developアカウントのap-northeast-1にあるmy-repoというレポジトリを
    • developアカウントのus-west-1にあるmy-repoに複製
    • managementアカウントのap-northeast-1にあるmy-reoに複製

リージョン単位でプロバイダ定義するのが面倒だったら、aws_ecr_replication_configurationregistry_idはアカウントIDでもOK(指しているものは一緒)。

#############################
# プロバイダー設定
#############################

# develop アカウント(東京リージョン:ap-northeast-1)
provider "aws" {
  alias   = "develop"
  profile = "develop"
  region  = "ap-northeast-1"
}

# develop アカウント(北カリフォルニアリージョン:us-west-1)
provider "aws" {
  alias   = "develop_us"
  profile = "develop"
  region  = "us-west-1"
}

# management アカウント(東京リージョン:ap-northeast-1)
provider "aws" {
  alias   = "management"
  profile = "management"
  region  = "ap-northeast-1"
}

#############################
# ECR リポジトリの作成
#############################

# develop アカウント側の ECR リポジトリ(ソースリポジトリ)
resource "aws_ecr_repository" "develop_repo" {
  provider = aws.develop
  name     = "my-repo"
  force_delete = true
}

# develop アカウント側の ECR リポジトリ(レプリケーション先)
resource "aws_ecr_repository" "develop_us_repo" {
  provider = aws.develop_us
  name     = "my-repo"
  force_delete = true
}

# management アカウント側の ECR リポジトリ(レプリケーション先)
resource "aws_ecr_repository" "management_repo" {
  provider = aws.management
  name     = "my-repo"
  force_delete = true
}


#############################
# アカウントIDの取得
#############################

data "aws_caller_identity" "develop" {
    provider = aws.develop
}

data "aws_caller_identity" "management" {
    provider = aws.management
}


#############################
# レジストリポリシー(クロスアカウントの場合のみ。develop→management の ReplicateImage を許可)
#############################
resource "aws_ecr_registry_policy" "management_registry_policy" {
  provider = aws.management

  policy = jsonencode({
    Version  = "2012-10-17"
    Statement = [
      {
        Sid      = "ReplicationAccessCrossAccount"
        Effect   = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.develop.account_id}:root"
        }
        Action   = ["ecr:ReplicateImage"]
        Resource = [
          aws_ecr_repository.management_repo.arn
        ]
      }
    ]
  })
}


#############################
# レプリケーション設定(develop アカウント側で設定)
#############################
resource "aws_ecr_replication_configuration" "replication_config" {
  provider = aws.develop

  replication_configuration {
    rule {
      # レプリケーション対象リポジトリのフィルター
      # ※必要に応じてフィルター条件を変更してください
      repository_filter {
        filter      = "my-repo"
        filter_type = "PREFIX_MATCH"
      }

      # 1. 同一 develop アカウント内の別リージョン(us-west-1)へのレプリケーション
      destination {
        region = "us-west-1"
        registry_id = aws_ecr_repository.develop_us_repo.registry_id
      }

      # 2. 管理アカウント(management)の東京リージョン(ap-northeast-1)へのレプリケーション
      # ※ 別アカウントの場合は、destination ブロック内に対象アカウントの registry_id を指定します
      destination {
        region      = "ap-northeast-1"
        registry_id = aws_ecr_repository.management_repo.registry_id
      }
    }
  }
}

所感

  • ECRのリポジトリの複製は簡単。特に同一アカウントなら特にハマる要素はない
  • クロスアカウントになると、レポジトリポリシーとレジストリポリシーに気をつければOK。ここ間違えて2時間ぐらい溶かすのはさすがに…


Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です