LoginSignup
4
0

【AWS】複数Availability Zoneに跨るInternal ALBが名前解決で返してくるIPアドレスはどのIPアドレスか?

Last updated at Posted at 2024-05-27

転職して初めての記事です。
転職後の会社の先輩から表題の質問を貰い、即答できなかったので検証してみました。
たまたま前職でALBの環境の構築をTerraformで行ったので、今回もTerraformで環境構築を行いました。

予想される結論

この先輩の方とも社内のチャットでやり取りした時にもお話したのですが、複数のAvailability Zoneに跨ったALBは名前解決ではラウンドロビンでそれぞれのAvailability ZoneでALBに割り振られたIPアドレスを返してくるんじゃないかな?というのが私の予想です。
ではこの予想通りになるのか、違う動作をするのか検証をしましょう。

構成図

では早速構成図から。
論点がずれないように構成図で認識を合わせましょう。
構成図.png

VPCを2つ作ってVPC Peeringで接続します。
下の方の赤枠で囲っているVPCはオンプレミス相当です。

image.png

真ん中の赤枠部分、Web ServerのEC2は3台とも別のAvailability Zoneに分けて配置します。
本記事の主題部分ですね。
それぞれのAvailability Zoneにサブネットを構築し、Application Load BalancerもそれぞれのAvailability Zoneに跨る構成としました。

image.png

更に上部にある赤枠で囲ったPublic SubnetはNAT GatewayからInternet Gatewayに接続し、Web ServerのEC2がインターネットに接続できる構成しました。
このPublic Subnetの構成は今回の主題である複数Availability Zoneに跨るALBと直接関係ないですが、Terraformで構築しているEC2のヒアドキュメント内で予めhttpdをインストールしているので、このために必要な構成です。
副産物ですね。
ただこの副産物でもTerraformについて面白いことが分かったので、これは別の記事で書きます。

image.png

リージョンは日本から比較的近くて安いインドのムンバイリージョンを使っています。

Terraform

変数設定

まずは変数設定から。

variables.tf
# ---------------------------
# 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です。

vpc.tf
# ---------------------------
# 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.tf
# ---------------------------
# 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タイプは構築できないよ!」と怒られてコケます。

image.png

一番下のEC2はWindowsにしました。
鍵のところが少々面倒ですが、まぁ誤差の範囲ですね。
Windowsの特筆すべき点はAMIのバージョンが昨年11月から更新されているので、これを調べるのにちょっと時間がかかったくらいです。

ALB

続いて主題のALBです。

alb.tf
# ---------------------------
# 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.tf
# ---------------------------
# 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.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.tf
# ---------------------------
# 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.tf
# ---------------------------
# 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実行します。
image.png
問題なく完了しますね。
47個もリソース作っちゃいました。

Web Serverの動作およびALBの設定の確認

ALBがあるのでWeb Serverの確認は瞬殺ですね。
image.png
キタ!

image (1).png

これですよ!
これこれ!

美しいですね!
3台のWeb Server、全部Health CheckがHealthy!
最早神々しさすら感じますね!(文字の色を金色にしたのですが、黄色っぽいですね。)

集中線がうるさいですが、よく見てもらうとゾーンもちゃんとap-south-1a、ap-south-1b、ap-south-1cとなっているので設計通りです。

いや、ほんと、Terraformは素晴らしいですね!

当然ですがALBも設計通りに構築できていることが確認できました。

ALBの動作確認

オンプレミス相当のVPCに構築したWindowsへログインして確認します。
LBのDNS名をEdgeに入力すると
image.png
こんな感じですね。
ちゃんとホスト名出力するようにしたので、F5で画面を更新するたびにこのホスト名が変わります。
image.png
image.png

はい。
これでALBの動作確認も終わります。

本題の確認

はい。
最後に本題です。
結局この
ALBが返してくるIPアドレスはどのサブネット(=Availability Zone)のものか?
に対する回答です。
パケットキャプチャとってきました!
昔に比べたらずいぶん簡単にキャプチャとれるようになりましたね!
image.png
はい。
まずは10.0.1.51というap-south-1aのAvailability ZoneにあるサブネットのIPアドレスを返してきました。
(サブネット=Availability ZoneのIPアドレスセグメントはTerraformのVariable.tfとVPC.tfを見てくださいね!)
ipconfig\flushdnsを実行してブラウザを再起動して再度ALBのFQDNにアクセスすると
image.png
次は10.0.1.102というap-south-1bのAvailability ZoneにあるサブネットのIPアドレスを返してきました。
image.png
最後に10.0.1.143というap-south-1cのAvailability ZoneにあるサブネットのIPアドレスを返してきました。

2024/5/27 追記

本依頼のきっかけになった先輩から
「DNSの返答どうなってるかパケットキャプチャの結果見せてもらっていいですか?」と言われ、調べてみたら一発で解決しました。
Aレコード3つ返ってくる.png
DNSサーバからの返答がAレコードとして登録されている3つサブネットに跨るそれぞれのIPアドレスを返してくれてますね!
先輩!ありがとうございました!

結論

はい。
やっぱり当初の予想通り、複数のAvailability Zoneに跨ったALBは名前解決ではラウンドロビンでそれぞれのAvailability ZoneでALBに割り振られたIPアドレスを返してきましたね。

日付も変わっちゃったので本日はここまで。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0