Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save peco8/ef2d95f595d2af339e3c6b47a6b5a10d to your computer and use it in GitHub Desktop.
Save peco8/ef2d95f595d2af339e3c6b47a6b5a10d to your computer and use it in GitHub Desktop.
sysctl

sysctlで設定可能な項目について調査した内容。

ファイルディスクリプタ

OS全体のファイルディスクリプタ数の制限値

fs.file-max で設定。 プロセス単位の制限については /etc/security/limits.conf で設定する必要がある。 これらの値はPAM認証(sudoやユーザーでログインした場合)がないと反映されないため、デーモンプロセスなどでファイルディスクリプタ数を増やしたいときには ulimit コマンドを起動スクリプト( /etc/rc.d/init.d/ など)で指定する必要がある。

OS全体の非同期I/Oの最大数

fs.aio-max-nr で設定。 デフォルトは65536で、Oracleなどは1048576といった大きめの値を推奨している。 値が小さいとアプリケーションが起動できないといったケースが発生する。

現在値は /proc/sys/fs/aio-nr で確認できる。

参考資料

システム全体のスレッド数

システム全体のスレッド数は kernel.threads-max で設定。デフォルトではメモリサイズに応じて算出された値となる。

なおLinuxではスレッドもプロセスとして扱われるため、スレッドにもpidを必要とする。 kernel.threads-max を設定した場合は、同じ値を kernel.pid_max にも設定しなければならない。

プロセス番号の最大値のデフォルト値は 32768 (1 << 15)。 32 bit 環境では上げられない。 64 bit 環境では 2^22 (4Mぐらい) まで上げることが可能。 http://www.yunabe.jp/docs/maximum_number_of_threads.html

またpthreadは1スレッドあたり、2つのメモリマップを必要としているため、メモリマップの最大数の半分を超えるスレッドを作ることはできない。 kernel.threads-max を設定する場合は、合わせて vm.max_map_count に倍の値を設定する。

大量のスレッドを利用する環境で Cannot allocate memory エラーが発生した場合に、このチューニングを行う。

参考資料

メモリスワップの度合いを調整

vm.swappiness で設定。

0 から 100 の値でシステムがスワップを行う程度を制御します。値が高い場合はシステムのパフォーマンスを優先し、物理メモリがアクティブでない場合にメモリからプロセスを積極的にスワップします。値が低いと対話機能を優先し、できるだけ長く物理メモリからのプロセスのスワップを回避することから、反応の待ち時間が低下します。デフォルト値は 60 です。 https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/performance_tuning_guide/s-memory-tunables

一般的に低く設定するのが良いが、ネットで見かけるような0などの値は問題になることがあるので注意。 詳細は こちら

ユースケースとしては

  • DBサーバなど起動してるアプリケーションが少ない環境下では1〜10など
  • 様々なアプリケーションを同時に起動してるような環境下では10〜20程度?

参考資料

TCPの状態遷移図

前提として知っておく情報。

  • 3ウェイ・ハンドシェイクによるTCP接続のオープン処理 3ウェイ・ハンドシェイクによるTCP接続のオープン処理
  • TCP接続のクローズ処理 TCP接続のクローズ処理
  • TCPの状態遷移図 TCPの状態遷移図

backlogとsyn backlog

listen()システムコールでLISTEN状態にあるソケットはbacklogというキューを持つ。backlogにはクライアントからのTCP接続要求が確立(ESTABLISH状態)し、まだaccept()されていない接続が並ぶ。 LISTENソケットは、backlog以外にもTCP接続中(SYN_RECV状態)の接続を格納しておくsyn backlogというキューも持つ(実際にはハッシュテーブルで実装) backlog

backlogのサイズ

listen()時に指定されるbacklog引数の値

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);

ただし、 net.core.somaxconn < backlog の場合は net.core.somaxconn の値に切り詰められる。

syn backlogのサイズ

backlogと net.ipv4.tcp_max_syn_backlog の値を元に決定する。

syn backlogのサイズはbacklog値を 8 〜 net.ipv4.tcp_max_syn_backlog の範囲に収めた後、一つ上の2のべき乗の値に切り上げた値になる。

表1 syn backlogサイズ計算例 (tcp_max_syn_backlog = 400の場合)

backlog syn backlog
128 256
200 256
256 512
512 512 (net.ipv4.tcp_max_syn_backlog < backlogなので400を切り上げた512となる)

ネットワークインタフェース

パケット受信時にキューに繋ぐことができるパケット最大数

net.core.netdev_max_backlog で設定。

カーネルの処理速度よりも早い速度でネットワーク・インタフェースがパケットを受信する場合に、カーネルがバッファ(キューイング)するパケットの最大個数を指定します。

TCPウィンドウスケーリング有効化

net.ipv4.tcp_window_scaling で設定。 RFC1323のTCPウィンドウスケーリングを有効にするためのもので、64KB以上にしたいときに1を設定する。 最近のカーネルはデフォルト1なので、意識しなくても大丈夫。

