Terraform 実践 #4 ALB と Auto Scaling — launch template、ターゲットグループ、スケーリングポリシー
前回のインスタンスは 1 台だけで、落ちたら終わりで、トラフィックが集中しても増えません。今回はこの 3 つの問題を一度に解決します。ALB が入口を受け持ち、Auto Scaling グループ(ASG)がインスタンスの台数と生死を管理する構成です。Terraform の観点で今回の学びどころは、5 種類のリソース(launch template、ALB、ターゲットグループ、リスナー、ASG)が参照でつながっていく様子、そしてすでに存在するリソースをコードから削除する経験です。
launch template — インスタンス定義を再利用できる形に #
ASG には「この定義どおりにインスタンスを起動しろ」という型が必要です。前回 aws_instance に書いた内容を launch template に移します。
# 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(新規ファイル)
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 つを束ねます。
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 のアドレスに接続します。
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 バケット、そしてデータ系リソースに掛ける安全装置です。