こしあん
2025-01-17

Transit Gatewayを試す


19{icon} {views}


VPCピアリングと違い、Transit Gatewayを使えば複数のVPCを一元的に接続できるため、拡張性のある構成を簡単に実現できます。 各VPCのルートテーブルを設定するだけで、すべてのEC2が相互に通信できるようになるのを試してみました。

はじめに

  • VPCピアリングを試すで、2つのVPCにまたがるEC2間の通信ができたのを確認した
  • VPCピアリングはあくまで1:1間の通信で、VPCが複数になったときに管理するのが大変になる。
  • 大量になったときはTransit Gatewayを使うのがセオリーということで試してみた
  • SAP勉強してたらよく出てきたので試してみた

作るもの

  • VPCピアリングの例のVPCを3個に拡張する
  • 各VPCのプライベートサブネットにnginxをインストールしておき、各VPCから別のVPCのEC2に向かってcurlを飛ばしてnginxのトップページが返ってくるかを確認する
  • 各VPCへの接続にはSession Managerを使う。よって、プライベートサブネットからインターネットに出ていける必要があり、NATインスタンスとしてfck-natを使用。

VPCモジュール

ディレクトリは以下の通り。VPCモジュールはmodules/vpc以下。

.
├── main.tf
├── vpc_cluster.tf
├── ec2.tf
├── modules
│   └── vpc
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf

VPCピアリングのときのVPCモジュールをそのまま使い回す。

Transit Gateway(vpc_cluster.tf)

以下のコードで、VPCを3個作成し、Transit Gatewayに紐づけられる。

VPCピアリングとの違いは以下の通り。

  • Transit Gateway自体にルートテーブルがある
    • 各VPCのアップデートのほかに、Transit Gatewayのルートテーブルへの登録が必要。
    • デフォルトのTransit Gatewayのルートテーブルで良ければ、各VPCのTransit Gatewayに紐づけた時点で登録される
  • (同一アカウントであるかぎりは)VPCピアリングのような承認は不要
    • VPCピアリングのときは同一アカウント間でも承認が必要だったが、Transit Gatewayは不要。クロスアカウントになったときはわからない
    • Transit GatewayにVPCを紐づけた時点で自動的に通信ができるようになる
  • 各VPCで、通信したい他のVPCをルートテーブルに登録する操作は、VPCピアリングのときと同様に必要
##########################
# VPC Modules Deployment #
##########################

module "vpc1" {
  source           = "./modules/vpc"
  vpc_name         = "VPC1"
  vpc_cidr_block   = "10.10.0.0/20"
  availability_zones = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
}

module "vpc2" {
  source           = "./modules/vpc"
  vpc_name         = "VPC2"
  vpc_cidr_block   = "10.10.16.0/20"
  availability_zones = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
}

module "vpc3" {
  source           = "./modules/vpc"
  vpc_name         = "VPC3"
  vpc_cidr_block   = "10.10.32.0/20"
  availability_zones = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
}

locals {
  vpc_modles = {
    VPC1 = module.vpc1
    VPC2 = module.vpc2
    VPC3 = module.vpc3
  }
}


##########################
# Transit Gateway Setup  #
##########################

# Create Transit Gateway
resource "aws_ec2_transit_gateway" "tgw" {
  description = "Transit Gateway for inter-VPC communication"
}

# Create Transit Gateway Attachments for each VPC
resource "aws_ec2_transit_gateway_vpc_attachment" "tgw_attach_vpc" {
  for_each = local.vpc_modles

  transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  vpc_id             = each.value.vpc_id
  subnet_ids         = each.value.private_subnet_ids

  tags = {
    Name = "TGW-Attachment-${each.key}"
  }
}

# Create routes in each VPC's private route table to direct traffic to the Transit Gateway
resource "aws_route" "vpc1_to_tgw" {
  for_each = {
    vpc2 = module.vpc2.vpc_cidr_block
    vpc3 = module.vpc3.vpc_cidr_block
  }
  route_table_id         = module.vpc1.private_route_table_id
  destination_cidr_block = each.value
  transit_gateway_id     = aws_ec2_transit_gateway.tgw.id
}

resource "aws_route" "vpc2_to_tgw" {
  for_each = {
    vpc1 = module.vpc1.vpc_cidr_block
    vpc3 = module.vpc3.vpc_cidr_block
  }
  route_table_id         = module.vpc2.private_route_table_id
  destination_cidr_block = each.value
  transit_gateway_id     = aws_ec2_transit_gateway.tgw.id
}

resource "aws_route" "vpc3_to_tgw" {
  for_each = {
    vpc1 = module.vpc1.vpc_cidr_block
    vpc2 = module.vpc2.vpc_cidr_block
  }
  route_table_id         = module.vpc3.private_route_table_id
  destination_cidr_block = each.value
  transit_gateway_id     = aws_ec2_transit_gateway.tgw.id
}

