多段CNAMEとパフォーマンス、およびAWS Route53のエイリアスレコードについて

## 概要
CNAMEレコードの設定値（参照先[^1]）に、別の箇所でCNAMEとして設定されたドメイン名を指定することはできるか。できたとして、問題は無いのか気になったので調べた。

言葉にするとわかりにくいが、下記のような設定が可能かどうかだ。

```
web2.example.con. IN CNAME web1.example.com. 
web1.example.com. IN CNAME www.example.com.
www.example.com. IN A  192.0.2.1
```

"web2"のCNAMEで"web1"が指定され、"web1"のCNAMEで"www"が指定されるよな、CNAMEが連鎖する設定を「多段CNAME」とこの記事では呼ぶ。[^2]

そして、多段CNAMEの短所を補うAWS Route53のエイリアスレコード(Aliasレコード)について最後に紹介する。

[^1]: CNAMEは"canonical name"なので"正規名"などどしたほうが正確だが、わかりやすさを重視して"参照先"とした
[^2]: 「多段CNAME」をググるとそれなりにヒットするので、ある程度一般的な言葉かもしれない

## 結論

 * 多段CNAMEの設定は可能
 * ただし、パフォーマンスに問題があるため避けたほうがよい。使う場合も多段の階層を深くしない
 * AWSでは、エイリアスレコード(Aliasレコード)を使える場合は使ったほうがよい

## 多段CNAMEは可能か

直感的に多段CNAMEは問題を起こしそうな気がしなくもないが、実際のところどうなのか。

DNSの基本的な仕様はRFCは1034と1035にまとめられているが[^3]、いずれも多段CNAMEを禁止する記述は見当たらなかった。
下記の『CNAMEとパフォーマンス』でも軽く触れたが、RFC 1034に書かれているネームサーバとリゾルバの動作を見ても、問題なさそうだ。

ただし、多段CNAMEは循環的な参照を作りうる[^4]ので、キャッシュDNSサーバ側でなんらかの制限が必要になる。
（BINDでは再帰問い合わせの回数に制限があるらしい）

CNAMEの循環的な参照については、RFCでも触れられている。

> The amount of work which a resolver will do in response to a
     client request must be limited to guard against errors in the
     database, such as circular CNAME references, and operational
     problems, such as network partition which prevents the
     resolver from accessing the name servers it needs.  While
     local limits on the number of times a resolver will retransmit
     a particular query to a particular name server address are
     essential, the resolver should have a global per-request
     counter to limit work on a single request. 
(RFC 1034)


深すぎる多段CNAMEは、キャッシュDNSによる問い合わせが打ち切られる可能性があるため、避けるべきといえる。

[^3]: 関連するRFCは他にもある. 参考文献の3を参照
[^4]: 「web.example.com -> www.example.com -> web.example.com ...」のような循環的な参照
 
### (余談)CNAMEにおける制限
CNAMEに関する制限事項といえば、よく知られた話だが「CNAMEは他のリソースデータと共存できない」というものがある。

```
If a CNAME RR is present at a node, no other data should be present;
(RFC 1034より引用）
```

この件に関してはRFC 1912が詳しく、このRFC内の例を用いると、下記の設定が許可されない。(NOT ALLOWED)

```
           podunk.xx.      IN      NS      ns1
                           IN      NS      ns2
                           IN      CNAME   mary
           mary            IN      A       1.2.3.4
```

podunk.xx.に`CNAME`を指定すると同時に、`NS`としても指定しているため、「no other data should be present」に反している。

修正するには、下記のようする。

```
           podunk.xx.      IN      NS      ns1
                           IN      NS      ns2
                           IN      A       1.2.3.4
           mary            IN      A       1.2.3.4
```

CNAMEをAレコードに変更している。

## CNAMEとパフォーマンス

CNAMEをつかうことで、問い合わせのパフォーマンスが低下する。

なぜなら、キャッシュDNSサーバはCNAMEの応答を受け取ったとき、その受け取ったドメイン名についての問い合わせを始めから実行する必要があるためだ。

つまり、キャッシュDNSサーバが"www.example.com" のAレコードを問い合わせている最中で、「www.example.com の正規名はwww.example.org である」と知った場合、今までの問い合わせを打ち切って"www.example.org" の問い合わせを実行する必要がある。

通常の２回分の問い合わせが行われることになり、あきらかに問い合わせの効率が下がる。
さらにいうと、CNAMEの段数が増えれば増えるほど、問い合わせの回数は増えていく。

このときの動作の詳細は、RFC 1034の『5.5.2 Aliases』を読むとよい。

ただし、CNAMEを多段に用いてもパフォーマンスの低下が少ないケースもある。それは、自身のゾーン内、もしくは委譲先のゾーンでCNAMEが解決される場合だ。
この点については、同じくRFC 1034の『4.3.2. Algorithm』を読んでほしい。

