【Linux】カーネルパラメータのパフォーマンスチューニングについて

2019年6月14日

シェルのコマンドライン

LinuxのカーネルにはカーネルパラメータというOSの各種設定を調整するための機能があります。

カーネルパラメータがデフォルト値のままでも一般的な使用であれば特に問題はありません。

しかしアクセスの多いサーバや負荷の高いサーバ、特殊な条件で使用するサーバなどで一部パラメータのパフォーマンスチューニングが必要になることがあります。

カーネルパラメータについて

カーネルはオペレーティングシステム(OS)のコアとなる部分であり、その各種設定を調整するパラメータがカーネルパラメータです。

カーネルパラメータはprocファイルシステムを通してアクセスすることが可能となっています。

カーネルパラメータの確認

カーネルパラメータの確認は/proc/sys以下の対応するディレクトリのファイルを読み出すことにより確認が可能です。

// 例:TCP Fast Openの設定値を確認
# cat /proc/sys/net/ipv4/tcp_fastopen
3

また以下のようにsysctlコマンドでも確認することができます。

// 各ディレクトリは.でつなげる。また先頭のprocとsysは省略する
# sysctl net.ipv4.tcp_fastopen
net.ipv4.tcp_fastopen = 3

全ての設定値を確認したい場合はsysctlコマンドの-aオプションを使います。

# sysctl -a
debug.exception-trace = 1
dev.scsi.logging_level = 0
dev.tty.ldisc_autoload = 1
fs.aio-max-nr = 65536
fs.aio-nr = 0
・・・以下省略・・・

※カーネルパラメータの初期値はメモリー搭載量などのリソース量によってOSの起動時に調整されるものがあります。

※カーネルパラメータの初期値はカーネルバージョンによって変更されているものがあります。

カーネルパラメータの設定

カーネルパラメータの設定は/proc/sys以下の対応するディレクトリのファイルに値を書き込むことにより設定が可能です。

// 例:TCP Fast Openの設定値を0に設定
# echo 0 > /proc/sys/net/ipv4/tcp_fastopen

また以下のようにsysctlコマンドでも設定することができます。

# sysctl -w net.ipv4.tcp_fastopen=0
net.ipv4.tcp_fastopen = 0

※カーネルパラメータに設定可能な設定値は新しいカーネルバージョンで変更になっている場合があります。(いままで0と1しか設定できなかったけど2が設定できるようになったなど)

カーネルパラメータ設定の恒常化

/proc/sys以下のファイルに書き込んだりsysctlコマンドで設定したカーネルパラメータは一時的なものでOSを再起動すると初期値に戻ってしまいます。

/etc/sysctl.confというファイルに設定値を記述することでOSの起動時に読み込んで自動的に反映してくれます。

/etc/sysctl.confファイルの記述例
# で始まる行はコメント
# TCP Fast Open の設定
net.ipv4.tcp_fastopen = 3

またsysctlコマンドの-pオプションで/etc/sysctl.confに記述した設定を即時に反映することができます。

# sysctl -p
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.all.rp_filter = 2
net.ipv4.tcp_syncookies = 0
net.ipv4.ip_forward = 0
・・・以下省略・・・

-pの後に設定ファイルを指定するとそのファイルから設定を読み込むことができます。

省略時は/etc/sysctl.confファイルから設定を読み込みます。

パフォーマンスチューニングについて

OSの使用のされ方によってはカーネルパラメータのチューニングが必要になってきます。

バッファーサイズなどのパラメータは負荷試験などを行いながら問題があれば最適値を探すことになります。

チューニングに関係する代表的なパラメータを説明してきます。

ネットワーク関連のパラメータ

net.ipv4.tcp_ecn

LinuxにはExplicit Congestion Notification(ECN)というネットワークの混雑具合を通知する機能があります。

このECNという機能を有効(1)にするか無効にするか(0)ECNを通知してきた相手には有効にするか(2)を設定します。

最近のLinuxではデフォルト値は2となっており変更する必要はないことがほとんどです。

