AWS ECS利用時にECRからDockerコンテナを、プライベートネットワーク経由でpull出来るようにする為に、AWS ECRのPrivateLinkをTerraformを使って作成した。

概要

用語

  • Private Link:インタフェースタイプのVPCエンドポイント
  • VPCエンドポイント
    • VPCと他のサービス間の通信を可能にするVPCコンポーネント
    • 以下の2種類がある
      • インタフェースタイプ(=Private Link)
        • 実態には、プライベートアドレスを持つENI
      • ゲートウェイタイプ
        • ルートテーブルで、送信先に対するターゲットとして指定する(ルーティングの設定が必要)

必要な対応の概要

  • 公式Docに沿って確認
    • EC2起動タイプを使用するECSタスクでECRからpullするには、ECSのPrivateLinkを作成する必要がある(Fargateの場合は不要)
    • ECRに対してAPIコールを発行するリージョンと同じリーションにエンドポイントを作成する必要がある(クロスリージョンリクエストをサポートしていない)
    • ECR PrivateLinkのSGでは、443ポートを解放する必要がある
    • プライベート DNS ホスト名を有効にすると、APIを叩く時に--endpoint-url を指定しなくてもよい

やってみた

対応概要

以下のVPCエンドポイントを作成する対応が必要になる

  • AWS PrivateLink endpoints for ECR
    • Dockerマニフェストファイルをダウンロードする
  • Gateway VPC endpoint for Amazon S3
    • インスタンスはプライベートなS3バケットに接続し、そこに配置されたDockerレイヤーファイルをダウンロードする
  • 以下、両方のエンドポイントが必要になる
    • com.amazonaws.region.ecr.dkr(Fargate/EC2起動タイプ両方で必要)
    • com.amazonaws.region.ecr.api(EC2起動タイプでのみ必要)

Terraformで作成

  • Terraformで作成すると以下の形(VPCなど他のリソースは別途作成している前提)
resource "aws_vpc_endpoint" "sample-ecr-api" {
  service_name      = "com.amazonaws.ap-northeast-1.ecr.api"
  vpc_endpoint_type = "Interface"
  vpc_id            = "${aws_vpc.sample.id}"
  subnet_ids        = ["${aws_subnet.sample-private-subnets.*.id}"]

  security_group_ids = [
    "${aws_security_group.sample-ecr-privatelink.id}",
  ]

  private_dns_enabled = true
}

resource "aws_vpc_endpoint" "sample-ecr-dkr" {
  service_name      = "com.amazonaws.ap-northeast-1.ecr.dkr"
  vpc_endpoint_type = "Interface"
  vpc_id            = "${aws_vpc.sample.id}"
  subnet_ids        = ["${aws_subnet.sample-private-subnets.*.id}"]

  security_group_ids = [
    "${aws_security_group.sample-ecr-privatelink.id}",
  ]

  private_dns_enabled = true
}
  • ちなみに、この状態でDockerイメージをpullしようとしても、S3にアクセス出来ない場合は、失敗する
$ docker pull XXXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/sample:latest
latest: Pulling from sample
84ed7d2f608f: Pulling fs layer
be2bf1c4a48d: Pulling fs layer
a5bdc6303093: Pulling fs layer
e9055237d68d: Pulling fs layer
3e84841fa9bd: Pulling fs layer

Gateway VPC endpoint for Amazon S3

注意事項

  • S3 Gatewayを追加する際には、S3への接続が瞬断する ので、本番運用中のシステムで作業時には注意する必要がある。

Amazon S3 ゲートウェイエンドポイントを作成する際、コンテナに既に Amazon S3 に対する接続がある場合は、ゲートウェイが追加される間にその接続が一時的に中断される可能性があります。この中断を回避するには、Amazon S3 ​ゲートウェイエンドポイントを使用する新しい VPC を作成してから、Amazon ECS クラスターとそのコンテナを新しい VPC に移行します。

Terraformで作成

resource "aws_vpc_endpoint" "sample-s3" {
  vpc_id          = "${aws_vpc.sample.id}"
  service_name    = "com.amazonaws.ap-northeast-1.s3"
  route_table_ids = ["${aws_route_table.sample-private.id}"]
}

結果

EKS上にECRからイメージを取得するPodを立てて検証

  • EKSのWorker Nodeにアタッチしたセキュリティグループのアウトバウンドを、S3 GatewayとECR PrivateLinkのSGしか開けていない状態で、ECRからpullする事が出来た
$ cat pull-from-ecr.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pull-from-ecr
spec:
  imagePullSecrets:
  - name: ap-northeast-1-ecr-registry
  containers:
  - name: pull-from-ecr
    image: XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/sample:latest
    imagePullPolicy: Always
$ kubectl get pods | grep pull
pull-from-ecr                                  1/1       Running   0          19s

まとめ

  • S3 Gatewayの作成が必要なのが、ちょっとハマりポイント(Dockerレイヤーファイルをダウンロードするため)
  • ECRのAPIも、FargateとEC2起動タイプで利用するAPIが異なるので、ちょっとハマりポイント