CentOS Stream 9でsysctlによるカーネルパラメータチューニング:TCP/IPスタック、仮想メモリ、ファイルディスクリプタの本番環境最適化

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

5分でできる:基本的な最適化設定

CentOS Stream 9のサーバーが稼働中でパフォーマンスをすぐに改善したい場合は、以下のコマンドを実行してください:

# 任意のパラメータの現在値を確認する
sysctl net.core.somaxconn

# 即時適用(再起動不要だが、再起動後は元に戻る)
sudo sysctl -w net.core.somaxconn=65535
sudo sysctl -w net.ipv4.tcp_fin_timeout=30
sudo sysctl -w vm.swappiness=10

# 適用されたか確認する
sysctl net.core.somaxconn vm.swappiness net.ipv4.tcp_fin_timeout

注意:sysctl -wは現在のセッションのみに適用されます。永続化するには設定ファイルを作成する必要があります。次のセクションで説明します。

本番環境でsysctlを調整する理由

sysctlは仮想ファイルシステム/proc/sys/を通じてカーネルパラメータを直接読み書きするツールです。各パラメータは特定の動作に対応しています。net.core.somaxconnはTCP接続のaccept待ちキューの最大数を決定し、vm.swappinessはカーネルがスワップを使い始めるタイミングを制御します。

問題はCentOS Stream 9のデフォルト値が汎用サーバー向けに設計されており、本番ワークロード向けではない点です。somaxconn=128のままNginxで1,000リクエスト/秒を処理しようとすると、小さなトラフィックスパイクでも接続がドロップされ始めます。

CentOS 8がEOLになった際、1週間で5台のサーバーをRocky Linuxへ急いで移行しました。その時に気づいたのですが、すべてのサーバーが10年以上前のデフォルトTCP設定のまま動いていました。適切なパラメータを適用したところ、平均レイテンシが明らかに改善されました。特にマイクロサービス環境での短時間接続で効果が顕著でした。

本番サーバーに最も影響を与える3つのパラメータグループ:

  • net.* — TCP/IPスタック、接続キュー、ポートレンジ
  • vm.* — 仮想メモリ、スワップ動作、ダーティページ
  • fs.* — ファイルディスクリプタ、inotify制限

各パラメータグループの詳細解説

TCP/IPスタック:Webサーバーとリバースプロキシのためのチューニング

/etc/sysctl.d/に専用の設定ファイルを作成します。/etc/sysctl.confを直接編集するのは上書きされやすく管理が難しいため避けましょう:

sudo nano /etc/sysctl.d/99-production.conf

以下の内容をファイルに貼り付けます:

# --- TCP/IPスタック ---
# ソケットのバックログキューを増やす(デフォルト128はWebサーバーには低すぎる)
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535

# TIME_WAIT状態のソケットを再利用する(ポート枯渇を防ぐ)
net.ipv4.tcp_tw_reuse = 1

# 接続クローズの待機時間を短縮する(デフォルト60秒)
net.ipv4.tcp_fin_timeout = 30

# アウトバウンド接続のポートレンジを拡大する(リバースプロキシで重要)
net.ipv4.ip_local_port_range = 1024 65535

# ソケットバッファサイズ — 高レイテンシ環境でのスループット向上
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# SYNバックログ — 基本的なSYNフラッド対策
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_syncookies = 1

somaxconn = 65535:accept()待ちキューに入れる最大接続数です。デフォルト値の128では、500リクエスト/秒を受け付けるWebサーバーは小さなスパイクでも接続をドロップし始めます。

tcp_tw_reuse = 1:TIME_WAIT状態のソケットを新しい接続に再利用することをカーネルに許可します。マイクロサービスが数千の短時間接続を生成する環境では、最も効果が顕著なパラメータの一つです。

ip_local_port_range:アウトバウンド接続のポートレンジです。Nginxが10台のバックエンドアップストリームに接続する場合、各リクエストがポートを1つ消費します。デフォルトでは約28,000ポートしかなく、TIME_WAITが長引く場合は300リクエスト/秒で2分以内に枯渇します。

仮想メモリ(vm.*):ピーク時のOOMキラー発動を防ぐ

# --- 仮想メモリ ---
# スワップの使用傾向を下げる(10 = 本当に必要な時だけスワップ)
vm.swappiness = 10

# RAMが少ない時のページキャッシュ回収圧力
vm.vfs_cache_pressure = 50

# ダーティページをより頻繁にディスクへフラッシュする(突然のI/Oスパイクを防ぐ)
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5

