LinuxサーバーのTCPネットワークのパフォーマンスを決定するカーネルパラメータ – 2編

連載

LinuxサーバーのTCPネットワークのパフォーマンスを決定するカーネルパラメータ – 1編

4.ネットワークcapacity関連パラメータ

4.1 maximum file count

Linuxをはじめとする一般的なUnixソケットは、まるでファイルのような扱いを受けます。
システム全体で保有できるファイル数が制限されていれば、当然ソケットの全体数にも影響を与えるでしょう。

Linuxでシステム全体が保有できる最大ファイル数は、fs.file-maxカーネルパラメータで設定します。

以下のようなコマンドで現在の設定値を確認できます。

$ sysctl fs.file-max
fs.file-max = 775052

一般的に、この値は適度に大きな値が設定されているので、余程のことがない限り、修正することはないでしょう。
ただ、システムが非常に多くのファイルとソケットを使用している場合は、この値によってシステムが誤動作することがあります。
(この値を超えると、open()システムコールでToo many open filesのようなエラーが発生します。)

言い換えれば、システム全体に対する許容ソケット数は、fs-file-maxカーネルパラメータの設定値が適度に高く設定されていることから、大きな問題はありません。
またプロセスが保有できるソケット数の制限は、プロセス別の制限設定であるuser limit値から確認できます。

次のようなコマンドで確認できます。

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 30473
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 30473
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

ここに表示されているのは、open filesがプロセスが保有できるソケットを含むファイルの数です。
増加させるには、次のようなコマンドを使用します。

$ ulimit -SHn 65535

たくさんの数のソケットを使用するサーバープログラムなら、駆動前にulimitコマンドから各プロセスの最大ファイル数を増加させましょう。
(あるいは、当該アプリケーションロジック内でsetrlimit()システムコールによって増加させる方法もあります。)

fs.file-maxと似たような名前でfs.file-nrというカーネルパラメータがありますが、このパラメータは一般的なパラメータではなく、現在開いているファイルのステータスを表します。
現在の状況は、次のようなコマンドで確認できます。

$ sysctl fs.file-nr
fs.file-nr = 5024    0    775052

3つの値はそれぞれ、現在開いているファイルの数、現在開いているが使用していないファイルの数、開くことができるファイルの最大数、を意味します。もちろんシステム全体に対する数値です。

4.2 backlogs

ネットワークパケットは、その生成、伝達、消費に至るまで多くの処理過程を経ることになります。それぞれの処理をパイプと見做すと、すべての処理過程の前に、それぞれqueueが存在します。ネットワークパケットのスループットが急増したとき、queueのサイズがこれより小さい場合は、溢れるパケットについては処理されずに捨てられます。

サーバーのカーネル設定値においては、out-bound queueよりもin-bound queueの方がより敏感ですが、これは、out-boundに送信されるパケット量がサーバーアプリケーションで調整できるからです。(それぞれの要請はその処理時間が異なるので、out-bound時に適度にランダムで分配される効果もあります。)
また、in-bound queueで溢れて捨てられるパケットは、アプリケーションでは全く認知できないので、大規模なパケット処理が必要なサーバーでは、適度にin-bound queueの長さを増加させなければなりません。

まず、net.core.netdev_max_backlogカーネルパラメータについて説明しましょう。
このパラメータは、ネットワークデバイス別にカーネルが処理できるように貯めておくqueueのサイズを設定します。カーネルのパケット処理速度がこのqueueに追加されるパケットの導入速度よりも低ければ、まだqueueに追加されていないパケットは捨てられるでしょう。

このカーネルパラメータも、trade-offの関係がメモリ使用量しかないので、適当に増加させておきましょう。
次のようなコマンドで設定値を増加することができます。

$ sysctl -w net.core.netdev_max_backlog="30000"

Googleで検索できるカーネルパラメータのチューニングに関連した記事には、このカーネルパラメータがlisten backlogと誤って紹介されているようです。
listen backlog、すなわちlisten()にバインドされたサーバーソケットで、accept()を待つソケット数に関わるカーネルパラメータはnet.core.somaxconnです。
この値は、listen()システムコールのパラメータとして設定されるbacklog値のhard limitです。サーバーアプリケーションでlisten()するとき、適当に設定しておく必要があります。まずこのhard limitを増加させておきましょう。

次のようなコマンドで、この設定値を増加できます。

$ sysctl -w net.core.somaxconn="1024"

ちなみに、一般的なLinuxディストリビューションのデフォルト設定値は128です。Apacheウェブサーバーの場合は、listen()時に指定されているbacklogのデフォルト値が511です。しかし、このカーネルパラメータがhard limitですので、アプリケーションで511に指定していても、実際に割り当てられているlisten backlogの数は128個になります。

また、net.ipv4.tcp_max_syn_backlogというlisten backlogに関連するカーネルパラメータがあります。net.core.somaxconnがaccept()を待っているESTABLISHED状態のソケット(つまり、connection completed)のqueueであれば、net.ipv4.tcp_max_syn_backlogはSYN_RECEIVED状態のソケット(つまり、connection incompleted)のqueueです。

この設定値も、このように適当に増加させます。

$ sysctl -w net.ipv4.tcp_max_syn_backlog="1024"

ここで、カーネルパラメータの設定値を上方修正しても、実際のサーバーアプリケーションでlisten backlogを増加させるには、listen()システムコール呼び出し時のパラメータbacklogに必要な値を渡す必要がある、ということに注意しましょう。

4.3 port range

TCP接続を結ぶ際、クライアントソケットは、1つのポートを占有する必要があります。TCP接続は出発地(source)アドレス、出発地ポート、目的地(destination)アドレス、目的地ポートをその区切り記号とするためです。クライアントからサーバーへ連結するとき、特にbind()システムコールで出発地ポートを指定(bind)しなければ、カーネルは任意のポートを割り当てます。これらのポートは通常、ephemeral portと呼ばれます。

クライアントソケットは、サーバーに連結する際、ポートと呼ばれるリソースを1つ消費します。ポートは有限リソースなので、システムで同時に保有できるクライアントソケットの数は限定的です。

一方、listen()でクライアントの要請を待っているサーバーポートは、TCP接続を結ぶ際、追加のポートを消費しません。
そのため、一般的なサーバーでは、保有できるポート数とサーバークライアントの同時接続数は大きく関係しません。

ただし、サーバータイプのうち、proxyサーバーに対しては考慮する必要があります。
ユーザ(クライアント)からの要請を受け、これを他のバックエンドサーバーに転送するタイプのサーバーのことですが、この場合は、当該サーバーは、他のバックエンドサーバーに接続するためのクライアントソケットが必要です。
もし、ユーザの要請が同時に10,000個入ってきても、サーバーが同時に保有できるクライアントソケット数が100個までとしたら、9,900個の要請は処理されず、待機しなければなりません。

システムで同時に保有できるクライアントソケット数を決定するカーネルパラメータがnet.ipv4.ip_local_port_rangeです。
カーネルは、ephemeral portを作成するときに、この範囲内に使用しないポートを選んで割り当てることになります。

次のようなコマンドで、現在の設定値を確認できます。

$ sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768    61000

上記の2つの値は、それぞれ使用するポート範囲の開始と終了を表します。このシステムでは、最大28,232個のephemeral portを割り当てることができます。
最大限広いephemeral port範囲を持つには、次のようなコマンドを使用できます。(well-known portは除く)

$ sysctl -w net.ipv4.ip_local_port_range="1024 65535"

しかし、このように設定しても、実際のシステムで同時に保有できるクライアントソケット数は、これに及ばないことがあります。

TCP接続は非常に結合度の低いネットワーク環境を想定しています。そのため、ネットワークの状況によって、パケットの順序が入れ替わったり、流出したりするなどの処理のために、ソケット終了時にもなるべくゆっくり終了(gracefully shutdown)するようになっています。これはつまり、ソケットが使用するリソースを可能な限り遅く返却するということです。ephemeral portも含まれます。

特に、クライアントソケットからclose()システムコールで先にソケットを閉じた場合、ソケットはTIME_WAIT状態にとどまることになります。この間、ソケットに割り当てられているephemeral portは使用ができず、その分、同時に保有できるクライアントソケットの数は制限されます。


3編では「5.TIME WAIT socket」から始めて、今回の話をまとめていきたいと思います。

TOAST Meetup 編集部

TOASTの技術ナレッジやお得なイベント情報を発信していきます
pagetop