6/23: S3へのエンドポイントも必要なことを追記
概要
インターネットゲートウェイを設定せずにインターネット接続が制限されたサブネットにあるEC2は、ECR(Elastic Container Registry)との疎通もないため、Dockerなどでコンテナイメージをpullしたりすることができない。

ここではVPCエンドポイントを使って、EC2(が接続されているの制限されたサブネット)とECRをプライベート接続して接続できるようにする設定をお試し。

ECRへ接続するための設定は以下の2つのサービスのエンドポイントを作成する。(エンドポイントはそれぞれ1つずつ作成する)
(6/23追記) ECRへ接続するための設定は以下の2つのサービスのVPCエンドポイントとS3ゲートウェイエンドポイントを作成する。(合計3つのエンドポイントを作成)
- VPCエンドポイント
- com.amazonaws.<リージョン名>.ecr.api
- com.amazonaws.<リージョン名>.ecr.dkr
- S3ゲートウェイエンドポイント
- com.amazonaws.<リージョン名>.s3
なぜS3(へのアクセス)も必要かというと、ECRはAmazon S3を使用してイメージレイヤーを保存するため、pullの際はS3へのアクセスも発生する。
Amazon ECS タスクで Amazon ECR からプライベートイメージをプルするには、Amazon S3 のゲートウェイエンドポイントを作成する必要があります。Amazon ECR は Amazon S3 を使用してイメージレイヤーを保存するため、ゲートウェイエンドポイントが必要です。Amazon ECR からイメージをダウンロードするコンテナは、Amazon ECR にアクセスしてイメージマニフェストを取得してから Amazon S3 にアクセスして実際のイメージレイヤーをダウンロードする必要があります
webポータルから
VPCエンドポイントの作成
VPCmニューの「エンドポイント」から。
「エンドポイントを作成」して以下を入力する。
※ サービスにcom.amazonaws.<リージョン名>.ecr.apiを選択したものとcom.amazonaws.<リージョン名>.ecr.dkrを選択したものを1個ずつ作る
- エンドポイントの設定
- サービス
- サービス:
com.amazonaws.<リージョン名>.ecr.apiとcom.amazonaws.<リージョン名>.ecr.dkr※1個しか選択できないので2つ作る
- サービス:
- VPC
- サブネット
- サブネット: 接続対象のAZとサブネットを選択
- セキュリティグループ
- 接続(インバウンド443/TCP)に必要なセキュリティグループを選択
- ポリシー
- デフォルトのまま (フルアクセス)
入力したら画面下部の「エンドポイントを作成」押下。 しばらく待てばステータスが「使用可能」になり、さらに少し時間を置けば、EC2からECRへ接続できるようになる。
[ec2-user@ip-10-1-2-229 ~]$ curl https://********.dkr.ecr.ap-northeast-1.amazonaws.com/container:tag Not Authorized
※ 疎通確認用でDocker/Podman入ってないのでとりあえずcurlで疎通のみ確認。(未認証のレスポンスがあるのでHTTPSレベルで疎通できている)
VPCエンドポイントが無い状態だと以下の通り。
[ec2-user@ip-10-1-2-229 ~]$ curl https://********.dkr.ecr.ap-northeast-1.amazonaws.com/container:tag curl: (7) Failed to connect to ********.dkr.ecr.ap-northeast-1.amazonaws.com port 443: Connection timed out
以下6/23追記
ただしこの時点ではS3への疎通はないため、レジストリへのログイン(podman login)は可能だけど、イメージpullは失敗する。
[ec2-user@ip-10-1-2-72 ~]$ podman login -u AWS ********.dkr.ecr.ap-northeast-1.amazonaws.com Password: Login Succeeded! [ec2-user@ip-10-1-2-72 ~]$ podman image pull ********.dkr.ecr.ap-northeast-1.amazonaws.com/redis:7 Trying to pull ********.dkr.ecr.ap-northeast-1.amazonaws.com/redis:7... WARN[0030] Failed, retrying in 1s ... (1/3). Error: parsing image configuration: Get "https://prod-ap-northeast-1-starport-layer-bucket.s3.ap-northeast-1.amazonaws.com/ ... : dial tcp *.*.*.*:443: i/o timeout
S3へのアクセスエラーはpublic ipが表示されている。
S3用ゲートウェイエンドポイントの作成
VPCエンドポイントの作成と同じ要領で、
- サービスカテゴリ: AWSのサービス
- サービス:
gatewayでフィルターして、タイプが「Gateway」になっているcom.amazonaws.<リージョン名>.s3を選択 - VPC: 接続対象のVPC
- ルートテーブル: オフラインのサブネットが使うルートテーブルを選択
以上でエンドポイントを作成する。
これでECRに正しくアクセスできるようになる。
[ec2-user@ip-10-1-2-72 ~]$ podman image pull ********.dkr.ecr.ap-northeast-1.amazonaws.com/redis:7 Trying to pull ********.dkr.ecr.ap-northeast-1.amazonaws.com/redis:7... Getting image source signatures Copying blob 1adf6e0f68a8 done | Copying blob 1adf6e0f68a8 done | Copying blob e50738daf1e5 done | : :
Terraformで構築
上で操作した内容をそのままコードに落とし込めばOK
使うリソースはaws_vpc_endpoint
VPCはaws_vpc.example_dev_vpcで、サブネットはaws_subnet.example_dev_disconnected_subnetで作成済みで、リージョン名は変数でvar.region_nameにセットされてる構成。
また、ルートテーブルはVPCのデフォルトのものを使用。
resource "aws_vpc_endpoint" "example_dev_endpoint_to_ecr_api" { vpc_id = aws_vpc.example_dev_vpc.id service_name = "com.amazonaws.${var.region_name}.ecr.api" vpc_endpoint_type = "Interface" private_dns_enabled = true subnet_ids = [ aws_subnet.example_dev_disconnected_subnet.id ] security_group_ids = [ aws_security_group.example_dev_sg.id ] tags = { Name = "example-endpoint-ecr-api" } } resource "aws_vpc_endpoint" "example_dev_endpoint_to_ecr_dkr" { vpc_id = aws_vpc.example_dev_vpc.id service_name = "com.amazonaws.${var.region_name}.ecr.dkr" vpc_endpoint_type = "Interface" private_dns_enabled = true subnet_ids = [ aws_subnet.example_dev_disconnected_subnet.id ] security_group_ids = [ aws_security_group.example_dev_sg.id ] tags = { Name = "example-endpoint-ecr-dkr" } } resource "aws_vpc_endpoint" "example_dev_endpoint_to_s3" { vpc_id = aws_vpc.example_dev_vpc.id service_name = "com.amazonaws.${var.region_name}.s3" vpc_endpoint_type = "Gateway" # これはオフラインのサブネットはデフォルトのルートテーブルを使用してる場合 route_table_ids = [aws_vpc.example_dev_vpc.default_route_table_id] tags = { Name = "example-endpoint-s3" } }
参考
- Amazon ECR インターフェイス VPC エンドポイント (AWS PrivateLink) - Amazon ECR
- VPC 内から AWS サービスに接続する - Amazon Elastic Container Service
- Amazon S3 のゲートウェイエンドポイント - Amazon Virtual Private Cloud
- VPCエンドポイント ってなんのこと? #AWS - Qiita
- VPCエンドポイントについて整理してみた #AWS - Qiita
6/23追記
ECR用エンドポイントだけ作ってcurlでHTTPS的にリポジトリと疎通確認してOKだと思い込んでたけど、実際にpodman image pullるとうまくいかず、S3のエンドポイント設定が必要なことに後から気づいた。
ドキュメントはちゃんと最後までよんで、最後までやりたい操作をきちんと実行して確認しましょう。