TerraformでALBとターゲットグループを設計する方法|aws_lb・aws_lb_listener・ヘルスチェックとHTTPSリダイレクトの実践

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Terraform > TerraformでALBとターゲットグループを設計する方法|aws_lb・aws_lb_listener・ヘルスチェックとHTTPSリダイレクトの実践
「ALBをコンソールから手作業で設定したが、次にステージング環境を複製するときにまた全部やり直すのか。」
そういう場面でTerraformを使えば、ALBの構成全体をHCLで宣言し、terraform apply一発で再現できます。

この記事では、TerraformでALBを設計するときに欠かせない aws_lb・aws_lb_target_group・aws_lb_listener の3リソースの関係を整理し、HTTP→HTTPSリダイレクト・ヘルスチェック設計・path-basedルーティングまでを実例のHCLで解説します。AWS provider 5.x・Terraform 1.9で動作を確認しています。

この記事のポイント

・ALBはaws_lb・aws_lb_target_group・aws_lb_listenerの3リソースで構成される
・HTTP→HTTPSリダイレクトはaws_lb_listenerのredirectアクションで1本宣言できる
・ヘルスチェックのthreshold値を絞りすぎると正常インスタンスが外れ続ける
・path-basedルーティングはaws_lb_listener_ruleのpriority順で評価される


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜALBをTerraformで管理するのか

AWS Management ConsoleでALBを作ると、「どのセキュリティグループを付けたか」「ヘルスチェックのパスは何だったか」「どのルーティングルールを追加したか」といった設定が画面の奥深くに埋もれていきます。変更履歴は残らず、メンバーが別のメンバーの設定を上書きしても気づきにくい。

TerraformでALBを管理すると、構成全体がHCLファイルとして残ります。terraform planでどこが変わるかを事前に確認でき、git diffでレビューも通せます。コンソール手作業の暗黙的な設定が全て可視化されるのが最大のメリットです。

aws_lb・aws_lb_target_group・aws_lb_listenerの構成関係

TerraformでALBを作るとき、最低限必要なリソースは次の3つです。

・aws_lb:ALB本体。DNSエントリやIPアドレスはここに紐づく
・aws_lb_target_group:リクエストを転送する先のグループ。ヘルスチェック設定もここに書く
・aws_lb_listener:ポートとプロトコルのルール。どのターゲットグループへ転送するかを決める

3つの参照関係は「aws_lb_listenerがaws_lbのARNを参照し、default_actionの中でaws_lb_target_groupのARNを参照する」流れになります。aws_lb_target_groupはaws_lbのARNを参照しない点に注意してください。ターゲットグループは単独で作れます。どのALBに紐づくかはリスナー側が決めます。

ALB本体(aws_lb)の設計

1. internet-facingとinternalの選択基準

internal = falseがインターネット向け(パブリックサブネット)、internal = trueがVPC内専用(プライベートサブネット)です。外部からリクエストを受けるWebアプリケーションはinternal = false一択です。バックエンドAPIやマイクロサービス間通信にだけ使うALBはinternalにして、インターネットからアクセスできない構成にします。

# ALB本体 resource "aws_lb" "main" { name = "app-alb" internal = false load_balancer_type = "application" security_groups = [aws_security_group.alb.id] subnets = [ aws_subnet.public_a.id, aws_subnet.public_c.id, ] # アクセスログをS3へ保存(本番では有効化推奨) # access_logs { # bucket = aws_s3_bucket.alb_logs.bucket # prefix = "app-alb" # enabled = true # } }

2. セキュリティグループとサブネットの指定

ALBに付けるセキュリティグループには、受け付けるポートへのインバウンドを開けます。外部からHTTP(80)とHTTPS(443)を受ける場合は次のように書きます。

resource "aws_security_group" "alb" { name = "alb-sg" vpc_id = aws_vpc.main.id ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }

subnetsには2つ以上のAZのサブネットを指定してください。1つだけ指定するとエラーになります。internet-facingの場合はパブリックサブネットを、internalの場合はプライベートサブネットを指定します。

ターゲットグループのヘルスチェック設計(aws_lb_target_group)

1. ヘルスチェックのパスとthresholdを決める

