🌐

[Terraform] Fargate v1.4で必要なVPC endpoint設定

2021/01/03に公開

ECS/Fargateで、コンテナーで動くWebアプリケーションのインフラを構築したい場合で、よくあるのはセキュリティーを考慮して、Fargateはプライベートサブネット内に置きたいというケースがあると思います。

ただプライベートサブネットにあるFargateでも、ECRなど別リソースと通信したい場合に手っ取り早くNAT gatewayを使うでもいいですが、コスト面でNAT gatewayはよろしくないので、比較的低コストなVPC endpointを使う方法があります。

https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/vpc-endpoints.html

https://dev.classmethod.jp/articles/fargate_pv14_vpc_endpoint/

今回のユースケース

  • NAT gatewayを使わずにECRからDockerイメージをpullしたい
  • Fargate上で、AWS Systems Manager パラメータストア経由で環境変数を使用したい
  • コンテナー内のログをCloudWatchに送信したい

Terraformによる設定

  • S3は、Gateway型なので、ルートテーブルとの紐付けが必要
  • それ以外は、Interface型なので、それぞれのエンドポイント用のセキュリティグループが必要
  • 前提として、VPC, private subnet, route tableなど各種設定も必要

必要なエンドポイント

  • s3
  • ecr.dkr
  • ecr.api
  • logs
  • ssm

注:var.xxx の部分は適時自身の環境で置き換えてもらえますと🙏🏻

resource "aws_vpc_endpoint" "s3" {
  vpc_id            = aws_vpc.app_network.id
  service_name      = "com.amazonaws.ap-northeast-1.s3"
  vpc_endpoint_type = "Gateway"
}

resource "aws_vpc_endpoint_route_table_association" "private_s3" {
  count           = length(aws_subnet.app_private)
  vpc_endpoint_id = aws_vpc_endpoint.s3.id
  route_table_id  = aws_route_table.app_private[count.index].id
}

resource "aws_security_group" "vpc_endpoint" {
  name   = "vpc_endpoint_sg"
  vpc_id = aws_vpc.app_network.id

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [aws_vpc.app_network.cidr_block]
  }

  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [aws_vpc.app_network.cidr_block]
  }
}

resource "aws_vpc_endpoint" "ecr_dkr" {
  vpc_id              = aws_vpc.app_network.id
  service_name        = "com.amazonaws.ap-northeast-1.ecr.dkr"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.app_private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoint.id]
  private_dns_enabled = true
}

resource "aws_vpc_endpoint" "ecr_api" {
  vpc_id              = aws_vpc.app_network.id
  service_name        = "com.amazonaws.ap-northeast-1.ecr.api"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.app_private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoint.id]
  private_dns_enabled = true
}

resource "aws_vpc_endpoint" "logs" {
  vpc_id              = aws_vpc.app_network.id
  service_name        = "com.amazonaws.ap-northeast-1.logs"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.app_private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoint.id]
  private_dns_enabled = true
}

resource "aws_vpc_endpoint" "ssm" {
  vpc_id              = aws_vpc.app_network.id
  service_name        = "com.amazonaws.ap-northeast-1.ssm"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.app_private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoint.id]
  private_dns_enabled = true
}

resource "aws_vpc" "app_network" {
  cidr_block           = var.vpc.vpc
  enable_dns_hostnames = true
  enable_dns_support   = true
  instance_tenancy     = "default"
}

resource "aws_subnet" "app_private" {
  count             = length(var.vpc.private_subnets)
  vpc_id            = aws_vpc.app_network.id
  cidr_block        = var.vpc.private_subnets[count.index].cidr_block
  availability_zone = var.vpc.private_subnets[count.index].availability_zone

  tags = {
    Name = "${var.terraform_environment}_private_${var.vpc.private_subnets[count.index].name}"
  }
}

resource "aws_route_table" "app_private" {
  count  = length(aws_subnet.app_private)
  vpc_id = aws_vpc.app_network.id
}

resource "aws_route_table_association" "app_private" {
  count          = length(aws_subnet.app_private)
  subnet_id      = aws_subnet.app_private[count.index].id
  route_table_id = aws_route_table.app_private[count.index].id
}

必要な設定は以上。

Discussion