転職して初めての記事です。
転職後の会社の先輩から表題の質問を貰い、即答できなかったので検証してみました。
たまたま前職でALBの環境の構築をTerraformで行ったので、今回もTerraformで環境構築を行いました。
予想される結論
この先輩の方とも社内のチャットでやり取りした時にもお話したのですが、複数のAvailability Zoneに跨ったALBは名前解決ではラウンドロビンでそれぞれのAvailability ZoneでALBに割り振られたIPアドレスを返してくるんじゃないかな?というのが私の予想です。
ではこの予想通りになるのか、違う動作をするのか検証をしましょう。
構成図
では早速構成図から。
論点がずれないように構成図で認識を合わせましょう。
VPCを2つ作ってVPC Peeringで接続します。
下の方の赤枠で囲っているVPCはオンプレミス相当です。
真ん中の赤枠部分、Web ServerのEC2は3台とも別のAvailability Zoneに分けて配置します。
本記事の主題部分ですね。
それぞれのAvailability Zoneにサブネットを構築し、Application Load BalancerもそれぞれのAvailability Zoneに跨る構成としました。
更に上部にある赤枠で囲ったPublic SubnetはNAT GatewayからInternet Gatewayに接続し、Web ServerのEC2がインターネットに接続できる構成しました。
このPublic Subnetの構成は今回の主題である複数Availability Zoneに跨るALBと直接関係ないですが、Terraformで構築しているEC2のヒアドキュメント内で予めhttpdをインストールしているので、このために必要な構成です。
副産物ですね。
ただこの副産物でもTerraformについて面白いことが分かったので、これは別の記事で書きます。
リージョンは日本から比較的近くて安いインドのムンバイリージョンを使っています。
Terraform
変数設定
まずは変数設定から。
# ---------------------------
# Variables - 変数設定
# ---------------------------
# region
# ap-northeast-1 東京リージョン
# ap-south-1 ムンバイリージョン
variable "region" {
default = "ap-south-1"
}
# 環境種別(本番:prd,ステージング:stg,開発:dev)
variable "env_type" {
default = "dev"
}
# システム名
variable "sys_name" {
default = "test_lb_dns"
}
# availability_zone
variable "availability_zone" {
type = object({
a = string
b = string #ムンバイリージョンで利用
c = string
})
default = {
a = "ap-south-1a" # ムンバイ(インド)のアベイラビリティゾーン
b = "ap-south-1b" # ムンバイ(インド)のアベイラビリティゾーン
c = "ap-south-1c" # ムンバイ(インド)のアベイラビリティゾーン
}
}
# ------------------------------
# Web Server と Application Load Balancer用VPC作成
# ------------------------------
# vpc address
variable "vpc_address_pre" {
default = "10.0."
}
variable "vpc_address_suf" {
default = "0.0/23"
}
# private subnet suffix address01
variable "private_address_suf01" {
default = "1.0/26"
}
# private subnet suffix address02
variable "private_address_suf02" {
default = "1.64/26"
}
# private subnet suffix address03
variable "private_address_suf03" {
default = "1.128/26"
}
# public subnet suffix address01
variable "public_address_suf01" {
default = "0.0/24"
}
# ------------------------------
# Client用VPC作成
# ------------------------------
# vpc address
variable "cli_vpc_address_pre" {
default = "10.1."
}
variable "cli_vpc_address_suf" {
default = "0.0/24"
}
# public subnet suffix address01
variable "cli_public_address_suf01" {
default = "0.0/24"
}
# EC2のインスタンスタイプ
variable "ec2_instance_type" {
default = "t2.micro"
}
# VPC Endpoint
variable "vpc_endpoints" {
type = list(any)
default = ["ssm", "ssmmessages", "ec2messages"]
}
最後の方でEC2のインスタンスタイプをt2.microと定義していますが、ムンバイのAvailability Zoneではt2.microが使えないので、このAvailability Zoneに構築するEC2はこの変数からではなくt3.nanoを構築するようにEC2のtfファイルに直接記載しています。
VPC
VPCです。
# ---------------------------
# Web Server用VPC 構築
# ---------------------------
# VPC
resource "aws_vpc" "vpc" {
cidr_block = "${var.vpc_address_pre}${var.vpc_address_suf}"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.env_type}-${var.sys_name}-vpc"
}
}
# ---------------------------
# Subnet 構築
# ---------------------------
resource "aws_subnet" "sn_private_1a" {
vpc_id = aws_vpc.vpc.id
cidr_block = "${var.vpc_address_pre}${var.private_address_suf01}"
availability_zone = var.availability_zone.a
tags = {
Name = "${var.env_type}-${var.sys_name}-sn-private-1a"
}
}
resource "aws_subnet" "sn_private_1b" {
vpc_id = aws_vpc.vpc.id
cidr_block = "${var.vpc_address_pre}${var.private_address_suf02}"
availability_zone = var.availability_zone.b
tags = {
Name = "${var.env_type}-${var.sys_name}-sn-private-1b"
}
}
resource "aws_subnet" "sn_private_1c" {
vpc_id = aws_vpc.vpc.id
cidr_block = "${var.vpc_address_pre}${var.private_address_suf03}"
availability_zone = var.availability_zone.c
tags = {
Name = "${var.env_type}-${var.sys_name}-sn-private-1c"
}
}
resource "aws_subnet" "sn_public_1a" {
vpc_id = aws_vpc.vpc.id
cidr_block = "${var.vpc_address_pre}${var.public_address_suf01}"
availability_zone = var.availability_zone.a
tags = {
Name = "${var.env_type}-${var.sys_name}-sn-public-1a"
}
}
# Internet Gateway
resource "aws_internet_gateway" "igw_vpc" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.env_type}-${var.sys_name}-igw-vpc"
}
}
# EIP
resource "aws_eip" "eip_nat_1a" {
tags = {
Name = "${var.env_type}-${var.sys_name}-eip-nat1a"
}
}
# Nat Gateway
resource "aws_nat_gateway" "nat_1a" {
allocation_id = aws_eip.eip_nat_1a.id
subnet_id = aws_subnet.sn_public_1a.id
tags = {
Name = "${var.env_type}-${var.sys_name}-nat-1a"
}
}
# ---------------------------
# Route table 作成
# ---------------------------
resource "aws_route_table" "rt_sn_public" {
vpc_id = aws_vpc.vpc.id
# Default GatewayをInternet Gatewayに向ける
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw_vpc.id
}
tags = {
Name = "${var.env_type}-${var.sys_name}-rt-sn-public"
}
}
resource "aws_route_table" "rt_sn_private" {
vpc_id = aws_vpc.vpc.id
# Default GatewayをNAT Gatewayに向ける
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat_1a.id
}
# VPC Peering先のネットワークへのルートを確保
route {
cidr_block = "${aws_vpc.cli_vpc.cidr_block}"
vpc_peering_connection_id = "${aws_vpc_peering_connection.vpc-peering.id}"
}
tags = {
Name = "${var.env_type}-${var.sys_name}-rt-sn-private-1a"
}
}
# SubnetとRoute tableの関連付け
resource "aws_route_table_association" "associate_rt_sn_public_1a" {
subnet_id = aws_subnet.sn_public_1a.id
route_table_id = aws_route_table.rt_sn_public.id
}
resource "aws_route_table_association" "associate_rt_sn_private_1a" {
subnet_id = aws_subnet.sn_private_1a.id
route_table_id = aws_route_table.rt_sn_private.id
}
resource "aws_route_table_association" "associate_rt_sn_private_1b" {
subnet_id = aws_subnet.sn_private_1b.id
route_table_id = aws_route_table.rt_sn_private.id
}
resource "aws_route_table_association" "associate_rt_sn_private_1c" {
subnet_id = aws_subnet.sn_private_1c.id
route_table_id = aws_route_table.rt_sn_private.id
}
# ---------------------------
# Client用VPC 構築
# ---------------------------
# VPC
resource "aws_vpc" "cli_vpc" {
cidr_block = "${var.cli_vpc_address_pre}${var.cli_vpc_address_suf}"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.env_type}-${var.sys_name}-cli-vpc"
}
}
# ---------------------------
# Subnet 構築
# ---------------------------
resource "aws_subnet" "cli_sn_public_1a" {
vpc_id = aws_vpc.cli_vpc.id
cidr_block = "${var.cli_vpc_address_pre}${var.cli_public_address_suf01}"
availability_zone = var.availability_zone.a
tags = {
Name = "${var.env_type}-${var.sys_name}-client-sn-public-1a"
}
}
# Internet Gateway
resource "aws_internet_gateway" "cli_igw_vpc" {
vpc_id = aws_vpc.cli_vpc.id
tags = {
Name = "${var.env_type}-${var.sys_name}-cli-igw-vpc"
}
}
# ---------------------------
# Route table 作成
# ---------------------------
resource "aws_route_table" "rt_cli_sn_public" {
vpc_id = aws_vpc.cli_vpc.id
# Default GatewayをInternet Gatewayに向ける
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.cli_igw_vpc.id
}
# VPC Peering先のネットワークへのルートを確保
route {
cidr_block = "${aws_vpc.vpc.cidr_block}"
vpc_peering_connection_id = "${aws_vpc_peering_connection.vpc-peering.id}"
}
tags = {
Name = "${var.env_type}-${var.sys_name}-cli-rt-sn-public"
}
}
# SubnetとRoute tableの関連付け
resource "aws_route_table_association" "cli_associate_rt_sn_public_1a" {
subnet_id = aws_subnet.cli_sn_public_1a.id
route_table_id = aws_route_table.rt_cli_sn_public.id
}
# ---------------------------
# VPC Peering 作成
# ---------------------------
resource "aws_vpc_peering_connection" "vpc-peering" {
peer_vpc_id = "${aws_vpc.vpc.id}"
vpc_id = "${aws_vpc.cli_vpc.id}"
auto_accept = true
tags = {
Name = "vpc-peering"
}
}
Security Group以外は全部ここで構成しています。
ルーティングテーブルくらいは分けても良かったんですが、面倒なのでここでまとめて記載しました。
これ以上肥大化するなら分けようかなと思います。
VPC2つとInternet Gatewayも2つ、VPC Peering、と盛りだくさんなので、まぁまぁ大きくなっちゃいましたね。
EC2
続いてEC2です。
# ---------------------------
# EC2作成
# ---------------------------
# Amazon Linux 2023 の最新版AMIを取得
data "aws_ssm_parameter" "al2023-ami-kernel-default-x86_64" {
name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
}
# ---------------------------
# 必要台数のEC2を作成
# ---------------------------
# Front End(Web Server) 1台目
resource "aws_instance" "ec2_app01" {
ami = data.aws_ssm_parameter.al2023-ami-kernel-default-x86_64.value
instance_type = var.ec2_instance_type
availability_zone = var.availability_zone.a
vpc_security_group_ids = [aws_security_group.sg_ec2_app01.id]
subnet_id = aws_subnet.sn_private_1a.id
associate_public_ip_address = "false"
iam_instance_profile = aws_iam_instance_profile.instance_prof.name
user_data = <<-EOF
#!/bin/bash
sudo hostnamectl set-hostname ${var.env_type}-${var.sys_name}-ec2-app01
sudo dnf update -y
sudo dnf install -y httpd php
sudo systemctl start httpd
sudo systemctl enable httpd
sudo usermod -a -G apache ec2-user
sudo usermod -a -G apache ssm-user
sudo chown -R ec2-user:apache /var/www
sudo chown -R ssm-user:apache /var/www
sudo chmod 2775 /var/www
find /var/www -type d -exec sudo chmod 2775 {} \;
find /var/www -type f -exec sudo chmod 0664 {} \;
echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php
echo "It Works! ec2-app01" >/var/www/html/index.html
EOF
root_block_device {
volume_size = 30 # GB
volume_type = "gp3" # 汎用SSD
encrypted = false
tags = {
Snapshot = "false"
}
}
tags = {
Name = "${var.env_type}-${var.sys_name}-ec2-app01"
}
depends_on = [
aws_route_table_association.associate_rt_sn_private_1a
]
}
# Front End(Web Server) 2台目
resource "aws_instance" "ec2_app02" {
ami = data.aws_ssm_parameter.al2023-ami-kernel-default-x86_64.value
instance_type = var.ec2_instance_type
availability_zone = var.availability_zone.b
vpc_security_group_ids = [aws_security_group.sg_ec2_app02.id]
subnet_id = aws_subnet.sn_private_1b.id
associate_public_ip_address = "false"
iam_instance_profile = aws_iam_instance_profile.instance_prof.name
user_data = <<-EOF
#!/bin/bash
sudo hostnamectl set-hostname ${var.env_type}-${var.sys_name}-ec2-app02
sudo dnf update -y
sudo dnf install -y httpd php
sudo systemctl start httpd
sudo systemctl enable httpd
sudo usermod -a -G apache ec2-user
sudo usermod -a -G apache ssm-user
sudo chown -R ec2-user:apache /var/www
sudo chown -R ssm-user:apache /var/www
sudo chmod 2775 /var/www
find /var/www -type d -exec sudo chmod 2775 {} \;
find /var/www -type f -exec sudo chmod 0664 {} \;
echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php
echo "It Works! ec2-app02" >/var/www/html/index.html
EOF
root_block_device {
volume_size = 30 # GB
volume_type = "gp3" # 汎用SSD
encrypted = false
tags = {
Snapshot = "false"
}
}
tags = {
Name = "${var.env_type}-${var.sys_name}-ec2-app02"
}
depends_on = [
aws_route_table_association.associate_rt_sn_private_1b
]
}
# Front End(Web Server) 3台目
resource "aws_instance" "ec2_app03" {
ami = data.aws_ssm_parameter.al2023-ami-kernel-default-x86_64.value
instance_type = "t3.nano"
availability_zone = var.availability_zone.c
vpc_security_group_ids = [aws_security_group.sg_ec2_app03.id]
subnet_id = aws_subnet.sn_private_1c.id
associate_public_ip_address = "false"
iam_instance_profile = aws_iam_instance_profile.instance_prof.name
user_data = <<-EOF
#!/bin/bash
sudo hostnamectl set-hostname ${var.env_type}-${var.sys_name}-ec2-app03
sudo dnf update -y
sudo dnf install -y httpd php
sudo systemctl start httpd
sudo systemctl enable httpd
sudo usermod -a -G apache ec2-user
sudo usermod -a -G apache ssm-user
sudo chown -R ec2-user:apache /var/www
sudo chown -R ssm-user:apache /var/www
sudo chmod 2775 /var/www
find /var/www -type d -exec sudo chmod 2775 {} \;
find /var/www -type f -exec sudo chmod 0664 {} \;
echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php
echo "It Works! ec2-app03" >/var/www/html/index.html
EOF
root_block_device {
volume_size = 30 # GB
volume_type = "gp3" # 汎用SSD
encrypted = false
tags = {
Snapshot = "false"
}
}
tags = {
Name = "${var.env_type}-${var.sys_name}-ec2-app03"
}
depends_on = [
aws_route_table_association.associate_rt_sn_private_1c
]
}
# クライアントPC相当のWindows Serverマシンを構築
# ---------------------------
# Windowsマシン用のKey pair作成
# ---------------------------
variable "key_name" {
default = "kp-ec2-01"
}
variable "key_path" {
default = "OneDrive\\ドキュメント\\03.技術\\16.GitHub\\AWS\\alb_qiita01"
}
# 秘密鍵のアルゴリズム設定
resource "tls_private_key" "kp-ec2-01" {
algorithm = "RSA"
rsa_bits = 2048
}
# クライアントPCにKey pair(秘密鍵と公開鍵)を作成
# - Windowsの場合はフォルダを"\\"で区切る(エスケープする必要がある)
# - [terraform apply] 実行後はクライアントPCの公開鍵は自動削除される
locals {
public_key_file = "${var.key_path}\\${var.key_name}.id_rsa.pub"
private_key_file = "${var.key_path}\\${var.key_name}.id_rsa"
}
resource "local_file" "kp_ec2_01_pem" {
filename = "${local.private_key_file}"
content = "${tls_private_key.kp-ec2-01.private_key_pem}"
}
# 上記で作成した公開鍵をAWSのKey pairにインポート
resource "aws_key_pair" "kp_ec2_01" {
key_name = "${var.key_name}"
public_key = "${tls_private_key.kp-ec2-01.public_key_openssh}"
}
# ---------------------------
# クライアント相当のEC2を作成
# ---------------------------
#20240523時点のAMI
#Microsoft Windows Server 2022 Base:ami-0f346136f3b372267
#Microsoft Windows Server 2019 Base:ami-01bd28d73d0053a15
#Microsoft Windows Server 2016 Base:ami-063d3d00f8a97a6d1
resource "aws_instance" "ec2_client01" {
ami = "ami-0f346136f3b372267" #Microsoft Windows Server 2022 Base
instance_type = "m4.large"
availability_zone = var.availability_zone.a
vpc_security_group_ids = [aws_security_group.sg_ec2_client01.id]
subnet_id = aws_subnet.cli_sn_public_1a.id
associate_public_ip_address = "true"
key_name = "${var.key_name}"
iam_instance_profile = aws_iam_instance_profile.instance_prof.name
root_block_device {
volume_size = 100 # GB
volume_type = "gp3" # 汎用SSD
encrypted = false
tags = {
Snapshot = "false"
}
}
tags = {
Name = "${var.env_type}-${var.sys_name}-ec2-client01"
}
}
赤枠内構成図真ん中の3台のEC2はec2-app01~app03という名称にしており、これは全てAmazonlinux2です。
サブネット(=Availability Zone)と名称以外は全て同じ構成ですが、Availability Zone Cに構築しているEC2は前述の通りt3.nanoです。
ちなみにこのゾーンCでt2.microを構築すると「このAvailability Zoneで指定のEC2タイプは構築できないよ!」と怒られてコケます。
一番下のEC2はWindowsにしました。
鍵のところが少々面倒ですが、まぁ誤差の範囲ですね。
Windowsの特筆すべき点はAMIのバージョンが昨年11月から更新されているので、これを調べるのにちょっと時間がかかったくらいです。
ALB
続いて主題のALBです。
# ---------------------------
# ALB 作成
# ---------------------------
# ALB
resource "aws_lb" "alb01" {
load_balancer_type = "application"
name = "alb01"
internal = true # Internal(内部)用LB設定、External(外部)用LB設定はfalse
security_groups = [ aws_security_group.sg_alb01.id ]
subnets = [ aws_subnet.sn_private_1a.id, aws_subnet.sn_private_1b.id, aws_subnet.sn_private_1c.id ]
}
# httpリスナー
resource "aws_lb_listener" "https_listener" {
load_balancer_arn = aws_lb.alb01.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.test_target_group.arn
}
}
# Target Group
resource "aws_lb_target_group" "test_target_group" {
name = "for-webserver-lb-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.vpc.id
health_check {
path = "/phpinfo.php"
}
}
# ec2_app01用ターゲットグループ
resource "aws_lb_target_group_attachment" "for_ec2_app01" {
target_group_arn = aws_lb_target_group.test_target_group.arn
target_id = aws_instance.ec2_app01.id
port = 80
}
# ec2_app02用ターゲットグループ
resource "aws_lb_target_group_attachment" "for_ec2_app02" {
target_group_arn = aws_lb_target_group.test_target_group.arn
target_id = aws_instance.ec2_app02.id
port = 80
}
# ec2_app03用ターゲットグループ
resource "aws_lb_target_group_attachment" "for_ec2_app03" {
target_group_arn = aws_lb_target_group.test_target_group.arn
target_id = aws_instance.ec2_app03.id
port = 80
}
ALBで特筆すべき点は
internal
と
subnets
の部分ですね。
まずinternal
ですが、このALBがInternal Load BalancerかExternal Load Balancerかを定義しています。
コメントにも書いてますが、この設定はBool値なのですが、TrueだとInternal、FalseだとExternalという扱いです。
続いてsubnets
ですが、ここでこのALBが所属するサブネットを定義しています。
当然ALBはこのサブネットが所属するAvailability Zoneに関連付きます。
後は特にどうってことないですね。
Security Group
続いてSecurity Groupです。
# ---------------------------
# Security Group
# ---------------------------
# ---------------------------
# EC2用 Security Group作成
# ---------------------------
#ec2_app01(Front End EC2 1台目用SG)
resource "aws_security_group" "sg_ec2_app01" {
name = "${var.env_type}-${var.sys_name}-sg-ec2-app01"
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.env_type}-${var.sys_name}-sg-ec2-app01"
}
# インバウンドルール
# For SSM
ingress {
description = "AllowHttpsInBoundFromVPC(for SSM)"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# For ALB
ingress {
description = "AllowAnyInBoundFromVPC"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# アウトバウンドルール
egress {
description = "AllowAnyOutBound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
#ec2_app02(Front End EC2 2台目用SG)
resource "aws_security_group" "sg_ec2_app02" {
name = "${var.env_type}-${var.sys_name}-sg-ec2-app02"
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.env_type}-${var.sys_name}-sg-ec2-app02"
}
# インバウンドルール
# For SSM
ingress {
description = "AllowHttpsInBoundFromVPC(for SSM)"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# For ALB
ingress {
description = "AllowAnyInBoundFromVPC"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# アウトバウンドルール
egress {
description = "AllowAnyOutBound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
#ec2_app03(Front End EC2 3台目用SG)
resource "aws_security_group" "sg_ec2_app03" {
name = "${var.env_type}-${var.sys_name}-sg-ec2-app03"
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.env_type}-${var.sys_name}-sg-ec2-app03"
}
# インバウンドルール
# For SSM
ingress {
description = "AllowHttpsInBoundFromVPC(for SSM)"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# For ALB
ingress {
description = "AllowAnyInBoundFromVPC"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# アウトバウンドルール
egress {
description = "AllowAnyOutBound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# クライアント相当Windows Server用SG
resource "aws_security_group" "sg_ec2_client01" {
name = "${var.env_type}-${var.sys_name}-sg-ec2-client01"
vpc_id = aws_vpc.cli_vpc.id
tags = {
Name = "${var.env_type}-${var.sys_name}-sg-ec2-client01"
}
# インバウンドルール
# For SSM
ingress {
description = "AllowHttpsInBoundFromVPC(for SSM)"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# For EC2-app01 ICMPv4
ingress {
description = "AllowICMPv4InBoundFromEC2(for app01)"
from_port = -1
to_port = -1
protocol = "icmp"
security_groups = [aws_security_group.sg_ec2_app01.id]
}
# For EC2-app02 ICMPv4
ingress {
description = "AllowICMPv4InBoundFromEC2(for app02)"
from_port = -1
to_port = -1
protocol = "icmp"
security_groups = [aws_security_group.sg_ec2_app02.id]
}
# For EC2-app03 ICMPv4
ingress {
description = "AllowICMPv4InBoundFromEC2(for app03)"
from_port = -1
to_port = -1
protocol = "icmp"
security_groups = [aws_security_group.sg_ec2_app03.id]
}
# For RDPアクセス
ingress {
description = "AllowRdpInBoundFromHome"
from_port = 3389
to_port = 3389
protocol = "tcp"
cidr_blocks = ["123.225.10.132/32"]
}
# アウトバウンドルール
egress {
description = "AllowAnyOutBound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# ---------------------------
# SSM用 Vpc endpoint Security Group作成
# ---------------------------
resource "aws_security_group" "sg_ssm" {
name = "${var.env_type}-${var.sys_name}-sg-ssm"
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.env_type}-${var.sys_name}-sg-ssm"
}
# インバウンドルール
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# アウトバウンドルール
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "sg_ssmmessages" {
name = "${var.env_type}-${var.sys_name}-sg-ssmmessages"
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.env_type}-${var.sys_name}-sg-ssmmessages"
}
# インバウンドルール
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# アウトバウンドルール
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "sg_ec2messages" {
name = "${var.env_type}-${var.sys_name}-sg-ec2messages"
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.env_type}-${var.sys_name}-sg-ec2messages"
}
# インバウンドルール
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
# アウトバウンドルール
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# ---------------------------
# ALB用 Security Group作成
# ---------------------------
resource "aws_security_group" "sg_alb01" {
name = "sg_alb01"
description = "For alb01"
vpc_id = aws_vpc.vpc.id
# インバウンドルール
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["${var.cli_vpc_address_pre}${var.cli_vpc_address_suf}"]
}
# アウトバウンドルール
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "sg_alb01"
}
}
ここは特別に何かということは無いです。
main
main.tfです。
# ---------------------------
# main
# ---------------------------
terraform {
required_version = ">= 1.4" # Terraformのバージョンを1.4以上に指定
required_providers {
aws = {
source = "hashicorp/aws"
version = "= 5.22.0" # AWSプロバイダのバージョンを5.22.0に固定
}
}
}
# プロバイダー設定
provider "aws" {
region = var.region
}
ここも特に何かといのは無いですね。
output
Terraform実行後のoutputデータです。
# ---------------------------
# Output
# ---------------------------
# 作成したNAT GatewayのパブリックIPアドレスを出力
output "nat_gateway_global_ips" {
value = aws_nat_gateway.nat_1a.*.public_ip
}
NAT GatewayのElastic IPアドレスを出力してます。
iam
SSMのiam設定です。
# ---------------------------
# IAM
# ---------------------------
# IAM ROLE 定義
resource "aws_iam_role" "ec2_role" {
name = "${var.env_type}-${var.sys_name}-iam-role"
assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json
}
# IAMポリシー定義
data "aws_iam_policy_document" "ec2_assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
# IAM ROLEのインスタンスプロフィールの作成
resource "aws_iam_instance_profile" "instance_prof" {
name = "${var.env_type}-${var.sys_name}-instance-profile-ssm"
role = aws_iam_role.ec2_role.name
}
# ---------------------------
# FOR SSM
# ---------------------------
# SSM用ポリシーをEC2ロールに設定
resource "aws_iam_role_policy_attachment" "ssm_control" {
role = aws_iam_role.ec2_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
AmazonlinuxでSSM使っているので、その定義です。
まぁこれもどうってことないですね。
Terraformはまぁこんなもんですかね。
皆さんもTerraformの初期設定が終わっていれば、たぶんこれらの設定コピペで動きます。
動作確認
では早速Terraform実行します。
問題なく完了しますね。
47個もリソース作っちゃいました。
Web Serverの動作およびALBの設定の確認
ALBがあるのでWeb Serverの確認は瞬殺ですね。
キタ!
これですよ!
これこれ!
美しいですね!
3台のWeb Server、全部Health CheckがHealthy!
最早神々しさすら感じますね!(文字の色を金色にしたのですが、黄色っぽいですね。)
集中線がうるさいですが、よく見てもらうとゾーンもちゃんとap-south-1a、ap-south-1b、ap-south-1cとなっているので設計通りです。
いや、ほんと、Terraformは素晴らしいですね!
当然ですがALBも設計通りに構築できていることが確認できました。
ALBの動作確認
オンプレミス相当のVPCに構築したWindowsへログインして確認します。
LBのDNS名をEdgeに入力すると
こんな感じですね。
ちゃんとホスト名出力するようにしたので、F5で画面を更新するたびにこのホスト名が変わります。
はい。
これでALBの動作確認も終わります。
本題の確認
はい。
最後に本題です。
結局この
ALBが返してくるIPアドレスはどのサブネット(=Availability Zone)のものか?
に対する回答です。
パケットキャプチャとってきました!
昔に比べたらずいぶん簡単にキャプチャとれるようになりましたね!
はい。
まずは10.0.1.51というap-south-1aのAvailability ZoneにあるサブネットのIPアドレスを返してきました。
(サブネット=Availability ZoneのIPアドレスセグメントはTerraformのVariable.tfとVPC.tfを見てくださいね!)
ipconfig\flushdnsを実行してブラウザを再起動して再度ALBのFQDNにアクセスすると
次は10.0.1.102というap-south-1bのAvailability ZoneにあるサブネットのIPアドレスを返してきました。
最後に10.0.1.143というap-south-1cのAvailability ZoneにあるサブネットのIPアドレスを返してきました。
2024/5/27 追記
本依頼のきっかけになった先輩から
「DNSの返答どうなってるかパケットキャプチャの結果見せてもらっていいですか?」と言われ、調べてみたら一発で解決しました。
DNSサーバからの返答がAレコードとして登録されている3つサブネットに跨るそれぞれのIPアドレスを返してくれてますね!
先輩!ありがとうございました!
結論
はい。
やっぱり当初の予想通り、複数のAvailability Zoneに跨ったALBは名前解決ではラウンドロビンでそれぞれのAvailability ZoneでALBに割り振られたIPアドレスを返してきましたね。
日付も変わっちゃったので本日はここまで。