TCPの送受信バッファサイズ

以下で設定。

  • net.ipv4.tcp_rmem
  • net.ipv4.tcp_wmem

いずれも、 min default max の3つの値をスペース区切りで指定する。

値が小さいと一度に送信できるデータ量が制限され、場合によってはACK待ちとなる。 大きくするとメモリ使用量や通信エラー時のリカバリに影響がある。

デフォルト値がかなり小さい場合があるので、1回でやりとりするデータ量が大きいサーバの場合はチューニングすると効果的。

またOS全体のあらゆるコネクションのバッファサイズを設定する項目もある。

  • net.core.rmem_default
    • 受信バッファサイズのデフォルト値
  • net.core.rmem_max
    • 受信バッファサイズの最大値
  • net.core.wmem_default
    • 送信バッファサイズのデフォルト値
  • net.core.wmem_max
    • 送信バッファサイズの最大値

net.core.[r|w]mem_[default|max]net.ipv4.tcp_[r|w]mem で内容的にはかぶるが、max値はnet.coreが優先され、デフォルト値はnet.ipv4が優先される。

Slow Startを無効化

net.ipv4.tcp_slow_start_after_idle で設定。 輻輳制御アルゴリズムの一つであるSlow Startを利用しないときに0を設定する。Slow Startは、Keepaliveしててもしばらく通信がない場合に、TCPウィンドウサイズが増えたままだと適切な値じゃなくなってる場合があるので、その辺を調整してくれるための機構。

デフォルトは1が多そう。輻輳が少ない環境下でKeepaliveが使われている場合に0にしておくと効率が良さそう。

ソケットの補助データの最大バッファサイズ

net.core.optmem_max で設定。

以下、関連する個人的な説明。

まずソケットの補助データにアクセスするために利用するのがCMSGマクロで、補助データはソケット上に乗るものではない。例えばドメインソケットでは、これを利用してファイルディスクリプタやプロセスのcredential情報などを受け渡してる。 補助データ用のバッファサイズが小さいとバッファ内に収まらなくなり、一部のデータを切り捨てられたことを示すMSG_CTRUNCフラグが立つとのこと。 ただMSG_CTRUNCフラグが立ったときの挙動はわからず… 自分で実装した場合は、バッファサイズを増やして再度ハンドリングするとかになるみたいだけど https://stackoverflow.com/questions/48861927/how-do-i-get-the-size-of-the-msg-control-buffer-for-recvmsg/48913873#48913873 実際にnet.core.optmem_maxを調整してパフォーマンスが向上してるケースはあるみたい。 「転送効率がよくなって」という説明だったけど、バッファサイズが直接効率につながる気はしないので、MSG_CTRUNCフラグが立った場合のハンドリングに何かがありそう。 http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=2405&forum=11 ネットでみかけるnet.core.optmem_maxのチューニング値は、40960もあれば16777216(EMQとか)とだいぶまちまち。 補助データがどれだけ利用されるアプリケーションを実行しているか次第、というところかもしれない。

ソケット

TIME_WAIT状態の許容最大数

net.ipv4.tcp_max_tw_buckets で設定。

この制限が存在するのは、 単純な使用不能 (denial-of-service) 攻撃を防ぐために過ぎない。 デフォルト値は NR_FILE で、システムのメモリーに応じて調整される。この数値を越えると、そのようなソケットはクローズされ、警告が表示される。 https://linuxjm.osdn.jp/html/LDP_man-pages/man7/tcp.7.html

TIME_WAIT状態のソケットの再利用

net.ipv4.tcp_tw_reuse = 1 で設定。

net.ipv4.tcp_tw_reuse を有効にすると、 TIME_WAIT 状態のソケットでもタイムスタンプが1秒でも新しければ、新しい接続で再利用できるようになります。 ただしすぐに再利用されるというわけではなく、外向きの接続で、なおかつ接続先が同じ場合に限られます。 https://qiita.com/tmshn/items/b49f1b51bfc472968b30

以下の条件を満たした場合のみ利用される。

  • 送信元も受信先も、 net.ipv4.tcp_timestamps=1 でTCP Timestamp Optionを有効にしている
  • 送信元のIPとポート番号、受信元のIPとポート番号が一致している

送信時に利用可能なローカルポートの範囲

net.ipv4.ip_local_port_range で設定。 可能であればどちらかを偶数、もう片方を奇数にした方が良い。 詳しくは https://patchwork.ozlabs.org/patch/476002/ デフォルトの値は基本的に 32768 60999

FIN-WAIT2 から TIME_WAIT に状態変化するまでの時間

net.ipv4.tcp_fin_timeout で秒数を設定。 TIME_WAIT状態のセッションが大量に残ってしまうような場合、この値を短く設定することで解決できる。

サーバの用途によって最適値は異なる。 Webサーバは 20〜30秒 程度が目安の模様。 ISUCONでは同一クライアントからの大量アクセスが行われるため、 10秒 程度が良さそう。

参考資料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment