5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS FargateではVPC Endpointを使う

Last updated at Posted at 2022-03-02

AmazonECRからコンテナイメージをpullし、private-subnetにおいてFargateでホストされるAmazonECSタスクを運用します。コンテナイメージをpullする際は、インターフェイスタイプのVPCエンドポイントを使用するようにし、それ以外はNatGatewayを使用することで通信費を大幅に低減させつつ、セキュリティを高めることができるのでおすすめです。
ちなみにNatGatewayを使用する場合、後で請求を見て驚かないように下記3点のコストを意識する必要があります。
だとしてもSquidを構築して運用する気にはならない...

  • 時間単位料金
  • データ処理料金
  • 転送料金:インターネットへの通信、NatGatewayとEC2インスタンスが異なるアベイラビリティーゾーンにある場合に発生
    冗長構成で1GB使用する想定だと $90.64となります。
runningcost
730 時間 (1 か月) x 0.062 USD = 45.26 USD (ゲートウェイの 1 時間あたりの使用料金)
1 GB/月 x 0.062 USD = 0.06 USD (NAT ゲートウェイのデータ処理料金)
45.26 USD + 0.06 USD = 45.32 USD (NAT ゲートウェイ処理および月間使用時間)
2 NAT ゲートウェイ x 45.32 USD = 90.64 USD (NAT ゲートウェイの使用料金とデータ処理コストの合計額)
NAT ゲートウェイの使用料金とデータ処理料金の合計額 (毎月): 90.64 USD

ややこしいのですが、 FargateでホストされるAmazonECSタスクでは、下記のAmazonECSインターフェイスVPCエンドポイントは必要ありません。

  1. com.amazonaws.region.ecs-agent
  2. com.amazonaws.region.ecs-telemetry
  3. com.amazonaws.region.ecs

Fargateを使用する場合、下記のエンドポイントが必要です。

  • プラットフォームバージョン1.3.0以前

    1. com.amazonaws.region.ecr.dkr
    2. com.amazonaws.ap-northeast-1.s3
  • プラットフォームバージョン1.4.0以降(4〜6は用途では必要)

    1. com.amazonaws.ap-northeast-1.ecr.dkr
    2. com.amazonaws.ap-northeast-1.ecr.api
    3. com.amazonaws.ap-northeast-1.s3
    4. com.amazonaws.ap-northeast-1.logs(CloudWatchLogsを使用)
    5. com.amazonaws.ap-northeast-1.ssmmessages(ECSExecを使用)
    6. com.amazonaws.ap-northeast-1.kms(ECSExecを使用)
data source
main.tf
data "aws_vpc" "example_function" {
  tags = {
    Name = "example-function"
  }
}

data "aws_subnet" "example_function_private_1a" {
  filter {
    name   = "tag:Name"
    values = ["example-function-private-1a"]
  }
}

data "aws_subnet" "example_function_private_1c" {
  filter {
    name   = "tag:Name"
    values = ["example-function-private-1c"]
  }
}

data "aws_subnet" "example_function_public_1a" {
  filter {
    name   = "tag:Name"
    values = ["example-function-public-1a"]
  }
}

data "aws_subnet" "example_function_public_1c" {
  filter {
    name   = "tag:Name"
    values = ["example-function-public-1c"]
  }
}

data "aws_route_table" "example_function_private_1a" {
  vpc_id = data.aws_vpc.example_function.id
  filter {
    name   = "tag:Name"
    values = ["example-function-private-1a"]
  }
}

data "aws_route_table" "example_function_private_1c" {
  vpc_id = data.aws_vpc.example_function.id
  filter {
    name   = "tag:Name"
    values = ["example-function-private-1c"]
  }
}
security_group VPCEndpointにアタッチしたセキュリティグループでは、VPCプライベートサブネットから、TCP:443で着信接続を許可する
security_group.tf
resource "aws_security_group" "example_function_ecr" {
  vpc_id      = data.aws_vpc.example_function.id
  name        = "example-function-ecr"
  description = "example-function-ecr traffic"

  ingress {
    from_port   = "443"
    to_port     = "443"
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name        = "example-function-ecr"
    Service     = "example-function"
  }
}
variables
variables.tf
variable "example_function_ecr_service_names" {
  type = map(any)

  default = {
    example-function-ecr-dkr = {
      service_name = "com.amazonaws.ap-northeast-1.ecr.dkr"
    }
    example-function-ecr-api = {
      service_name = "com.amazonaws.ap-northeast-1.ecr.api"
    }
  }
}

variable "example_function_ecr_ssm_kms_service_names" {
  type = map(any)

  default = {
    example-function-ecr-ssm = {
      service_name = "com.amazonaws.ap-northeast-1.ssmmessages"
    }
    example-function-ecr-kms = {
      service_name = "com.amazonaws.ap-northeast-1.kms"
    }
  }
}
vpc_endpoint
  • VPCEndpoint policyは、明示的に定義しない場合、サービスへのフルアクセスを許可するデフォルトのポリシーがAWSによって自動的にアタッチされます
  • AmazonECRに最低限のAmazonS3バケットアクセス許可のみを許可
          "Action" : [
            "s3:GetObject",
            "s3:GetBucketLocation"
          ],
          "Effect" : "Allow",
          "Resource" : [
            "arn:aws:s3:::ap-northeast-1-starport-layer-bucket",
            "arn:aws:s3:::ap-northeast-1-starport-layer-bucket/*",
            "arn:aws:s3:::example-function-front",
            "arn:aws:s3:::example-function-front/*",
           ]