昔はECN機能に対応していないルーターが通信経路上に存在している場合、ECN機能を有効(1)にすると通信相手と正常に通信ができなくなる問題がありました。

そのため昔はサーバのECN機能を無効(0)にする設定を行う事が多かったと思います。

net.ipv4.tcp_rfc1337

RFC1337で報告された「time-wait assassination hazards in tcp」の問題に対応する設定です。

有効(1)になっているとTIME_WAIT状態のソケットへのRSTパケットを破棄します。

しかしこれは標準的なTCPの挙動に反するため規定値では0となっています。

無効(0)ではTIME_WAIT状態のソケットへのRSTパケットで即座にソケットがクローズされますが「time-wait assassination hazards in tcp」の危険性があります。

「time-wait assassination hazards in tcp」の詳しい情報は以下を参照して下さい。

RFC 1337 – TIME-WAIT Assassination Hazards in TCP

net.ipv4.tcp_timestamps

TCPのタイムスタンプオプションを有効(1)にするか無効(0)にするか設定します。

TCPレベルでパケットの順番を判断できるようになりTCP通信が効率よくできるようにするオプションですが色々と問題があるため無効(0)推奨です。

タイムスタンプオプションのセキュリティホールをついた攻撃やNAT下でのソケット再利用時の問題が報告されています。

また有効時はオプション分パケットサイズが大きくなりますのでほんの僅かに通信帯域の効率が低下してしまいます。

net.ipv4.tcp_syncookies

SYN FLOOD攻撃を防ぐSYN Cookie機能を有効(1)にするか無効(0)にするか設定します。

フロントのサーバであればSYN FLOOD攻撃にさらされる危険性がありますので有効(1)に設定した方がいいでしょう。

WANに直接接続されていないバックエンドのサーバであればSYN Cookieの機能を無効(0)にして接続処理を軽くする設定がおすすめです。

net.ipv4.tcp_rmem

net.ipv4.tcp_wmem

net.core.rmem_default

net.core.wmem_default

net.core.rmem_max

net.core.wmem_max

送信(wmem)と受信(rmem)に関するソケットバッファのサイズを設定します。

アプリケーションがソケットオプションでソケットバッファのサイズを指定しない場合はデフォルト(_default)の値が使用されます。

またアプリケーションは設定の最大値(_max)を超えてソケットオプションでソケットバッファのサイズを指定することはできません。

net.ipv4.tcp_rmemとnet.ipv4.tcp_wmemに関してはTCPのソケット1つが使用するバッファサイズの最小値、初期値、最大値を設定します。

初期値の関してはこちらの値が優先されますが最大値に関しては_maxが優先されます。

設定できるソケットバッファの最大値は16MBとなっています。

環境により最適地は異なり通信効率に影響を与えるため初期値と最大値はパフォーマンステストを行いながらチューニングしていきます。

net.ipv4.tcp_mem

システム全体でTCP通信に使用できるメモリ量をページ単位で設定します。

前述のrmemやwmemの値を大きくした場合はこちらの値も大きくしておく必要があります。

指定はlow, pressure, highとありメモリ使用量がlow以下であればメモリの調整を行わず、pressureを超えるとメモリ節約がオンになります。

使用できるメモリの最大ページ数はhighの値となります。

パフォーマンス的にはメモリ使用量の調整処理が入らないように3つの値を同じにしておくのがおすすめです。

net.ipv4.tcp_window_scaling

TCPのウィンドウサイズの上限を拡張する設定です。

よっぽど古いカーネルバージョン出ない限りデフォルトは有効(1)となっています。

基本的に現在のネットワーク環境ではデフォルトの有効(1)のままで良いと思います。

net.core.netdev_budget

net.core.dev_weight

netdev_budgetはNAPIの1回のソフトウェア割り込みでポーリングする上限パケット数を設定します。

dev_weightはNAPIの1つのデバイスから1回のソフトウェア割り込みでポーリングする上限のパケット数を設定します。

大量の受信トラフィックでポーリング処理が追い付かずNICにパケットのドロップが発生する状況では値を増加させると症状が改善する場合があります。

