午前2時のPagerDutyによる呼び出し
午前2時、私の電話が鳴り響きました。コアゲートウェイの監視ダッシュボードは真っ赤に染まっていました。ユーザーはウェブサイトが固まる理由を調べる間もなくアプリケーションにアクセスできず、pingのパケットロス率は80%に達し、SSHセッションはハングアップを繰り返していました。ようやく安定したターミナルセッションを確保できたとき、カーネルログが犯人を突き止めました。画面は同じメッセージで埋め尽くされていたのです。
[84321.123456] net_ratelimit: 542 callbacks suppressed
[84321.123457] neighbor table overflow!
[84321.123458] neighbor table overflow!
これは、私たちが運用しているような高密度な仮想化クラスターなど、大規模なレイヤー2 networkにおける典型的な障害です。サーバーは、これまで調整されたことのないハードリミットに達していました。端的に言えば、Linuxカーネルのアドレス帳がいっぱいになり、近隣機器(ネイバー)を覚えられなくなっていたのです。
ネイバーテーブルの仕組み
ネイバーテーブル(IPv4におけるARPキャッシュ)は、IPアドレスとMACアドレスのマッピングリストだと考えてください。ローカルネットワーク上のデバイスと通信するには、サーバーはそのデバイスのMACアドレスを知る必要があります。サーバーはARPリクエストをブロードキャストし、応答を受け取り、そのマッピングを保存します。これにより、次回から再度の問い合わせが不要になります。
Ubuntu 22.04やDebianなどの標準的なLinuxディストリビューションは、汎用的な環境向けに調整されています。これらは、せいぜい数百のネイバーを想定しています。しかし、フラットなKubernetesネットワークや2,000以上のノードを持つVLANなどのモダンな構成は、これらのデフォルト値を圧倒します。デバイス数がカーネルの内部制限を超えると、新しいエントリの受け入れが停止します。これがネットワーク接続が切断される瞬間です。
制限値の確認
ボトルネックを診断するには、まず現在のネイバーエントリ数をカウントします。
ip neighbor show | wc -l
次に、Address Resolution Protocol(ARP)のガベージコレクター(GC)を制御する3つの sysctl しきい値を確認します。
sysctl -a | grep gc_thresh
net.ipv4.neigh.default.gc_thresh に関する3つの特定の値が見つかります:
- gc_thresh1(デフォルト 128): 下限値。エントリ数が128未満の場合、カーネルはキャッシュのクリーニングを検討さえしません。
- gc_thresh2(デフォルト 512): ソフト上限。キャッシュが5秒以上にわたって512を超えている場合、ガベージコレクターが削除を開始します。
- gc_thresh3(デフォルト 1024): ハードリミット。1,024に達すると、カーネルは即座にエントリの消去を試みます。失敗した場合、「overflow」エラーが発生し、新しい接続は破棄されます。
私たちのケースでは、ネットワーク上のアクティブなデバイスが2,400台に急増していました。gc_thresh3 がデフォルトの1024のままだったため、サーバーは狭い部屋に群衆を押し込もうとしていたのです。これではうまくいくはずがありません。
解決のための2つの方法
1. 「クイックフィックス」(注意して使用してください)
本番環境で障害が発生している場合は、ARPテーブルのフラッシュを検討するかもしれません:
sudo ip -s -s neigh flush all
リスク: これにより、サーバーはすべての接続に対して一度に再ARPを強制されます。トラフィックの多いゲートウェイでは「ARPストーム」が発生し、数分間にわたってネットワーク帯域が飽和する可能性があります。これは最終手段としてのみ使用してください。
2. 恒久的なSysctlチューニング
より良い方法は、混雑した環境が正常であることをカーネルに伝えることです。最新のデータセンターの多くでは、デフォルト値を4倍にすることを推奨します。8,000エントリあっても、メモリ使用量はごくわずかです。ARPエントリ1つは約256バイトで、8,192エントリでも約2MBのRAMしか消費しません。Linux ネットワークパフォーマンス向上と安定性を考えれば、非常に小さな代償です。
本番環境向けの構成
/etc/sysctl.conf を直接変更しないでください。管理を容易にするために、/etc/sysctl.d/ に専用のファイルを作成します。
高密度な環境に対応するために、以下の設定を適用します。私はこの構成を5,000以上のノードを持つクラスターで使用してきましたが、問題は一度も起きていません。
# /etc/sysctl.d/20-neighbor-limits.conf に保存
# IPv4 ネイバーテーブルのチューニング
net.ipv4.neigh.default.gc_thresh1 = 1024
net.ipv4.neigh.default.gc_thresh2 = 4096
net.ipv4.neigh.default.gc_thresh3 = 8192
# IPv6 ネイバーテーブルのチューニング
net.ipv6.neigh.default.gc_thresh1 = 1024
net.ipv6.neigh.default.gc_thresh2 = 4096
net.ipv6.neigh.default.gc_thresh3 = 8192
# エントリを1時間キャッシュに保持することでARPトラフィックを削減
net.ipv4.neigh.default.gc_stale_time = 3600
net.ipv6.neigh.default.gc_stale_time = 3600
再起動せずに新しい設定を即座にロードします:
sudo sysctl -p /etc/sysctl.d/20-neighbor-limits.conf
なぜこの値なのか?
gc_thresh1 を1,024に設定することで、小さなテーブルに対してカーネルがガベージコレクションにCPUサイクルを浪費するのを防ぎます。gc_thresh3 の8,192という制限は、パニックを引き起こすことなく、新しいコンテナやVM、ネットワークスキャンの急増に対して十分なバッファを提供します。
結果の検証
変更を適用した後、カーネルログ(dmesg)を確認してください。このような深夜2時のネットワーク障害を解決する際に現れる「overflow」メッセージは即座に消えるはずです。テーブルが自然に増加する様子を確認するには、watchコマンドを使用します:
watch -n 1 "ip neigh show | wc -l"
カウントは、以前の制限値である1,024を難なく超え、実際のデバイス数で安定するはずです。もし際限なく上昇し続ける場合は、ネットワークループや悪意のあるアクターによるARPスイープの可能性があります。
まとめ
大規模なネットワーキングには、「そのままで使える」設定からの脱却が必要です。「Neighbor Table Overflow」は、小さなデフォルト値がいかに大きな頭痛の種になるかを示す好例です。最新のデータセンターや大規模なVLANでサーバーを管理しているなら、午前2時の呼び出しを待つ必要はありません。今すぐネイバー数を確認し、Linux ネットワークパフォーマンスのベンチマークを行いながら、テーブルが上限に達する前に制限値を調整しておきましょう。

