マルチVPCにおける名前解決の集約構成をTerraformで構築してみた
AWS事業本部のイシザワです。
マルチVPC環境での各種リソースの集約をやってみたかったので、手始めに名前解決の集約構成を構築してみます。
また、個人的にTerraformの勉強をしているのでTerraformで構築してみようと思います。
構成要素
Route 53 Resolver
Route 53 ResolverはEC2等のAWSリソースからのDNSクエリに再帰的に応答するDNSサーバーです。VPCの構築時に自動的に作成され、IPアドレスはVPCのネットワークアドレス+2(VPC+2アドレス)となります。例えばVPCのCIDRが10.8.0.0/24
の場合は10.8.0.2
に接続することでRoute 53 Resolverにアクセスできます。
EC2が名前解決に利用するDNSサーバーはデフォルトでVPC+2アドレスとなっています。
$ resolvectl dns Global: Link 2 (ens5): 10.8.0.2 Link 3 (docker0):
このエンドポイントにVPC外からアクセスすることはできません。VPCにRoute 53 Resolver Inbound Endpointを作成することでVPC外からのアクセスを受け付けることができます。
DHCPオプションセット
DHCPオプションセットでEC2インスタンスのネットワーク構成を設定できます。EC2インスタンスが利用するDNSサーバーはデフォルトでVPC+2アドレスとなっていますが、DHCPオプションセットを使えば変更することができます。
Transit Gateway
Transit GatewayはVPCやオンプレミスネットワークの中継ハブです。VPCどうしを接続するために利用します。
VPC IPAM (IP Address Manager)
IPAMはCIDRの払い出しやIPアドレスの管理を行うリソースです。名前解決の集約を行うのに必須ではありませんが、重複するCIDRが無いことを保証できるのがTransit Gatewayを使う上で便利なので使っています。
VPCの構築時にIPAMプールを指定することで、VPCのCIDRをプール内から自動的に払い出して設定してくれます。
IPAMプールは階層構造を作ることができ、今回は以下のような階層構造のIPAMプールを作成します。
- トップレベルIPAMプール(10.0.0.0/12)
- 基盤用IPAMプール(10.0.0.0/13)
- ワークロード用IPAMプール(10.8.0.0/13)
全体構成
以下の設定によりDNSの設定を名前解決用VPCに集約します。
- 名前解決用VPCにRoute 53 Resolver Inbound Endpointを作成する
- 他VPCのDHCPオプションセットでDNSサーバーとして↑で作成したエンドポイントを指定する。
全体の構成は以下の図の通りです。
Terraformコード
ソースコードはGitHubに載せています。いくつかピックアップして解説をしていきます。
Transit Gateway
今回の構成だとVPC間の接続はAny-to-Any接続で十分のため、Transit Gatewayのデフォルト関連付けルートテーブルとデフォルト伝播ルートテーブルを有効にしています。
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" } }
Route 53 Resolver Inbound Endpoint
Route 53 Resolver Inbound Endpointにはセキュリティグループを設定する必要があります。名前解決を行うVPCからのtcp/53とudp/53を許可すればOKです。ここではトップレベルIPAMプールのCIDRからのtcp/53とudp/53を許可しています。
エンドポイントはip_address
を2つ以上設定する必要があります。今回は同じサブネット内に設定していますが、高可用性構成の場合は異なるAZのサブネットを指定します。
resource "aws_Route 53_resolver_endpoint" "main" { name = "${var.system_id}-dns-endpoint" direction = "INBOUND" security_group_ids = [ aws_security_group.allow_dns.id ] ip_address { subnet_id = aws_subnet.private.id ip = local.primary_dns_ip_address } ip_address { subnet_id = aws_subnet.private.id ip = local.secondary_dns_ip_address } } resource "aws_security_group" "allow_dns" { name = "allow_dns" description = "Allow DNS inbound traffic" vpc_id = aws_vpc.main.id ingress { from_port = 53 to_port = 53 protocol = "udp" cidr_blocks = [var.toplevel_pool_cidr] } ingress { from_port = 53 to_port = 53 protocol = "tcp" cidr_blocks = [var.toplevel_pool_cidr] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "allow_dns" } }
DHCPオプションセット
DHCPオプションセットの設定でRoute 53 Resolver Inbound EndpointのIPアドレスをDNSサーバーに指定します。このDHCPオプションセットをワークロード用VPCと関連付けます。
resource "aws_vpc_dhcp_options" "main" { domain_name = "${var.region_name}.compute.internal" domain_name_servers = [ var.primary_dns_ip_address, var.secondary_dns_ip_address, ] tags = { Name = "${local.prefix}-dhcp-options" } } resource "aws_vpc_dhcp_options_association" "main" { vpc_id = aws_vpc.main.id dhcp_options_id = aws_vpc_dhcp_options.main.id }
テスト用インスタンス
テスト用インスタンスにはSSMセッションマネージャーでログインするため、それ用のIAMロールを作成してアタッチします。
data "aws_iam_policy_document" "instance_assume_role_policy" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ec2.amazonaws.com"] } } } resource "aws_iam_role" "instance_ssm_role" { name = "${var.system_id}-iamrole-instance-ssm" path = "/" assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json managed_policy_arns = [ "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore", ] } resource "aws_iam_instance_profile" "instance_ssm_role" { name = "${var.system_id}-instance-profile-instance-ssm" role = aws_iam_role.instance_ssm_role.name }
疎通確認のためのICMPを許可するセキュリティグループをアタッチします。
resource "aws_instance" "test_instance" { ami = data.aws_ami.latest_amazon_linux_2023.id instance_type = "t3.micro" subnet_id = aws_subnet.public.id iam_instance_profile = var.instance_profile_name vpc_security_group_ids = [aws_security_group.allow_icmp.id] tags = { Name = "${local.prefix}-test-instance" } } resource "aws_security_group" "allow_icmp" { name = "allow_icmp" description = "Allow ICMP inbound traffic" vpc_id = aws_vpc.main.id ingress { from_port = -1 to_port = -1 protocol = "icmp" 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 = "allow_icmp" } }
また、名前解決用VPCのPrivate Hosted Zoneにテスト用インスタンスのDNSレコードを登録します。
resource "aws_Route 53_record" "test_instance" { zone_id = var.Route 53_zone_id name = "${local.domain_host_name}.${var.parent_domain_name}" type = "A" ttl = 300 records = [aws_instance.test_instance.private_ip] }
動作確認
以下の変数の設定でリソースの構築を行います。
system_id = "sample" ipam_topleve_pool_cidr = "10.0.0.0/12" domain_name = "example.com"
作成されたテスト用インスタンスsample-workload0-test-instance
にSSMセッションマネージャーでログインします。
DNSサーバーにRoute 53 Resolver Inbound EndpointのIPアドレスが設定されていることが確認できます。
$ resolvectl dns Global: Link 2 (ens5): 10.0.0.253 10.0.0.254 Link 3 (docker0):
名前解決用VPCのPrivate Hosted Zoneに、別のワークロードVPCに作成したインスタンスのDNS名が登録されています。 このDNS名が解決できることを確認できます。
$ resolvectl query workload1.example.com workload1.example.com: 10.8.2.237 -- link: ens5 -- Information acquired via protocol DNS in 4.0ms. -- Data is authenticated: no; Data was acquired via local or encrypted transport: no -- Data from: network
このDNS名に対してpingが通ることが確認できます。
$ ping workload1.example.com -c 5 PING workload1.example.com (10.8.2.237) 56(84) bytes of data. 64 bytes from ip-10-8-2-237.ap-northeast-1.compute.internal (10.8.2.237): icmp_seq=1 ttl=126 time=0.565 ms 64 bytes from ip-10-8-2-237.ap-northeast-1.compute.internal (10.8.2.237): icmp_seq=2 ttl=126 time=0.395 ms 64 bytes from ip-10-8-2-237.ap-northeast-1.compute.internal (10.8.2.237): icmp_seq=3 ttl=126 time=0.379 ms 64 bytes from ip-10-8-2-237.ap-northeast-1.compute.internal (10.8.2.237): icmp_seq=4 ttl=126 time=0.391 ms 64 bytes from ip-10-8-2-237.ap-northeast-1.compute.internal (10.8.2.237): icmp_seq=5 ttl=126 time=0.384 ms --- workload1.example.com ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4005ms rtt min/avg/max/mdev = 0.379/0.422/0.565/0.071 ms
注意
リソース削除時、IPAMプールの削除に失敗することがあります。これはVPCの削除とIPAMプールの割り当てIPアドレス領域の解放との間でタイムラグがあるのが原因です。少し時間を置けば削除できます。
まとめ
マルチVPC環境における名前解決の集約構成をTerraformで構築しました。今回は単独アカウントで行いましたが、マルチアカウントでも同様の構成を取ることができると思います。
次回はVPCエンドポイントの集約構成をTerraformで構築してみたいと思います。
2023/04/20 追記)VPCエンドポイントの集約構成も試してみました。