通常の使用の範囲内ではデフォルト値のままで特に問題はありません。

net.core.somaxconn

待ち受けできるソケット数の上限を設定します。

ファイルディスクリプタの上限を超えて設定はできません。

古いカーネルだとデフォルト値が128となっているものがあるので必要に応じて値を大きくする必要があります。

大量のコネクションを待ち受けて処理するサーバでは4096以上の値にしておいた方がいいでしょう。

最適な上限値は負荷テストを行う必要があります。

net.core.netdev_max_backlog

受信パケットの処理待ちのキューの上限値を設定します。

値を大きくすると大量の受信パケットが一度に来てスパイクしてもある程度耐えることができるようになります。

受信パケット数がスパイクする環境では値を大きくしておいた方がいいでしょう。

値をどこまで大きくすればいいのかは受信パケットのスパイクテスト等を行って調査する必要があります。

net.ipv4.tcp_max_syn_backlog

新規コネクション開始時のSYNパケットを受信した際の処理待ちキューの上限値を設定します。

値を大きくすると新規コネクションが短時間で大量に来てスパイクしても受け付けることができるようになります。

値をどこまで大きくすればいいのかは新規コネクションのスパイクテスト等を行って調査する必要があります。

net.ipv4.ip_local_port_range

コネクションを作る際に使用できるローカルポート番号の範囲を設定します。

初期値は32768~60999くらいの範囲となっていますので、このポート範囲広げることでコネクションの接続と切断を頻繁に繰り返す環境でも使用できるポートが枯渇しにくくなります。

頻繁に接続と切断を繰り返す環境でコネクションのポートが枯渇するようなエラーがでている場合は10000~とかに設定を変更することをおすすめします。

net.ipv4.ip_no_pmtu_disc

パケットのDFビットをオフにしてPath MTU Discoveryを行わない設定をすることができます。

有効(1)にするとパケットのDFビットがオフになり送信パケットの分割が可能となりPath MTU Discoveryが無効となります。

パケットの分割が許可されると経路上の機器に設定されたMTUに関係なく送信パケットが宛先に到達するようになりますが通信効率が下がります。

パケットのDFビットがオンの状態で経路上にicmpを落とすような機器が存在するとMTUが原因で送信パケットがドロップされたことを送信元が知ることが出来ずに送信パケットが失われます。

net.ipv4.tcp_mtu_probing

TCP層でブラックホールルータが検出された場合の挙動を設定します。

通常MTUが原因でパケットをドロップすると送信元にicmpで通知が行きますが、経路上にicmpをドロップする機器が存在すると上手く通知できずにパケットが失われます。

そのような状態でパケットが失われるルータをブラックホールルータと呼んでいます。

MTUの検出を無効(0)に設定するとパケットサイズの調整は行われずに一定時間後にパケットは再送されます。(しかしドロップします)

ブラックホールルータ検出後有効(1)にするとパケットに対する再送時にパケットサイズを小さくして送信します。

常に有効(2)ではブラックホールルータの検出に関係なく常にMTUの検出が有効になります。

net.ipv4.tcp_no_metrics_save

通信のメトリック情報(RTTやウィンドウサイズ、ホップ数など)を保存しないように設定できます。

同じ接続先に接続を行う場合は以前通信した最後の情報を元に接続を開始しますが、無効にすると初期値で接続を開始できます。

クライアントからの通信経路が変わる可能性の高いフロントのサーバではメトリック情報を保存しないように有効(1)に設定するのがおすすめです。

常に同じ環境で決まった相手と通信するバックエンドのサーバでは無効(0)にして保存したメトリック情報を使用した方が効率が良くなるでしょう。

net.ipv4.tcp_syn_retries

net.ipv4.tcp_synack_retries

net.ipv4.tcp_retries1

net.ipv4.tcp_retries2

パケットの再送回数を設定できます。

この回数を減らすことでタイムアウトまでの時間を減らすことが可能となります。

通常のサーバでは初期値のままで特に問題ありません。

