TerraformでRoute 53のaliasレコードとCNAMEを使い分ける設計|ALBへの向き先をHCLで宣言し名前解決を安定させる

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > TerraformでRoute 53のaliasレコードとCNAMEを使い分ける設計|ALBへの向き先をHCLで宣言し名前解決を安定させる
「TerraformでRoute 53のaliasレコードを設定しようとしたら、CNAMEと書くべきか、aliasブロックを使うべきかわからなくなった」
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は別物)がよくあるトラブルの原因


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

aliasレコードとCNAMEはHCLの書き方から全く別物

DNSのaliasレコードとCNAMEは「別のDNS名を返す」という点で似ているが、Route 53のHCL表現はまるで違う。まずその全体像を押さえる。

CNAMEはtype = "CNAME"recordsttlを持つ、最もシンプルなレコードタイプだ。

# 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"] }

aliasレコードはまったく異なる。type"A"または"AAAA"を指定し、ttlrecordsの代わりに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_namezone_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)

TTLが60秒になっているが、これはAWS Route 53がaliasレコードに自動的に設定するTTLだ。HCL上でTTLを指定していなくても、AWSが管理する。digコマンドでDNSを調査する詳細な使い方はこちらを参照してほしい。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Route 53 aliasレコードとCNAMEをHCLで正しく使い分けるTerraform実践スキルを、セミナーで習得できます。
>> 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"] }

CNAMEの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.]

修正方法はaliasに書き直すことだ。

# 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 }

Route 53のzone_idはホストゾーンを識別するIDで、ALBのzone_idはALBが稼働するリージョンを識別するAWSリソースのIDだ。この2つは全く別の意味を持つ。

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. ...

この状態はエラーではなくRoute 53の正常動作だ。ALBのターゲットを正常に戻せばDNSが応答を返し始める。サービス停止中に意図せず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とCNAMEの最大の違いは「aliasはAWSリソース専用でapexでも使える、CNAMEは外部向けでサブドメインのみ」という点だ。HCLでの構文差異(alias {}ブロック vs records+ttl)を手が覚えるまで実装を繰り返すと、apply時のエラーが格段に減る。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Route 53 aliasレコードとCNAMEをHCLで正しく使い分けるTerraform実践スキルを、セミナーで習得できます。
>> Terraform実践セミナーの詳細はこちら

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

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

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

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

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

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

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

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

この記事を書いた人

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

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

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