見出し画像

TerraformでVPCピアリングを実装してみたよ

こんにちは、すずきです。

最近、あるタスクを達成するために、VPC1内のプライベートサブネットに設置したLambdaアプリケーションを、同一VPC内のパブリックサブネットに存在するNAT Gatewayを介して、VPC2内のパブリックサブネットにあるALB(接続先はプライベートサブネットのECS)のパブリックURLを利用するように設定しました。

しかし、ALBへのトラフィックはLambdaからのものだけで、以下の非機能要求を満たすために、VPCピアリングを介したプライベートな接続に変更することにしました。

  • コスト: NAT Gatewayを利用するよりもデータ転送量が少なくて済み、コスト削減につながる。

  • パフォーマンス: VPC間のネットワーク遅延を最小化でき、大量のデータ転送や低レイテンシを要求するアプリケーションに有効。

  • セキュリティ: 通信がAWS内のプライベートなネットワーク接続で行われるため、通信がインターネットに出るリスクを排除できる。

こちらの記事では、これらの要求を満たすためにTerraformを用いてVPCピアリングを実装する方法を紹介します。


実装

今回は以下のようなディレクトリ構成を採用しています。
各環境(dev, stg, prod)に対応するディレクトリと、再利用可能なモジュール(route_table, security_group, vpc_peering)を用意しています。

-- terraform-project/
   -- environments/
      -- dev/
         -- backend.tf
         -- main.tf
      -- stg/
         -- backend.tf
         -- main.tf
      -- prod/
         -- backend.tf
         -- main.tf
   -- modules/
      -- route_table/
         -- main.tf
         -- variables.tf
         -- outputs.tf
         -- provider.tf
         -- README.md
      -- security_group/
         -- main.tf
         -- variables.tf
         -- outputs.tf
         -- provider.tf
         -- README.md
      -- vpc_peering/
         -- main.tf
         -- variables.tf
         -- outputs.tf
         -- provider.tf
         -- README.md

VPCピアリング

VPCピアリングはAWS内の2つのVPC間で直接的なネットワーク接続を作る機能です。
以下のvpc_peering/main.tfでは、SSMパラメータストアからVPCのIDを取得し、それらを用いてVPCピアリングを設定しています。

data "aws_ssm_parameter" "lambda_vpc_id" {
  name = "/vpc/id/lambda"
}

data "aws_ssm_parameter" "ecs_vpc_id" {
  name = "/vpc/id/ecs"
}

// 同一アカウント内でのVPCピアリングのため、接続と承認を一度に行う
resource "aws_vpc_peering_connection" "vpc_peering" {
  vpc_id      = data.aws_ssm_parameter.lambda_vpc_id.value
  peer_vpc_id = data.aws_ssm_parameter.ecs_vpc_id.value
  auto_accept = true
}

さらに、VPCピアリングのIDを他のモジュールで利用できるようにoutputs.tfで出力しています。

output "vpc_peering_connection_id" {
  description = "Connection Id of VPC Peering"
  value       = aws_vpc_peering_connection.vpc_peering.id
}

ルートテーブル

次に、VPC内のネットワークトラフィックのルートを設定するルートテーブルを作成します。
ここでは、各VPCのCIDRブロックとVPCピアリングのIDを用いて、ピアリング接続を経由するルートを設定しています。
Lambdaが配置されているサブネットのルートテーブルをlambda_local_nat、ALBが配置されているサブネットのルートテーブルをecs_localとしています。

data "aws_ssm_parameter" "lambda_vpc_cidr" {
  name = "/vpc/cidr/lambda"
}

data "aws_ssm_parameter" "ecs_vpc_cidr" {
  name = "/vpc/cidr/ecs"
}

resource "aws_route_table" "lambda_local_nat" {
  vpc_id = data.aws_ssm_parameter.lambda_vpc_id.value
  route {
    cidr_block = "0.0.0.0/0"
    nat_gateway_id = data.aws_ssm_parameter.lambda_nat_gw_id.value
  }
  tags = {
    Name = "lambda-vpc-private-local-nat-rtb"
  }
}

// 既存ルートへの影響を考慮して分割
resource "aws_route" "peering_route" {
  route_table_id            = aws_route_table.lambda_local_nat.id
  destination_cidr_block    = data.aws_ssm_parameter.ecs_vpc_cidr.value
  vpc_peering_connection_id = var.vpc_peering_connection_id
}

resource "aws_route_table" "ecs_local" {
  vpc_id = data.aws_ssm_parameter.ecs_vpc_id.value

  tags = {
    Name = "ecs-vpc-private-local-rtb"
  }
}

// 既存ルートへの影響を考慮して分割
resource "aws_route" "ecs_peering_route" {
  route_table_id                = aws_route_table.ecs_local.id
  destination_cidr_block        = data.aws_ssm_parameter.lambda_vpc_cidr.value
  vpc_peering_connection_id     = var.vpc_peering_connection_id
}

ルートテーブルモジュールでは、VPCピアリングのIDを入力として受け取るために、variables.tfで変数を定義しています。

variable "vpc_peering_connection_id" {
  description = "Connection Id of VPC Peering"
  type        = string
}

セキュリティグループ

最後に、ALBのセキュリティグループを設定します。
これにより、特定のIP範囲からの通信だけを許可するというルールを作ることができます。
今回、Lambdaは2つのプライベートサブネットに存在するので、各サブネットのCIDRブロックを指定しています。

data "aws_ssm_parameter" "lambda_private_subnet_cidr1" {
  name = "/subnet/private/cidr1/lambda"
}

data "aws_ssm_parameter" "lambda_private_subnet_cidr2" {
  name = "/subnet/private/cidr2/lambda"
}

resource "aws_security_group_rule" "elb_sg_ingress_443_lambda" {
  type              = "ingress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  security_group_id = aws_security_group.elb_sg.id
  cidr_blocks       = [data.aws_ssm_parameter.lambda_private_subnet_cidr1.value, data.aws_ssm_parameter.lambda_private_subnet_cidr2.value]
}

プライベートDNSと証明書の設定

Terraformの実装は以上なのですが(あとはterraform applyするだけ)、VPCピアリングはプライベートな接続を扱うため、Lambdaのアプリケーション内でApplication Load Balancer(ALB)の接続先をパブリックURLからプライベートDNSに変更する必要があります。

加えて、HTTPS接続を行うためには、AWS Certificate Manager(ACM)のPrivate Certificate Authority(CA)を用いてプライベート証明書を作成し、その証明書をALBのリスナーに割り当てる作業が必要になります。

以下が設定手順です。

1.Private CAの作成

  • AWS Certificate Manager(ACM)のダッシュボードから「Private CAs」を選択し、「Create CA」をクリックします。

  • Root CAまたはSubordinate CAのタイプを選択し、CAの詳細を入力します。

  • その後、「Next」をクリックし、設定を確認します。

  • 問題がなければ、「Confirm and create」をクリックして、Private CAを作成します。

2.プライベート証明書の発行

  • Private CAが作成されたら、「Certificate Manager」ダッシュボードに戻り、「Get started」をクリックします。

  • 証明書の設定画面で、「Private」を選択し、「Request a certificate」をクリックします。

  • プライベートDNS名を含むドメイン名を入力し、詳細を設定します。

  • 設定が完了したら、「Confirm and request」をクリックします。

3.ALBのリスナーに証明書を割り当てる

  • ALBの設定画面に移動し、対象のリスナーを選択します。

  • 「Actions」ドロップダウンから「Change certificate」を選択し、作成したプライベート証明書を選択します。

採用情報


この記事が気に入ったらサポートをしてみませんか?