タイムアウトを短くしないといけないような状況の時は再送回数を調整して短くすることでアプリケーションの無駄な待ち時間を減らすことができます。

net.ipv4.tcp_fin_timeout

セッションクローズ時のソケットのFIN状態の待機時間を設定できます。

ネット上の情報ではソケットのTIME_WAIT状態を減らす設定であると間違って説明してあるところがありますが関係ありません。

もちろん短くすることでクローズ後のソケットが解放されるまでのトータルの時間は短くなります。

net.ipv4.tcp_tw_reuse

ソケットクローズ後のTIME_WAIT状態のソケットを再使用可能にします。

同じような設定でnet.ipv4.tcp_tw_recycleというものがありますが危険なので使用しない方がよいです。

危険なのでカーネル4.12からrecycleの設定は廃止となっています。

こちらのreuseの設定は安全に使用できますのでTIME_WAITが多くて使用可能なポートが枯渇するような環境であれば有効(1)に設定しておきましょう。

net.ipv4.tcp_slow_start_after_idle

一定時間無通信状態が続いたらウィンドウサイズを初期値から開始する設定です。

無効(0)にすることで一定時間無通信状態が続いた後でウィンドウサイズを保ったまま通信することが可能となります。

ある程度通信環境が安定した相手との送受信で効率が良くなる可能性があります。

頻繁に通信環境が変わるようなクライアントとの通信ではウィンドウサイズを初期化した方が良い場合もあります。

net.ipv4.tcp_fastopen

TCP通信開始時の3ハンドシェイクを効率化するTCP Fast Openを有効にするか設定します。

TCP Fast Openでは一度通信した相手との3ハンドシェイクを省略できますので通信開始までにかかる時間が圧倒的に少なくて済みます。

有効(1)で自分がクライアントとして通信を開始する際、TCP Fast Open機能を有効にします。

有効(2)では自分がサーバとして通信を待ち受けている際、TCP Fast Open機能を有効にします。

1と2をORした値の3ではクライアントとサーバ両方のTCP Fast Open機能が有効になります。

その他のビットフラグを立てた際の挙動もありますがここでは割愛します。

ディスク関連のパラメータ

vm.dirty_ratio

vm.dirty_background_ratio

vm.dirty_writeback_centisecs

vm.dirty_expire_centisecs

ディスク書き込みキャッシュを実際にディスクに書き込む閾値やチェック間隔を設定できます。

閾値を下げチェック間隔を短くすることでシステム障害時に失われる書き込みデータの量を減らすことができます。

また閾値を上げチェック間隔を長くすることで書き込みパフォーマンスを上げることができますが、システムの書き込み負荷によってはディスクへの書き込みがスパイクしたり、障害時のデータ損失が大きくなります。

vm.swappiness

メモリをディスクへスワップする閾値を設定することができます。

値が低いほどスワップしにくくなりますのでパフォーマンスを要求されるサーバ用途では極力スワップしない(1)などの設定値にしておいたほうがいいでしょう。

その他のパラメータ

今回紹介したパラメータは主なものだけとなっています。

ページのサイズが大きくなってきましたのでその他で解説した方がよいパラメータにつきましては別の機会に行いたいと思います。

※紹介していない重要なカーネルパラメータを忘れている可能性があります

(すっかりI/Oスケジューラ関連のカーネルパラメータの紹介を忘れておりました・・・が最近のカーネルではBulk MQが主流なので今回は良しとします)

まとめ

Linuxのカーネルパラメータは奥が深くて柔軟性があります。

通常Linuxはカーネルパラメータを調整しなくてもある程度までは問題なく動作しますので、負荷的に余裕をもった冗長性のあるサーバ運用等であればデフォルトのまま使用するのが一番安全かもしれません。

カーネルパラメータをチューニングしないといけないような用途での使用は稀かと思いますが覚えておくと将来役に立つと思います。

どうやっても解決しない問題がカーネルパラメータ1つの変更で解決できるかもしれません。

Linuxを運用する技術者の方は自分でもカーネルパラメータについて調べてみて下さい。

きっと新しい世界が広がるはずです。