conntrack の枯渇を調査し、接続追跡テーブルの容量変更を検証する

conntrack の枯渇を調査し、接続追跡テーブルの容量変更を検証する

conntrack の枯渇を調査し、接続追跡テーブルの容量変更を検証する

サービスで接続タイムアウトが断続的に発生する一方、既存のセッションは通信を継続し、アプリケーションのプロセスや待ち受け状態にも異常がない場合は、Linux の接続追跡テーブルを確認します。ステートフルファイアウォール、アドレス変換、一部のコンテナネットワークは conntrack に依存しています。エントリを作成できなくなると、新しい通信がアプリケーションに届く前に破棄されることがあります。本記事では Debian 12 を例に、証拠の収集、原因の切り分け、容量の調整、ロールバックの順に手順を説明します。ファイアウォールのルール変更や既存接続の一括削除は行いません。

1. 障害が発生しているネットワーク名前空間を確認する

CloudDDR の物理サーバーは AMD EPYC 7543P と 128GB または 256GB の ECC メモリを搭載していますが、メモリ容量が conntrack の上限を直接決めるわけではありません。確認すべき対象は、カーネル、ネットワーク名前空間、サービスのトラフィックです。支払い後にコンソールで認証情報を確認し、SSH で接続します。アドレスが複数ある場合は、最初のアドレスを使用してください。

コマンドは、実際にトラフィックを処理しているネットワーク名前空間で実行します。ホスト上の結果がゲストシステムの状態を示すとは限りません。コンテナのトラフィックがホストのアドレス変換を経由する場合は、ホストも確認する必要があります。まず、カーネルのバージョン、現在のネットワーク名前空間、エントリ数、上限を記録します。

uname -r
readlink /proc/self/ns/net
sudo sysctl net.netfilter.nf_conntrack_count
sudo sysctl net.netfilter.nf_conntrack_max
sudo sysctl net.netfilter.nf_conntrack_buckets

キーが存在しない場合、その名前空間では該当するパラメータが公開されていません。それだけで枯渇と判断してはいけません。先に、関連モジュールがロードされているか、サービスが接続追跡を使用しているかを確認します。出力をそろえるためだけに、障害対応中に新たな機能を有効化しないでください。

conntrack -F は実行しません。テーブルを消去すると追跡状態が削除され、既存のセッションやアドレス変換のマッピングが壊れるおそれがあります。原因の特定に必要な証拠も失われます。

2. カウンターの増分から証拠を積み上げる

ユーザー空間のツールをインストールしても既存のルールは変わりませんが、インストールは通常の変更管理手順に従って実施します。以下の操作では現状を読み取り、時間を置いて取得した 2 つのサンプルから、カウンターが増え続けているかを確認します。

sudo apt-get install conntrack
umask 077
sudo conntrack -S > /tmp/conntrack-before.txt
sudo journalctl -k --since "-10 min" --no-pager \
  | grep -Ei 'nf_conntrack|table full'
sleep 30
sudo conntrack -S > /tmp/conntrack-after.txt
diff -u /tmp/conntrack-before.txt /tmp/conntrack-after.txt

conntrack -S は通常、CPU ごとに累積カウンターを出力します。同じ CPU の行を比較し、insert_failed、drop、early_drop の増分に注目してください。起動後の累積値を、そのまま現在の障害の証拠として扱わないことが重要です。カウンターの意味や出力フィールドはカーネルによって異なる場合があり、挿入失敗のすべてが容量不足によるものとは限りません。

併せて、サービスのタイムアウト件数、テーブルの使用数、カーネルログを記録します。使用数が上限に近づき、失敗カウンターが増え、同じタイミングでサービスのエラーも発生していれば、より確かな裏付けになります。ログに該当する出力がなくても、問題を否定することはできません。ログ出力がレート制限を受けている可能性があるためです。サンプル取得の合間に発生する短いピークは見逃すことがあります。必要に応じて観測間隔を短くしますが、テーブル全体の出力をループで繰り返さないでください。

3. 接続数と接続の生成レートを区別する

サンプルからエントリが蓄積する理由を調べる

エントリ数は、現在のリクエスト数と同じではありません。1 本の TCP 接続で複数のリクエストを処理でき、UDP も追跡エントリを使用します。また、通信が終わった後も、対応するタイムアウトに達するまでエントリが残ることがあります。そのため、安定して維持される長時間接続、短時間接続の急増、異常な送信元を区別する必要があります。

高負荷時に接続テーブル全体を繰り返し出力するのではなく、取得範囲を限定したサンプリングを 1 回行います。

sudo timeout 5s conntrack -L -o extended 2>/tmp/conntrack-sample.err \
  | head -n 200 > /tmp/conntrack-sample.txt

このコマンドは出力の一部だけを取得します。途中で終了してゼロ以外の終了ステータスを返すことがあり、得られるサンプルは完全な統計ではありません。ファイルにはアドレス情報が含まれるため、読み取り権限を制限し、問い合わせチケットに添付する場合は必要に応じてマスキングしてください。出力には元方向と応答方向のタプルが含まれます。送信元を分析するときは、この 2 組のアドレスを混在させて集計しないでください。

