社内LANで時々ネットワークアクセス障害が発生していて、一部PCを切り戻すと復活する。ある程度繋がっている範囲は絞れているが個々の業務PCのため家宅捜査できずに、切り離して他のルータ配下(2段NAT配下)への島流しで対応していた。
ネットワークの更改とともにルーターをIX2107に変更したのを機に再加入させてみたら再度障害が発生。
今度はsyslogサーバーに転送していたので本気で解析を行なった結果、野良Prometheusだということが判明。あまり情報がないのでメモとしておいておく。
結論から書くと
- 有線 LAN でルーター(NEC IX2107)にPingが届かなくなり、一部の PC群を切り離すと回復するという奇妙な症状が発生していた。過去のRTX830やSynologyルーターでも同様の謎症状有
- syslog を見ると、1台の PC からの TCP/53(DNS over TCP)ストームでルーターの DNS プロキシが枯渇し、その煽りで他端末への ARP 解決まで失敗していた
- 元凶は Windows + WSL2 + Docker Desktop で動かしていた Prometheus コンテナ
- 多段の DNS チェーン(Container → Docker → WSL2 → Windows → ルーター)のどこにもキャッシュが効いていない状態で、Go の非キャッシュリゾルバ ×
dns_sd_configs× TCP フォールバックが重なり、ルーターのセッションを使い切り障害発生
一見「ルーターの調子が悪い」だけに見える事象が、監視スタックの内部挙動と DNS の仕様、ルーターのリソース上限の合わせ技で発生した話です。ということで、調査の過程を。
症状
有線 LAN 配下のいくつかの PC から、ゲートウェイである 192.168.1.1(NEC IX2107)に通信が届かなくなった。Web 管理画面も開けない、当然外にも出られない。頻繁に発生するわけでないが、ある程度の頻度で発生する。
ポイントは:
- 全PCが一斉に死んだわけではなく、使えるPCもある
- 一部PCを物理的に切り離すと、残った PC のネットが復活する
「ルーターが死んだわけではない」「特定の端末がパケットを撒いてるっぽい」までは直感で見えたが、どの端末が犯人なのか分かりませんでした。今回、syslogをちゃんと記録していたので調査する気になりました。
第1段: syslog を読む
事象発生時刻周辺の IX2107 syslog(約2分間で 1,000 行)を取得して眺めると、特定パターンが目立つ:
TKY-RT TCP[006]: Child TCP status changed for port 53, type transmit
TKY-RT TCP[006]: Child TCP status changed for port 53, type receive
TKY-RT TCP[006]: Sending RESET to host 192.168.1.126, GigaEthernet1.0
TKY-RT TCP[006]: Child TCP read stop for port 53, reason close-cb-error
集計すると:
| 種別 | 件数 |
|---|---|
Child TCP status changed for port 53 |
401 |
Child TCP read stop for port 53, reason cleanup |
118 |
Child TCP read stop for port 53, reason close-cb-error |
98 |
Sending RESET to host 192.168.1.126 |
181 |
Sending RESET to host 192.168.1.104 |
12 |
Sending RESET to host 192.168.1.101 |
3 |
約2分間で 192.168.1.126 に 181 発の RESET。ルーターの DNS プロキシが受けた TCP/53 セッションが 600件超、close-cb-error の連発はソケットの強制切断。
つまり .126 がルーターに TCP/53 を投げ続け、ルーターが応えきれずに RESET を打ち返していた。
巻き添え:他端末の通信も落ちていた
同じ時間帯にこういうログも:
TKY-RT IP[006]: Packet 192.168.1.1 > 192.168.1.155 discarded for
LINK-FRMWRK: NO ENTRY IN LOOKUP TABLE TO COMPLETE OPERATION, GigaEthernet1.0
TKY-RT IP[006]: Packet 8.8.8.8 > 192.168.1.155 discarded for ...
TKY-RT IP[006]: Packet 192.168.1.1 > 192.168.1.127 discarded for ...
NO ENTRY IN LOOKUP TABLE = ARP/ND テーブル参照失敗。ルーターが他端末(.155 や .127)への戻りパケットを「宛先 MAC が分からない」と落としている。本来なら ARP リクエストを出して解決するはずが、それすらこなせていない。
CPU/制御プレーンが TCP/53 ストームの処理に取られて、ARP の応答処理が間に合っていなかった、という構図。
第2段: 犯人を物理特定する
MACアドレスでASUSのものだとはわかったけど、マザーボードNICなのでどれかわからず。ということで、そのマシンから全通信をルーターでDropさせて待つ。
「PCつながらないんですけど...」 という申告にて特定。
プロセス調査
ログからDNSリクエストであることは判明していたので問題のPCで、TCP/53 を出しているプロセスを PowerShell で探す:
# TCP/53 を開いている接続を全部見る
Get-NetTCPConnection -RemotePort 53 |
Select-Object LocalAddress, LocalPort, RemoteAddress, State, OwningProcess |
ForEach-Object {
$proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
$_ | Add-Member -NotePropertyName ProcessName `
-NotePropertyValue $proc.ProcessName -PassThru
}
# 古典的に
netstat -ano -p tcp | findstr ":53"
tasklist /FI "PID eq <上で出たPID>"
実行すると犯人プロセスは vmmem(または vmmemWSL)。これは WSL2 の VM プロセスで、内部で動いている Linux 上の何かが TCP/53 を投げまくっているということ。
WSL2 に入って更に追う:
# WSL2 のシェルから
sudo ss -tnp 'dport = :53'
sudo lsof -nP -iTCP:53
docker ps
# 順に止めていく、または docker stats で CPU/Network が突出しているものを探す
「Prometheus コンテナ」が犯人! で確定。
第3段: UFS Cacheオーバーフロー
ZabbixのSNMPログを見てみると同じ時刻に、Zabbix の IX2107: IPv6 UFS Cache Overflows グラフが階段状に上昇していた。事件の最中にカウンタが進み、その後平坦。
ルーターで現在値:
IPv6 UFS Cache - 1175 entries, 8825 frees, 10000 peaks
17938700 creates, 4366 overflows
IPv4 UFS Cache - 972 entries, 19028 frees, 6949 peaks
12488008 creates, 0 overflows
UFS(Unified Forwarding Subsystem)は IX シリーズの高速転送フローキャッシュ。peaks = 10000 は構造上限への到達、overflows = 4366 はあふれた累積回数。
- 障害の主因 = DNS プロキシの TCP ソケットプール枯渇(UFS とは別の資源)
-
IPv6 UFS overflow = 副次的な現象(
.126が並行して張っていた外部向け IPv6 通信が UFS を食った) - 「他端末まで切れた」根本理由 = ARP 解決パスの詰まり
今回はIPv6通信で溢れているが、IPv4 単独環境でも、DNS プロキシ枯渇 + ARP 失敗の経路で同じ症状になると思われる(なった。
第4段: Prometheus の問題点は?
Prometheus がなぜそんなに大量のTCP/53 を投げるのかを調査。
Windows + WSL2 + Docker の DNS チェーンでキャッシュが無い。
DNS クエリは以下の経路を通る:
[Prometheus container]
↓ (resolv.conf: nameserver 127.0.0.11)
[Docker 組み込み DNS]
↓ (WSL2 distro の resolv.conf に従う、通常 10.255.255.254 等)
[WSL2 仮想 NIC の DNS プロキシ]
↓ (Windows のネットワーク設定)
[Windows DNS Client サービス (Dnscache)]
↓ (ホストの DNS 設定)
[192.168.1.1 (NEC IX2107 の DNS プロキシ)]
↓
[ISP の DNS / 1.1.1.1 など]
各層を見ていくと、DNS素通しで誰もキャッシュしていません。
Go の net パッケージは DNS をキャッシュしない
Prometheus は Go 製。Go 標準リゾルバはスクレイプごとに毎回 DNS を引きに行く。OS リゾルバ任せなので、上述のチェーンに有効なキャッシュが無いと 1スクレイプ = 1問い合わせになる。
dns_sd_configs の SRV と再解決
dns_sd_configs を使っていると:
- 定期的に SRV を引いてターゲット一覧を更新(デフォルト 30秒ごと)
- SRV で返ってきた各ターゲットの A レコード(および環境次第で AAAA)を引く
- その後、スクレイプ毎にターゲット名を毎回再解決
ターゲット数が増えると比例して問い合わせが増えるが、それ以上に効くのが次の TCP フォールバック。
応答が 512 バイトを超えると TCP フォールバック
DNS 仕様(RFC 1035)で UDP メッセージは最大 512 バイト。これを超えると応答に TC ビットが立ち、クライアントは同じクエリを TCP で再送する。
何が 512 バイトを超えるか:
- 多数の A/AAAA レコード(CDN 等)
-
SRV レコード(
dns_sd_configsが引くやつ。ターゲット数次第で簡単に超える) - DNSSEC 署名付き
- TXT レコード(SPF/DKIM の長文)
EDNS0 で 1232 や 4096 まで拡張できるが、経路上のミドルウェアが落とすケースもあり、現実には TCP フォールバックは普通に起きる。今回のログで TCP/53 が大量に観測されたのはこれ。
A + AAAA の二重問い合わせ
デュアルスタック環境では IPv4 と IPv6 の両方を引く。これだけで ×2。
補足: IPv4 単独環境でも、Go や glibc は
AI_ADDRCONFIGの判定次第で AAAA を引きに行くことがある(loopback::1が有効だと「IPv6 capable」とみなす実装もある)。なので「IPv4 単独だから AAAA は来ない」とは言い切れない。
全部掛け算するとどうなるか
控えめに見積もっても:
| 要素 | 倍率 | IPv4 単独でも該当? |
|---|---|---|
| 純粋なスクレイプ起因の解決 | 数 lookups/sec | ◯ |
| Go の非キャッシュ仕様 | ×1(実質キャッシュ無し) | ◯ |
| TCP フォールバック(>512B) | ×2(UDP の後 TCP) | ◯ |
| 失敗時リトライ | ×2 | ◯ |
| A + AAAA 二重問い合わせ | ×2 | △ (AI_ADDRCONFIG 次第) |
dns_sd_configs 多ターゲット |
×N | ◯ |
ざっくり 数十 TCP/53 connections/secなので、IX2107 の DNS プロキシ TCP ソケットプールが2分で枯渇するのに十分な量。
重要:このうち IPv6 特有なのは「A + AAAA の ×2」だけ。残りは全て IPv4 単独環境でも起きる。私が以前 IPv4 単独環境で同様の症状を経験していたのは、これで説明がつく。
対策
ベストプラクティスは「Prometheus に DNS を引かせない、もしくは引いてもローカルで止める」仕組みを作ること。Windows + WSL2 + Docker 環境向けに優先順位ごとに:
1. Docker ネットワーク内に DNS キャッシュコンテナを置く(最優先)
dnsmasq などをコンテナで立て、Prometheus の DNS をそこに向ける。Windows / WSL2 の事情に依存せず、Docker ネットワーク内で完結するのが利点。
# docker-compose.yml
services:
dns-cache:
image: 4km3/dnsmasq
container_name: dns-cache
cap_add:
- NET_ADMIN
command:
- --no-daemon
- --server=1.1.1.1
- --server=8.8.8.8
- --cache-size=10000
- --neg-ttl=60
networks:
monitoring:
ipv4_address: 172.20.0.2
prometheus:
image: prom/prometheus
dns:
- 172.20.0.2
dns_search: []
dns_opt:
- ndots:1
- timeout:2
- attempts:2
networks:
- monitoring
networks:
monitoring:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
2. Prometheus の DNS 周り設定を見直す
scrape_configs:
- job_name: 'nodes'
dns_sd_configs:
- names: ['_metrics._tcp.example.lan']
refresh_interval: 5m # デフォルト 30s から延長
dns_sd_configs が必須でないなら、static_configs で IP 直書きにする。
# 解決負荷あり(毎スクレイプで DNS を引く)
- targets: ['node1.example.lan:9100', 'node2.example.lan:9100']
# 解決負荷なし
- targets: ['192.168.1.10:9100', '192.168.1.11:9100']
3. WSL2 / Windows 側の見直し
-
/etc/wsl.confに[network] generateResolvConf = falseを入れて/etc/resolv.confを固定し、外部 DNS(1.1.1.1など)に直接向ける選択肢もある - Windows の Dnscache サービスが動いているか確認:
Get-Service Dnscache
ipconfig /displaydns | more
4. ルーター側の制限
根本対策が他のホストで再発する可能性を考えて、TCP/53 に per-host レートリミットや NAT セッション数の上限を設定しておく。IX2107 なら ACL でホスト単位の TCP/53 制限が組める。
わかったこと
ログ大事
「特定の PC が原因」までは皆気づくが、その先に「監視ツールの DNS 挙動 + WSL2 の DNS チェーン + ルーターのリソース上限」という三段重ねがあるとは、症状からは想像しづらい。ログとカウンタを横断的に見ないと辿り着けなかった。
ルーターの DNS プロキシは注意
ルーターの DNS プロキシ機能は便利だが、内部の TCP ソケットプールサイズは限られている。LAN 内に「DNS を大量に引くデバイス」が現れた瞬間に、全体のネットが詰まる場合がある。
Prometheus は DNS を引きすぎる
これは Prometheus の中の人も認識していて、issue #15262 などで議論されている。でも、ちょっとひどすぎん?
参考リンク
- Prometheus blackbox_exporter で大量の DNS クエリが発生する事例 (issue #437)
- blackbox_exporter の ICMP probe で IP 直書きでも DNS を引く (issue #978)
- Prometheus の remote-write における DNS リゾルバの議論 (issue #15262)
- RFC 1035 (DNS の基本仕様、512 バイト制限)
- RFC 6891 (EDNS0)
- WSL2 の DNS 設定について (Microsoft Learn)
- Docker の DNS 関連ドキュメント
おまけ
トラブル時に手早く状況を掴むためのメモ:
Windows 側
# プロセス別 TCP/53 接続
Get-NetTCPConnection -RemotePort 53 |
Group-Object OwningProcess |
Sort-Object Count -Descending
# DNS キャッシュ確認
Get-DnsClientCache | Sort-Object Entry
# Windows DNS Client が動いているか
Get-Service Dnscache
# パケットキャプチャ(管理者 PowerShell)
pktmon filter add -p 53
pktmon start --etw -m real-time
# ... Ctrl+C で停止
pktmon filter remove
# DNS テスト
Resolve-DnsName example.com -Type A
Resolve-DnsName _metrics._tcp.example.lan -Type SRV
WSL2 側
# DNS 設定確認
cat /etc/resolv.conf
cat /etc/wsl.conf # 無いことも多い
# TCP/53 接続
sudo ss -tnp 'dport = :53'
# DNS 引き比較(UDP vs TCP)
dig example.com @192.168.1.1 +bufsize=512
dig example.com @192.168.1.1 +tcp
dig _metrics._tcp.example.lan SRV @192.168.1.1
Docker / コンテナ側
# コンテナの DNS 設定
docker exec prometheus cat /etc/resolv.conf
# Docker ネットワーク確認
docker network inspect bridge
docker network inspect monitoring
# Prometheus 自身の DNS 統計
curl -s http://localhost:9090/metrics | grep -E 'dns_lookups|sd_dns'
# コンテナ内パケットキャプチャ
docker exec prometheus sh -c 'apk add tcpdump 2>/dev/null || apt-get install -y tcpdump'
docker exec prometheus tcpdump -i any -nn -c 200 port 53
IX2107(ルーター)側
show ip arp # IP/MAC 対応
show mac-address-table # MAC が居る物理ポート
show ufs # UFS の状態サマリ
show ufs cache statistics # キャッシュ使用率・overflow
show dhcp server binding # DHCP リースのホスト名