kernelをチューニングしてTIME_WAIT値を変更する
kernelのチューニングについて
まぁ個人的な所感なんですけど。。
10年前ぐらい前、とあるシステムで原因のリンクダウンが頻発するトラブルがあり、まったく原因がわからなかったのですが、当時の先輩がkernelのソースコードをいじくって直したことがありました(なんかパッチ送ってました)。
私はその頃ペーペーだったのですが、なにこの変態と思いつつも、その技術力にとても感動しました。
10年たった今はだいぶ安定しておりkernelレベルのチューニングはあまり必要性を感じていませんが、どうしてもって時はこうやるよって感じのメモです。
まぁTIME_WAIT値を変えるぐらいなら大したことないんですけどね。あのリンクダウンってどうやって解決したのかな。10年たった今でもさっぱりわからん。変態め
TIME_WAITを5秒に変更
れっつらごー
src.rpmをおとしてインスト
# wget http://vault.centos.org/5.8/os/SRPMS/kernel-2.6.18-308.el5.src.rpm
おとして・・
# rpm -ivh kernel-2.6.18-308.el5.src.rpm 1:kernel ########################## [100%] #
インストしておきます。
編集箇所
/usr/src/redhat/SOURCES配下(CentOS6からは~~/rpmbuild/SOURCES/らへん?)にある"linux-2.6.18.4.tar.bz2"のがkernelの本体です。
パッチを作っていればこのファイルをいじる必要はありませんが、初めて編集する場合は展開してコードを修正します。
# tar jxf linux-2.6.18.4.tar.bz2
で展開した後にできる
- include/net/tcp.h
- net/ipv4/ipvs/ip_vs_proto_tcp.c
の2ファイルにTIME_WAITの設定値があるのでviで開いて編集します。
# vi include/net/tcp.h # vi net/ipv4/ipvs/ip_vs_proto_tcp.c
それぞれのdiffはこんな感じ
# diff include/net/tcp.h.bk include/net/tcp.h 106c106 < #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT --- > #define TCP_TIMEWAIT_LEN (5*HZ) /* how long to wait to destroy TIME-WAIT #
と
# diff net/ipv4/ipvs/ip_vs_proto_tcp.c.bk net/ipv4/ipvs/ip_vs_proto_tcp.c 269c269 < [IP_VS_TCP_S_TIME_WAIT] = 2*60*HZ, --- > [IP_VS_TCP_S_TIME_WAIT] = 1*5*HZ, #
今後のためにここでpatchを作っておくと便利です。
編集がすんだら同じファイル名でアーカイブし直します
# tar jcf linux-2.6.18.4.tar.bz2 linux-2.6.18.4
展開したファイルはいらないのでrm -rf linux-2.6.18.4で消しておきます。
リビルド
/usr/src/redhat/SPECS/に移ってspecファイルを編集します。
specファイルの中身をみると
# Polite request for people who spin their own kernel rpms: # please modify the "buildid" define in a way that identifies # that the kernel isn't the stock distribution kernel, for example, # by setting the define to ".local" or ".bz123456" # #% define buildid
らしいので、こいつをちょろっと編集します
# vi kernel.spec # diff kernel.spec.bk kernel.spec 74a75 > %define buildid .time_wait_5sec #
まぁ適当にかえる。
patchを作成している場合はspecファイルにパッチを追記します。
あとはリビルドしてやります。
これ結構時間がかかる
# rpmbuild -ba kernel.spec
(10年後…)
# cd /usr/src/redhat/RPMS/x86_64/ # # ls -1 kernel-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-debug-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-debug-debuginfo-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-debug-devel-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-debuginfo-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-debuginfo-common-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-devel-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-headers-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-xen-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-xen-debuginfo-2.6.18-308.el5.time_wait_5sec.x86_64.rpm kernel-xen-devel-2.6.18-308.el5.time_wait_5sec.x86_64.rpm #
できた/(^o^)\
ね、簡単でしょ(棒
ハマりどころ
TIME_WAIT値を5秒未満にするとTIME_WAIT値がなくなる
いろいろなバージョンで検証したのですが、64bitの場合、TIME_WAIT値を5未満にするとTIME_WAITがなくなりました。
上でだした値を10秒→9秒→8秒・・・と順に短くしていったのですが5秒未満になるととたんになくなります。
で、謎なのが32bitの場合は5秒未満でもその数値となります。
これは結構調べたのですが結局わからずじまいです。。
gpgのエントロピーが足りない
これはべつにkernelチューニングに限ったことではないのですが、rpmbuildを実行すると
Not enough random bytes available. Please do some other work to give the OS a chance to collect more entropy! (Need 284 more bytes)
などと出て止まってしますことがあります。
(検証サーバとして新品のサーバ作ったりリブートしたてだったりするとエントロピーが足りなくてでる)
でググったりするとマウスをぐりぐりしたりキーボードを適当に叩いてみたりするといいよとか出てくるんだけどいまいち効果がない。
そんなときは
# /sbin/rngd -r /dev/urandom
としてエントロピーを供給してやると進めるようになります。
id:kenjiskywalker
これでいいだろ!さあ子供たちを開放しろ!
logrotateでローテート後のファイルを消さないようにする
しょーもないことだけど意外にできなかったので。。
rotateオプションあるいはmaxageオプションだと
man logrotateで出てくるsampleを例にとると
/var/log/messages { rotate 5 weekly postrotate /usr/bin/killall -HUP syslogd endscript }
こんな感じの設定だとして、この「rotate」オプションが世代管理の設定。
例の場合だと5世代残す設定になるわけだけど、これを世代管理なんかしなくて永久にファイルを残したい。
でもmanを見てみてもそれっぽいオプションがなさそう。
はじめ"rotate 0"かなーって考えてたけど"rotate 0"の場合は世代管理が0ってことで、1世代も残さずにローテート後のファイルが削除されちゃう。
If count is 0, old versions are removed rather than rotated.
manにはこんな感じの記述。
あとそれっぽいオプションにmaxageってのがあるけど、これは日数をキーとしてるだけで結局消えゆく運命は覆せないみたい。
sharedscriptsでログを退避させる
軽く調べたけど良い方法が見つからなかったので結局スクリプトでログを退避。
/var/log/messages { rotate 1 sharedscripts postrotate /usr/bin/killall -HUP syslogd BASE="/var/log/messages" DATE=`date -d '-1 day' +%Y%m%d` SRCFILE="$BASE.1" DSTFILE="$BASE.$DATE" if [ -f $SRCFILE -a ! -f $DSTFILE ]; then mv $SRCFILE $DSTFILE fi endscript }
上の例をそのまま使うとこんな感じ。
ローテーションされたファイルを日付けでリネーム。
まぁ単純だけどこんなんで良し。
圧縮したい場合は、mvのあとにgzip $DSTFILEするか、
またはcompressオプション使うならリネームするところはlastactionオプション使って分けたほうがいいかもしれない。
Cactiのグラフを1分間隔で出力するには + おまけ
Cactiのグラフを1分間隔で出力するときのメモ。まぁCactiってよりrrdtoolがポイントなんだけど。。
Poller
値を取得するPollerの設定は2つです。
Cactiの管理画面
Cactiにログインして
(Configuration) Settings > Poller
と進んで「Poller Interval」と「Cron Interval」をそれぞれ「Every Minute」とする。
cron
pollerのcronを1分間隔に変更
* * * * * php /path/to/cacti/poller.php > /dev/null 2>&1
> /dev/nullってやるとイス投げられるらしいよ
Data Templates
Pollerは値を取るだけで、こっちだけ変えても肝心のグラフは相変わらず5分間隔で出力されるので、グラフも1分間隔で出力されるようにData Templatesも変更する。
同じくCactiにログインして
(Templates) Data Templates
から変更したいテンプレートを選択する。
テンプレート設定の「Data Source」項目の「Step」が300(=5分)になっているのでこれを「60」に変更する。
…今こうしてまとめてみて思ったんだけど、今後の運用のことを考えるとチェックボックにチェック付けておくのもいいかもしれない
もう作っちゃったグラフはどうすんだよ問題
↑の2つの設定にしておいて新しくグラフを作成すると1分間でグラフが出力されるようになるけど、Data Templatesの設定はグラフ作成時(rrdtool create)の設定項目なので、既存のグラフには反映されない。
既存のグラフも設定を変更する場合はrrdtoolコマンドをつかう。(これが書きたかった)
で、よくCPUやメモリの最大値を変更するときはrrdtool tuneってオプションつけて値を変更してて、今回もこれでいけるもんだと考えてたんだけど(余裕余裕って調子のってた)、どうやら「Step」はtuneで変更できないらしい。
Stepは rrdtool create 時の項目ですね。
んでまぁ色々試行錯誤してみたら
してやれば普通に値が変更されることを確認したので
#!/bin/sh TMPDIR="/path/to/tmpdir" cd /path/to/cacti/rra for rrd in `ls -1 *.rrd`; do rrdtool info $rrd | grep step rrdtool dump $rrd $TMPDIR/$rrd.xml sed -i s/\<step\>300/\<step\>60/g $TMPDIR/$rrd.xml mv $rrd $TMPDIR/$rrd.bk rrdtool restore $TMPDIR/$rrd.xml $rrd chown cactiuser:cactiuser $rrd rrdtool info $rrd | grep step done
みたいな簡単なシェルを書いて(すごく無理やりっぽいけど)変更しました。
いや、CPUだけでいいよって時は`ls -1 *cpu*.rrd`みたくしとけばおk
おまけ!
関係ないけどCactiつながりってことで。
Graph Tressの設定でItemを追加するとき(ホストとか)ツリーに表示する順番を変えるのって結構めんどい。
小さい↑↓のアイコンをポチポチって。。登録ホストがたくさんあるときはまじ地獄。
実は最近知ったんだけど、これ「Sorting Type」って設定がある。しかも目の前にある。。
デフォは「Manual Ordering(No Sorting)」になってるので、これを「Natural Ordering」にしてやるとそれっぽく勝手にソートしてくれる。
いや、これ盲点っていうか。。今までの苦労はなんだったんだよ。。
ApacheのDirectoryMatchとLocationMatch
ApacheのDirectoryディレクティブやLocationディレクティブで正規表現を使いたいときは、DirectoryMatchやLocationMatchを使用しますが、今日ちょっと設定していて、なるほどと思ったのでメモ。
あと今日、はてダからはてなブログにインポートしてみたのでテスト投稿。
やりたいこと
とある階層にある、特定の画像ファイルがなかった場合(404の場合)、特定のエラー画像ページを出したい。
これは、DirectoryとErrorDocumentディレクティブの組み合わせで出来そうなものなんだけど、この「とある階層」ってのがちょっと厄介。
例えばドキュメントルートを/foo/barとした場合
/foo/bar/img/a/image1.jpg /foo/bar/img/a/b/image2.jpg /foo/bar/img/a/b/c/image3.jpg /foo/bar/img/a/b/c/d/image4.jpg
こんなふうに/foo/bar/img配下のどこかにあるjpgファイルのみErrorDocumentの対象にしたい。
DirectoryMatch
/foo/bar/img配下のどこかにある…のだから初めは↓のように設定した。
DocumentRoot "/foo/bar" <DirectoryMatch "/foo/bar/img/.*\.jpg$"> ErrorDocument 404 /baz/404.jpg </DirectoryMatch>
こうすると
http://aaaaaaaa/img/a/b/image2.jpg
→ これはファイルがあるので当然200で返る
http://aaaaaaaa/img/a/b/hogehogehoge.jpg
→ これはファイルがない、かつ正規表現にマッチするのでErrorDocumentで返る
http://aaaaaaaa/img/a/b/c/d/hogehogehoge.jpg
→ これも同じ。階層が深くなっても正規表現にマッチするのでErrorDocumentで返る
http://aaaaaaaa/img/baz/hogehogehoge.jpg
→ 問題はこれ。defaultの404ページなる
正規表現的には、.*で全マッチなので、/img/baz/hogehogehoge.jpgはマッチする。
だけど、ApacheのDirectoryディレクティブは、指定のディレクトリにマッチするかどうかなので、ディレクトリそのものがないこのパターンではDirectoryディレクティブが適用されない。
ファイルがない404とディレクトリがない404は違う。
LocationMatch
で結局Locatonを使って↓みたく書きなおした。
DocumentRoot "/foo/bar" <LocationMatch "/img/.*\.jpg$"> ErrorDocument 404 /baz/404.jpg </LocationMatch>
LocationならばURLマッチなので、
http://aaaaaaaa/img/baz/hogehogehoge.jpg
のようなURLもErrorDocumentで返る。
Locationは
(なぜか)普段あまり使わないのでたまに使うと便利だなーと思う。
Apache2.4.1を入れてみたけどphp4系は動かなかった
最近良く書かれるApache2.4系の記事を見てwktkしていますが、
インフラエンジニアってのはそもそも安定志向なとろこがあって
遊ぶ分には良いもののやっぱり運用するのはちょっと敬遠してしまいますよね。
そんな訳で暫くは触る機会がないだろなーって思ってたんですけど
思いのほか早く触れる機会があって、せっかくなんでメモ。
インストール
インストの仕方はほとんど変わってないみたいです。
(MySQLみたいにcmakeになりましたーとかはないみたいです)
普通に
$ tar zxf httpd-2.4.1.tar.gz $ cd httpd-2.4.1
からの
$ ./configure --prefix=PREFIX $ make $ sudo make install
で入れます。
若干のはまりポイントとしてはaprとapr-utilがないとエラーになったりします。
元々インスト済みの場合はパスを指定してやればいいですし、入ってなければ
./srclib配下に置いてやれば良いです。(とINSTALLファイルに書いてあります)
なので、展開したあとに
$ tar zxf httpd-2.4.1.tar.gz $ cd httpd-2.4.1 $ cd ./srclib/ $ wget http://ftp.kddilabs.jp/infosystems/apache//apr/apr-1.4.6.tar.gz $ wget http://ftp.kddilabs.jp/infosystems/apache//apr/apr-util-1.4.1.tar.gz $ tar zxf apr-1.4.6.tar.gz $ tar zxf apr-util-1.4.1.tar.gz $ mv apr-1.4.6.tar.gz apr $ mv apr-util-1.4.1.tar.gz apr-util
ってしといて
$ cd ../ $ ./configure --prefix=PREFIX --with-included-apr $ make $ sudo make install
でおk
問題のPHP
インストールとかその他設定とは、最近色々記事になっているのでそっちのほうが参考になると思う。
だがPHP4系、テメーはダメだ
何と言うか…言いたいことは凄くわかるのだけど、気づけばこんな組み合わせになってしまったのでしょうがない。
結果的にはApache2.4.1はPHP4系に対応していないようです。
というかPHP4系はApache2.4.1に対応していないようです。
PHPのインストは
$ tar zxf php-4.4.9.tar.gz $ cd php-4.4.9 $ ./configure --prefix==PREFIX --with-apxs2=/usr/local/apache2.4.1/bin/apxs $ make $ sudo make install
みたく普通にやって特にエラーも出なかったんだけど
configtestひっぱ叩いたら↓のエラーがでました。
$ ./bin/apachectl configtest httpd: Syntax error on line 233 of /usr/local/apache2.4.1/conf/httpd.conf: Cannot load /usr/local/apache2.4.1/modules/libphp4.so into server: /usr/local/apache2.4.1/modules/libphp4.so: undefined symbol: ap_get_server_version
(>'A`)>ウワァァ!!
あ、いや、ショックっていうか、やっぱりなって感じでしたけど。
んで
undefined symbol: ap_get_server_version
らしいのでapiの部分が変更になったのでしょう。
http://httpd.apache.org/docs/2.4/developer/new_api_2_4.html
にも書いてあるのですが、ap_get_server_versionが変更になっているみたいですね。
$ cd httpd-2.4.1 $ grep "ap_get_server_version" ./include/httpd.h
ってやっても全く引っかかりません。
2.2.22は
$ cd httpd-2.2.22 $ grep "ap_get_server_version" ./include/httpd.h AP_DECLARE(const char *) ap_get_server_version(void); * ap_get_server_version().)
なんかそれっぽいのがでてきます。
PHP側も
$ cd php-4.4.9 $ grep "ap_get_server" sapi/apache2handler/php_functions.c return (char *) ap_get_server_version();
になってるしだめだこりゃ。。
ま、世界レベルでレアな組み合わせだわな。。
Apache2.4.1は
MPMがeventってのが注目ですよね。
運用してるサーバはほとんどpreforkで最近workerがちょこちょこって感じで。
eventはまだ全然動かしたことない。
バイナリログ(mysql-bin)の運用について
サーバ運用における基本的な監視にディスクの容量監視があります。
ディスクの使用率が100%になるとサービスに影響がでるのはもちろんですが、
サーバ管理者として100%になるまで気が付かないと恥ずかしかったりします。
(落ちた原因はなんだよ!!! → デ、ディスクの使用率が100%です(///)ってなる)
なのでだいたい90%を閾値にディスクの使用率監視をしているのですが、
アラートを検知してそれがMySQLの動いてるサーバだとバイナリログの肥大化を疑います。
バイナリログは更新系のログが記録されますが、放っておくと際限なく増えるので
ディスク逼迫の原因になってしまいます。
ので不要なログは定期的に削除したりしますが、そのまとめ。
expire_logs_days
my.cnfに
[mysqld] expire_logs_days=10
と設定したり、set global で
mysql> set global expire_logs_days = 10; Query OK, 0 rows affected (0.02 sec) mysql> mysql> show variables like '%days'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | expire_logs_days | 10 | +------------------+-------+ 1 row in set (0.00 sec) mysql>
みたくしてやるとバイナルログが自動削除されます。
↑の例だと10日間経過したバイナリログを削除します。
削除のタイミングはバイナリログのローテーション時かMySQLの再起動等を行った時です。
これはMySQLそのものが管理してくれるので確実です。
てゆうかこれが一番便利で簡単で基本です。
PURGE MASTER LOGS
バイナリログは手動でPURGEコマンドを実行しても消せます。
PURGEコマンドはバイナリログを指定したり、日付を指定して削除できたりします。
例:
PURGE MASTER LOGS TO 'mysql-bin.010';
http://dev.mysql.com/doc/refman/5.1/ja/purge-master-logs.html
PURGE MASTER LOGS BEFORE '2003-04-02 22:46:26';
BEFORE 異型の date 引数は 'YYYY-MM-DD hh:mm:ss' フォーマットになり得ます。MASTER と BINARY は同義語です。
マニュアルの例が分かりやすのでまんま引用しますが、
BEFORE 'YYYY-MM-DD hh:mm:ss'を指定してやると日付を基準に消せるので結構便利です。
cronなんかで簡単なスクリプトを仕込んでおけば定期的にバイナリログを削除できます。
ただPURGEの場合はあくまでMySQLのコマンドなので、MySQLのアカウント/パスワードをどうするかを
考えるのが若干めんどくさいです。若干
スクリプトを使って圧縮/削除する。その1
基本的にはexpire_logs_daysを設定しますが、バイナリログを消さずに圧縮しといてって良く言われます。
自分の管轄外のサーバだったりすると、消すのはどうもなぁ…とりあえず圧縮しとくかってやったりします。
個人的には圧縮なんてやらずに消しちゃえよ派なんですが。。
まぁ本気を出せばバイナリログからリカバリが可能だったりするので
そういうの考えると…どっちでもいいんじゃね派でいいやもう
で圧縮処理を入れるとMySQLの管理から外れてしまうので別にスクリプトを組む必要があります。
こんな感じ。
#!/bin/sh DIR=/var/lib/mysql # datadir BINLOG="mysql-bin.[0-9]*" # log-bin COMMAND="find $DIR -maxdepth 1 -type f -name $BINLOG" MTIME=10 for FILE in `$COMMAND -mtime +$MTIME`; do #/sbin/fuser -s $FILE || echo "nice rm $FILE" /sbin/fuser -s $FILE || nice rm $FILE done MTIME=5 for FILE in `$COMMAND -mtime +$MTIME`; do #/sbin/fuser -s $FILE || echo "nice gzip $FILE" /sbin/fuser -s $FILE || nice gzip $FILE done
簡単なシェルスクリプトですが、
10日経過したバイナリログは削除、5日経過したバイナリログは圧縮
としています。
こんなのをcronで定期的に実行して管理します。
スクリプトを使って圧縮/削除する。その2
その1は日付の経過で管理してますが、バイナリログの個数で管理したいときもあります。
そんなときは↓こんなん。
#!/bin/sh DIR=/var/lib/mysql # datadir BINLOG="mysql-bin.[0-9]*" # log-bin # rm COUNT=`find $DIR -maxdepth 1 -type f -name $BINLOG | wc -l` REMOVE=50 if [ $COUNT -gt $REMOVE ]; then NUM=`expr ${COUNT} - ${REMOVE}` for FILE in `ls -1rt \`find $DIR -maxdepth 1 -type f -name $BINLOG\` | head -${NUM}`; do #/sbin/fuser -s $FILE || echo "nice rm $FILE" /sbin/fuser -s $FILE || nice rm $FILE done fi
こっちはバイナリログが50個以上で削除…みたいにしてます。
まぁ結局
expire_logs_daysでいいんじゃね派
mount関連の小ネタ
このブログはメモとしても使ってるので、たまに自分自身でも見るのですが
もう半年以上も更新してないですねってことで、大きなネタもあるのですが
それはそれでまとめるのがめんどいので、そんな大したことないけど微妙に重要で
しかし普段はあまり使わないのでいざって時に忘れがちな小ネタ。
ソフトマウント
NFSはオワコンオワコン言われて久しいですが、実際小規模な環境では便利ですよね。
NFSを運用してて困る場面はマウント先のサーバが落ちた時です。
マウント先が落ちてる時にマウントしてるサーバでdfコマンドを打つとプロンプトが持っていかれます。
Ctrl + C とか Ctrl + D とかやっても抜けられず、わざわざ別窓でログインして
dfコマンドをkillしたりしますよね?よね?
そんな経験があるのでNFSやってるサーバーみると
NFSとかwwwwwwマウント先が落ちたら固まるしwwwwwwww
とかバカにしてましたが、バカは私でした。すみませんでした。
この症状は"soft"オプションで回避できます。
# mount -t nfs -o soft [host]:/foo/bar /baz
こんな感じでsoftオプションを付加すると、リトライを行わずにエラーで返るようになります。
まぁdfコマンド程度なら別にいいんですが、アプリケーションがマウント先に見に行ってる場合は
タイムアウト待ちにならずに、即エラーが返るので便利ですよと。
/etc/init.d/netfs
もう一つ。
NFSを設定した場合は、サーバが再起動した時にも勝手にマウントしてほしいので
/etc/fstabに一行追加しますよね。
[host]:/foo/bar /baz nfs rw,soft 0 0
こんな感じで。
この時にありがちな罠でfstabに記述してるのにサーバ起動時NFSが自動マウントされない時があります。
無理やり/etc/rc.localにmount -aを書いてるとこありますが、本来はこれ違う。
本当は/etc/init.d/にnetfsってのがあって、こいつを実行するとサーバ起動時にマウント、
シャットダウン時にアンマウントしてくれます。
でこれ普段は意識しないのですが、よくoffになってます。
なんでかっていうと「linux 不要サービス」みたいなキーワードでググると
netfsはNFSを使わない限り不要なサービスなので落としましょうって書いてあるんですね。
まぁ実際そうなんですが、NFSを使おう!ってときにportmapとかは
必要に迫られて起動するのですが、netfsはサーバが再起動して初めて気がつくので
ついつい忘れがちになりますねって話です。