マルチVPCにおけるインターネット通信の集約構成をTerraformで構築してみた
AWS事業本部のイシザワです。
今回はインターネット通信の集約構成をTerraformで構築してみました。
一応、以下の記事の続き物となりますが、新環境を構築するため以前の記事の内容は把握していなくても大丈夫です。
構成要素
Transit Gateway
Transit GatewayはVPCやオンプレミスネットワークの中継ハブです。VPCにTransit Gateway Attachmentを作成することで、VPCとTransit Gatewayを接続できます。
Transit Gateway経由の通信は、Transit Gatewayルートテーブルの「関連付け」と「ルート伝播」によって制御できます。
「関連付け」はVPCからTransit Gatewayへ向かう通信を制御するもので、Transit Gatewayに入った通信はTransit Gateway Attachmentに関連付けられたルートテーブルを参照して行き先を決定します。
「ルート伝播」はTransit GatewayからVPCへ向かう通信を制御するもので、ルート伝播をしたルートテーブルにはVPCのCIDRをDestinationとするルートが自動的に追加されます。
また、DestinationのCIDRブロックと宛先のTransit Gateway Attachmentを設定することで、静的にルートを作成することもできます。
VPC IP Address Manager (IPAM)
IPAMはCIDRの払い出しやIPアドレスの管理を行うリソースです。VPCにIPAMプールを設定することで、IPAMプールに事前にプロビジョニングしたIPプールからCIDRが払い出されます。 今回はVPC CIDRの重複防止とルーティング設計のためにIPAMを利用します。
IPAMプールの階層構造は以下のようになっています。
- トップレベルIPAMプール (10.0.0.0/12)
- 基盤IPAMプール (10.0.0.0/13)
- ワークロードIPAMプール (10.8.0.0/13)
通信要件
今回はVPC間の通信要件ごとにTerraformコードを作成したいと思います。各パターン共通の要件は以下の通りです。
- ワークロード用VPC (Workload-VPC) は複数ある
- Workload-VPCからインターネットへ向かう通信は必ずインターネット集約用VPC (Internet-VPC) からインターネットへ出る
- Workload-VPCから共通サービス用VPC (Common-VPC) は通信可能
以下はWorkload-VPC間の通信要件です。各パターンごとにネットワークを設計します。
- パターン1:Workload-VPC間は通信可能
- パターン2:Workload-VPC間は通信不可
- パターン3:Workload-VPCでグループを作成する。同じグループのVPC間では通信可能、異なるグループのVPC間では通信不可
通信の到達可能性の検証方法
通信の到達可能性の検証にはVPC Reachability Analyzerを利用します。
各VPCにENIを作成してVPC Reachability AnalyzerでENI間が到達可能かを検証することで、VPC間で通信が可能かを検証します。
検証用に以下の分析パスを作成します。Transit Gatewayを経由する場合はリバースパスの検証が行われないため、行きの通信と戻りの通信両方の分析パスを作成します。
- workloadX-to-internet
- Workload-VPC Xとインターネットとの通信(行き)
- workloadX-from-internet
- Workload-VPC Xとインターネットとの通信(戻り)
- workloadX-to-common-service
- Workload-VPC XとCommon-VPCとの通信(行き)
- workloadY-from-common-service
- Workload-VPC XとCommon-VPCとの通信(戻り)
- workloadX-to-workloadY
- Workload-VPC XからWorkload-VPC Yへ向かう通信
パターン1:Workload-VPC間は通信可能
ルーティング設計
ルーティング設計は以下のようになっています。Internet-VPC以外のVPCのルートテーブルはWorkload-VPC0と同様のものになっています。
インターネットとの通信は次のような経路を通ります。
通信経路で特筆すべき箇所をピックアップして解説します。
- 1:Workload-VPCのプライベートサブネットのデフォルトルートをTransit Gatewayに設定しているため、インターネットへの通信はTransit Gatewayに向かいます。
- 3:Transit Gatewayのルートテーブルに0.0.0.0/0をDestinationとする静的ルートを設定しているため、インターネットへの通信はInternet-VPCに向かいます。
- 7:Internet-VPCのパブリックサブネットのルートテーブルにトップレベルIPAMプールのCIDR(10.0.0.0/12)をDestinationとするルートを設定しているため、このネットワーク内のVPCに向かう通信はTransit Gatewayに向かいます。
他のWorkload-VPCとの通信は以下のようになっています。Common-VPCとの通信も同様です。
Terraformコード
ソースコードはGitHubに載せています。内容をピックアップして解説します。
Transit Gateway
今回の構成だと全てのVPC間で通信可能であるため、デフォルト関連付けルートテーブルとデフォルト伝播ルートテーブルを有効にしても問題ありません。
resource "aws_ec2_transit_gateway" "main" { default_route_table_association = "enable" default_route_table_propagation = "enable" auto_accept_shared_attachments = "enable" dns_support = "enable" tags = { Name = "${var.system_id}-tgw" } }
Transit Gatewayルートテーブル
パブリックIPへの通信をInternet-VPCに向けるためのルートをTransit Gatewayルートテーブルに追加します。
resource "aws_ec2_transit_gateway_route" "to_internet" { destination_cidr_block = "0.0.0.0/0" transit_gateway_route_table_id = aws_ec2_transit_gateway.main.association_default_route_table_id transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.internet.id }
到達可能性の検証
以下の変数の値でterraform apply
を行います。
system_id = "sample" toplevel_pool_cidr = "10.0.0.0/12" workload_count = 3
Reachability Analyzerの検証パスも作成されるので、各検証パスを実行することで到達可能性の検証を行います。
Reachability Analyzerによる検証結果は以下の通りです。想定通り全ての通信パスが到達可能となっています。
パターン2:Workload-VPC間は通信不可
ルーティング設計
ルーティング設計は以下のようになっています。
Targetがblackholeとなっているのはブラックホールルートを表しています。ブラックホールルートはDestinationのCIDRに向かう通信を破棄するアクションです。
インターネットとの通信は次のような経路を通ります。
他Workload-VPCとの通信は以下のように途中で破棄されます。
ブラックホールルートのDestinationがワークロードIPAMプールのCIDR(10.8.0.0/13)となっているため、Workload-VPCから他Workload-VPCへ向かう通信はTransit Gatewayで破棄されます。
ちなみにブラックホールルートが無いとInternet-VPCを経由して他Workload-VPCに到達してしまいます。経路は以下の通りです。行きの通信と帰りの通信で図を分けています。
Common-VPCとの通信は次のような経路を通ります。
Terraformコード
ソースコードは以下になります。
Transit Gateway
この構成では一部のVPC間通信を遮断する必要があるので、デフォルト関連付けルートテーブルとデフォルト伝播ルートテーブルは無効としています。
resource "aws_ec2_transit_gateway" "main" { default_route_table_association = "disable" default_route_table_propagation = "disable" auto_accept_shared_attachments = "enable" dns_support = "enable" tags = { Name = "${var.system_id}-tgw" } }
Transit Gatewayルートテーブル
Transit GatewayルートテーブルはWorkload-VPC用とその他のVPC用の2つのルートテーブルを作成します。
Workload-VPC用ルートテーブルには、Internet-VPCに向かうルートとCommon-VPCに向かうルートを追加します。
# Route Table for Workload-VPCs resource "aws_ec2_transit_gateway_route_table" "workload_vpc" { transit_gateway_id = aws_ec2_transit_gateway.main.id } resource "aws_ec2_transit_gateway_route" "workload_vpc_to_internet" { transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.workload_vpc.id destination_cidr_block = "0.0.0.0/0" transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.internet.id } resource "aws_ec2_transit_gateway_route" "block_inter_workloads" { transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.workload_vpc.id destination_cidr_block = local.workload_pool_cidr blackhole = true } resource "aws_ec2_transit_gateway_route_table_propagation" "workload_vpc_to_common_service_vpc" { transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.workload_vpc.id transit_gateway_attachment_id = module.common_service_vpc.transit_gateway_attachment_id }
その他のVPC用のルートテーブルには、Internet-VPCに向かうルートとCommon-VPCに向かうルートに加えて、各Workload-VPCに向かうルートを追加します。
# Route Table for Infrastructure-VPCs resource "aws_ec2_transit_gateway_route_table" "infrastructure_vpc" { transit_gateway_id = aws_ec2_transit_gateway.main.id } resource "aws_ec2_transit_gateway_route" "infrastructure_vpc_to_internet" { transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.infrastructure_vpc.id destination_cidr_block = "0.0.0.0/0" transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.internet.id } resource "aws_ec2_transit_gateway_route_table_propagation" "infrastructure_vpc_to_workload_vpc" { count = var.workload_count transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.infrastructure_vpc.id transit_gateway_attachment_id = module.workload_vpc[count.index].transit_gateway_attachment_id } resource "aws_ec2_transit_gateway_route_table_propagation" "internet_vpc_to_common_service_vpc" { transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.infrastructure_vpc.id transit_gateway_attachment_id = module.common_service_vpc.transit_gateway_attachment_id } resource "aws_ec2_transit_gateway_route_table_association" "internet_vpc" { transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.infrastructure_vpc.id transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.internet.id } resource "aws_ec2_transit_gateway_route_table_association" "common_service_vpc" { transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.infrastructure_vpc.id transit_gateway_attachment_id = module.common_service_vpc.transit_gateway_attachment_id }
到達可能性の検証
以下の変数の値でterraform apply
を行います。
system_id = "sample" toplevel_pool_cidr = "10.0.0.0/12" workload_count = 3
Reachability Analyzerによる検証結果は以下の通りです。想定通りWorkload-VPC間の通信は到達不能になっています。
パターン3:同じグループのWorkload-VPC間は通信可能
ルーティング設定
Workload-VPCのグループ分けは以下のようになっているとします。
グループ番号 | 所属Workload-VPC |
---|---|
0 | Workload-VPC0, Workload-VPC1 |
1 | Workload-VPC2 |
ルーティング設定は以下のようになります。
同じグループのWorkload-VPCは同じルートテーブルに関連付けとルート伝播を行います。
インターネットへの通信とCommon-VPCとの通信の通信経路はパターン2と同様です。
同じグループどうしの通信は以下の経路を通ります。
最長一致のルートが優先されるため、ブラックホールルートよりも同じグループのWorkload-VPCへのルートの方が優先されます。
異なるグループのWorkload-VPCへの通信は以下の図のように、ブラックホールルートで破棄されます。
Terraformコード
ソースコードは以下になります。
インプット
変数group
でWorkload-VPCのグループ分けを指定します。
variable "system_id" {} variable "toplevel_pool_cidr" {} variable "workload_count" { type = number } variable "group" { type = list(list(number)) }
例えばWorkload-VPCを3つ作成し、以下のようなグループ分けをしたいとします。
グループ番号 | 所属Workload-VPC |
---|---|
0 | Workload-VPC0, Workload-VPC1 |
1 | Workload-VPC2 |
このとき、変数の値を以下のように設定します。
workload_count = 3 group = [[0, 1], [2]]
Transit Gateway
パターン2と同様に一部のVPC間通信を遮断する必要があるので、デフォルト関連付けルートテーブルとデフォルト伝播ルートテーブルは無効としています。
Transit Gatewayルートテーブル
Workload-VPC用のTransit Gatewayルートテーブルをグループごとに作成します。各ルートテーブルについて以下を設定しています。
- パブリックIPへの通信をInternet-VPCに向ける静的ルート(
aws_ec2_transit_gateway_route.workload_vpc_to_internet
) - Common-VPCのルート伝播(
aws_ec2_transit_gateway_route_table_propagation.workload_vpc_to_common_service_vpc
) - ワークロードIPAMプールのCIDRへの通信を破棄するブラックホールルート(
aws_ec2_transit_gateway_route.block_inter_workloads
) - グループに所属するWorkload-VPCとの関連付け(
aws_ec2_transit_gateway_route_table_association.workload_vpc
) - グループに所属するWorkload-VPCのルート伝播(
aws_ec2_transit_gateway_route_table_propagation.workload_vpc
)
# Route Table for Workload-VPCs resource "aws_ec2_transit_gateway_route_table" "workload_vpc" { count = length(var.group) transit_gateway_id = aws_ec2_transit_gateway.main.id } resource "aws_ec2_transit_gateway_route" "workload_vpc_to_internet" { count = length(var.group) transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.workload_vpc[count.index].id destination_cidr_block = "0.0.0.0/0" transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.internet.id } resource "aws_ec2_transit_gateway_route" "block_inter_workloads" { count = length(var.group) transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.workload_vpc[count.index].id destination_cidr_block = local.workload_pool_cidr blackhole = true } resource "aws_ec2_transit_gateway_route_table_propagation" "workload_vpc_to_common_service_vpc" { count = length(var.group) transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.workload_vpc[count.index].id transit_gateway_attachment_id = module.common_service_vpc.transit_gateway_attachment_id } resource "aws_ec2_transit_gateway_route_table_association" "workload_vpc" { for_each = merge([for v in [for k_1, v_1 in zipmap(range(length(var.group)), var.group) : [for v_2 in v_1 : [tonumber(k_1), v_2]]] : { for v_3 in v : join(",", v_3) => v_3 }]...) transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.workload_vpc[each.value[0]].id transit_gateway_attachment_id = module.workload_vpc[each.value[1]].transit_gateway_attachment_id } resource "aws_ec2_transit_gateway_route_table_propagation" "workload_vpc" { for_each = merge([for v in [for k_1, v_1 in zipmap(range(length(var.group)), var.group) : [for v_2 in v_1 : [tonumber(k_1), v_2]]] : { for v_3 in v : join(",", v_3) => v_3 }]...) transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.workload_vpc[each.value[0]].id transit_gateway_attachment_id = module.workload_vpc[each.value[1]].transit_gateway_attachment_id }
到達可能性の検証
以下の変数の値でterraform apply
を行います。
system_id = "sample" toplevel_pool_cidr = "10.0.0.0/12" workload_count = 3 group = [[0, 1], [2]]
Reachability Analyzerによる検証結果は以下の通りです。想定通り同じグループのVPC間は到達可能で、異なるグループのVPC間では到達不能であることが確認できます。
まとめ
マルチVPCにおけるインターネットの集約構成をTerraformで3パターンに分けて構築してみました。インターネットへの通信の集約をするときの参考になれば幸いです。