ヘルスチェックで見落としがちなのがthreshold(正常判定に必要な連続成功回数)の設定です。デフォルト(3回)のままにすると、デプロイ直後にインスタンスがヘルシーになるまで時間がかかりすぎます。逆に1回にするとノイズでインスタンスが外れやすくなります。現場ではhealthy_threshold = 2、unhealthy_threshold = 3に調整することが多いです。

resource "aws_lb_target_group" "app" { name = "app-tg" port = 8080 protocol = "HTTP" vpc_id = aws_vpc.main.id health_check { path = "/health" protocol = "HTTP" matcher = "200" interval = 30 timeout = 5 healthy_threshold = 2 unhealthy_threshold = 3 } }

matcherには期待するHTTPステータスコードを書きます。"200"のように単一指定も、"200-299"のように範囲指定もできます。アプリケーションが302を返すパスをヘルスチェックに使うと永遠にUnhealthyになるため注意してください。

2. ターゲットタイプ:instanceとipの使い分け

target_typeのデフォルトはinstanceです。EC2インスタンスIDで登録する通常のケースはこれで問題ありません。ECSのFargateタスクに転送したい場合はipを指定します。Fargateはインスタンスを持たないため、コンテナのIPアドレス直接で登録する必要があります。

# Fargate向けターゲットグループ resource "aws_lb_target_group" "fargate" { name = "fargate-tg" port = 3000 protocol = "HTTP" vpc_id = aws_vpc.main.id target_type = "ip" # Fargateはipを指定 health_check { path = "/healthz" matcher = "200" } }

リスナーの設計(aws_lb_listener)

1. HTTP→HTTPSリダイレクト

ポート80(HTTP)へのアクセスを全てポート443(HTTPS)へ301リダイレクトする設定は、redirectアクションで1本書けます。

resource "aws_lb_listener" "http_redirect" { load_balancer_arn = aws_lb.main.arn port = "80" protocol = "HTTP" default_action { type = "redirect" redirect { port = "443" protocol = "HTTPS" status_code = "HTTP_301" } } }

status_codeは"HTTP_301"(恒久リダイレクト)が一般的です。テスト用途で"HTTP_302"(一時的リダイレクト)を使う場合もありますが、本番では301に固定してください。ブラウザが302をキャッシュしないため、毎回リダイレクトを踏む無駄が生じます。

2. HTTPSリスナーとACM証明書の参照

HTTPSリスナーにはACM証明書のARNを渡します。証明書がすでに発行済みであればdataブロックで参照し、新規発行が必要ならaws_acm_certificateリソースで作ります。

# 既存証明書をdata sourceで参照する場合 data "aws_acm_certificate" "main" { domain = "example.com" statuses = ["ISSUED"] most_recent = true } resource "aws_lb_listener" "https" { load_balancer_arn = aws_lb.main.arn port = "443" protocol = "HTTPS" ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" certificate_arn = data.aws_acm_certificate.main.arn default_action { type = "forward" target_group_arn = aws_lb_target_group.app.arn } }

ssl_policyには最新のTLS 1.3対応ポリシーELBSecurityPolicy-TLS13-1-2-2021-06を指定するのが2024年以降の標準です。古いELBSecurityPolicy-2016-08はTLS 1.0/1.1を許可するため、PCI DSS準拠環境では使えません。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、TerraformでALBのHTTPS設定から本番ALB設計まで、ハンズオン形式で習得できるセミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら

ターゲットグループへのEC2登録(aws_lb_target_group_attachment)

固定のEC2インスタンスをターゲットグループに登録するにはaws_lb_target_group_attachmentを使います。

resource "aws_lb_target_group_attachment" "app_a" { target_group_arn = aws_lb_target_group.app.arn target_id = aws_instance.app_a.id port = 8080 } resource "aws_lb_target_group_attachment" "app_c" { target_group_arn = aws_lb_target_group.app.arn target_id = aws_instance.app_c.id port = 8080 }

Auto Scalingグループと連携させる場合はaws_lb_target_group_attachmentではなく、aws_autoscaling_groupのtarget_group_arnsにターゲットグループのARNを渡す方法を使います。固定登録とAuto Scalingを混在させると、Auto Scalingが起動したインスタンスとattachmentで登録したインスタンスが重複する場合があるため注意してください。

path-basedルーティングで複数サービスを振り分ける

