/etc/hostsとは何で、いつ勝つのか
一言でいうと
/etc/hostsはDNSより先に見るローカルの名前表で、digはそのファイルをまったく見ません。この一文が、多くの迷宮の出口です。
なぜ必要なのか
こんな報告が来ます。「DNSは正常のようなのに、アプリケーションだけが古いサーバーにつながります」。
確認してみると、本当にdig api.internalは新しいIPを返します。ところがアプリケーションは古いIPに行き続けます。ここでDNSサーバーを調べ始めると、1日が終わります。
犯人はたいてい/etc/hostsです。誰かが先週デバッグしながら一時的に入れた行が残っているのです。そしてこの事故はDNSエラーの顔では現れません。名前はきちんと解決されるからです。症状はconnection refusedやtimeoutとして現れるので、誰もDNS側を疑いません。
どう動くのか
まずファイルの形式です。1行はIP + 正規名 + 別名です。
127.0.0.1 localhost
127.0.1.1 myserver
::1 localhost ip6-localhost ip6-loopback
10.0.1.50 api.internal.mycompany.com api-internal
192.168.1.100 db-master.mycompany.com
- 2番目のフィールドが正規名(canonical name)で、3番目以降が別名です。別名でも同じように解決されます。
- IPv4とIPv6をそれぞれ別に書きます。
::1の行が抜けていると、IPv6でつなぐアプリケーションは名前を見つけられません。 - ワイルドカードもCNAMEもありません。
*.labhub.localのような表記はただの文字列で、どの名前にも一致しません。名前1つにつき1行が原則です。 - 同じ名前を複数のIPに書くことはできます。このとき
getent ahostsが複数行を返し、アプリケーションは上から順に試します。
その次が、順序を決めるファイル、/etc/nsswitch.confです。
grep '^hosts' /etc/nsswitch.conf
# hosts: files dns myhostname
# hosts: files mdns4_minimal [NOTFOUND=return] dns myhostname
# hosts: files resolve [!UNAVAIL=return] dns myhostname
トークンの意味は次のとおりです。
| トークン | 何を見るか |
|---|---|
files |
/etc/hosts |
dns |
/etc/resolv.confのnameserverに直接問い合わせます |
resolve |
D-Busでsystemd-resolvedに問い合わせます |
myhostname |
自分のホスト名とローカルインターフェースのアドレス |
mdns4_minimal |
.localの名前をmDNSで解決します |
角括弧の中にあるものは動作ルールです。[NOTFOUND=return]は、前のモジュールが「そんな名前はない」と答えたらそこで止まれという意味です。後ろにあるdnsまで進みません。
そのため、社内ドメインを.localで命名した組織がこの落とし穴にはまります。mdns4_minimal [NOTFOUND=return]が.localの名前を横取りして失敗すると、その場で終わるのでDNSに渡りません。ところがdigはnsswitchを見ないので、正常な応答を返します。digでは成功するのに、アプリケーションだけが失敗します。
ここで、このコースでいちばん重要な一文が出てきます。
digはDNSサーバーの答えを見せ、getentはアプリケーションが受け取る答えを見せます。2つの結果が違うなら、その違いこそが原因の場所です。
dig、nslookup、hostはすべてDNSプロトコル専用のツールです。/etc/hostsも、nsswitchも、systemd-resolvedのリンクごとの設定も見ません。resolv.confからnameserverのアドレスだけを取り出し、UDP 53で問い合わせを投げます。アプリケーションの経路を再現するツールは、getent hosts / getent ahosts / resolvectl queryだけです。
現場での姿
hostsで一時的に迂回して、消さないままにします。障害対応中の「とりあえずhostsに書き込んでしのごう」はよくある判断で、たいてい正しいものです。問題は、その行が3か月後にも残っていることです。そのため、hostsを触るときは必ずコメントに日付と理由と担当者を書くルールを設けているチームが多いです。
コンテナイメージにhostsが焼き込まれています。ビルド時に入れたエントリがイメージに残り、すべての環境で同じIPを指します。ステージングから本番DBにつながる事故は、こうして起きます。
次のラボですること
/etc/hostsに直接エントリを追加してgetentで確認したあと、壊れたhostsファイルのフィクスチャを監査して問題の行を見つけます。そして、ルールを満たす最終版を提出します。