現状と課題
AWSのコストカットをしたいと思い、Cost Exploer で確認したところ、VPCエンドポイントの利用料金が高額になっていることに気づきました。
2024年3月時点で、VPCエンドポイント(インターフェイスエンドポイント)の料金は東京リージョンで1時間あたり USD 0.014 となっています。
アプリケーションの実行環境には Fargate を利用しており、8つのVPCエンドポイントを、開発環境(dev)、ステージング環境(stg)、本番環境(prd)ごとにそれぞれ作成している状態でしたので、$0.014/hour * 24 hour * 30 days * 8 endpoint * 3 environment * 150(1ドル150円) = ¥36,288/month となっていました。
そこで、今回は shread 環境の VPC (以下 shared VPC)を作成し、それぞれの環境からそれを利用し、VPCエンドポイントを集約してみることにしました。そうすることで、コストを 1/3 に抑えることができ幸せになるはずです。
イメージは以下のような感じになります。
実施内容
1. VPCピアリングを dev VPC から shared VPC に向けて設定する。
- リクエスタ VPC: dev VPC (IPv4 CIDR: 10.2.0.0/16)
- アクセプタ VPC: shared VPC (IPv4 CIDR: 10.255.0.0/16)
Terraform のコードも載せておきます。
1 2 3 4 5 6 7 8 9 |
# VPC Peering resource "aws_vpc_peering_connection" "shared" { vpc_id = aws_vpc.main.id peer_vpc_id = data.aws_vpc.shared.id auto_accept = true tags = { Name = "dev-to-shared" } } |
2. shared 側で VPCエンドポイントを作成する
dev VPC からの接続を許可するために、CIDR、もしくはセキュリティグループを許可する設定をしてください。
※ 許可するポートは 443 番ポートだけで問題ありません
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
resource "aws_security_group" "shared_endpoint" { name = "shared-endpoint" description = "Allow traffic from other VPCs to VPC endpoints" vpc_id = aws_vpc.main.id ingress { from_port = 443 to_port = 443 protocol = "TCP" self = true cidr_blocks = [ "10.2.0.0/16", ] #security_groups = [] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# VPC Endpoint resource "aws_vpc_endpoint" "ecr_dkr" { vpc_id = aws_vpc.main.id service_name = "com.amazonaws.ap-northeast-1.ecr.dkr" vpc_endpoint_type = "Interface" subnet_ids = [ aws_subnet.ap_northeast_1a_private.id, aws_subnet.ap_northeast_1c_private.id, aws_subnet.ap_northeast_1d_private.id, ] security_group_ids = [ aws_security_group.shared_endpoint.id, ] private_dns_enabled = false ### Point } |
vpce-xxxxxxxxxxx-yyyyyyy.dkr.ecr.ap-northeast-1.vpce.amazonaws.com
3. Route53 でプライベートホストゾーンを作成する
Route53 で以下の設定のプライベートホストゾーンを作成してください。
- Domain: dkr.ecr.ap-northeast-1.amazonaws.com
- Comment: 任意
- Type: Private Host Zone
- VPC ID: shared (エンドポイントを持つ側のVPC) ※後述してますが、この時点で dev VPC も追加して構いません
次に、以下の設定のエイリアスレコードを作成したホストゾーンに追加してください。
- Host: なし
- Type: A (エイリアス)
- Value: 作成したエンドポイント (メモした vpce-xxxxxxxxxxx-yyyyyyy.dkr.ecr.ap-northeast-1.vpce.amazonaws.com になります。)
1 |
% aws route53 associate-vpc-with-hosted-zone --hosted-zone-id Z0XXXXXXXXXXX --vpc VPCRegion=ap-northeast-1,VPCId=vpc-0aaaaaaaaaaa |
An error occurred (ConflictingDomainExists) when calling the AssociateVPCWithHostedZone operation: The VPC vpc-0aaaaaaaaaaa in region ap-northeast-1 has already been associated with the hosted zone Z0XXXXXXXXXXX with the same domain name.
- dkr.ecr.ap-northeast-1.amazonaws.com
- *.dkr.ecr.ap-northeast-1.amazonaws.com
1 2 |
sh-5.2$ dig dkr.ecr.ap-northeast-1.amazonaws.com +short sh-5.2$ |
1 2 3 4 |
sh-5.2$ dig dkr.ecr.ap-northeast-1.amazonaws.com +short 10.255.140.244 10.255.174.255 10.255.104.44 |
サービスの要件に合わせて、ステージング環境や本番環境も同様に進めることで VPCエンドポイントを集約し、コストカットが可能となります。
補足
ssm.ap-northeast-1.amazonaws.com, ec2messages.ap-northeast-1.amazonaws.com, ssmmessages.ap-northeast-1.amazonaws.com も対応したにも関わらず、セッションマネージャーを立ち上げ時に以下のようなエラーが出る、もしくはそもそもセッションマネージャーでの接続ができないようなことがあれば、1. の shared側のルートテーブルを見直してください。
1 2 3 4 5 6 7 |
セッションが次の理由で終了されました。 ----------ERROR------- Setting up data channel with id accountname-xxxxxxxx failed: failed to create websocket for datachannel with error: CreateDataChannel failed with no output or error: createDataChannel request failed: failed to make http client call: Post "https://ssmmessages.ap-northeast-1.amazonaws.com/v1/data-channel/accountname-xxxxxxxx": context deadline exceeded (Client.Timeout exceeded while awaiting headers) |
dev 側のルートテーブルで 10.255.0.0./16 をピアリング接続させる設定を追加したように、shared側でも逆の設定が必要となります。VPCエンドポイントだけの利用であれば、インターネットゲートウェイや NAT Gateway など public サブネットも不要です。
また、Fargateなどでイメージを引っ張ってくる際は、 “account-id”.dkr.ecr.ap-northeast-1.amazonaws.com に対して、リクエストが飛ぶため、 dkr.ecr.ap-northeast-1.amazonaws.com 同様にレコードの追加が必要です。デプロイに失敗した際は以下のようなエラーがコンソールなどで確認できるはずです。
1 |
CannotPullContainerError: pull image manifest has been retried 5 time(s): failed to resolve ref 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/example/web:aaaaaa: failed to do request: Head "https://123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/example/web:aaaaaa": dial tcp: lookup 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com on 10.2.0.2:53: no such host |