ECRのクロスリージョン/クロスアカウントレプリケーションを試す
同一アカウント内での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_configuration
のregistry_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の中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内
技術書コーナー
北海道の駅巡りコーナー