新人ツチノコです。よろしくお願いします。

はじめまして。昨日からツチノコの仲間になりました。

DMMのエンジニアはなかなか表に出てこないと言われておりましたが最近は表に積極的に出てくるようになりました。現在発売中の、技術評論社「WEB+DB PRESS」Vol.78(12/21発売)で、DMM.comの特集「開発ノウハウ大公開」が掲載されています。

DMM.comラボリリース情報↓
http://labo.dmm.com/information/2013/1216/

DMM.comでは実は本も売っています。

DMM.com 本・コミック通販: WEB+DB PRESS Vol.78
http://www.dmm.com/mono/book/-/detail/=/cid=bkt33028429/

 

新人ツチノコも露出を増やすべく、来週の1/22〜24にかけて別府までJANOG33というイベントに参加しに行きます。

JANOG33 Meeting
http://www.janog.gr.jp/meeting/janog33/

インターネットの旬のネタについて、あれこれディスカッションする、という楽しいイベントです。
スタッフの中に新人ツチノコがいるはずなので、気軽に声をおかけくださいませ。ツチノコっぽい話も多少はできるかもしれません。

ウェブからの参加登録は締め切っていますが、当日会場にふらりと来ていただければ参加可能のはずです。懇親会等への参加については事務局にメールをしてみてくださいませ

tunaコマンド

デバイスがどのCPUに割り込みのリクエストを出すのかを調整するためにはaffinity設定を利用し実現します。

affinityの設定は/proc/irq配下のそれぞれのirq番号配下のsmp_affinityの値を16進数で修正しなくてはいけません。
マルチキューを持つネットワークインターフェースであればそれぞれ割り当てられているIRQ番号で設定を行っていく必要があります。
たとえばecho ffffff > /proc/irq/70/smp_affinityといった感じで設定します。
参考:マルチキューネットワークインターフェース

これを簡単に行ってくれるtunaというツールがredhatより提供されているのをつい先日知りましたので共有します。
guiからでもcliからでも利用が可能なツールです。

兎にも角にも使ってみます。

まずはインストール※centos6のupdatesレポジトリに含まれていました。

$ sudo yum -y install tuna

現在のaffinity値の確認

$ sudo tuna --show_irqs
# users affinity
0 timer 0xffffff
1 i8042 0x555555
8 rtc0 0x555555
9 acpi 0x555555
12 i8042 0x555555
16 megasas 0xaaaaaa
17 uhci_hcd:usb3 0xaaaaaa
18 uhci_hcd:usb4 0x555555
19 ehci_hcd:usb1 0xaaaaaa
20 uhci_hcd:usb6 0x555555
21 ehci_hcd:usb2,uhci_hcd:usb5 0xaaaaaa
23 ata_piix 0xaaaaaa
61 p1p1 0x555555 bnx2x
63 p1p1-fp-0 0x555555 bnx2x
64 p1p1-fp-1 0xaaaaaa bnx2x
65 p1p1-fp-2 0x555555 bnx2x
66 p1p1-fp-3 0xaaaaaa bnx2x
67 p1p1-fp-4 0x555555 bnx2x
68 p1p1-fp-5 0xaaaaaa bnx2x
69 p1p1-fp-6 0x555555 bnx2x
70 p1p1-fp-7 0xaaaaaa bnx2x
81 iodrive-0000:05:00.0-0 0x555555

CPUが24スレッドの環境において下記のような16進数の設定になっています。
aaaaaaは1,3,5,7,9,11,13,15,17,19,21,23のCPUを利用し、
555555は0,2,4,6,8,10,12,14,16,18,20,22のCPUを利用されることになります。

この値を変更する場合は

$ sudo tuna --irqs="p1p1*" --cpus=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 --move

これでp1p1のネットワークインターフェースですべてのCPUを利用することになります。

コマンドで確認してみます。