## AWS Route53のエイリアスレコードについて
### エイリアスレコード（Aliasレコード）とは
エイリアスレコードは、CNAMEとよく似た機能を提供するが、その弱点をカバーする。

まず、エイリアスレコードを知らない人のために、AWS公式ドキュメントから引用する。


> Amazon Route 53 は "エイリアス" レコードを提供します (Amazon Route 53 固有の仮想レコード)。[中略]エイリアスレコードは CNAME レコードのように機能するので、DNS 名（example.com）を別の「ターゲット」DNS 名（elb1234.elb.amazonaws.com）にマッピングできます。それらはリソルバーに表示されていないという点で、CNAME レコードと異なります。リソルバーには、A レコードと結果として生じるターゲットレコードの IP アドレスだけが表示されます。

> (AWS Route53ドキュメント、『よくある質問』より引用)


先ほど述べた通りだが、キャッシュDNSのサーバがCNAMEの応答を受け取ったときの動作は、返されたドメイン名に対する問い合わせを再度実行する。
これが問い合わせのコストの増加となり、問い合わせのパフォーマンス（主にクライアントへ応答を返す時間）が悪化してしまう。

しかし、エイリアスレコードを使うことで、Aレコードと同じコストでCNAMEと同様の機能を使うことができる。

### エイリアスレコードとCNAME
ELBを作成すると、`my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com`のようなドメイン名がAWSから発行される。

このドメイン名は人間にとってわかりにくいため、"my-web.example.com" のようなドメイン名を別名でつけることがある。

このとき、CNAMEを使った場合、キャッシュDNSサーバは"my-web.example.com" の問い合わせ中にCNAMEとして"my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com" を受け取り、"my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com"の問い合わせをルートドメインから行うことになる。

一方でエイリアスレコードを使った場合、CNAMEの応答は行われず、一連の"my-web.example.com" の問い合わせの中で、"my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com" のAレコードが返される。

### エイリアスレコードを使用するための条件
なぜこのようなことができるかというと、参照先のドメイン名の解決をRoute 53内部で行なっているためだ。

したがって、エイリアスレコードを使用するには２つの条件を満たす必要がある。

 * ユーザはRoute 53を使うこと（そもそもエイリアスレコードはAWSの拡張機能）
 * 参照先が、AWSの限定されたサービスであること（CloudFront, ELB, 静的ウェブサイトとしてのS3など）

エイリアスレコードを使った場合のデメリットは特に無いので、使用できるケースであれば積極的に使うべきだ。

エイリアスレコードの詳細については、Route 53のドキュメントの『エイリアスリソースレコードセットと非エイリアスリソースレコードセットの選択』を読んでほしい。

参照先として指定できるドメイン名についても、このドキュメント内に書いてある。

### (余談)パフォーマンスではない、エイリアスレコードのメリット

私が思うエイリアスレコードの最大のメリットはパフォーマンスだが、AWSのドキュメントでは触れられていない。

『よくある質問』には下記のように書いてある。

> 静的なウェブサイトをホスティングするよう設定されている CloudFront ディストリビューションおよび S3 バケットには、CNAME を使用する代わりに、CloudFront ディストリビューションまたは S3 ウェブサイトバケットにマッピングされる "エイリアス" レコードを作成することを推奨します。エイリアスレコードには 2 つの利点があります。1 つは、CNAME とは異なり、エイリアスレコードは zone apex (例えば、www.example.com ではなく example.com) に対しても作成できることです。もう 1 つは、エイリアスレコードへのクエリは無料であることです。

また、AWS Trusted AdvisorでAWSの設定をチェックしたときに、エイリアスレコードを使える箇所でCNAMEを使っていた場合にワーニングになる。


## 参考文献

1. RFC 1034 『DOMAIN NAMES - CONCEPTS AND FACILITIES
    * https://www.ietf.org/rfc/rfc1034.txt
1. RFC 1035『DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION』
    * https://www.ietf.org/rfc/rfc1035.txt
1. RFC 1912『Common DNS Operational and Configuration Errors』
    * https://www.ietf.org/rfc/rfc1912.txt
1. AWS Route53ドキュメント
    * よくある質問 ( https://aws.amazon.com/jp/route53/faqs/ )
    * エイリアスリソースレコードセットと非エイリアスリソースレコードセットの選択 ( http://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/resource-record-sets-choosing-alias-non-alias.html )
1. DNSのRFCの歩き方( http://dnsops.jp/event/20120831/DNS-RFC-PRIMER-2.pdf )
    * 記事を書いてから気づいたが、DNSのRFCを自力で読む前に目を通すと良さそう