この記事はBASE Advent Calendar 2019の2日目の記事です
SREの ngsw です。
BASEはAWSを採用しており、その中でもEC2に大きく依存する部分があります。EC2においては以下の制約があります。
DNS の制限 各 Amazon EC2 インスタンスは Amazon が提供する DNS サーバーへ送信できるパケット数を ネットワークインターフェイスあたり最大 1024 パケット/秒に制限しています。 この制限を増やすことはできません。
AWSのマネージドサービスはエンドポイントの提供になり、そのエンドポイントへのアクセスに名前解決は必要となります。BASE全体へのアクセス数は増加傾向にありますので、名前解決試行回数も同じく増加傾向にあると考えて良いでしょう。
「考えて良いでしょう」というのは平常時にはこの制約は顕在化せず問題視されないからです。 整理するとここでの課題は
- 突発的なアクセス増にも耐えうる名前解決方法を持ちましょう
- アクセス増時のスループットが名前解決制約で頭打ちにならないようにしましょう
ということになります。
AWS における名前解決制約回避のベストプラクティス
実際の対応方法は上記の記事がすべてといっても過言ではありません。しかしそれらがそのまま適用できたわけでもありません。その差分や行間をあわせてお伝えできたら幸いです。
そもdnsmasqってなに?
具体的にはEC2インスタンス内部にDNSキャッシュサーバを持つことができます。簡潔に申し上げれば dnsmasqを起動させ 127.0.0.1:53
で応答させることにより当該の名前解決制約が回避できるということです。
念の為回避できないかもしれない状況を(推測ではありますが)上げておきます。
- 個別のFQDN 1024個超の秒間名前解決
IPv6名前解決 も 有効な状態で個別のFQDN 512個超の秒間名前解決(都度AレコードとAAAAレコードを解決しようとする)- 今回お伝えした手順では
dnsmasq
はIPv6に関して問い合わせしないことを確認しました。
- 今回お伝えした手順では
主題は「dnsmasq がAWSリゾルバ問い合わせを秒間1024回超行ってしまう事自体は依然として制約されている」になります。ですからCNAMEレコードの都度問い合わせやTXTレコードの include
記述なども関わってくる問題と考えます。
dnsmasq 設定内容
以下にどのように設定したかを書きました。
起動プロセス名 | dnsmasq | |
実行User | dnsmasq | |
dnsmasq UID |
5353 | |
dnsmasq GID |
5353 | |
dnsmasq設定ファイル | /etc/dnsmasq.conf |
|
最大キャッシュTTL | 2 | |
最小キャッシュTTL | 1 | |
キャッシュレコード数 | 500 | |
ネガティブキャッシュTTL | 60 | |
dnsmasq用resolvファイル | /etc/resolv.dnsmasq | |
リゾルバIP | 169.254.169.253 |
VPC での DNS の使用 - Amazon Virtual Private Cloud |
dhclient設定ファイル | /etc/dhcp/dhclient.conf |
|
timeout | 300 | |
retry | 60 | |
dhclientが設定するリゾルバIP | 127.0.0.1, 169.254.169.253 | supersede domain-name-servers |
以下の観点を持っています。
- UID / GID はわかりやすいものを
- dnsmasq自身のキャッシュ時間をいたずらに長くしない
- dnsmasqプロセスが死んだ場合であってもAWS外部リゾルバをバックアップとして利用しない
UID / GIDについて
僕自身、UID / GID は明示しておきたいと考えるので明示した次第です。が、ここで現行環境適用時に問題が置きました。
サーバ構成は chef で管理されているのですが、dnsmasq導入以前に設定された既存ユーザにおいては UID / GID が明示されていない状況でした。
dnsmasq はサーバ設定の早い段階で入れておくのがよいだろうと考えたため、いの一番にインストールして設定するようレシピを書き換えたのですが、現在使用しているOSの仕様からか「UID / GIDを明示指定すると、それ以後に作成したユーザやグループは明示指定IDからのインクリメントになる」ということが起きてしまいました。つまり hoge 5354:5354
fuga 5355:5355
のような形です。
これでは既存のEC2と新規のEC2で差分がでてしまいます。構築時期によってサーバの状況が違うことになってしまうのはあまりいい気持ちではありませんので、レシピ自体を見直してすべてのユーザにUID / GIDを明記するよう変更しました。
dnsmasq自身のキャッシュ時間について
「キャッシュ時間をいたずらに長くしない」ということについてです。今回のdnsmasq導入目的は「名前解決制限という制約の解決」でした。ですので「1秒間だけキャッシュできればよく、それ以外の副作用を極力排除する」というスタンスでいました。キャッシュ時間を長くしたとしてもおそらくはTTLなりをみてdnsmasqはよしなに動作してくれると信じてはいるのですが、検証工数を最小にするために短い値を設定しています。
懸念を具体的に申し上げると「dnsmasqのキャッシュ時間を長くすることで、AWSマネージド・サービスがオートスケールした場合のALIASレコードを無視する時間が長くなるのではないか」でした。
dnsmasqプロセスが死んだ場合について
本来であればバックアップのDNSを外部に用意したいものですが、今回はその対応を行っていません。理由は「VPCオプションによるプライベートホストゾーンに依存しているから」となります。外部のDNSはこの情報を知り得ませんので、問い合わせができたところで期待した名前解決は行われず正常に動作しません。
先にも書きましたが今回の目的は「名前解決制限という制約の解決」に絞っていますので、完璧を目指すのではなく「よりよいものをなるだけ少ない工数で」という割り切りをしています。
「うまく機能しているのか」確かめよう
少なくともAWSリゾルバへの問い合わせ回数が激減しているか確かめられればいいかと思います。僕は tcpdump
で確認しました。
#!/bin/bash while : do dig +short 01endpoint.example.com >/dev/null 2>&1 & dig +short 02endpoint.example.com >/dev/null 2>&1 & dig +short 03endpoint.example.com >/dev/null 2>&1 & dig +short 04endpoint.example.com >/dev/null 2>&1 & done
上記スクリプト dig.sh
程度でも効果はみてとれました。
chmod +x dig.sh # dnsmasq導入前 ./dig.sh tcpdump -s 0 -i any port 53 -w dnsmasq_before.pcap # dnsmasq導入後 ./dig.sh tcpdump -s 0 -i any port 53 -w dnsmasq_after.pcap
ここで取得できた *.pcap
を wireshark
や Cocoa Packet Analyzer
で比較してみましょう。
dnsmasq before
172.31.0.2
がAWSリゾルバ- 都度問い合わせが発生していることがわかる
dnsmasq after
169.254.169.253
がAWSリゾルバ127.0.0.1
DOMAIN が dnsmasq- この画像ではAWSリゾルバが存在しない程度にdnsmasqで完結していることがわかる
対応を終えて
前職もAWSを利用していたのですが、dnsmasqではなく外部DNSをリゾルバ指定する対応でした。今思えば「dnsmasqの恩恵でもあるレイテンシ軽減」を手放していたのだなとすら感じます。今回は採用例(AWSベストプラクティスだから当然なのですが)でしたがSREという立場を考えるともっとたくさんの不採用例を持たなくてはならないなと考えます。
「手段と目的を取り違えるな」という言葉があります。その反対側には「手段の数とその質こそが、何を目的にできるかを制約している」とも個人的には考えていますので、技術職としていろいろなツール/ソフトウェアを触って手数そのものを増やしていきたい、その経験でもってプロダクトに貢献していきたいともあわせて考えた次第です。