$ sudo tuna --show_irqs
# users affinity
0 timer 0xffffff
1 i8042 0x555555
8 rtc0 0x555555
9 acpi 0x555555
12 i8042 0x555555
16 megasas 0xaaaaaa
17 uhci_hcd:usb3 0xaaaaaa
18 uhci_hcd:usb4 0x555555
19 ehci_hcd:usb1 0xaaaaaa
20 uhci_hcd:usb6 0x555555
21 ehci_hcd:usb2,uhci_hcd:usb5 0xaaaaaa
23 ata_piix 0xaaaaaa
61 p1p1 0xffffff bnx2x
63 p1p1-fp-0 0xffffff bnx2x
64 p1p1-fp-1 0xffffff bnx2x
65 p1p1-fp-2 0xffffff bnx2x
66 p1p1-fp-3 0xffffff bnx2x
67 p1p1-fp-4 0xffffff bnx2x
68 p1p1-fp-5 0xffffff bnx2x
69 p1p1-fp-6 0xffffff bnx2x
70 p1p1-fp-7 0xffffff bnx2x
81 iodrive-0000:05:00.0-0 0x555555

ffffffとなり設定が変更されたことがわかると思います。

注意点としてはirqbalanceで上書きされないように留意しなくてはいけないところですかね。

tuna –show_threadsとすると、たとえばnginxがどのCPUに割り込み要求を出すのかも分かりますので、
意図的にnginxの割り込み先のCPUを制御したいときなんかにも使えると思います。

うち例だとこんな感じでthreadsが表示されます。

# sudo tuna --show_threads
thread ctxt_switches
pid SCHED_ rtpri affinity voluntary nonvoluntary cmd
1936 OTHER 0 0xffffff 22 0 rsyslogd
2103 OTHER 0 0xffffff 226059 74 munin-node
2105 OTHER 0 0xffffff 39 0 sshd
2115 OTHER 0 0xffffff 28139 255 xinetd
2125 OTHER 0 0xffffff 58333 3 ntpd
2213 OTHER 0 0xffffff 31978 3 master
2234 OTHER 0 0xffffff 4440 1 qmgr
2245 OTHER 0 0xffffff 17 0 abrtd
2253 OTHER 0 0xffffff 82250 3 abrt-dump-oops
2265 OTHER 0 0xffffff 80 10 nginx
2291 OTHER 0 0xffffff 23661 129 crond
2625 OTHER 0 0xffffff 3 4 mingetty
2627 OTHER 0 0xffffff 1 3 mingetty
2629 OTHER 0 0xffffff 1 3 mingetty
6952 OTHER 0 0xffffff 175063273 563161 nginx
6953 OTHER 0 0xffffff 175870381 565885 nginx
6954 OTHER 0 0xffffff 176195651 565931 nginx
6955 OTHER 0 0xffffff 175946615 564565 nginx
6956 OTHER 0 0xffffff 175050339 565565 nginx
6957 OTHER 0 0xffffff 175240328 564748 nginx
6958 OTHER 0 0xffffff 175659578 566705 nginx
6959 OTHER 0 0xffffff 175320019 566252 nginx
6960 OTHER 0 0xffffff 176038292 568230 nginx
6961 OTHER 0 0xffffff 175995543 568237 nginx
6962 OTHER 0 0xffffff 176286171 570082 nginx
6963 OTHER 0 0xffffff 175878206 566134 nginx
6964 OTHER 0 0xffffff 176026074 569170 nginx
6965 OTHER 0 0xffffff 175662864 564618 nginx
6966 OTHER 0 0xffffff 175752639 566220 nginx
6967 OTHER 0 0xffffff 176798584 568727 nginx
6968 OTHER 0 0xffffff 331703 2550 nginx
13364 OTHER 0 0xffffff 21 1 pickup
15929 OTHER 0 0xffffff 2 254 tuna
18350 OTHER 0 0xffffff 3 1 zabbix_agentd
18356 OTHER 0 0xffffff 696866 9144 zabbix_agentd
18357 OTHER 0 0xffffff 515163 10291 zabbix_agentd
18358 OTHER 0 0xffffff 515370 10417 zabbix_agentd
18359 OTHER 0 0xffffff 515793 10259 zabbix_agentd
18360 OTHER 0 0xffffff 697169 204 zabbix_agentd

RPS/RFS設定

現状の仮想環境ではマルチキューが利用できません。したがってmemcachedなどのミドルウェアを仮想上で動作させると必ず一つのCPUに偏ります。これを回避するためにはrhelの7系のカーネルを待つしかありません。

と思っていたところ、