各VPCのルートテーブル

マネジメントコンソールで確認してみる。各VPCのCIDRへは、ターゲットがtgw-*となっており、Transit Gateway経由で接続していることがわかる。

Transit Gatewayのルートテーブル

Transit Gatewayのデフォルトルートテーブル。各VPCが関連付けられているのがわかる。

nginxのEC2(ec2.tf)

VPCピアリングと同様に各VPCのEC2を定義する。セキュリティグループは「接続相手だけ許可」をするとコードが煩雑になるので、「全VPCのEC2があるサブネットからの80からの接続を許可」に変える。

##########################
# EC2 Instances and Supporting Resources
##########################

# Amazon Linux 2023 AMI lookup
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["137112412989"]

  filter {
    name   = "name"
    values = ["al2023-ami-2023*-kernel-*-x86_64"]
  }

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

# Security Groups for EC2 instances in all subnets
resource "aws_security_group" "ec2_sgs" {
  for_each = local.vpc_modles

  name        = "${each.key}-ec2_sg"
  description = "A security group for EC2 instances"
  vpc_id      = each.value.vpc_id

  ingress {
    description = "Allow HTTP traffic from all EC2 subnet"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = [for key, vpc in local.vpc_modles : vpc.private_subnet_cidr_blocks[0]]
  }

  egress {
    description      = "Allow all outbound traffic"
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}

# IAM Role for SSM
resource "aws_iam_role" "ssm_role" {
  name = "ec2_ssm_role_nginx"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Service = "ec2.amazonaws.com" }
      Action    = "sts:AssumeRole"
    }]
  })
}

resource "aws_iam_role_policy_attachment" "ssm_policy_attachment" {
  role       = aws_iam_role.ssm_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_instance_profile" "ssm_instance_profile" {
  name = "ec2_ssm_instance_profile_nginx"
  role = aws_iam_role.ssm_role.name
}

# nginx EC2 instances in VPC1 and VPC2
resource "aws_instance" "nginx" {
  for_each = local.vpc_modles

  ami                         = data.aws_ami.amazon_linux.id
  instance_type               = "t3.micro"
  subnet_id                   = each.value.private_subnet_ids[0]
  associate_public_ip_address = false
  vpc_security_group_ids      = [aws_security_group.ec2_sgs[each.key].id]
  iam_instance_profile        = aws_iam_instance_profile.ssm_instance_profile.name

  user_data = <<-EOF
              #!/bin/bash
              dnf update -y
              dnf install nginx -y
              systemctl start nginx
              systemctl enable nginx
              EOF

  tags = {
    Name = "${each.key}-nginx"
  }
}

# 出力: EC2のプライベートIP
output "nginx_ips" {
  value = { for k, v in aws_instance.nginx : k => v.private_ip }
}

テスト

terraform applyをすると以下のような出力になったとする。

nginx_ips = {
  "VPC1" = "10.10.4.14"
  "VPC2" = "10.10.21.140"
  "VPC3" = "10.10.37.211"
}

VPC1のEC2インスタンスからの接続

Session ManagerからEC2に入り、VPC2のEC2(10.10.21.140)、VPC3のEC2(10.10.37.211)に対してcurlが飛ばせるか確認する。nginxのトップページが返ってきており、正常にアクセスできている。

[root@ip-10-10-4-14 ~]# curl http://10.10.21.140
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@ip-10-10-4-14 ~]# curl http://10.10.37.211
(略)
<h1>Welcome to nginx!</h1>
(略)

VPC2のEC2インスタンスからの接続確認

VPC1のEC2(10.10.4.14)、VPC3のEC2(10.10.37.211)に対してcurlが飛ばせるか確認する。正常にアクセスできている。

[root@ip-10-10-21-140 ~]# curl http://10.10.4.14
(略)
<h1>Welcome to nginx!</h1>
(略)

[root@ip-10-10-21-140 ~]# curl http://10.10.37.211
(略)
<h1>Welcome to nginx!</h1>
(略)

VPC3のEC2インスタンスからの接続確認

VPC1のEC2(10.10.4.14)、VPC2のEC2(10.10.21.140)に対してcurlが飛ばせるか確認する。正常にアクセスできている。

[root@ip-10-10-37-211 ~]# curl http://10.10.4.14
(略)
<h1>Welcome to nginx!</h1>
(略)

[root@ip-10-10-37-211 ~]# curl http://10.10.21.140
(略)
<h1>Welcome to nginx!</h1>
(略)

また自分自身のIPのnginxに対しても接続できる。(10.10.37.211

[root@ip-10-10-37-211 ~]# curl http://10.10.37.211
(略)
<h1>Welcome to nginx!</h1>
(略)

所感

  • Transit Gateway難しそうだったけど思ったより簡単だった。CIDRが重複しないという縛りは必要だけど、VPCが増えたらこれかな
  • あとはコスト次第


Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です