DNS設定は今も本番環境を壊している
DNSは単純に見えて壊れやすい。macOSの更新、resolv.conf、mDNS、.internal TLDが本番障害を引き起こす仕組みを解説。

Appleの最新macOSリリースで、数千人の開発者のカスタムDNS設定が壊れました。具体的には、多くの企業がプライベートネットワークのリソースに使っている.internal TLDへのクエリを横取りし、設定済みのDNSリゾルバではなくmDNS(マルチキャストDNS)に転送するようになったのです。何年も問題なく動いていたサービスに突然届かなくなりました。修正方法もすぐには分からず、エラーメッセージも役に立ちませんでした。そしてDNSの問題にはよくあることですが、症状はDNSとは全く関係なさそうに見えるのです。
これは、すべての開発者が触れるものの、深く理解している人は少なく、そして本番環境で午前2時に何かを壊すまで誰もが軽く見てしまうDNS設定についての話です。
「.internal TLD」の障害
多くの組織では、内部サービス用のプライベートTLDとして.internalを使っています。例えばapi.internal、db.staging.internal、grafana.internalのように。公開TLDではなく、「これは社内用だ」と明確に示せ、何十年もこの用途で使われてきたので、安全な選択に思えました。
ところがAppleの新しいOSは、.internalをmDNSで処理することにしました。myprinter.localを家庭内ネットワークで解決するのと同じプロトコルです。Macでapi.internalを解決しようとすると、設定済みのDNSサーバーに問い合わせる代わりに、ローカルネットワークにmDNSクエリをブロードキャストします。DNSサーバーはそのクエリを一切見ません。応答は何もないか(タイムアウト)、そのローカルネットワーク上の何かがその名前に応答した場合は誤ったアドレスになります。
症状としては、curl api.internalが5秒間止まってから失敗する、社内ホストへのSSH接続がタイムアウトする、VPN利用者が社内リソースに到達できない、Dockerコンテナがサービス名を解決できない、などがあります。失敗は一般的な接続タイムアウトとして現れるため、多くの開発者はVPN、ファイアウォール、アプリケーションをデバッグし始めます。DNS以外のすべてを疑うのです。
最新システムでのDNS名前解決の実際
「DNSは単純」というメンタルモデル、つまりコンピュータがDNSサーバーにIPを尋ね、サーバーが応答する、という理解は、もう何年も正確ではありません。最新のシステムには複数段階の解決パイプラインがあり、それぞれがクエリを横取り、改変、転送できます。
DNS resolution pipeline on a typical macOS/Linux system:
Application calls getaddrinfo("api.internal")
↓
1. /etc/hosts — checked first. Static overrides.
↓
2. NSSwitch / resolver configuration
- Linux: /etc/nsswitch.conf controls lookup order
- macOS: /etc/resolver/* for per-domain DNS servers
↓
3. System resolver (systemd-resolved, mDNSResponder)
- May intercept .local, .internal, or other special TLDs
- May apply DNSSEC validation
- May cache responses
↓
4. DNS server (from DHCP, VPN, or manual config)
- Queries sent to configured upstream resolver
- May go through a corporate DNS proxy
↓
5. Recursive resolver (8.8.8.8, 1.1.1.1, corporate)
- Walks the DNS hierarchy: root → TLD → authoritative
↓
Response flows back up the chain
各段階で問題は起こり得ます。システムリゾルバは、クエリが設定済みのDNSサーバーに届く前に横取りするかもしれません。VPNがDNS設定を上書きするかもしれません。DHCPが、手動設定より優先されるDNSサーバーを配布するかもしれません。さらに、あらゆるレベルでのキャッシュにより、修正がすぐには反映されないこともあります。
/etc/resolv.confの嘘
Linuxでは、/etc/resolv.confが長らくDNS設定の唯一の情報源でした。そこにネームサーバーを書けば、システムはそれを使います。単純でした。
最新のLinuxディストリビューションは、これを劇的に複雑にしました。systemd-resolvedを使っている場合(Ubuntu、Fedora、そして多くのsystemdベースのディストリビューションがデフォルトでそうです)、/etc/resolv.confは127.0.0.53のスタブリゾルバへのシンボリックリンクです。実際のDNS設定はsystemd-resolvedの状態として保持され、resolvectlで管理します。/etc/resolv.confを直接編集しても、次のネットワーク変更で上書きされるか、スタブリゾルバを壊すかのどちらかです。
# What you THINK your DNS config is:
$ cat /etc/resolv.conf
nameserver 127.0.0.53 # This is systemd-resolved's stub
# What your DNS config ACTUALLY is:
$ resolvectl status
Global:
DNS Servers: 8.8.8.8 8.8.4.4
DNS Domain: ~.
Link 2 (eth0):
DNS Servers: 10.0.0.1 # From DHCP — this takes precedence
DNS Domain: corp.internal
Link 5 (wg0): # WireGuard VPN
DNS Servers: 10.100.0.1
DNS Domain: ~internal # VPN claims the .internal domain
# Three different DNS servers, each handling different domains.
# /etc/resolv.conf shows none of this.
Dockerにはさらに層があります。Dockerコンテナは独自の/etc/resolv.confを持ち、通常はコンテナ起動時にホストの設定からコピーされます。ホストのDNSが変わると(VPN接続、ネットワーク切り替え)、実行中のコンテナは再起動するまで古いDNS設定のままです。これは「再起動すると動くが、しばらくすると壊れる」というバグのよくある原因です。
スプリットDNSとVPNの衝突
スプリットDNS、つまりドメインごとに異なるDNSサーバーを使う方式は、企業環境では標準的です。VPNは*.corp.internalへのクエリを社内DNSサーバーに転送し、公開向けのクエリは通常のリゾルバに送ります。正しく設定されていればうまく機能しますが、そうでないと非常に分かりにくい形で失敗します。
よくある障害パターンは以下のとおりです。
- VPNがDNSサーバーをグローバルなデフォルトとして配布する。 一部のVPNクライアントは、社内向けだけでなく全クエリのデフォルトリゾルバとしてVPNのDNSサーバーを設定します。すると個人的なブラウジングを含むすべてのDNSクエリが社内DNSを経由することになります。社内DNSは別のデータセンターにあるため遅く、プライバシーの懸念もあります。
- DNSサーバーの順序の衝突。 複数のDNSサーバーが設定されていると、システムは順番に試します。最初のサーバーが遅い、または到達できない場合(VPNは切断されたがDNS設定は残っている場合など)、すべてのDNSクエリは次のサーバーを試す前にタイムアウトを待つことになります。これにより、接続のたびに5秒の遅延が加わります。
- 検索ドメインが黙って付加される。 検索ドメインが
corp.internalの場合、apiのクエリはapi.corp.internalも試されます。これは便利(ssh api.corp.internalではなくssh apiと打てる)ですが、短いホスト名が内部サービス名と衝突すると、誤ったIPに解決されてしまうまでは、の話です。 - mDNSが内部ドメインを横取りする。
.internalの障害が示すように、システムリゾルバは、設定されたDNSサーバーに届く前に特定のTLDへのクエリを横取りすることがあります。これは特に厄介で、横取りが静かに起こるため、エラーメッセージはなく、タイムアウトか誤った応答が返るだけです。
DNSのデバッグ:ツールキット
DNSが壊れたら、解決パイプラインの各段階で実際に何が起きているかを示すツールが必要です。
# 1. Check what your system actually resolves:
$ dig api.internal
# Shows: query sent to which server, response received
# The 'SERVER' line tells you which DNS server answered
# 2. Query a specific DNS server directly:
$ dig @10.100.0.1 api.internal
# Bypasses the system resolver — goes directly to the specified server
# If this works but 'dig api.internal' doesn't, the problem is
# in the system resolver, not the DNS server
# 3. Check systemd-resolved state (Linux):
$ resolvectl query api.internal
# Shows which interface/server handled the query
# 4. Check macOS DNS routing:
$ scutil --dns
# Shows per-domain DNS configuration on macOS
# Look for your .internal domain — is it being routed correctly?
# 5. Monitor DNS queries in real time:
$ sudo tcpdump -i any port 53
# See actual DNS packets. If no packets go out for your query,
# the system resolver is intercepting it locally.
# 6. Flush DNS cache (when fixing config):
$ sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder # macOS
$ resolvectl flush-caches # Linux with systemd-resolved
最も重要な診断ステップは、dig @server hostnameでDNSサーバーに直接問い合わせることです。これが成功して通常の解決が失敗する場合、問題はアプリケーションとDNSサーバーの間にあります。システムリゾルバによる横取り、キャッシュ、ルーティングなどです。直接の問い合わせも失敗するなら、問題はDNSサーバー自体か、そこへのネットワーク接続にあります。
プライベートTLDの選び方
.internalの障害は、実務上の問いを投げかけます。社内サービスにはどのTLDを使うべきか。万能に安全な答えはありませんが、選択肢には良し悪しがあります。
- .internal — RFC 6762は内部用途として予約していますが、AppleのmDNS処理によりmacOSでは問題になります。Linuxでは問題なく動くかもしれません。
- .local — mDNS用に予約されています(RFC 6762)。DNSで解決するサービスに使うとBonjour/Avahiと衝突します。使わないでください。
- .corp、.home、.mail — 予約されていません。ICANNがいつでも公開TLDとして割り当てる可能性があり、そうなると社内の名前解決が壊れます。使わないでください。
- 所有するドメインのサブドメイン。
internal.yourcompany.comのような形式です。これが最も安全な選択肢です。親ドメインを自分で管理しているので、衝突のリスクがありません。欠点は、入力が長くなることです。 - .test、.example、.invalid、.localhost — RFC 6761で予約されており、公開TLDとして割り当てられることは決してありません。本番以外の社内サービスには
.testが最もすっきりした選択肢です。
地味ですが正しい答えは、所有するドメインのサブドメインを使うことです。api.internal.yourcompany.comは曖昧さがなく、何とも衝突せず、すべてのOSとDNSリゾルバで正しく動きます。長くはなりますが、午前2時のDNS問題は、数文字余分に打つことよりずっと大変です。
本番システムへの教訓
DNS障害はどれも同じ教訓を残しますが、私たちは何度も学び直しています。
- DNSサーバーだけでなく、名前解決を監視する。 DNSサーバーが正常でも、名前解決が機能しているとは限りません。経路全体を監視しましょう。アプリケーションが必要なホスト名を実際に解決できるか。30秒ごとに重要なホスト名を解決する合成チェックは、サーバー監視よりも速く問題を検知します。
- 社内DNSのTTLは短く設定する。 社内DNSレコードを変更する際、長いTTLはキャッシュされた古いデータを意味します。社内サービスには60秒のTTLを設定すると、リゾルバへの負荷を抑えつつ、素早いフェイルオーバーが可能になります。
- OSのアップデートをDNS構成で検証する。
.internalの不具合はmacOSユーザーにしか影響しませんでした。メジャーなOSアップデートの後は開発環境を検証しましょう。DNS解決の変更がリリースノートに載ることはめったにありません。 - DNSアーキテクチャを文書化する。 どのDNSサーバーがどのドメインを担当するのか。分割ポイントはどこか。VPNが切断されたら何が起きるのか。このドキュメントは、障害時に最初に必要になるものであり、誰もが最後に書くものでもあります。
- フォールバックを用意する。 内部サービスのDNS解決が失敗したとき、アプリケーションはハードコードされたIPを使えるでしょうか。スマートなやり方ではありませんが、動くハードコードされたIPは、解決しないDNS名よりマシです。
DNSは、うまく動いている間は見えず、壊れると壊滅的になる基盤サービスの一つです。.internalの不具合は、DNS設定が見た目以上に脆いことの再確認でした。OSの一度のアップデートで、何百万人ものユーザーの名前解決の挙動が変わり得るのです。DNSはいつか壊れると前提にシステムを設計しておけば、壊れたときに驚くことも少なくなるでしょう。