vpc_endpoint.tf
resource "aws_vpc_endpoint" "example_function_ecr_cw" {
  vpc_id            = data.aws_vpc.example_function.id
  service_name      = "com.amazonaws.ap-northeast-1.logs"
  vpc_endpoint_type = "Interface"

  security_group_ids = [
    aws_security_group.example_function_ecr.id,
  ]

  private_dns_enabled = true

  tags = {
    Name        = "example-function-ecr-cw"
    Service     = "example-function"
  }
}

resource "aws_vpc_endpoint_subnet_association" "sn_private_1a_cw" {
  vpc_endpoint_id = aws_vpc_endpoint.example_function_ecr_cw.id
  subnet_id       = data.aws_subnet.example_function_private_1a.id
}

resource "aws_vpc_endpoint_subnet_association" "sn_private_1c_cw" {
  vpc_endpoint_id = aws_vpc_endpoint.example_function_ecr_cw.id
  subnet_id       = data.aws_subnet.example_function_private_1c.id
}

resource "aws_vpc_endpoint" "example_function_ecr_s3" {
  vpc_id            = data.aws_vpc.example_function.id
  service_name      = "com.amazonaws.ap-northeast-1.s3"
  vpc_endpoint_type = "Gateway"

  policy = jsonencode(
    {
      "Statement" : [
        {
          "Sid" : "Access-to-specific-bucket-only",
          "Principal" : "*",
          "Action" : [
            "s3:GetObject",
            "s3:GetBucketLocation"
          ],
          "Effect" : "Allow",
          "Resource" : [
            "arn:aws:s3:::ap-northeast-1-starport-layer-bucket",
            "arn:aws:s3:::ap-northeast-1-starport-layer-bucket/*",
            "arn:aws:s3:::example-function-front",
            "arn:aws:s3:::example-function-front/*",
           ]
        }
      ]
    }
  )
  tags = {
    Name        = "example-function-ecr-s3"
    Service     = "example-function"
  }
}

resource "aws_vpc_endpoint_route_table_association" "sn_private_1a" {
  vpc_endpoint_id = aws_vpc_endpoint.example_function_ecr_s3.id
  route_table_id  = data.aws_route_table.example_function_private_1a.id
}

resource "aws_vpc_endpoint_route_table_association" "sn_private_1c" {
  vpc_endpoint_id = aws_vpc_endpoint.example_function_ecr_s3.id
  route_table_id  = data.aws_route_table.example_function_private_1c.id
}

resource "aws_vpc_endpoint" "example_function_ecr" {
  for_each = var.example_function_ecr_service_names

  vpc_id            = data.aws_vpc.example_function.id
  service_name      = each.value.service_name
  vpc_endpoint_type = "Interface"

  security_group_ids = [
    aws_security_group.example_function_ecr.id,
  ]

  private_dns_enabled = true

  tags = {
    Name        = each.key
    Service     = "example-function"
  }
}

resource "aws_vpc_endpoint_subnet_association" "sn_private_1a" {
  for_each = var.example_function_ecr_service_names

  vpc_endpoint_id = aws_vpc_endpoint.example_function_ecr[each.key].id
  subnet_id       = data.aws_subnet.example_function_private_1a.id
}

resource "aws_vpc_endpoint_subnet_association" "sn_private_1c" {
  for_each = var.example_function_ecr_service_names

  vpc_endpoint_id = aws_vpc_endpoint.example_function_ecr[each.key].id
  subnet_id       = data.aws_subnet.example_function_private_1c.id
}




resource "aws_vpc_endpoint" "example_function_ecr_ssm_kms" {
  for_each = var.example_function_ecr_ssm_kms_service_names

  vpc_id            = data.aws_vpc.example_function.id
  service_name      = each.value.service_name
  vpc_endpoint_type = "Interface"

  security_group_ids = [
    aws_security_group.example_function_ecr.id,
  ]

  private_dns_enabled = true

  tags = {
    Name        = each.key
    Service     = "example-function"
  }
}

resource "aws_vpc_endpoint_subnet_association" "sn_public_1a" {
  for_each = var.example_function_ecr_ssm_kms_service_names

  vpc_endpoint_id = aws_vpc_endpoint.example_function_ecr_ssm_kms[each.key].id
  subnet_id       = data.aws_subnet.example_function_public_1a.id
}

resource "aws_vpc_endpoint_subnet_association" "sn_public_1c" {
  for_each = var.example_function_ecr_ssm_kms_service_names

  vpc_endpoint_id = aws_vpc_endpoint.example_function_ecr_ssm_kms[each.key].id
  subnet_id       = data.aws_subnet.example_function_public_1c.id
}
5
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?