物理サーバーで新規接続が失敗する原因は conntrack の枯渇か?
サービスのプロセスは正常で、既存の長時間接続では通信できているのに、新しいリクエストだけが断続的にタイムアウトする。このような症状は、必ずしもアプリケーションのリッスンキューが原因とは限りません。物理サーバーがステートフルファイアウォールやネットワークアドレス変換(NAT)を担っている場合、Linux の接続追跡テーブルがボトルネックになることもあります。調査で重要なのは、失敗した時刻とテーブル使用量、カーネルのカウンターを突き合わせることです。接続数が多いという理由だけで上限を引き上げるべきではありません。
この記事では Debian 13 を例に、すべての確認をホストのネットワーク名前空間で実施します。CloudUDP のサーバーでは、支払い後にコンソールで認証情報を確認し、Linux には SSH で接続します。アドレスが複数ある場合は、最初のアドレスを使用してください。以下の操作では、ネットワークルールの変更や既存の接続状態の消去は行いません。
1. 障害の起きた通信が接続追跡の経路を通るか確認する
接続追跡(conntrack)は、通信フローのプロトコル、アドレス、状態、タイムアウトを記録し、状態に基づくマッチングや NAT に利用します。サービスの接続が 1 本あることと、アプリケーションのスレッドが 1 本あることは同じではありません。また、アプリケーションが接続を閉じた後も、テーブルのエントリがしばらく残る場合があります。短時間の接続の大量生成、異常なスキャン、転送トラフィックの負荷はいずれも使用量を押し上げる要因です。
まず確認用のツールをインストールし、設定を読み取ります。
sudo apt-get update
sudo apt-get install conntrack
sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
sudo conntrack -S
sudo nft list ruleset
ルールに ct state や NAT が含まれていれば、接続追跡の容量を確認すべき手掛かりになります。ただし、これらのルールがなくても、接続追跡が一切行われていないとは断定できません。パラメーターの読み取りで存在しないと表示された場合は、まずモジュールと実際のトラフィック経路を確認します。「出力をそろえる」ためだけに、むやみにモジュールをロードしないでください。
コンテナ内で得た結果を、そのままホストの結果として扱うことはできません。ホストでの転送とコンテナ内部の通信は、異なる名前空間を通る可能性があるため、コマンドを実行した場所を必ず記録します。そうしないと、コンテナ内の使用量が少ないことを根拠に、ホストでのパケット損失を誤って解釈しかねません。
通常の復旧手段として
conntrack -Fを実行しないでください。テーブルを消去すると既存の状態が失われ、状態に基づくマッチングや NAT に依存するセッションを壊す可能性があります。保持すべき障害発生時の証拠も失われます。
2. 同じ時間帯の使用量とエラーカウンターの差分を収集する
nf_conntrack_count は現在のテーブルエントリ数、nf_conntrack_max は容量の上限です。上限に近づいていることは余裕の減少を示しますが、それだけではリクエスト失敗の原因とは証明できません。conntrack -S はプロセッサーごとの統計を出力するため、各項目を全プロセッサー分合計したうえで、2 回のサンプリングの差分を比較します。出力の 1 行をサーバー全体の合計として扱わないでください。
umask 077
out=$(mktemp -d /var/tmp/ct-check.XXXXXX)
for sample in 1 2; do
{
date -Is
sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
sudo conntrack -S
} > "$out/sample-$sample.txt"
if [ "$sample" -eq 1 ]; then
sleep 10
fi
done
sudo journalctl -k --since "-10 min" --no-pager \
> "$out/kernel.txt"
printf '%s\n' "$out"
ここでのサンプリング間隔は操作例にすぎません。実際には、サービスで失敗が発生した時間帯を含めて測定してください。特に確認すべきなのは、insert_failed、drop、early_drop が継続して増えているか、カーネルログに接続追跡テーブルが満杯であることを示すメッセージが出ているかです。フィールドはカーネルのバージョンによって異なる場合があり、特定のフィールドがないことを理由に正常と判断してはいけません。
アプリケーションの新規接続失敗数、リクエスト数、エラーメッセージの原文も同時に保存します。サーバーを再起動した直後であれば、累積カウンターの基準が変わっています。再起動前後の値をそのまま差し引かないでください。閑散時にエラーカウンターが増えなくなっても、ピーク時の容量検証に合格したことにはなりません。
3. エントリが蓄積する理由を見極める
まず「正常な同時接続数の増加」と「異常な接続生成速度」を区別します。流入するリクエスト、アプリケーションから開始する外向き接続、転送トラフィック、接続の再利用状況を突き合わせてください。短時間の接続は、存続時間が短くても生成速度が高ければエントリが蓄積します。一方、長時間接続では実際の同時接続数の影響がより大きくなります。
最初からテーブル全体をダンプしない
負荷の高いホストでは、全エントリを列挙する操作自体が調査の負荷を増やします。出力には通信アドレスも含まれます。まず集計カウンターを読み、必要性が確認できた場合にだけ、プロトコルと出力量を制限して取得します。
sudo timeout 5s conntrack -L -p tcp -o extended 2>/dev/null \
| head -n 100
この出力は偏りのあるサンプルであり、状態の分布を示す統計ではありません。タイムアウトやパイプの早期終了によってコマンドの終了コードが 0 以外になることもあるため、それを根拠にテーブルが破損していると判断しないでください。アドレスを含む記録はアクセスを制限したディレクトリにのみ保存し、サポートチケットを提出する前に無関係なサービス情報を取り除きます。
待ち受け中のプロセス、アプリケーションのエラー、上流の応答も並行して確認する必要があります。容量に余裕があり、関連カウンターも増えていない場合は、アプリケーションのブロッキング、ルーティング、その他のパケット損失箇所へ調査対象を移します。conntrack のパラメーター調整に固執しないでください。
最初から全プロトコルのタイムアウトを短縮してはいけません。タイムアウトはアイドル状態の接続情報をどれだけ保持するかを決めるもので、過度な短縮は長時間接続に影響する可能性があります。接続プールの再利用ができていない、失敗時のリトライが制御されていないといった問題は、呼び出し側で修正すべきです。
4. メモリ予算に合わせて元に戻せる変更を行う
上限を引き上げても、より多くのエントリを保持できるようになるだけで、異常なトラフィックの発生源は解消しません。エントリはカーネルメモリを消費し、その具体的な使用量はプロトコル拡張やカーネル設定などに左右されます。固定の「1 エントリ当たり何バイト」という値で、正確なメモリ予算を算出することはできません。
変更前に free -h と /proc/meminfo の内容を記録し、sudo slabtop -o でカーネルのオブジェクトキャッシュを確認します。Slab はカーネルがオブジェクトを管理するためのキャッシュ機構です。そこに表示される接続追跡オブジェクトの統計も、関連するメモリコストのすべてを表すわけではありません。
容量不足を裏付ける証拠があり、メモリにも余裕がある場合は、小幅な変更で検証できます。以下では元の上限を 4 分の 1 だけ増やします。これは試験用の変更幅であり、固定の推奨設定ではありません。
old=$(sysctl -n net.netfilter.nf_conntrack_max) || exit 1
case "$old" in
''|*[!0-9]*) exit 1 ;;
esac
new=$((old + old / 4))
printf 'old=%s new=%s\n' "$old" "$new"
sudo sysctl -w net.netfilter.nf_conntrack_max="$new"
このセッションと元の値を保持し、同程度のリクエスト数で再度サンプリングします。失敗が改善しなければ、続けてパラメーターを引き上げず、元の値に戻して別の証拠を確認します。
sudo sysctl -w net.netfilter.nf_conntrack_max="$old"
元に戻す前に、現在の使用量を確認してください。すでに元の上限を超えている場合は、先に新規接続の負荷を下げる必要があります。そうしないと、元に戻すことで容量問題が再発する可能性があります。ハッシュバケット、プロトコルのタイムアウト、ファイアウォールルールを同時に変更しないでください。検証では、変更する変数を 1 つに絞る必要があります。
5. サービスの結果を基準に永続化を判断する
検証を合格とするには、3 種類の証拠が同時にそろう必要があります。同程度のサービス負荷で新規接続の成功率が回復していること、関連するエラーカウンターが継続して増えていないこと、メモリとカーネルのオブジェクトキャッシュに新たな負荷が生じていないことです。使用率が下がっただけでは、修正完了とはいえません。
効果を確認したら、検証済みの数値を /etc/sysctl.d/90-conntrack-capacity.conf に記述します。書き込む前に、ほかの設定ファイルで同じパラメーターが重複定義されていないか確認し、読み込み順による上書きを避けてください。今回のファイルだけを読み込み、サーバー全体の未検証の設定までまとめて適用しないようにします。
sudo sysctl -p /etc/sysctl.d/90-conntrack-capacity.conf
sysctl net.netfilter.nf_conntrack_max
ファイルには net.netfilter.nf_conntrack_max = 検証済みの整数 という 1 行を記述し、説明部分は必ず実際の数値に置き換えます。運用担当者が予定した次回の再起動後に、有効な値と読み込みエラーを再確認してください。パラメーターを検証するためだけに、本番サーバーをすぐに再起動してはいけません。
最後に、元の値、新しい値、サンプリング期間、リクエスト数、カウンターの差分、元に戻す条件を保存します。接続追跡の容量管理のゴールは、数値を大きくすることではありません。接続失敗を説明でき、変更の効果を証明でき、負荷が変わった後にも同じ一連の証拠を再確認できることが重要です。
よくある質問
conntrack の使用量が上限に近ければ、枯渇と判断できますか?
できません。同じ時間帯の insert_failed、drop、early_drop の増分を、カーネルログと新規接続エラーに照らして確認します。高い使用量は容量リスクの兆候であり、単独のカウンターでも原因は確定できません。
早く復旧するために conntrack テーブルを消去してもよいですか?
通常の復旧手段にはしません。既存接続の追跡状態が消え、状態判定ルールや NAT に依存するセッションが途切れる可能性があります。まずカウンターとログを保存し、異常な接続元への対処か、メモリ予算を踏まえた容量変更を行います。
EPYC 7543P 専用物理サーバー、月額 $454〜