vm.swappiness = 10:デフォルト値の60では、RAMがまだ空いている状態でもカーネルがかなり積極的にスワップを使用します。ディスクの読み書きはRAMより数百倍から数千倍遅いため(HDDかNVMeかによって異なる)、これは大きなレイテンシスパイクを引き起こします。10に設定することでアプリケーションをRAMに長く留め、本当に不足した時だけスワップを使うようになります。

MySQLやPostgreSQLを運用している場合、多くのDBAは0ではなくvm.swappiness = 1を推奨しています。0に設定してもスワップが完全に無効化されるわけではなく、一部のエッジケースでカーネルの動作が不安定になることがあります。1の方が新しいカーネルでの副作用が少ないです。

vm.dirty_ratio = 15:カーネルが強制フラッシュを行う前にダーティページが占有できるRAMの最大割合です。デフォルト値の20から15に下げることでI/Oがより均等に分散され、大量の一括フラッシュによる突発的なI/O wait高騰を防ぎます。

ファイルディスクリプタの制限:Nginx、Node.js、Elasticsearchに必要な設定

ファイルディスクリプタの制限はToo many open filesエラーがログに現れるまで見落とされがちです。これはサーバーが高負荷にある時に発生することが多いです。CentOS Stream 9のデフォルト制限はプロセスあたり1024ファイルで、本番サービスには低すぎます。

# --- ファイルシステム ---
# システム全体の最大ファイルディスクリプタ数
fs.file-max = 2097152

# Inotifyウォッチ — ファイル監視ツールに必要(Webpack、Vite、Elasticsearch)
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 512

sysctlに加えて、PAM経由でプロセスごとの制限も設定する必要があります:

sudo nano /etc/security/limits.d/99-nofile.conf
# 全ユーザーに適用
*       soft    nofile  65536
*       hard    nofile  65536

# Nginxを実行するユーザー専用
nginx   soft    nofile  200000
nginx   hard    nofile  200000

systemdサービスの場合は、制限をオーバーライドするためにユニットファイルにも追加する必要があります:

sudo systemctl edit nginx
[Service]
LimitNOFILE=200000

応用:設定の適用と確認

# /etc/sysctl.d/内の全ファイルを適用する — 再起動不要
sudo sysctl --system

# 特定のパラメータを確認する
sysctl net.core.somaxconn

# パラメータを検索する(完全な名前を覚えていない場合に便利)
sysctl -a 2>/dev/null | grep tcp_fin

# 実行中のプロセスのファイルディスクリプタ制限を確認する
cat /proc/$(pgrep nginx | head -1)/limits | grep "open files"

# TIME_WAITソケット数を数える — tcp_tw_reuse有効化後に減少する
ss -ant | grep TIME-WAIT | wc -l

Elasticsearchには独自の必須パラメータがあり、設定がない場合はサービスが起動を拒否してすぐにエラーを報告します:

# Elasticsearchに必須
echo 'vm.max_map_count = 262144' | sudo tee /etc/sysctl.d/99-elasticsearch.conf
sudo sysctl --system

実践的なヒント

1. 変更前後にベンチマークを取る

ss -sを使って変更前後のソケット統計を確認しましょう。24時間の本番トラフィック後と比較できるよう、出力をキャプチャしておくのがおすすめです:

# TCP接続統計のスナップショット
ss -s

# 状態別の詳細を確認
ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn

2. 闇雲にコピペしない — まず現在の値を確認する

パラメータを変更する前に、現在の値を確認してカーネルのドキュメントを読みましょう。カーネル5.x以降では多くのTCPバッファパラメータが自動チューニングされており、手動でオーバーライドすると改善どころか遅くなることがあります。

3. 本番適用前にステージング環境でテストする

ほとんどのパラメータは安全に試せますが、vm.overcommit_memory = 2は例外です。RAMの合計を誤算した場合にOOMキラーがより積極的に動作してプロセスをkillする可能性があります。自分はMySQLサーバーが適用直後にkillされるのを経験しましたが、幸いステージング環境でした。

4. カーネルアップデートごとに確認する

/etc/sysctl.d/内のファイルは起動時に自動的に読み込まれますが、カーネルアップデートによってデフォルト値が変わることがあります。今日オーバーライドした値が、数バージョン後には必要なくなっている可能性もあります。以下のコマンドで確認しましょう:

# 実行中の値と設定ファイルの内容を比較する
sysctl -a 2>/dev/null | grep -E 'somaxconn|swappiness|file-max|tcp_fin_timeout'

# 読み込まれているすべてのsysctlファイルを確認する
sysctl --system --dry-run 2>&1 | head -20

ネットワークパラメータを適用した後は、dmesg | tail -20を実行してカーネルが警告をログに記録していないか確認しましょう。ハードウェアの制限を超えたパラメータは、実際の問題が発生する前にここに現れます。

Share: