Terraform 実践 #4 ALB と Auto Scaling — launch template、ターゲットグループ、スケーリングポリシー

読了 4分

前回のインスタンスは 1 台だけで、落ちたら終わりで、トラフィックが集中しても増えません。今回はこの 3 つの問題を一度に解決します。ALB が入口を受け持ち、Auto Scaling グループ(ASG)がインスタンスの台数と生死を管理する構成です。Terraform の観点で今回の学びどころは、5 種類のリソース(launch template、ALB、ターゲットグループ、リスナー、ASG)が参照でつながっていく様子、そしてすでに存在するリソースをコードから削除する経験です。

launch template — インスタンス定義を再利用できる形に #

ASG には「この定義どおりにインスタンスを起動しろ」という型が必要です。前回 aws_instance に書いた内容を launch template に移します。

compute.tf
# compute.tf 修正
resource "aws_launch_template" "web" {
  name_prefix   = "myapp-${var.env}-web-"
  image_id      = data.aws_ami.al2023.id
  instance_type = "t3.micro"

  vpc_security_group_ids = [aws_security_group.web.id]

  user_data = base64encode(<<-EOF
    #!/bin/bash
    dnf install -y nginx
    echo "myapp ${var.env} - $(hostname)" > /usr/share/nginx/html/index.html
    systemctl enable --now nginx
  EOF
  )

  lifecycle {
    create_before_destroy = true
  }
}

内容はほぼそのままですが、違いが 2 つあります。user_data を base64encode() で包む必要があること(launch template の API 要件)、そして name_prefix と create_before_destroy のセットをここにも掛けたことです。template が変わると新しいバージョンが作られ、ASG が新しいインスタンスからそれを使う仕組みなので、前回の「user_data の変更は置き換え」という性質が、ここではローリング入れ替えの材料になります。

ALB、ターゲットグループ、リスナー #

ロードバランサーは 3 つのリソースの組み合わせです。ALB 本体(入口)、ターゲットグループ(後ろに並ぶサーバーの集まりとヘルスチェック)、リスナー(入口と集まりをつなぐルール)です。

lb.tf
# lb.tf(新規ファイル)
resource "aws_lb" "main" {
  name               = "myapp-${var.env}"
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = [for s in aws_subnet.public : s.id]
}

resource "aws_lb_target_group" "web" {
  name_prefix = "web-"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = aws_vpc.main.id

  health_check {
    path                = "/"
    healthy_threshold   = 2
    unhealthy_threshold = 3
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.main.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.web.arn
  }
}

ALB はパブリックサブネットに、前回作っておいた ALB SG を付けて配置されます。リスナーはひとまず HTTP 80 だけ開けます。HTTPS には証明書が必要で、それはドメインと一緒に #10 で扱うことにしたので、今の 80 リスナーはそのときリダイレクトに変わる予定です。

Auto Scaling グループとスケーリングポリシー #

これで 3 つを束ねます。

compute.tf
resource "aws_autoscaling_group" "web" {
  name_prefix         = "myapp-${var.env}-web-"
  min_size            = 2
  max_size            = 4
  desired_capacity    = 2
  vpc_zone_identifier = [for s in aws_subnet.private : s.id]
  target_group_arns   = [aws_lb_target_group.web.arn]
  health_check_type   = "ELB"

  launch_template {
    id      = aws_launch_template.web.id
    version = "$Latest"
  }

  lifecycle {
    ignore_changes = [desired_capacity]
  }
}

resource "aws_autoscaling_policy" "cpu" {
  name                   = "cpu-target-tracking"
  autoscaling_group_name = aws_autoscaling_group.web.name
  policy_type            = "TargetTrackingScaling"

  target_tracking_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ASGAverageCPUUtilization"
    }
    target_value = 60
  }
}

設計のポイントを整理します。

  • min 2、2 つの AZ のプライベートサブネット: 1 台が落ちても、1 つの AZ に問題が起きてもサービスが残る最小構成です。
  • health_check_type = “ELB”: EC2 のステータスチェックだけでなく、ターゲットグループのヘルスチェック失敗も入れ替えの理由にします。nginx が落ちたインスタンスは ASG が自動で入れ替えます。
  • ignore_changes = [desired_capacity]: 基礎 #7で予告したまさにその事例です。スケーリングポリシーが調整した台数を、次の apply が 2 に戻さないようにします。
  • ターゲット追跡ポリシー: 平均 CPU 60% を保つように自動で増減します。段階的なアラームを自分で組むより宣言的で、Terraform と相性の良い方式です。

単一インスタンスの削除 — コードの削除がそのままリソースの削除です #

前回の aws_instance.web は役目を終えました。IaC でのリソース削除はコマンドではなくコードの削除です。compute.tf から aws_instance ブロックを消して plan を見ると、「1 to destroy」としてそのインスタンスだけが正確に削除対象に挙がります。新しいリソースを作りながら古いものを消すこの 1 回の plan に今回の変更のすべてが要約されているので、シリーズで初めて plan を上から下までじっくり読んでみる価値のある場面です。

apply が終わったら、terraform output に追加しておいた ALB のアドレスに接続します。

outputs.tf
output "alb_dns_name" {
  value = aws_lb.main.dns_name
}

ブラウザで myapp dev - ip-10-0-11-x... というレスポンスが返り、リロードを繰り返すとホスト名が交互に変わります。2 台が交互に応答する、このシリーズで最初の動作確認です。ALB とインスタンスが動いているので、今日はここまでにする場合は destroy を忘れないでください。

まとめ #

今回扱った内容です。

  • インスタンス定義を launch template に移しました。user_data は base64 エンコードが必要で、template の変更は新しいバージョンになってローリング入れ替えの材料になります
  • ALB は本体、ターゲットグループ、リスナーの 3 リソースの組み合わせです。HTTPS は #10 でドメインと一緒に付けます
  • ASG は min 2 で 2 つの AZ にまたがり、health_check_type ELB でアプリケーションレベルの自己修復を備えました
  • desired_capacity に ignore_changes を掛けて、スケーリングポリシーと Terraform が衝突しないようにしました
  • リソースの削除はコードの削除です。単一インスタンスを消す plan で、IaC の削除の流れを確認しました

次回(#5 RDS と S3)ではデータ層を作ります。プライベートサブネットの PostgreSQL RDS と静的アセット用の S3 バケット、そしてデータ系リソースに掛ける安全装置です。

X