Terraformを使ってAWSインフラをHCLで管理する現場では、この混乱が珍しくない。aliasとCNAMEはDNSの世界で似た役割を担うが、HCLの構文はまるで違う。書き方を間違えると
terraform applyが通っても名前解決が壊れるか、apexドメインに書けないエラーで詰まることになる。この記事では、
aws_route53_recordリソースでaliasブロックを使うパターンとCNAMEを使うパターンを実際のHCLで示し、ALBへの向き先をどう宣言するかを解説する。DNSルーティング方式の選定論には踏み込まない。aliasとCNAMEをHCLでどう書くかという実装に絞って説明する。この記事のポイント
・ aliasレコードはHCLでalias {}ブロックで書く。CNAMEとは構文が全く異なる
・ ALBをRoute 53で向ける場合はaliasが正解(apexドメインでも使えてTTL不要)
・ apexドメイン(example.com)にCNAMEは書けない。HCLでのエラー回避に重要
・ zone_idの混同(Route 53のIDとALBのzone_idは別物)がよくあるトラブルの原因
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
aliasレコードとCNAMEはHCLの書き方から全く別物
DNSのaliasレコードとCNAMEは「別のDNS名を返す」という点で似ているが、Route 53のHCL表現はまるで違う。まずその全体像を押さえる。CNAMEは
type = "CNAME"にrecordsとttlを持つ、最もシンプルなレコードタイプだ。# CNAMEの基本構文 resource "aws_route53_record" "cname_example" { zone_id = data.aws_route53_zone.main.zone_id name = "api.example.com" type = "CNAME" ttl = 300 records = ["backend.partner-service.com"] }
typeは"A"または"AAAA"を指定し、ttlとrecordsの代わりにalias {}ブロックを使う。# aliasレコードの基本構文 resource "aws_route53_record" "alias_example" { zone_id = data.aws_route53_zone.main.zone_id name = "www.example.com" type = "A" alias { name = aws_lb.main.dns_name zone_id = aws_lb.main.zone_id evaluate_target_health = true } }
alias {}ブロックはttlが不要で、recordsも書かない。代わりに向き先リソースのdns_nameとzone_id、そしてevaluate_target_healthを必ず宣言する。aws_route53_recordでaliasブロックを宣言する方法
ALBをRoute 53のaliasレコードで向ける実装について、3ステップで説明する。1. data sourceでホストゾーンIDを取得する
Route 53のホストゾーンIDは、aws_route53_zoneのdata sourceで取得するのが定石だ。ハードコードすると環境ごとのtfvarsが煩雑になる。# ホストゾーンをドメイン名で検索して取得 data "aws_route53_zone" "main" { name = "example.com." private_zone = false }
nameの末尾に必ずドット(.)を付ける。これはDNSの完全修飾ドメイン名(FQDN)の慣習で、付けないとTerraformがホストゾーンを見つけられない場合がある。private_zone = falseはパブリックゾーンを明示する指定だ。2. ALBへのaliasレコードをHCLで書く
ALBはTerraformでaws_lbリソースとして管理する。そのDNS名とzone_idをaliasブロックに渡す。# ALBのリソース定義(同一モジュール内に存在する前提) resource "aws_lb" "main" { name = "prod-alb" internal = false load_balancer_type = "application" subnets = var.public_subnet_ids } # ALBへのaliasレコード resource "aws_route53_record" "www" { zone_id = data.aws_route53_zone.main.zone_id name = "www.example.com" type = "A" alias { name = aws_lb.main.dns_name zone_id = aws_lb.main.zone_id evaluate_target_health = true } }
zone_idが2つ登場することだ。・ aws_route53_record の zone_id:Route 53ホストゾーンのID。data sourceで取得した値
・ alias { zone_id }:向き先リソース(ALB)のzone_id。ALBがデプロイされているAWSリージョンと紐づいたID
この2つを混同すると
terraform applyは成功しても名前解決が壊れる。alias内のzone_idはALBリソースの属性(aws_lb.main.zone_id)を直接参照するのが安全だ。evaluate_target_health = trueを設定すると、ALB配下のターゲットが全台unhealthyになった場合にRoute 53がDNS応答を返さなくなる。サービス死活と連動するため、通常はtrue推奨だ。3. terraform applyとdigコマンドで動作確認する
terraform apply完了後はdigコマンドでaliasレコードの動作を確認する。aliasはAWSが内部的にAレコードとして解決するため、dig結果にはIPアドレスが返ってくる。$ dig www.example.com A ; <<>> DiG 9.16.23-RH <<>> www.example.com A ;; ANSWER SECTION: www.example.com. 60 IN A 203.0.113.10 www.example.com. 60 IN A 203.0.113.11 ;; Query time: 18 msec ;; SERVER: 205.251.197.150#53(205.251.197.150)
>> Terraform実践セミナーの詳細はこちら
CNAMEをHCLで書く場面と構文
aliasが使えない、あるいは適さない場面でCNAMEを使う。ポイントが2つある。1. CNAMEが使える条件(apexドメインでは使えない)
CNAMEには制約がある。apexドメイン(ゾーンの頂点、example.comのようにサブドメインなしのドメイン)にCNAMEは設定できない。これはDNSの仕様上の制限で、Terraformに関係なく発生する。CNAMEを使えるのはサブドメイン(api.example.com、staging.example.com等)に限られる。外部のSaaSや他社サービスのDNS名に向ける場合に使うケースが多い。
2. HCLでのCNAME宣言例
# 外部SaaSへのCNAME resource "aws_route53_record" "docs" { zone_id = data.aws_route53_zone.main.zone_id name = "docs.example.com" type = "CNAME" ttl = 300 records = ["mysite.notion.so"] } # 社内システムの別名 resource "aws_route53_record" "legacy" { zone_id = data.aws_route53_zone.main.zone_id name = "legacy.example.com" type = "CNAME" ttl = 60 records = ["new-system.example.com"] }
recordsはリストで書くが、CNAMEは複数の値を持てない仕様のため実質1要素になる。ttlは省略できない(aliasと違い、Route 53がTTLを管理しないため)。aliasとCNAMEをどちらにするか|HCLからみた判断基準
実装上の使い分けは次の軸で決まる。・ 向き先がAWSリソース(ALB・CloudFront・S3 Website・API Gateway等)なら:alias
・ 向き先がapexドメイン(example.com)なら:alias(CNAMEは使用不可)
・ 向き先が外部サービスのDNS名で、サブドメインなら:CNAME
・ TTLをAWSに任せてよい場合:alias(AWSが自動管理)
・ TTLを自分でコントロールしたい場合:CNAME
AWSリソースへの向き先は基本的にaliasが正解だ。aliasはRoute 53クエリ料金が発生しない(CNAMEは発生する)という運用上のメリットもある。
トラブルシュート|よくあるエラーと対処法
1. 「RRSet of type CNAME with DNS name xxx is not permitted at apex」
apexドメインにCNAMEを設定しようとすると発生するエラーだ。# NG: apexドメインへのCNAMEは書けない resource "aws_route53_record" "root_cname" { zone_id = data.aws_route53_zone.main.zone_id name = "example.com" # apexドメイン type = "CNAME" # これが弾かれる ttl = 300 records = ["alb-xxx.ap-northeast-1.elb.amazonaws.com"] } # エラー内容(terraform apply 実行時): # Error: [InvalidChangeBatch 400: RRSet of type CNAME with DNS name # example.com. is not permitted at apex in zone example.com.]
# OK: apexドメインはaliasで書く resource "aws_route53_record" "root_alias" { zone_id = data.aws_route53_zone.main.zone_id name = "example.com" type = "A" alias { name = aws_lb.main.dns_name zone_id = aws_lb.main.zone_id evaluate_target_health = true } }
2. alias { zone_id }にRoute 53のzone_idを渡してしまう
よくある混乱がzone_idの取り違えだ。# NG: alias内のzone_idにRoute 53のIDを渡すと名前解決が壊れる alias { name = aws_lb.main.dns_name zone_id = data.aws_route53_zone.main.zone_id # これは間違い evaluate_target_health = true } # OK: alias内のzone_idはAWSリソース(ALB)のzone_idを渡す alias { name = aws_lb.main.dns_name zone_id = aws_lb.main.zone_id # ALBのzone_id evaluate_target_health = true }
3. evaluate_target_health = trueでSERVFAILになる
ALBのターゲット(EC2インスタンス等)が全台unhealthyになると、evaluate_target_health = trueの設定でRoute 53がNXDOMAINやSERVFAILを返す場合がある。# 確認: ALBのターゲットヘルスチェック状態 $ aws elbv2 describe-target-health \ --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/prod-tg/xxx # ANSWER SECTIONが空の場合、ALBターゲットが全台unhealthy $ dig www.example.com A ; <<>> DiG 9.16.23-RH <<>> www.example.com A ;; ANSWER SECTION: ;; (empty) ;; AUTHORITY SECTION: example.com. 900 IN SOA ns-xxx.awsdns-xx.net. ...
evaluate_target_health = trueを設定している場合はfalseに戻すか、ALBのヘルスチェック設定を見直す。本記事のまとめ
TerraformでRoute 53のaliasレコードとCNAMEを使い分ける実装ポイントをまとめる。| やりたいこと | HCLの書き方 |
|---|---|
| ALBをapexドメインで向ける | type = "A" + alias {} ブロック |
| ALBをサブドメインで向ける | type = "A" + alias {} ブロック |
| 外部SaaSをサブドメインで向ける | type = "CNAME" + records = [...] |
| ALBのzone_idを参照する | aws_lb.main.zone_id(Route 53のzone_idと別物) |
| ALBのヘルスと連動させる | evaluate_target_health = true |
| apexドメインでCNAMEを使う | 不可(RFC違反。aliasに書き直す) |
alias {}ブロック vs records+ttl)を手が覚えるまで実装を繰り返すと、apply時のエラーが格段に減る。
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 次のページへ:Terraformで環境ごとにstateの保管先を切り替える設計|部分設定ファイルと環境変数によるinit時の分岐
- 前のページへ:TerragruntでTerraformをDRY化する設計入門|includeとdependencyで環境差分を管理する方法
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます