AWS/Terraform

【Terraform】NLBのターゲットにALBを登録してみた

今回は、TerraformでNLBのターゲットにALBを登録した環境を構築しました。

<補足>
2021年9月27日、NLBのターゲットにALBを登録することができるようになっています。
参考:https://aws.amazon.com/jp/about-aws/whats-new/2021/09/application-load-balancer-aws-privatelink-static-ip-addresses-network-load-balancer/

Terraformは上記のアップデートに対応済みです。
参考:https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group
target_type – (May be required, Forces new resource) Type of target that you must specify when registering targets with this target group. See doc for supported values. The default is instance.
→Terraform公式ドキュメントに、AWS公式ドキュメント内にあるパラメータ(ターゲットタイプがALB)が使えますよ、という旨が記述されています。

構成図

構成図は以下です。
パブリックサブネットのNLB→プライベートサブネットのALB→プライベートサブネットのEC2インスタンス(Webサーバー)の構成です。

tfファイルの内容

今回はtfファイルを3つに分けました。
各tfファイルの概要は以下です。

ファイル名 概要
infra.tf VPCやサブネット、セキュリティグループ等のインフラを構築
ec2.tf ALBのターゲットとなるWebサーバー用EC2を構築
elb.tf ALB、NLBを構築

infra.tf

ALB用セキュリティグループのインバウンドは便宜上、0.0.0.0/0 としています。
上記のIPアドレスを絞る場合は、NLBを配置しているサブネットではなく、クライアント(実際にHTTPのリクエストを出す端末)のCIDRを許可する必要があります。
メタ引数である「for_each」の使い方については、以下の記事でご紹介しています。
【Terraform】3AZ分のサブネットを1つの定義で構築してみた

############################################################################
# VPC
############################################################################
resource "aws_vpc" "vpc" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "test-vpc"
  }
}

############################################################################
# インターネットゲートウェイ
############################################################################
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "test-igw"
  }
}

############################################################################
# サブネット
############################################################################
# NLB用パブリックサブネット
resource "aws_subnet" "nlb_subnet" {
  for_each = {
    "a" = "10.0.0.0/24"
    "c" = "10.0.1.0/24"
  }
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = each.value
  availability_zone = "ap-northeast-1${each.key}"
  tags = {
    Name = "test-nlb-subnet-${each.key}"
  }
}

# ALB用プライベートサブネット
resource "aws_subnet" "alb_subnet" {
  for_each = {
    "a" = "10.0.2.0/24"
    "c" = "10.0.3.0/24"
  }
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = each.value
  availability_zone = "ap-northeast-1${each.key}"
  tags = {
    Name = "test-alb-subnet-${each.key}"
  }
}

# EC2用プライベートサブネット
resource "aws_subnet" "ec2_subnet" {
  for_each = {
    "a" = "10.0.4.0/24"
    "c" = "10.0.5.0/24"
  }
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = each.value
  availability_zone = "ap-northeast-1${each.key}"
  tags = {
    Name = "test-ec2-subnet-${each.key}"
  }
}

############################################################################
# ルートテーブル
############################################################################
resource "aws_route_table" "public_route" {
  vpc_id = aws_vpc.vpc.id
  route {
    gateway_id = aws_internet_gateway.igw.id
    cidr_block = "0.0.0.0/0"
  }
  tags = {
    Name = "test-public-route"
  }
}

# サブネットとの紐づけ
resource "aws_route_table_association" "route_public_association" {
  for_each = {
    "a" = "1"
    "c" = "2"
  }
  subnet_id      = aws_subnet.nlb_subnet["${each.key}"].id
  route_table_id = aws_route_table.public_route.id
}

