ALBを1 AZ, 1 subnetで使う方法 with Terraform

# Version

```tf
required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "4.59.0"
  }
}
```

# ALBにはAvailability Zoneが2つ必要

ALBにはAvailability Zoneが2つ必要である。
Availability Zoneを1つにしてALBを作ろうとすると次のようなエラーが出る。

```
│ Error: creating ELBv2 application Load Balancer (alb-qiita-production-alb): ValidationError: At least two subnets in two different Availability Zones must be specified
│       status code: 400, request id: 94fc711b-2ad0-4161-a15b-f0e65488f099
│ 
│   with aws_lb.application_load_balancer,
│   on alb.tf line 1, in resource "aws_lb" "application_load_balancer":
│    1: resource "aws_lb" "application_load_balancer" {
```

しかし、可用性が低くても問題にならないサービスでは、Availability Zoneもサブネットも1つで運用したいときがある。
そのようなときには、ALBに何も存在しないダミーサブネットを設定すればよい。

# VPCなど

variablesなど省略している部分がある。

```tf
resource "aws_vpc" "aws_vpc" {
  cidr_block           = "10.10.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = {
    Name        = "${var.product_name}-vpc"
    Environment = var.app_environment
  }
}

resource "aws_internet_gateway" "aws_igw" {
  vpc_id = aws_vpc.aws_vpc.id
  tags = {
    Name        = "${var.product_name}-igw"
    Environment = var.app_environment
  }
}

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.aws_vpc.id
  cidr_block              = var.public_subnet
  availability_zone       = var.availability_zone
  map_public_ip_on_launch = true

  tags = {
    Name        = "${var.product_name}-public-subnet"
    Environment = var.app_environment
  }
}

# ダミーサブネットを定義
resource "aws_subnet" "public_dummy" {
  vpc_id                  = aws_vpc.aws_vpc.id
  cidr_block              = var.public_dummy_subnet
  availability_zone       = var.availability_zone_dummy
  map_public_ip_on_launch = true

  tags = {
    Name        = "${var.product_name}-public-dummy-subnet"
    Environment = var.app_environment
  }
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.aws_vpc.id

  tags = {
    Name        = "${var.product_name}-routing-table-public"
    Environment = var.app_environment
  }
}

resource "aws_route" "public" {
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.aws_igw.id
}

resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

# 必ずダミーサブネットもinternet gatewayに接続!!
resource "aws_route_table_association" "public_dummy" {
  subnet_id      = aws_subnet.public_dummy.id
  route_table_id = aws_route_table.public.id
}

```

# ALB

variablesなど省略している部分がある。

```tf
resource "aws_lb" "application_load_balancer" {
  name               = "${var.product_name}-${var.app_environment}-alb"
  internal           = false
  load_balancer_type = "application"
  # サブネットにサービスのあるパブリックサブネットとダミーサブネットを指定
  subnets            = [aws_subnet.public.id, aws_subnet.public_dummy.id]
  security_groups    = [aws_security_group.load_balancer_security_group.id]

  tags = {
    Name        = "${var.product_name}-alb"
    Environment = var.app_environment
  }
}

resource "aws_security_group" "load_balancer_security_group" {
  vpc_id = aws_vpc.aws_vpc.id

  ingress {
    from_port        = 80
    to_port          = 80
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name        = "${var.product_name}-sg"
    Environment = var.app_environment
  }
}

resource "aws_lb_target_group" "target_group" {
  name        = "${var.product_name}-${var.app_environment}-tg"
  port        = 8080
  protocol    = "HTTP"
  target_type = "ip"
  vpc_id      = aws_vpc.aws_vpc.id

  health_check {
    interval            = 30
    path                = "/health"
    port                = 8080
    protocol            = "HTTP"
    timeout             = 5
    unhealthy_threshold = 2
    matcher             = 200
  }

  tags = {
    Name        = "${var.product_name}-lb-tg"
    Environment = var.app_environment
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_lb_listener" "listener" {
  load_balancer_arn = aws_lb.application_load_balancer.id
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.target_group.id
  }
}

```

# 詰まりやすいところ

ここで重要なのは、ALBに設定するサブネットは2つともパブリックサブネットでなければならないという点である。
[google chrome - AWS Elastic Load Balancing: Seeing extremely long initial connection time - Stack Overflow](https://stackoverflow.com/questions/35523421/aws-elastic-load-balancing-seeing-extremely-long-initial-connection-time)

つまり、何も存在しないサブネットだとしても、ダミーサブネットはinternet gatewayに接続されていなければならない。
必ずダミーサブネットにも`aws_route_table_association`を指定しよう。

これをしなければ`Initial Connect`に1分から2分程度かかることになる。