プロトコル、TCP の状態、タイムアウトの残り時間、元方向の送信元を確認します。短時間接続が大量にある場合は、アプリケーションの接続プールやリトライログも調べます。少数の送信元が新しいフローを作り続けている場合は、アクセス記録と照合して判断します。UDP のエントリが長期間蓄積する場合は、実際の通信周期を確認してください。状態名だけで攻撃と断定したり、トラフィックの異常を単なる容量問題として扱ったりしてはいけません。

接続の生成レートが下がらなければ、テーブルを拡大しても再び上限に達するまでの時間を延ばすだけです。無制限のリトライの修正、接続再利用の復旧、異常な送信元への対処は、容量調整とは分けて検証する必要があることが多いものです。

4. メモリを評価してから、変更する変数を 1 つに絞る

上限はメモリの事前確保量でも、スループットの保証でもない

nf_conntrack_max を増やしても、すべてのエントリが直ちに作成されるわけではありません。ただし、テーブルが継続的に埋まっていけば、カーネルメモリを消費します。free -h、/proc/meminfo、sudo slabtop -o でメモリと slab の使用状況を確認してください。表示されるキャッシュのオブジェクトサイズには、拡張フィールドや割り当てのオーバーヘッドなど、すべてのコストが含まれるわけではありません。「1 接続あたり何バイト」という固定の式を当てはめるのは適切ではありません。

nf_conntrack_buckets はハッシュバケットの数であり、接続容量ではありません。上限だけを増やすと、バケットのチェーンが長くなり、検索コストが増える可能性があるため、CPU 使用率とサービスの遅延も観測する必要があります。バケット数を稼働中に変更できるかどうかは、カーネルと名前空間の制約によります。今回の手順では、上限と同時には変更しません。

以下は、上限を 262144 にする一時的な変更の例です。実行前に、この値が現在の値より大きいこと、メモリ予算に収まること、別の管理セッションが利用できることを必ず確認してください。すべてのマシンに推奨する値ではありません。コマンドは管理権限を持つシェルで実行します。

sudo -i
OLD=$(sysctl -n net.netfilter.nf_conntrack_max)
test "$OLD" -lt 262144 || exit 1
umask 077
printf '%s\n' "$OLD" > /run/conntrack-max.before
sysctl -w net.netfilter.nf_conntrack_max=262144
sysctl -n net.netfilter.nf_conntrack_max

元の値を保存したうえで、同じサービスの疎通確認とカウンターのサンプリングを繰り返します。同時に TCP established のタイムアウトを短くしないでください。アイドル状態の長時間接続もサービスで使われている可能性があり、タイムアウトを一律に短縮すると、新たな切断原因を持ち込むことになります。

5. 検証、ロールバック、永続化を分けて行う

検証では、少なくとも元の障害を引き起こしたトラフィックと、アイドル状態から通信を再開する長時間接続を対象にします。新規接続の成功率、失敗カウンターの増分、使用数の増加傾向、メモリの余裕、CPU 使用率、サービスの遅延を確認してください。上限が大きくなったことを確認しただけでは、問題が解決したとはいえません。

メモリの逼迫、CPU 使用率、遅延が悪化した場合は、保存した値を使ってロールバックします。

sudo sysctl -w \
  net.netfilter.nf_conntrack_max="$(sudo cat /run/conntrack-max.before)"

上限を下げても、既存のエントリが直ちに削除されるわけではありません。使用数が復元後の上限を超えている場合は、新しいエントリの作成が引き続き妨げられる可能性があります。エントリが自然に消えるのを待つとともに、トラフィックの送信元に対処してください。復旧を早めるためにテーブルを消去してはいけません。

一時的な調整の検証が完了したら、/etc/sysctl.conf と /etc/sysctl.d/ に同名の設定がないかを確認し、複数のファイルが互いに上書きする状態を避けます。検証済みの値を、バージョン管理の対象となる専用の設定ファイルに記述します。例は次のとおりです。

net.netfilter.nf_conntrack_max = 262144

そのファイルだけを読み込み、実際に適用された値を再確認します。障害対応中に、無関係なパラメータまで一括で再読み込みしないでください。後日、管理された手順で再起動する際には、モジュールのロード後の最終的な値も確認します。設定ファイルに書き込んだからといって、起動時に必ず有効になるとは限りません。

最終的な記録には、トラフィックの送信元、変更前後のカウンター、サービスの疎通確認結果、メモリの観測結果、元の値、復元手順を含めます。接続追跡テーブルの容量管理が完了したといえるのは、障害の証拠を説明でき、調整結果を再現できるときです。エントリ数の上限に、より大きな数字が表示されること自体が目的ではありません。

よくある質問

テーブルが上限に近ければ、パケット損失の原因と断定できるか?

断定できない。同じ観測期間の insert_failed、drop、early_drop の増分を、カーネルログと業務側の失敗に照合する必要がある。使用数だけでは容量の圧迫しか分からない。

nf_conntrack_max を増やす際、TCP のタイムアウトも短くするべきか?

同時に変更する必要はない。メモリ予算を確認して上限だけを検証する。TCP established の全体タイムアウトを短くすると、通信のない時間が長い接続を壊す可能性がある。

AMD EPYC 7543P 搭載の専用物理サーバー。月額$447から。

物理サーバーの構成はメモリ容量から

AMD EPYC 7543P、128GBと256GBのDDR4-3200 ECCメモリから選べる2構成。