ksoftirqdのボトルネックを解消:NICオフロードと割り込み合体(Interrupt Coalescing)実践ガイド

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

午前2時14分の危機:なぜサーバーが悲鳴を上げているのか

午前2時14分、私のスマートフォンは鳴り止まないバイブレーターと化した。PagerDutyのアラートが3秒おきに飛んでくる。ラップトップを開く頃には、ダッシュボード上の成功率は99.9%から悲惨な42%まで急落していた。一見すると、CPU使用率の合計は30%程度で問題なさそうに見えたが、1.2 Gbpsあったスループットはわずか200 Mbpsまで落ち込んでいた

ターミナルにログインして top コマンドを実行した。ほとんどのコアはアイドル状態だったが、Core 0だけが悲鳴を上げていた。si(ソフトウェア割り込み)カラムは100%に張り付き、ksoftirqd/0 プロセスが利用可能なサイクルをすべて食いつぶしていた。

これこそが、多くの高トラフィックサーバーが直面する壁だ。アプリケーション自体は軽量でも、OSとネットワークインターフェースカード(NIC)がパケットの波に溺れている状態だ。1500バイトのパケット一つひとつがCPU割り込みを発生させると、カーネルはコードを実行するよりもコンテキストスイッチに時間を費やすことになる。本番環境において、NICオフロードをマスターできるかどうかは、効率的にスケーリングできるか、あるいは問題を解決できないハードウェアに無駄金を投じるかの分かれ道となる。

ボトルネック:CPU対パケット

パケットが届くたびに、CPUに対して「データがあるぞ!」という信号が送られる。CPUは現在のタスクを中断し、割り込みを処理してヘッダーを解析する。秒間1,000パケット程度なら問題ないが、秒間800,000パケットともなると、CPUは「割り込みストーム(Interrupt Storm)」に陥る。もはや処理が追いつかないのだ NICオフロードと割り込み合体(Interrupt Coalescing)は、この状況を打破する不可欠な修正策だ。これらの機能により、重い処理をソフトウェアからネットワークカード上のハードウェアへと肩代わりさせることができる。

ツールキットの準備

これらの設定をチューニングするには ethtool が必要だ。これはネットワークドライバと通信するための標準的なユーティリティだ。ほとんどのディストリビューションに含まれているが、新規構築した環境ではインストールが必要な場合もある。

# Debian/Ubuntuの場合
sudo apt update && sudo apt install ethtool -y

# RHEL/CentOS/AlmaLinuxの場合
sudo dnf install ethtool -y

まずは、現在のハードウェアの性能を確認しよう。メインのインターフェースを eth0 と仮定して、以下のコマンドを実行する。

# すべてのオフロード機能とそのステータスを表示
ethtool -k eth0

tcp-segmentation-offloadgeneric-receive-offload のステータスを確認しよう。もしこれらが off になっていたら、それが最初の改善ポイントだ。

スループット向上のためのNICオフロード設定

オフロードは、パケット処理をNIC自体のシリコン(チップ)上に移す。これは、ほぼすべてのWebサーバーを支えるTCPトラフィックに対して非常に効果的だ。

1. TCPセグメンテーションオフロード (TSO) & ジェネリックセグメンテーションオフロード (GSO)

通常、カーネルは大きなデータを送信前に1500バイトのパケットに分割する。TSOを使用すると、カーネルは64KBの大きなバッファを直接NICに渡す。ハードウェアが分割処理を担うため、CPUは何千もの個別のチェックサムやヘッダーを計算する手間から解放される。

# TSOとGSOを有効にする
sudo ethtool -K eth0 tso on gso on

2. ジェネリック受信オフロード (GRO)

受信側ではGROが頼もしい味方になる。NICは、流れてくる小さなパケットのストリームを集め、カーネルに渡す前に一つの大きな「スーパーパケット」に結合する。これにより、CPUが処理すべき割り込みの回数を最大90%削減できる。

# GROを有効にする
sudo ethtool -K eth0 gro on

割り込み合体(Interrupt Coalescing):ストームを静める

オフロードがパケットを大きくするのに対し、合体(Coalescing)は割り込みを少なくする。パケットごとにCPUへ叫ぶのではなく、数マイクロ秒待つか、パケットがある程度まとまるまで待つようNICに指示する。

現在の設定を確認するには:

ethtool -c eth0

重要なパラメータは rx-usecs だ。もしこれらは 0 に設定されているなら、NICはパケット一つごとに割り込みを発生させている。これはゲーミングには低遅延で最適だが、Webサーバーのスループットを著しく低下させる。私は通常、30マイクロ秒の遅延から始めるようにしている。

# RX割り込み遅延を30マイクロ秒に設定
sudo ethtool -C eth0 rx-usecs 30

最近の多くのカードは、アダプティブ割り込み合体(Adaptive Interrupt Coalescing)をサポートしている。これは遅延を動的に調整するスマートな機能だ。トラフィックが少ない時は低遅延を維持し、急増した時はバッチ処理のサイズを大きくしてくれる。

# アダプティブ合体を有効にする
sudo ethtool -C eth0 adaptive-rx on adaptive-tx on

結果の検証とモニタリング

設定を変更するのは始まりに過ぎない。その影響を必ず検証しなければならない。午前2時の修正後、私は各コアのCPU使用率を注意深く監視した。mpstat を使って、ソフトウェア割り込みの負荷が下がったか確認しよう:

# CPU統計を1秒おきに監視
mpstat -P ALL 1

以前負荷が集中していたコアの %soft カラムが大幅に低下しているはずだ。また、生の割り込み回数をリアルタイムで観察することもできる:

# eth0の割り込み回数を監視
watch -n 1 "cat /proc/interrupts | grep eth0"

スループットが高いままでカウントの増加が緩やかになれば、成功だ。これらの設定は再起動後にリセットされることが多いので注意しよう。Netplanの設定やシンプルなsystemdユニットを使って永続化させる必要がある。ネットワークスタックをチューニングすることで、CPUを本来の役割、つまりユーザーへのサービス提供に専念させることができるのだ。

Share: