高負荷マシンのネットワークチューニング
ネットワーク(TCP)同時接続数が数千ほどあるマシンがあります。
負荷が高くなってきたので、ハードウェア増強やソフトウェア(アプリ)の見直しをしています。 同時に、Linux(kernel)のパラメータに工夫の余地があるか調べてみました。
Linuxのネットワーク系のパフォーマンスチューニングで調べると、以下のようなサイトが見つかります。
- http://www.performancewiki.com/linux-tuning.html
- http://people.redhat.com/alikins/system_tuning.html
- http://www.zdnetindia.com/index.php?action=article&prodid=8288
- http://www.enigma.id.au/linux_tuning.txt
- http://www.acc.umu.se/~maswan/linux-netperf.txt
ここに書いてある設定を愚直に設定すれば改善するかと言うと、そんなに単純ではありません。 なぜなら、ネットワークのパフォーマンスと言っても、前提条件が色々と異なるからです。 特に危険なのが、ネットワークパフォーマンスと言っているのが、サーバとクライアントを超高速回線で直結して、その時のスループットを最大化する設定だったりする場合です。ほとんどの実運用環境において、そのような設定値は無意味です。
今回考慮した前提条件は以下のようになっています。
- 普通のインターネットからの接続を受けつける(同一リンクからの接続は基本的にありません。接続相手は超低速でもないですが、超高速でもありません)
- TCPのセッションを張りっぱなし(パケットは流れ続けている)の接続もあれば、つないですぐに切れる接続もある
- それなりにパケットは落ちている(再送が発生している)
- 接続数が多いので、反応が無さそうな接続は(なるべく)早めに見切りをつけたい
- 接続待ちのキューにあまりクライアントが待たれても困るので、(クライアントに)早めに見切りをつけてもらいたい(接続を諦めてもらう)
- ムダなメモリ消費をなるべく抑えたい
上の参考サイトの中で、比較的無害と思われる次の設定を /etc/sysctl.conf に書くことにしました。無害そうである一方、実は(パフォーマンス的な)効果もあまり期待できそうにありませんが。
# Drop it so lack of FIN times out quicker net.ipv4.tcp_fin_timeout = 30 # reuse TIME-WAIT sockets net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 # Turn off timestamps # Turn this back on if you're on a gigabit or very busy network # Having it off is one less thing the IP stack needs to work on net.ipv4.tcp_timestamps = 0 # Turn syn-cookie protection on net.ipv4.tcp_syncookies = 1 # Enable really big (>65kB) TCP window scaling if we want it. net.ipv4.tcp_window_scaling = 1 #デフォルト1だったので書かなくても同じ
これ以外、当然の設定(net.ipv4.conf.default.forwarding = 0)などありますが(*)、デフォルトで適切な値になっているので特に言及しません。
(*)ここでの「当然」の意味は、意図に反してforwardingがenableになっていたらおかしい、という意味での「当然」です。
上記サイトの多くで、次のようにソケットのバッファ用メモリサイズを増やす設定を勧めています(値はページによって色々です。以下の例は見つけた中で最大値を設定している例です)。
# Bump up max r/wmem net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 # Increase size of socket buffers net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216
この設定は却下しました。理由は同時接続数が数千あるという事情からです。例えば同時接続数が2000で、接続ごとのバッファサイズが2Mバイトあれば、掛け算すると4Gバイトです。もちろん、全ての接続が一斉にバッファを埋める可能性はあまりありません。しかし、メモリ周りのリスクを冒す気にはなれませんでした。
# Widen local port range net.ipv4.ip_local_port_range = 1024 65000
上の設定を勧めているサイトもありました。これは、accept(2)するだけのサーバ系ソフトの場合は関係ありません。このため、設定はしません(デフォルトのまま)。
# Drop keep-alive time net.ipv4.tcp_keepalive_time = 1800
上のようにTCPのkeepaliveタイムのインターバル値を下げる設定を勧めるサイトもありました。これがパフォーマンスに与える効果は良く分かりません。 今回考慮したマシンに関しては、keepalive相当の処理はアプリ層でやっていて、TCP層のkeepalive機能に頼っていないので、この設定値には意味がありません。良く分からないモノはいじるなの鉄則で、デフォルトのままです。
# Increase number of incoming connections backlog net.core.netdev_max_backlog = 1024 # Increase number of incoming connections backlog net.core.somaxconn = 512
サイトによっては上の設定は有効かもしれません(短期で切れるセッションが多数のサイト。小容量のコンテンツ配信が中心のWebサーバなど)。今回に関しては、前提に書いたように、ビジー状態で大量にクライアントに待たれても困るので、この値は増やしませんでした。
# Turn off sack net.ipv4.tcp_sack = 0 # Turn off sack/fack net.ipv4.tcp_fack = 0
この推奨を勧めるサイトもあります(手元のGNU/Linuxマシンのデフォルトは、どちらも1(=有効)になっていました)。パケットの再送がそれなりの率で発生する環境では有効な機能のはずです(元々そういう意図でTCPに追加された機能なので)。無効にすることを勧める理由は...たぶん、理想的なスループットを発揮できるネットワーク環境では不要な機能だ、という判断でしょうか。よく分かりません。結局、これらは、そのまま(デフォルト)にしました。
- Category(s)
- カテゴリなし
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/inoue/network-tuning/tbping
apacheのコードの静的解析
少し前ですが、apacheのMLに以下のメールがありました。
apacheのソースコードを静的解析して、バグ(らしいモノ)を見つけたという投稿です。apacheのソースは、保守的かつ慎重で、非常に多数の目が通っています。今さら静的解析ツールでバグを見つけたとしたら驚きます。
メールから引用します。
The 6 bugs can be categorized into the following 2 groups:
Category-1: missing of a check of the return value of a function A function may return an error code such as 0, -1 or NULL to indicate that an error occured inside of a function. We've found several potential bugs where a check of the return value is likely to be missing for certain functions.
Category-2: missing of a function call This normally happens when a function call is missing in a set of function calls that always need to be invoked together, for example, malloc() and free().
前者(category-1)は、たいしたことありません。関数の戻り値を無視するコードなら、(成熟したソフトでも)珍しくはありませんし、静的解析で見つけたと聞いても驚きません。
後者(category-2)は典型的なバグで、ある条件下では発見が難しいバグです。これに関するバグをツールで見つけたとしたら、たいしたものです。
理想的には、このように呼び出し側が順序を気にして呼ぶAPIを無くすべきですが、現実に無くすことはできません。局所的には、設計の工夫や言語の持つ機能で可能ですが、全てを無くせると思うのは幻想です。GCを持ったJavaでmalloc()/free()相当のペアを無くせても、close()相当のメソッドを完全に追放できないことからも分かります。Rubyのブロックでclose()相当の呼び出しを隠蔽できることを知っている人は、まだ幻想を持つかもしれませんが、解決できるのは局所的な場合のみです(解決できるのはC++のRAIIのテクニックでカバーできる部分と同程度)。
結果的には、category-2として見つけたふたつのバグらしいものはバグでは無いという結論でした。apacheのコードの場合、メモリプールの解放時にコールバック関数を登録できて、そこで暗黙にリソースの解放をできます。今回、ツールが指摘した箇所は、そういうコードだったようです。静的解析ツールの言うがままに解放処理を入れてしまうと、想定より早い解放のために誤動作(たぶんクラッシュ)します。
ある程度の規模のソフトウェアの場合、こういう風に、局所的にコードに見えている部分だけではなく、より上位のメタ知識が暗黙に要求されるコードがあります。これは、プログラミングで最も難しいことのひとつです。逆に言えば、暗黙の要求が少ないコードほど、可読性が高いコードであり、多くのプログラマが書きたいと思うコードでもあります。
今回は残念な結果でしたが、静的解析ツールで「We found a potential rule requiring that the ap_destroy_sub_req() be executed to release the object returned by ap_sub_req_lookup_uri()」を発見できるのはクールだと思いました。
- Category(s)
- カテゴリなし
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/inoue/code-analysis/tbping