RPS/RFSの設定でCentOS6.xで仮想環境でCPUの偏りをなくすことができるようです。
RPS/RFSを使用するとソフトウェア的にNICの割り込みに使用するCPUを分散してくれます。
dmmで利用しているmemcachedのサーバで設定してみたところ、
実際にCPUコアの偏りが無くなったことが確認できました。

入れ替え前のmemcachedサーバのCPU負荷
cpuの1に偏っています。

02:40:01 PM CPU %user %nice %system %iowait %steal %idle
02:41:01 PM all 0.17 0.00 1.52 0.00 0.00 98.30
02:41:01 PM 0 0.00 0.00 0.02 0.00 0.00 99.98
02:41:01 PM 1 0.63 0.00 4.95 0.00 0.00 94.42
02:41:01 PM 2 0.00 0.00 0.03 0.00 0.00 99.97
02:41:01 PM 3 0.05 0.00 1.10 0.00 0.00 98.85

RPS/RFS設定を行ったmemcachedサーバのCPU負荷
cpuの0,1,2,3と綺麗に分散されています。

02:48:01 PM CPU %user %nice %system %iowait %steal %idle
02:49:01 PM all 0.16 0.00 1.51 0.01 0.05 98.26
02:49:01 PM 0 0.15 0.00 1.89 0.00 0.12 97.83
02:49:01 PM 1 0.17 0.00 1.44 0.00 0.05 98.34
02:49:01 PM 2 0.17 0.00 1.36 0.00 0.03 98.44
02:49:01 PM 3 0.17 0.00 1.37 0.03 0.03 98.39

設定例

echo "f" > /sys/class/net/eth0/queues/rx-0/rps_cpus
echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

/sys/class/net/eth0/queues/rx-0/rps_cpus は使用するCPUを指定します。
各コアを使用するかのフラグと2進数で各ビットを立て、16進数に変換します。
1,2コアを使用する場合は
2進数で 11 → 16進数に変換して3を設定。
1,2,3,4コアを使用する場合は
2進数で 1111 → 16進数に変換してfを設定。

これにより、ネットワーク系のエラーやmemcachedのレスポンスタイムが改善しています。

・入れ替え前
before_rps

・入れ替え後
after_rps

netfilterモジュール

私の中でちょっと問題になった部分を共有します。

kernel: nf_conntrack: table full, dropping packet
このエラーでよく紹介されている解決方法が
sysctl.confに設定を追記してsysctl -pで設定を読み込ませるという方法です。

確かにこれでも問題はありません。わたしもこの方法でやってます。
ただし、iptablesを使っている方には問題がでる可能性があります。

conntrackテーブルはiptablesで利用されているnetfilterモジュールに設定される値ですので、iptablesを再起動など行ってしまうと一度そのモジュールが読み込みなおされることになり、設定が初期化されてしまいます。この事実を知らずにiptablesを再起動しちゃうと、混雑する深夜の時間帯にkernel: nf_conntrack: table full, dropping packetとなり、たたき起こされてしまうことになりかねません。私がそうです。^^;

これを回避するためは
sysctl.confではなくmodprobe.dディレクトリ配下に設定ファイルを置きます。

vi /etc/modprobe.d/conntrack.conf
options nf_conntrack hashsize=25000

例えば上記のように設定しておけばnetfilterモジュールの読み込み時(iptablesの再起動時)にこの設定が読み込まれ、値に合わせたテーブルサイズに変更してくれます。

ちなみにnf_conntrack_maxの値を200000にする場合は
hashsizeで8分の1の値(25000)を設定しておけば20万となります。

hashsizeで設定を行う場合は下記のパラメーターに変更が加えられます。
※下記は6系の例

net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_buckets
net.netfilter.nf_conntrack_expect_max
net.nf_conntrack_max

あと、rhelの5系と6系でも違いがあります。
6系になるとiptablesの起動スクリプトでsysctlも親切に読み込んでくれるようになっています。なのでsysctl.confで設定しておいても値が初期化されることはなくなります。
※5系のiptablesの起動スクリプトではsysctlを読み込んでくれません。

ただし、modprobe.d配下の設定ファイルとsysctl.confのファイルが混在している場合は
modprobe.d配下の設定ファイルが優先されていますので注意が必要です。

sysctl.confで設定を増加させた後、6系なのでと安心しきっていると、
誰かがiptablesを再起動してmodprobe.dの設定が読み込まれて再度問題となるなんてこともあるかもしれませんよ。