1つのALBに複数のサービスを乗せるときはaws_lb_listener_ruleでpath-basedルーティングを設定します。HTTPSリスナーに対してルールを追加し、/api/*はAPIサーバー、それ以外はWebサーバーに転送する構成は次のように書けます。

# APIサーバー用ターゲットグループ resource "aws_lb_target_group" "api" { name = "api-tg" port = 8081 protocol = "HTTP" vpc_id = aws_vpc.main.id health_check { path = "/api/health" matcher = "200" } } # /api/* をAPIサーバーへ転送するルール resource "aws_lb_listener_rule" "api" { listener_arn = aws_lb_listener.https.arn priority = 100 # 低い数値が先に評価される action { type = "forward" target_group_arn = aws_lb_target_group.api.arn } condition { path_pattern { values = ["/api/*"] } } }

priorityは1~50000の範囲で指定します。数値が小さいほど先に評価されます。デフォルトアクション(aws_lb_listenerのdefault_action)は全ルールを評価した後に動くフォールバックです。priorityの重複はAPIエラーになるため、チームで番号帯を決めて管理してください(例:フロントエンド=100番台、API=200番台)。

トラブルシュート|503エラー・ヘルスチェック失敗の切り分け

ALBが503を返す場合、原因は大きく2つに分かれます。

原因1:全ターゲットがUnhealthy
ターゲットグループのコンソール、またはAWS CLIでターゲットの状態を確認します。

$ aws elbv2 describe-target-health --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/app-tg/abcd1234 # 出力例 { "TargetHealthDescriptions": [ { "Target": { "Id": "i-0a1b2c3d4e5f67890", "Port": 8080 }, "TargetHealth": { "State": "unhealthy", "Reason": "Target.ResponseCodeMismatch", "Description": "Health checks failed with these codes: [302]" } } ] }

Target.ResponseCodeMismatchが出ている場合は、ヘルスチェックパスが302を返しています。matcherを"200,302"にするか、リダイレクトしない専用のヘルスチェック用パスを作ってください。

原因2:セキュリティグループがバックエンドへの通信をブロック
ALBからEC2インスタンスへの通信は、EC2のセキュリティグループ側で許可する必要があります。ALBのセキュリティグループからのインバウンドが開いていないと、ALBがターゲットに到達できずにUnhealthyになります。

# EC2のセキュリティグループ — ALBのSGからのアクセスを許可 resource "aws_security_group_rule" "ec2_from_alb" { type = "ingress" from_port = 8080 to_port = 8080 protocol = "tcp" security_group_id = aws_security_group.ec2.id source_security_group_id = aws_security_group.alb.id }

IPアドレスのCIDRで直接許可するのではなく、source_security_group_idでALBのSGを指定するのがIaC設計のベストプラクティスです。CIDR指定では将来のALB IP変更で壊れますが、SG参照なら変更に追従します。

本記事のまとめ

TerraformでALBを設計するときの重要ポイントをまとめます。
設計要素 リソース 実務上のポイント
ALB本体 aws_lb internet-facing/internalを用途で使い分ける。サブネットは2AZ以上必須
ターゲットグループ aws_lb_target_group healthy/unhealthy_thresholdを絞る。FargateはtargetType=ipに変更
HTTPリダイレクト aws_lb_listener(port=80) type="redirect"でHTTP→HTTPS 301を1本で宣言できる
HTTPSリスナー aws_lb_listener(port=443) ssl_policyはTLS13対応ポリシーを指定。古い2016-08は非推奨
EC2固定登録 aws_lb_target_group_attachment Auto Scalingと混在禁止。ASGはtarget_group_arnsで連携
pathルーティング aws_lb_listener_rule priorityは番号帯を管理。低い数値が先に評価される
503の切り分け TargetHealth確認 ResponseCodeMismatchはヘルスチェックパスのstatusコードを確認
TerraformでALBを設計するときは、aws_lb・aws_lb_target_group・aws_lb_listenerの3リソースの関係を押さえてから始めると整理しやすくなります。ヘルスチェックのthreshold値とセキュリティグループの設計が、実際の運用トラブルの大半を防ぐポイントです。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、TerraformでALBをコード化する実践設計から本番運用まで、ハンズオン形式で習得できるセミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。