############################################################################
# セキュリティグループ
############################################################################
# ALB用セキュリティグループ
resource "aws_security_group" "sg_alb" {
  name        = "test-sg-alb"
  description = "Allow http from NLB"
  vpc_id      = aws_vpc.vpc.id

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

# EC2用セキュリティグループ
resource "aws_security_group" "sg_ec2" {
  name        = "test-sg-ec2"
  description = "Allow http from ALB"
  vpc_id      = aws_vpc.vpc.id
}

# EC2用セキュリティグループのインバウンドへALBのセキュリティグループをソースに指定
resource "aws_security_group_rule" "sg_ec2_rule" {
  type                     = "ingress"
  to_port                  = 80
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.sg_alb.id
  from_port                = 80
  security_group_id        = aws_security_group.sg_ec2.id
}

ec2.tf

AMIは、事前にApacheをインストールして、「/var/www/html/index.html(中身はHello world」を作成したものを使用しています。
※プライベートなAMIであるためブランクとさせていただいています。
サブネットと同様、「for_each」でEC2インスタンスをまとめて2つ構築しています。

##############################################################################
# EC2インスタンス
##############################################################################
resource "aws_instance" "ec2" {
  for_each = {
    "a" = "1"
    "c" = "2"
  }
  ami                    = "ami-xxxxxxxxxxxxxxxxx"
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.ec2_subnet["${each.key}"].id
  vpc_security_group_ids = [aws_security_group.sg_ec2.id]
  tags = {
    Name = "test-ec2-${each.key}"
  }
}

elb.tf

NLBのターゲットグループ定義にて、属性「target_type」でALBを指定する事ができます。
NLBのターゲットグループへのターゲット紐づけ定義では、「target_id」にALBのARNを指定する必要があります。

############################################################################
# ELB
############################################################################
#---------------------------------------------------------------------------
# ALB
#---------------------------------------------------------------------------
# ALB本体
resource "aws_lb" "alb" {
  name               = "test-alb"
  internal           = true
  load_balancer_type = "application"
  security_groups    = [aws_security_group.sg_alb.id]
  subnets = [
    aws_subnet.alb_subnet["a"].id,
    aws_subnet.alb_subnet["c"].id
  ]
  tags = {
    Name = "test-alb"
  }
}

# ALBのターゲットグループ
resource "aws_lb_target_group" "tg_alb" {
  name     = "test-tg-alb"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.vpc.id
}

# ALBのターゲットグループへのターゲットを紐づけ
resource "aws_lb_target_group_attachment" "tg_alb_attachment" {
  for_each = {
    "a" = "1"
    "c" = "2"
  }
  target_group_arn = aws_lb_target_group.tg_alb.arn
  target_id        = aws_instance.ec2["${each.key}"].id
}

# ALBのリスナー設定(ALBとターゲットグループの紐づけ)
resource "aws_lb_listener" "listener_alb" {
  load_balancer_arn = aws_lb.alb.arn
  port              = 80
  protocol          = "HTTP"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.tg_alb.arn
  }
}

#---------------------------------------------------------------------------
# NLB
#---------------------------------------------------------------------------
# NLB本体
resource "aws_lb" "nlb" {
  name               = "test-nlb"
  internal           = false
  load_balancer_type = "network"
  subnets = [
    aws_subnet.nlb_subnet["a"].id,
    aws_subnet.nlb_subnet["c"].id
  ]
  tags = {
    Name = "test-nlb"
  }
}

# NLBのターゲットグループ
resource "aws_lb_target_group" "tg_nlb" {
  name        = "test-tg-nlb"
  port        = 80
  protocol    = "TCP"
  vpc_id      = aws_vpc.vpc.id
  target_type = "alb"
}

# NLBのターゲットグループへのターゲット紐づけ
resource "aws_lb_target_group_attachment" "tg_nlb_attachment" {
  target_group_arn = aws_lb_target_group.tg_nlb.arn
  target_id        = aws_lb.alb.arn
  port             = 80
}

# NLBのリスナー設定(NLBとターゲットグループの紐づけ)
resource "aws_lb_listener" "listener_nlb" {
  load_balancer_arn = aws_lb.nlb.arn
  port              = 80
  protocol          = "TCP"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.tg_nlb.arn
  }
}

NLB→ALB→EC2の接続を確認する

上記の各tfファイルについて、terraform applyが完了したら、通信の疎通を確認します。

まず、構築した各ターゲットグループでステータスが「Healthy」であることを確認した上で、手元のブラウザでNLBのDNS名にアクセスします。

index.htmlの内容が表示され、無事接続できていることを確認できました。

参考情報

・AWS公式ドキュメント(NLBでALBの指定が可能に)
https://aws.amazon.com/jp/about-aws/whats-new/2021/09/application-load-balancer-aws-privatelink-static-ip-addresses-network-load-balancer/
・Terraform公式ドキュメント(LBのターゲットグループ定義)
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group

今回は、TerraformでNLBのターゲットにALBを登録した環境構築をご紹介しました。
この記事を通して、少しでもTerraform習得のお役に立てれば幸いです。