「高性能」ハードウェアが本領を発揮できないとき
以前、16コアと64GBのRAMを誇るサーバーに高トラフィックのAPIをデプロイしたことがありました。スペック上は「モンスターマシン」でした。しかし、同時実行ユーザー数が5,000人という中規模なスパイクが発生した際、アプリケーションは「Connection Refused(接続拒否)」エラーを吐き出し始めました。モニタリングツールを見ると、CPU使用率は15%でアイドル状態に近く、メモリにも十分な空きがありました。サーバーは過負荷だったのではなく、単に自身のオペレーティングシステムによって制限をかけられていたのです。
このような苛立ちは、開発者やシステム管理者にとって珍しいことではありません。生のパワーはあるのに、Linuxカーネルが汎用デスクトップ向けの「安全な」デフォルト設定で動作しているのです。ハードウェアの可能性を解き放つには、sysctlの世界に飛び込む必要があります。
問題点:現代にそぐわないレガシーなデフォルト設定
Linuxカーネルは、Raspberry Piからスーパーコンピュータまで、あらゆるデバイスで動作するように設計されています。そのため、デフォルト設定は保守的です。これらの設定は低スペックのノートPCでは安定性を確保しますが、毎秒数千のリクエストを処理する本番サーバーにとってはボトルネックとなります。
ほとんどのパフォーマンス問題は、次の3つのカテゴリに分類されます。攻撃的なスワップ:
- ネットワークバックログ: 着信接続のキューが非常に小さい(多くの場合、デフォルトはわずか128)。これにより、NginxやNode.jsが接続を認識する前に、カーネルがパケットをドロップしてしまいます。
- ソケットの枯渇: システムのエフェメラルポートが不足する。これは、閉じた接続をデフォルトの60秒間「TIME_WAIT」状態に保持し続けることで発生します。
- 過度なスワップ: RAMに空きがあるにもかかわらず、カーネルがデータをディスクに移動させる。これにより、1ミリ秒のデータベースクエリが、ディスクI/O待機による100ミリ秒の悪夢に変わる可能性があります。
ハードウェア増設 vs カーネルチューニング
サイトが遅くなると、クラウドコンソールの「アップグレード」ボタンをクリックしたくなるのが反射的な反応です。RAMを増やせば余裕は生まれますが、設定ミスのあるネットワークスタックがそれで直ることは稀です。
| アプローチ | メリット | デメリット |
|---|---|---|
| 垂直スケーリング | リソースを即座に増強できる。 | 月額コストの上昇。ソフトウェアのボトルネックを無視している。 |
| ロードバランシング | 冗長性に優れている。 | 構成の複雑化とレイテンシの増加を招く。 |
| カーネルチューニング (sysctl) | コストゼロ。効率を最大化できる。 | 不安定化を避けるためのテストが必要。 |
既存のリソースを最適化することが、最も賢明な第一歩です。sysctlを介したチューニングは、アプリケーションとその背後にあるハードウェアの間の摩擦を取り除きます。
sysctlへの体系的なアプローチ
数多くの本番クラスターを管理してきた経験から、闇雲に設定を変更するよりも体系的なアプローチの方が優れていることがわかりました。タイポ(打ち間違い)一つでカーネルパニックを引き起こしたり、サーバーから締め出されたりする可能性があります。これらの変更は、必ず最初にステージング環境でテストしてください。
1. sysctlの基本
sysctlコマンドは、実行時にカーネルパラメータを変更します。これらの設定は/proc/sys/ディレクトリにあります。
システムの現在の全設定を確認するには、以下を実行します:
sysctl -a
特定の値(例:オープンできるファイルの最大数)を確認するには、以下を使用します:
sysctl fs.file-max
永続化せずに変更をテストしたい場合は、-wフラグを使用します。この設定は再起動後に消えるため、安全策として最適です。
2. ネットワークスタックの修正
Webサーバーにとって、ネットワークスタックは最も頻繁なボトルネックです。サーバーが短命な接続を多数処理する場合、「パイプ」を広げ、ソケットをより速く再利用する必要があります。
設定ファイルを開きます:
sudo nano /etc/sysctl.conf
高い並行トラフィックを処理するために、以下のパラメータを追加します:
# 保留中の接続キューを増やす
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 8192
# 送信接続用に使用できるポートの範囲を広げる (デフォルトは多くの場合 32768-60999)
net.ipv4.ip_local_port_range = 1024 65535
# 新しい接続のために TIME_WAIT 状態のソケットを再利用する
net.ipv4.tcp_tw_reuse = 1
# リソースを解放するために接続をより速く切断する (デフォルトは 60秒)
net.ipv4.tcp_fin_timeout = 15
# スループット向上のために TCP ウィンドウサイズを大きくする
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
3. メモリ管理とスワップ設定 (Swappiness)
デフォルトでは、LinuxはRAMの使用率が約40%に達すると、RAMからスワップパーティションへのデータの移動を開始します。これはデータベースにとって致命的です。SSDやHDDからのデータアクセスは、RAMよりも桁違いに遅いためです。
ほとんどの本番サーバーでは、swappinessを10に設定します。これにより、カーネルにRAMを優先させ、ディスクは最後の手段としてのみ使用するように指示します。
vm.swappiness = 10
vm.dirty_ratio = 60
vm.dirty_background_ratio = 2
dirty_ratioの設定は、データをディスクに書き込む前にカーネルがキャッシュする方法を制御します。値を大きくすると書き込みパフォーマンスが向上しますが、停電時のデータ損失のリスクが高まります。
4. ファイルハンドル制限の拡張
Linuxでは、すべてのユーザー接続、ログファイル、ソケットはファイル記述子(ファイルディスクリプタ)です。デフォルトの制限はプロセスあたり1,024であることが多く、忙しいNginxワーカーなら数秒で上限に達してしまいます。
fs.file-max = 2097152
fs.file-maxはグローバルな制限を設定しますが、アプリケーションユーザーごとの制限を上げるために/etc/security/limits.confの更新も必要になる場合があることを覚えておいてください。
5. 変更の適用
/etc/sysctl.confを更新したら、以下のコマンドで即座に変更を適用します:
sudo sysctl -p
値が有効になったことを必ず再確認してください。例えば、sysctl net.core.somaxconnを実行して、新しい制限値65535がアクティブであることを確認します。
安全なワークフロー
巨大な設定ファイルをただコピー&ペーストしないでください。環境の安定性を保つために、次のワークフローに従ってください:
- ベースライン:
wrkやk6を使用してロードテストを実行し、現在のスループットとエラー率を測定します。 - 反復: 一度に1つの設定グループ(例:ネットワークのみ)を変更します。
- 監視: カーネルの警告やエラーがないか、
dmesgや/var/log/syslogを監視します。 - 検証: ロードテストを再実行します。接続エラーが減少し、レスポンスタイムがより安定するはずです。
カーネルのチューニングは魔法ではありませんが、魔法のように感じられることもあります。同じCPU使用率でサーバーが2倍のトラフィックを処理できるのを見るのは、非常に満足感があります。まずはsomaxconnとswappinessから始めてみてください。これら2つの変更だけでも、ほとんどのワークロードで最大の効果が得られます。

