libeatmydataを使ってpostfixを劇速にする [用法用量要確認]
libeatmydataというLD_PRELOADを使って、起動したプロセスのfsyncを無効化するライブラリがあったので試してみています。
libeatmydata - disable fsync and SAVE!
fsyncがないと何が嬉しいかというとクラウドのようなIOまわりの環境が弱いところで、安全性は若干犠牲になりますが、パフォーマンスを稼ぐことができるようになります。
まず perlでfsyncを発行してどうなるかstraceをつかって確認してみます。
libeatmydataなし
$ strace perl -MIO::Handle -e 'open(my $fh,">:unix","test.txt"); print $fh "test"; IO::Handle::sync($fh);' open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 fstat(3, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0 fcntl(3, F_SETFD, FD_CLOEXEC) = 0 write(3, "test", 4) = 4 fsync(3) = 0 close(3)
libeatmydataあり
$ strace eatmydata perl -MIO::Handle -e 'open(my $fh,">:unix","test.txt"); print $fh "test"; IO::Handle::sync($fh);' open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 fstat(3, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0 fcntl(3, F_SETFD, FD_CLOEXEC) = 0 write(3, "test", 4) = 4 close(3)
IO::Handle::syncやってるはずなのに、fsyncが表示されていません。
postfixのパフォーマンス改善
某所でPostfixを経由して外部の配信サービスにメールを転送しているサーバがあり、このIO負荷が高いのが課題となってました。
一斉メール送信のタイミングでかなりIO-waitが上がっています。
postfixの構成
がわかりやすい。
postfixはいくつかのプロセスに分かれて動作します。メールを受けるときは、smtpdのプロセスが送信者からデータを受け取り、cleanupというプロセスに渡し、cleanupプロセスが実際にdiskに書き込む流れになっています。このcleanupプロセスがメールをdiskに書き込む時にfsyncを行うので、ここだけlibeatmydataを入れ込めば良いはず。
そこで /usr/libexec/postfix に cleanup_nosync というファイルを用意
#!/bin/sh exec /usr/bin/eatmydata /usr/libexec/postfix/cleanup $*
master.cfを変更する
cleanup unix n - n - 0 cleanup_nosync #最後だけ変更
postfixをreloadすれば設定が反映されます。
効果の確認
以上の設定をいれたサーバにて、受け取ったメールをすべて捨てるサーバ宛に大量にメールを送信して確認しました。
まず libeatmydata 適用前
$ vmstat 1 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 1 0 0 2420292 249248 1677904 0 0 0 41 2 0 4 0 95 1 0 1 0 0 2420284 249248 1677904 0 0 0 0 1264 325 23 2 74 0 0 1 3 0 2387640 249248 1677904 0 0 0 1180 2660 3530 21 3 64 11 0 0 0 0 2386628 249248 1677916 0 0 0 4356 5434 11669 9 4 77 10 0 0 5 0 2385868 249248 1677956 0 0 0 3620 4632 10255 13 3 75 9 0 0 0 0 2385844 249248 1678004 0 0 0 2792 4294 12083 4 4 88 4 0 0 0 0 2385596 249248 1678052 0 0 0 3724 4772 13095 3 4 88 4 0 0 0 0 2386200 249248 1678116 0 0 0 3408 4452 11660 4 4 86 6 0 0 0 0 2387968 249248 1678136 0 0 0 2908 4327 10611 4 3 82 11 0 0 0 0 2390704 249248 1678200 0 0 0 4724 5314 13459 4 5 86 6 0 0 0 0 2388580 249248 1678240 0 0 0 5212 5520 13300 4 5 85 6 0 3 1 0 2388208 249248 1678292 0 0 0 4716 5934 13486 4 5 86 6 0 0 1 0 2387960 249248 1678344 0 0 0 4608 5947 13964 4 4 85 7 0 0 1 0 2386720 249248 1678392 0 0 0 3164 4561 10527 3 4 81 11 0 0 0 0 2386464 249248 1678424 0 0 0 4544 5555 13150 4 4 86 6 0 0 0 0 2386092 249248 1678472 0 0 0 5052 6135 13801 4 4 85 7 0 0 1 0 2387456 249248 1678524 0 0 0 5232 5905 13530 5 5 84 7 0 0 0 0 2402424 249248 1678560 0 0 0 920 1203 2561 1 1 97 2 0 0 0 0 2402424 249248 1678560 0 0 0 8 160 300 0 0 100 0 0
iowaitが5-10%前後でています。
libeatmydata 適用後
$ vmstat 1 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 0 0 0 2421320 249256 1678660 0 0 0 8 266 474 0 0 100 0 0 0 0 0 2391500 249256 1678660 0 0 0 0 2248 4025 5 2 93 0 0 0 0 0 2391212 249256 1678652 0 0 0 0 6185 13856 4 4 92 0 0 0 0 0 2390824 249256 1678976 0 0 0 0 6106 13950 4 4 92 0 0 0 0 0 2391552 249256 1678804 0 0 0 0 5932 13648 4 4 92 0 0 0 0 0 2388644 249256 1678868 0 0 0 104 5901 13003 5 5 91 0 0 0 0 0 2389644 249256 1678896 0 0 0 0 5552 13448 4 4 92 0 0 4 0 0 2389280 249256 1678936 0 0 0 0 5912 13921 4 4 93 0 0 5 0 0 2391024 249256 1679004 0 0 0 0 6185 13774 4 4 93 0 0 1 0 0 2390528 249256 1679056 0 0 0 0 5848 13187 4 4 92 0 0 0 0 0 2390404 249256 1679108 0 0 0 116 6250 12982 5 4 91 0 0 0 0 0 2387496 249256 1679156 0 0 0 12 6977 13495 6 6 89 0 0 0 0 0 2389112 249256 1679176 0 0 0 0 6074 14039 4 4 92 0 0 0 0 0 2387616 249256 1679236 0 0 0 0 5966 13956 4 4 92 0 0 0 0 0 2403196 249256 1679296 0 0 0 0 1412 2950 1 1 99 0 0 0 0 0 2403444 249256 1679304 0 0 0 136 217 450 0 0 100 0 0
iowaitは常に 0。圧倒的な効果がでました。
データの安全性対策
念のため、1秒間隔でdiskへのsyncが行われるように設定しました。
$ sudo sysctl -w vm.dirty_writeback_centisecs=100
productionへの適用
IO-waitが消え、送信にかかる時間も短くなりました。やった!