クックパッドに入社した話をしよう
入社して3ヶ月ぐらい経ちました。
前職と同様にインフラとしてサービスを支えるお仕事をしています。地味かもしれませんが僕はインフラが大好きです。
こう言えるようになったのもクックパッドのおかげかもしれません。
クックパッドは「やりたい」「得意」「すべき」という3つの輪が重なるところが最も個人の能力が発揮され成果が出ると信じています。
僕は入社前からこれまで培ってきた技術と経験をインフラとしてシステムの安定化という課題に取り組むつもりでいたので、「やりたい」「得意」は見つけることができた。
でも、本当にこれを「すべき」なのか?という疑問は今でも持ち続けています。
システムの安定化はサービスの品質に関わることなのでやるべきことなのですが、プログラムをガリガリ書いたほうが多分もっとユーザの役にもたてるし会社としてのメリットも大きい事があると思うんですよね。
クックパッドはまだまだ小さい会社で、これからどんどん大きくなっていくと思いますしそれが社会からみた会社の意義でもあると思います。
クックパッドのインフラだからプログラムは書けないっていう話しではないです。僕の両サイドに座っている方々はインフラでプログラムを書きまくっています。
インフラというチームはただその中心的な役割であってアプローチはなんでもいいじゃなでしょうか。彼らはプログラムが得意なんですから。
クックパッドの本質は「毎日の料理を楽しみにすることで心からの笑顔を増やす」じゃないでしょうか。
僕の得意はネットワークやLinuxシステムの知識や経験を活かしたシステムを開発することです。
得意を活かしてシステムを安定化させればユーザにとってもメリットだし、僕にとっても最大限のパフォーマンスを出せるフィールドだと確信しています。
「耐障害性を向上させいつでもユーザに見てもらえるクックパッドにする」 これは僕が入社してからずっと思ってきてることですが、自分の本当にやりたいこと、得意なこと、やるべきことに邁進できる最高の環境です。
クックパッドは周りを見渡せば世界でも有名なスーパーヒーローのような方々がたくさんいてその道の超エキスパート集団が揃っています。
ここが僕は結構ミソだと思っていて普通の会社やベンチャーだ幅広くなんでも出来る人を求めるじゃないですか。その逆なんですよね。
エキスパート集団が周りにいて、そいう空気だから何も疑問を抱かずに今の仕事を続けれてるんだと思います。
そいう集団の中にいると自分の小ささを目の当たりにして不安になることもよくあります。
そのたびにもっと勉強して進んでいかないとって思える環境が一番エンジニアとして成長できると思っています。まだまだ努力が足らないんだなと感じますしね。
ストイックで冷静に成功までのプロセスをたてて、時にはがむしゃらに頑張れる人が多いと思います。
優秀な人を見つけたり「人」に通ずる環境をよくしようと一番努力しているのは人事とかなので社員の事を本当によく考えていてくれてありがたいです。
会社体制の話しです。
前職はトップダウンでしたがクックパッドではボトムアップで個々が意思決定である事が求められます。
トップダウンで何もしなくても指示される環境に慣れてしまっていた僕には全てにおいて最初の一歩が恐怖でした。
全ての意志と行動が自分の責任となって返ってくるわけですから当然だと思います。
クックパッドには優秀な人が揃っています。悩んで自分の意見をまとめて相談すれば誰でも話を聞いてくれます。
しかし最終的な決定者は自分自身であるべきだと思っています。
最後に。
クックパッドは本当にいい会社です。
企業理念と言えば堅苦しくなりますが会社としての考え方、ユーザに対する思いや、僕達のTOPはユーザであるという精神が社員一人ひとりに浸透していると思います。
テクノロジーは使って喜んでくれる人がいるから遣う意味があるんだと思うんですよね。
今以上の物を作るためにあらゆるテクノロジーを使いこなし努力を惜しまない集団です。
こいう事に共感できて料理を通じて世界を変えたい、人を楽しませたいと心から思うのであればクックパッドは向いているかもしれません。
回線遅延やパケットロスをシュミレートする方法
回線遅延(帯域)を考慮しない負荷試験なんて意味あるの?って思い使ってみました。
負荷試験はLAN内で行う事が殆どだと思いますが実際はインターネット経由でユーザからのアクセスがあり、それぞれ回線速度もバラバラのはずです。
この回線速度の違いはシステムにどのような影響を与えるかというと1アクセスに対して1プロセスが長時間専有されることでシステムリソースを消費するということです。
これらを考慮せずに安易にKeepAliveを有効にしてWebサーバを高速化しよう!なんてしてしまうと高負荷になり返ってレスポンスが遅くなったという人も多いと思います。
Linux2.6系(だったはず)からiproute2などが組み込まれてネットワーク系が強化されました。
その一部としてtc(Traffic Control)コマンドがあります。
tcコマンドでできること
・回線遅延
・パケットロス
・パケット入れ替え
・パケットの破損
・etc…
このコマンドはそのサーバに対して適応されるので、遅延速度を大きくしすぎるとSSHがタイムアウトすることもあるので注意してください。
複数NICがある環境が望ましいです。
eth0を100ms遅延させる場合
#tc qdisc add dev eth0 root netem delay 100ms //Delete Command #tc qdisc del dev eth0 root netem delay 100ms
qdisc=>’queueing discipline’
kernelがNICから送信する必要がある場合、一旦qdiscに格納されkernelはqdiscからNICドライバーへと引き渡そうとします。
その順番はキューに入った順に引き渡され、NICはできるだけ速くキューを送信しようとします。
それを操作して送信を遅延させてやろうというコマンドです。
別の端末からCurlを実行してみます。
通常の速度は9000msぐらいです。
# curl -o /dev/null -w time_total 10.0.60.24
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 9 0 9 0 0 2572 0 --:--:-- --:--:-- --:--:-- 9000
tcコマンドを実行すると。
綺麗に100ms遅延した結果が得られました。
# curl -o /dev/null -w time_total 10.0.60.24
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 9 0 9 0 0 44 0 --:--:-- --:--:-- --:--:-- 90
遅延をランダムにさせることもできます。100ms〜10msの遅延。
# tc qdisc add dev eth0 root netem delay 100ms 10ms
パケットロスはランダムにパケットをロスさせることができる。
このランダムというところが厄介なのでNICが1つしかない場合はSSHのパケットとHTTPのパケットが混ざってしまい正確な値の計測が難しい。
NICが1つの場合はlossの確率を10%程度まで上げても殆ど変化がみられないと思う。
NICが2つあり、殆どパケットがながれていない場合1%以下から検証することを勧める。
# tc qdisc del dev eth0 root netem loss 0.00003%
delayと組み合わせて使うこともできます。
# tc qdisc add dev eth0 root netem delay 100ms 10ms loss 0.00003%
パケットの破損はパケットの1bitを破壊する事ができる。TCPの場合は輻輳の機能があるので観測しづらいのでPingで試すのが吉。
# tc qdisc del dev eth0 root netem corrupt 10%
tcpdumpを実行しながらPingを打つとwrong data byteが発生して一定の確率で破損しているのが伺える。
# tcpdump -n -p icmp & # ping 10.0.60.24 PING 10.0.60.24 (10.0.60.24) 56(84) bytes of data. 20:59:15.942681 IP 10.0.74.150 > 10.0.60.24: ICMP echo request, id 47365, seq 1, length 64 20:59:15.943743 IP 10.0.60.24 > 10.0.74.150: ICMP echo reply, id 47365, seq 1, length 64 64 bytes from 10.0.60.24: icmp_seq=1 ttl=64 time=2.05 ms 20:59:16.942993 IP 10.0.74.150 > 10.0.60.24: ICMP echo request, id 47365, seq 2, length 64 20:59:16.943303 IP 10.0.60.24 > 10.0.74.150: ICMP echo reply, id 47365, seq 2, length 64 64 bytes from 10.0.60.24: icmp_seq=2 ttl=64 time=0.342 ms 20:59:17.943496 IP 10.0.74.150 > 10.0.60.24: ICMP echo request, id 47365, seq 3, length 64 20:59:17.943813 IP 10.0.60.24 > 10.0.74.150: ICMP echo reply, id 47365, seq 3, length 64 64 bytes from 10.0.60.24: icmp_seq=3 ttl=64 time=0.347 ms 20:59:18.943501 IP 10.0.74.150 > 10.0.60.24: ICMP echo request, id 47365, seq 4, length 64 20:59:18.943841 IP 10.0.60.24 > 10.0.74.150: ICMP echo reply, id 47365, seq 4, length 64 64 bytes from 10.0.60.24: icmp_seq=4 ttl=64 time=0.372 ms 20:59:19.943514 IP 10.0.74.150 > 10.0.60.24: ICMP echo request, id 47365, seq 5, length 64 20:59:19.943854 IP 10.0.60.24 > 10.0.74.150: ICMP echo reply, id 47365, seq 5, length 64 64 bytes from 10.0.60.24: icmp_seq=5 ttl=64 time=0.372 ms 20:59:20.943554 IP 10.0.74.150 > 10.0.60.24: ICMP echo request, id 47365, seq 6, length 64 20:59:20.943952 IP 10.0.60.24 > 10.0.74.150: ICMP echo reply, id 47365, seq 6, length 64 64 bytes from 10.0.60.24: icmp_seq=6 ttl=64 time=0.431 ms wrong data byte #45 should be 0x2d but was 0xd #16 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c d 2e 2f #48 30 31 32 33 34 35 36 37 20:59:21.943526 IP 10.0.74.150 > 10.0.60.24: ICMP echo request, id 47365, seq 7, length 64
NHKにお金を払うこと。Webコンテンツにお金を払うこと。
就職して一人暮らしを始めてから真面目に?NHKの料金を払い続けてるのですが去年9月の引越しから引き落としがうまく行かず振込用紙で支払っていました。
3ヶ月ぐらい前からでしょうか、色々と忙しくなり支払うのを忘れて毎月NHKの集金係の人に料金を支払っています。
NHKへの支払うんぬんは置いておき、お金を支払うという事はその対価として何かを得るという事は幼い子供でも知っていることだと思います。
殆どの人はその対価として自身が等価またはそれ以上の価値があると思うからお金を支払うと思うんですよね。
NHKの問題点はいくつかあると思いますが、TVを通じて彼らはコンテンツを配信していますがそのコンテンツに対する対価と見合わないから払わない人、お金の使途が気に入らない人、色々いると思います。
おそらくニュースなどで取り上げられる一部の不真面目人を除いて、NHKの中の人はその対価と見合ったコンテンツを配信するために必死になっていると信じているのですが、その努力が少なくとも僕の目には見えないし、成果もでていないんじゃないかと思っています。
僕はNHKと同様にユーザから金を頂いてサービスを展開している企業に勤めています。
インフラでシステムの安定化という事に取り組んでいるので直接ユーザに触れるようなサービスを開発しているわけではないですが、どうやってシングルポイントを無くすのか、いかにしてダウンタイムを短くするかという事を常に考えて行動してます。
実際にピークタイムになるとサーバの負荷が高くなりユーザのアクセスも相当なものになっていることを実感し、その中にはお金を払って使ってくれているユーザもたくさんいます。
Webサイトだとバーチャルな繋がりでしか無いものが目に見えて負荷が高くなったりログがたくさんでてたりとかで人がいるってよく実感するんですよね。
そいうのを想像するとシステムは絶対落とせないし、ユーザに申し訳ないって気持ちが凄く強くなって、そのためにより高度のテクノロジーを使う必要があったり、そして自分自身成長する必要があるって考えると凄くプレッシャーの掛かる仕事なんですが自分自身の成長が実感できるし、なにより何気なく使ってくれてるユーザ人がいると凄く嬉しんですよね。
当たり前のように使えるサービスを作ることは本当に大変な事なんだけど、そのバックグラウンドの努力って中の人じゃないと見えないんですよ。これはエンジニアだけではなくて、僕達を支えてくれている総務だったり人事だったりも同じなんです。
だから、NHKも実はバックグラウンドで凄く努力をしているのかもしれないんですよ。それを僕は信じてお金を払ってます。
ただユーザが僕達を信じてお金を払ってくれているということを絶対忘れてはいけないし、そのための努力惜しんではいけないんですよね。
集金係にお金を払うのは意味があって、自動引き落としやカード決済だとお金を払うっていう重さが伝わりにくいと僕は思っています。
ユーザがお金を払うってどいう気持ちなんだろうかという事を知るために、毎月数千円手渡しで払っています。別にNHKでなくともいいのですが、数百円だと自分にプレッシャーにならないし、少し財布に痛い料金であるNHKをいつも僕は使っています。
信じるは悪いことじゃないので、好きなWebコンテンツに思い切ってお金を払うのはありかもしれません。
ドワンゴを退職しました
はじめに
1月いっぱいで株式会社ドワンゴを退職することになりました。
20日が最終出社日で2月からは新しい職場でお世話になることになりました。
ドワンゴには2010年4月に新卒として入社しサーバインフラエンジニアとして配属されました。
ドワンゴは企画から開発、インフラまで全てを内製している会社です。
そのため高いクオリティかつスピーディにサービスを提供できる環境にあり開発者自身も驚くべきスピードで成長していきます。
そんな環境に憧れて新卒として入社しました。
入社後は研修として短い期間でサービスを作りそのサービスをリリースすることもでき、辛かったですが優秀な仲間たちと一つの物を作り上げ生放送を行いユーザに触っていただくこともできました。
研修終了後は複数のプロジェクトを担当させて頂き、インフラとして困難なプロジェクトもありましたが私のわがままなどもよく受け入れて頂きメンバーの方には迷惑をかけたと思います。
しかし、その困難を乗り越えていく事で自分自身が大きく成長していくのを実感しより高みへと志が移り変わりチャレンジしてきたつもりです。
311
2011年3月11日に関東大震災が起きました。実はこの地震、私は人生で二回目の大きな地震でした。
一度目の被災は小学二年生の時に起きた阪神大震災です。家も潰れ死に物狂いで公園まで逃げました。
その時人生で初めて死というものを身近に感じ、死という目に見えないものに怯えて過ごしてきました。
しかし、人は慣れるものです。
慣れから傲慢になったり怠けたりすることも何度もあり、いつしかこの経験は忘れるほど記憶から薄れていきました。
その中で3月11日の地震は薄れた記憶を再び呼び覚まし死というものを以前より増して意識するようになり今の自分を見直すきっかけとなりました。
NOの日
昨年亡くなったスティーブ・ジョブズがスタンフォード大でのスピーチを思い出しました。
“No” for too many days in a row, I know I need to change something.
このスピーチを聞いたのは大学生の頃ですが、義務教育から大学と決められたカリキュラムと経験者から示される道筋からNoと判断することは殆どなくそれに対して疑問を抱くことも殆どありませんでした。
社会に入りカリキュラムもなく、目標や将来についても全て自らの責任として時間は進んでいきます。
日々業務をする中で自分自身のやっていることについてNoが続いている事に気づきました。
同じような業界で務めている方はよくお分かりかもしれませんが、この業界の技術は進化も早く成熟も非常に早いのです。
新卒で入社した頃は最新の技術であっても一年も経てばより効率的な技術が生み出され、常にアンテナを張り巡らせている企業はいち早く取り入れ実績を上げていきます。
自分自身で勉強したりするものの、組織としてやっているような所と比べるとなかなかついていくのが難しいのが現状です。
そして検証できた頃には成熟し新しい技術が注目され始めてるという事が多くあり、エンジニアとして挑戦できない事も増え変化の早い業界で自分自身の将来への不安が徐々に募り始め又それが、不満であったのは事実です。
これが私の”No”であったと思っています。
今ある枠組み
新卒で入社し1年半程度で退職する事については、多くの方から貴重なご意見をいただきました。
今ある枠組の中で優秀なエンジニアと切羽琢磨しながらシステムを創り上げるは素晴らしいことだと思います。
表面的な価値観だけに囚われていてはだめで本当に自分は何がやりたいのか、それに本当の価値を見出すことが大切なんじゃないかと思っています。厳しい環境で自分自身の技術を磨きインフラとしてだけではなくサービス提供者の一人としてサービスをユーザに送り届けたい。それが自分自身に今できる最大の価値なんじゃないかと思いました。
最後に
1年半という短い期間でしたが、お世話になった方々には本当に感謝しています。
ドワンゴという会社と仲間がいなければここまで自分は成長出来なかったんじゃないかと思っています。
そして、これまで使っていただいたユーザの方もドワンゴは時より寄り道をすることもありますがユーザとしてどんどん意見を発してよいサービスを作るための手助けをしたいただけたらいいなと思います。
まだまだ未熟者ですがこれからも応援していただけば励みになります。
長文となりましたが、以上です。
EXT2をEXT3に変換する
Linux上でディスクの増設やパーティションの分割を行う時に利用partedコマンドを利用する事が多いと思います。
# parted /dev/hdd (parted) mkpartfs Partition name? []? 1 File system type? [ext2]? ext3 Start? 14k End? 10G No Implementation: Support for creating ext3 file systems is not implemented yet.
真面目にpartedコマンドでやると
No Implementation: Support for creating ext3 file systems is not implemented yet.
このようなerrorがでると思います。
そう、partedコマンドはext3に対応していないのでpartedコマンドだけでEXT3のファイルシステムを作ることはできません。
partedコマンドではEXT2としてパーティションだけ作ることにしてそこからEXT3へ変換することにします。
ext3になっていますが完成形はEXT2になります
# parted /dev/hdd (parted) mkpart primary ext3 14k 10G (parted) print free Number Start End Size File system Name Flags 1 14kB 10GB 10GB ext2 primary
EXT3に変換します
# mkfs -t ext3 /dev/hddp01 mke2fs 1.39 (29-May-2006) Filesystem label= OS type: Linux 略......
最後にfstabに書き忘れないようにしましょう。
Macで作った秘密鍵をWindowsのPuttyで使う方法がわからなかった
MacにはOpensslが入っているのでターミナルから簡単に秘密鍵(id_rsa)と公開鍵(id_rsa.pub)を作ることができます。
ssh-keygen -t rsa
この秘密鍵をWindowsで使う必要がありPutyyで読み込もうとしたのですがUnable to use key fileと怒られログインできません。
ちなみに秘密鍵を書いたファイルの拡張はなんでも大丈夫です。
結果としてPuttyGenでMacで作成した秘密鍵を読み込ませPutty用に生成すれば普通に使えました。
Puttygen起動->Load->適当にパスワードを付ける->Macで作った秘密鍵を選択->Save Private Key -> マウスを動かす -> 保存 -> 先ほど保存したキーを使ってアクセス
これで1時間ぐらい悩んでしまった。。。。。
nginx+PHP-FPMでどこまでチューニングできるか
このサイトもnginx+PHP-FPM+MySQLで動作しているのですがphpをfastcgiで動かしている例は多くあったのですがPHP-FPMで書いている記事があまりない。
yumなどは使わずにソースから全てインストールします。MySQLは事前にインストールされている物とします。
nginx
nginxユーザを追加します
adduser -u 500 nginx
使わないオプションは全てOFFにします。
./configure \ --with-http_stub_status_module \ --without-http_upstream_ip_hash_module \ --without-http_gzip_module \ --without-http_autoindex_module \ --without-http_geo_module \ --without-http_map_module \ --without-http_split_clients_module \ --without-http_referer_module \ --without-http_proxy_module \ --without-http_uwsgi_module \ --without-http_limit_zone_module \ --without-http_limit_req_module \ --without-http_browser_module \
特に–with-http_stub_status_module のオプションはサーバのステータスを見てチューニングするのには必須なので必ずいれます。
nginx設定ファイル
user nginx;
worker_processes 2;
error_log /var/log/nginx/blog/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /usr/local/nginx/conf/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/blog/access.log main;
sendfile on;
keepalive_timeout 0;
server_tokens off;
server {
listen 80;
server_name blog.kubox.info;
root /home/blog/public_html;
index index.php;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location ~*\.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /home/blog/public_html$fastcgi_script_name;
}
location = /status {
include /usr/local/nginx/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /status;
fastcgi_pass 127.0.0.1:9000;
access_log off;
}
location /nginx_status {
stub_status on;
access_log off;
allow all;
deny all;
}
location ~ /\.ht {
deny all;
}
}
}
コンピュータはCPUの数を超えて並列処理はできないのでworker_processesはCPUの数に合わせます。2Coreなので2します。
worker_connectionsはworker_processesが同時に処理できる接続数です。
以下の行を追加するとhttp://URL/statusでPHP-FPMのステータス、http://URL/nginx_statusでnginxのステータスを見ることができます。
deny allになっているので許可するIPをAllowしてください。
location = /status {
include /usr/local/nginx/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /status;
fastcgi_pass 127.0.0.1:9000;
access_log off;
}
location /nginx_status {
stub_status on;
access_log off;
allow all;
deny all;
}
PHP-FPM
PHP-FPMはPHP5.3.3から組み込まれており、オプションを追加するだけでPHP-FPMが利用できます。
fastcgiは別途lighttpdをmakeして抜き出したりする手間がありましたがその必要がなくなりより高負荷サイト用に機能が追加されました。
- 緩やかな (graceful) 停止/起動 機能を含む高度なプロセス管理
- 異なる uid/gid/chroot/environment でのワーカーの開始、 異なるポートでのリスン、異なる php.ini の使用 (safe_mode の代替)
- 標準出力および標準エラー出力へのログ出力
- opcode キャッシュが壊れた場合の緊急再起動
- 高速なアップロードのサポート
- “slowlog” – 非常に低速に動作するログ出力スクリプト (名前から想像される内容だけでなく、ptrace やそれと同等の仕組みを使ってリモートプロセスの execute_data からの PHP バックトレースも出力します)
- fastcgi_finish_request() – リクエストを終わらせてすべてのデータを出力した後で 何か時間のかかる処理 (動画の変換や統計情報の処理など) をさせるための特殊な関数
- 動的/静的 な子プロセスの起動
- 基本的な SAPI の動作状況 (Apache の mod_status と同等)
- php.ini ベースの設定ファイル
http://www.php.net/manual/ja/install.fpm.php
configureでenable-fpmオプションを追加してください。
'./configure' \ '--with-mysql=/usr/local/mysql' \ '--enable-zend-multibyte' \ '--with-pdo-mysql' \ '--with-zlib' \ '--with-xmlrpc' \ '--with-gd' \ '--with-curl' \ '--with-jpeg-dir=/usr/local' \ '--with-png-dir=/usr/local' \ '--enable-mbstring' \ '--enable-fpm' \ '--with-fpm-user=nginx' \ '--with-fpm-group=nginx' \ '--without-SQLite' \
PHP-FPM設定ファイル
[global] pid = /var/run/php-fpm.pid error_log = /var/log/php/php-fpm.log log_level = notice emergency_restart_threshold = 0 emergency_restart_interval = 0 process_control_timeout = 0 daemonize = yes [www] listen = 127.0.0.1:9000 listen.backlog = -1 listen.allowed_clients = 127.0.0.1 listen.owner = nginx listen.group = nginx listen.mode = 0666 user = nginx group = nginx pm = dynamic pm.max_children = 10 pm.start_servers = 5 pm.min_spare_servers = 3 pm.max_spare_servers = 8 pm.max_requests = 500 pm.status_path = /status request_slowlog_timeout = 2 request_terminate_timeout= 5 slowlog = /var/log/php/$pool.log.slow
注目するところはpmから始まる設定です。
PHP-FPMはWebサーバ(nginx)がユーザから受け取ったリクエストをPHPでればPHP-FPMに処理を投げ処理をするので、nginxとPHP-FPMが同程度処理できるのが理想です。
ひとまず負荷試験を行うまでどこにボトルネックがあるかわからないので適用な値にしておきます。
メモリ
nginxとPHP-FPMが無事に起動できたら一旦メモリの状態を見てみます。
# free -m
total used free shared buffers cached
Mem: 497 385 111 0 75 154
-/+ buffers/cache: 156 341
Swap: 1027 0 1027
予想以上にメモリを使っていたのでちょっとpsしてみます。
# ps axu | grep php root 32717 0.0 0.7 124672 3708 ? Ss 13:45 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) nginx 32718 0.2 9.0 139960 46368 ? S 13:45 0:00 php-fpm: pool www nginx 32719 0.0 5.7 132708 29424 ? S 13:45 0:00 php-fpm: pool www nginx 32720 0.1 4.6 129440 23700 ? S 13:45 0:00 php-fpm: pool www nginx 32721 0.1 5.0 129928 25940 ? S 13:45 0:00 php-fpm: pool www nginx 32722 0.0 5.2 130212 26900 ? S 13:45 0:00 php-fpm: pool www
PHP-FPMが結構メモリを使っているみたいです。
pmapコマンドでプロセスのメモリ利用状況をさらに詳しくみてみます。
# pmap 32717 32717: php-fpm: master process (/usr/local/etc/php-fpm.conf) 0000000000400000 8396K r-x-- /usr/local/sbin/php-fpm 0000000000e33000 464K rw--- /usr/local/sbin/php-fpm 0000000000ea7000 120K rw--- [ anon ] 0000000010021000 2288K rw--- [ anon ] 0000000040456000 4K ----- [ anon ] 0000000040457000 10240K rw--- [ anon ] 00000030f0c00000 112K r-x-- /lib64/ld-2.5.so 00000030f0e1c000 4K r---- /lib64/ld-2.5.so 00000030f0e1d000 4K rw--- /lib64/ld-2.5.so 00000030f1000000 1336K r-x-- /lib64/libc-2.5.so 00000030f114e000 2048K ----- /lib64/libc-2.5.so 00000030f134e000 16K r---- /lib64/libc-2.5.so 00000030f1352000 4K rw--- /lib64/libc-2.5.so 00000030f1353000 20K rw--- [ anon ] 00000030f1400000 8K r-x-- /lib64/libdl-2.5.so 00000030f1402000 2048K ----- /lib64/libdl-2.5.so 00000030f1602000 4K r---- /lib64/libdl-2.5.so 00000030f1603000 4K rw--- /lib64/libdl-2.5.so 00000030f1800000 520K r-x-- /lib64/libm-2.5.so 00000030f1882000 2044K ----- /lib64/libm-2.5.so 00000030f1a81000 4K r---- /lib64/libm-2.5.so 00000030f1a82000 4K rw--- /lib64/libm-2.5.so 00000030f1c00000 88K r-x-- /lib64/libpthread-2.5.so 00000030f1c16000 2044K ----- /lib64/libpthread-2.5.so 00000030f1e15000 4K r---- /lib64/libpthread-2.5.so 00000030f1e16000 4K rw--- /lib64/libpthread-2.5.so 00000030f1e17000 16K rw--- [ anon ] 00000030f2000000 80K r-x-- /usr/lib64/libz.so.1.2.3 00000030f2014000 2044K ----- /usr/lib64/libz.so.1.2.3 00000030f2213000 4K rw--- /usr/lib64/libz.so.1.2.3 00000030f2400000 236K r-x-- /lib64/libsepol.so.1 00000030f243b000 2048K ----- /lib64/libsepol.so.1 00000030f263b000 4K rw--- /lib64/libsepol.so.1 00000030f263c000 40K rw--- [ anon ] 00000030f2800000 84K r-x-- /lib64/libselinux.so.1 00000030f2815000 2048K ----- /lib64/libselinux.so.1 00000030f2a15000 8K rw--- /lib64/libselinux.so.1 00000030f2a17000 4K rw--- [ anon ] 00000030f2c00000 28K r-x-- /lib64/librt-2.5.so 00000030f2c07000 2048K ----- /lib64/librt-2.5.so 00000030f2e07000 4K r---- /lib64/librt-2.5.so 00000030f2e08000 4K rw--- /lib64/librt-2.5.so 00000030f3000000 52K r-x-- /lib64/libgcc_s-4.1.2-20080825.so.1 00000030f300d000 2048K ----- /lib64/libgcc_s-4.1.2-20080825.so.1 00000030f320d000 4K rw--- /lib64/libgcc_s-4.1.2-20080825.so.1 00000030f3800000 36K r-x-- /lib64/libcrypt-2.5.so 00000030f3809000 2044K ----- /lib64/libcrypt-2.5.so 00000030f3a08000 4K r---- /lib64/libcrypt-2.5.so 00000030f3a09000 4K rw--- /lib64/libcrypt-2.5.so 00000030f3a0a000 184K rw--- [ anon ] 00000030f4c00000 1204K r-x-- /lib64/libcrypto.so.0.9.8e 00000030f4d2d000 2044K ----- /lib64/libcrypto.so.0.9.8e 00000030f4f2c000 132K rw--- /lib64/libcrypto.so.0.9.8e 00000030f4f4d000 16K rw--- [ anon ] 00000030f5000000 84K r-x-- /lib64/libnsl-2.5.so 00000030f5015000 2044K ----- /lib64/libnsl-2.5.so 00000030f5214000 4K r---- /lib64/libnsl-2.5.so 00000030f5215000 4K rw--- /lib64/libnsl-2.5.so 00000030f5216000 8K rw--- [ anon ] 00000030f6000000 68K r-x-- /lib64/libresolv-2.5.so 00000030f6011000 2048K ----- /lib64/libresolv-2.5.so 00000030f6211000 4K r---- /lib64/libresolv-2.5.so 00000030f6212000 4K rw--- /lib64/libresolv-2.5.so 00000030f6213000 8K rw--- [ anon ] 00000030f6800000 8K r-x-- /lib64/libcom_err.so.2.1 00000030f6802000 2044K ----- /lib64/libcom_err.so.2.1 00000030f6a01000 4K rw--- /lib64/libcom_err.so.2.1 00000030f6c00000 8K r-x-- /lib64/libkeyutils-1.2.so 00000030f6c02000 2044K ----- /lib64/libkeyutils-1.2.so 00000030f6e01000 4K rw--- /lib64/libkeyutils-1.2.so 00000030f7c00000 1228K r-x-- /usr/lib64/libxml2.so.2.6.26 00000030f7d33000 2048K ----- /usr/lib64/libxml2.so.2.6.26 00000030f7f33000 36K rw--- /usr/lib64/libxml2.so.2.6.26 00000030f7f3c000 4K rw--- [ anon ] 00000030f8400000 144K r-x-- /usr/lib64/libk5crypto.so.3.1 00000030f8424000 2044K ----- /usr/lib64/libk5crypto.so.3.1 00000030f8623000 8K rw--- /usr/lib64/libk5crypto.so.3.1 00000030f8800000 32K r-x-- /usr/lib64/libkrb5support.so.0.1 00000030f8808000 2044K ----- /usr/lib64/libkrb5support.so.0.1 00000030f8a07000 4K rw--- /usr/lib64/libkrb5support.so.0.1 00000030f8c00000 584K r-x-- /usr/lib64/libkrb5.so.3.3 00000030f8c92000 2044K ----- /usr/lib64/libkrb5.so.3.3 00000030f8e91000 16K rw--- /usr/lib64/libkrb5.so.3.3 00000030f9400000 176K r-x-- /usr/lib64/libgssapi_krb5.so.2.2 00000030f942c000 2048K ----- /usr/lib64/libgssapi_krb5.so.2.2 00000030f962c000 8K rw--- /usr/lib64/libgssapi_krb5.so.2.2 00000030fa400000 280K r-x-- /lib64/libssl.so.0.9.8e 00000030fa446000 2048K ----- /lib64/libssl.so.0.9.8e 00000030fa646000 24K rw--- /lib64/libssl.so.0.9.8e 0000003100a00000 140K r-x-- /usr/lib64/libpng12.so.0.10.0 0000003100a23000 2048K ----- /usr/lib64/libpng12.so.0.10.0 0000003100c23000 4K rw--- /usr/lib64/libpng12.so.0.10.0 00002aaaaaac4000 40K r-x-- /lib64/libnss_files-2.5.so 00002aaaaaace000 2044K ----- /lib64/libnss_files-2.5.so 00002aaaaaccd000 4K r---- /lib64/libnss_files-2.5.so 00002aaaaacce000 4K rw--- /lib64/libnss_files-2.5.so 00002aaaaaccf000 32768K rw-s- /dev/zero (deleted) 00002aaaacccf000 4K rw-s- /dev/zero (deleted) 00002b9f39d31000 8K rw--- [ anon ] 00002b9f39d33000 4K rw-s- /dev/zero (deleted) 00002b9f39d3f000 4K rw--- [ anon ] 00002b9f39d40000 2904K r-x-- /usr/local/mysql/lib/libmysqlclient.so.18.0.0 00002b9f3a016000 1020K ----- /usr/local/mysql/lib/libmysqlclient.so.18.0.0 00002b9f3a115000 524K rw--- /usr/local/mysql/lib/libmysqlclient.so.18.0.0 00002b9f3a198000 24K rw--- [ anon ] 00002b9f3a19e000 132K r-x-- /usr/lib64/libjpeg.so.62.0.0 00002b9f3a1bf000 2044K ----- /usr/lib64/libjpeg.so.62.0.0 00002b9f3a3be000 4K rw--- /usr/lib64/libjpeg.so.62.0.0 00002b9f3a3bf000 4K rw--- [ anon ] 00002b9f3a3c0000 324K r-x-- /usr/local/lib/libcurl.so.4.2.0 00002b9f3a411000 2044K ----- /usr/local/lib/libcurl.so.4.2.0 00002b9f3a610000 12K rw--- /usr/local/lib/libcurl.so.4.2.0 00002b9f3a613000 4K rw--- [ anon ] 00002b9f3a614000 196K r-x-- /usr/lib64/libidn.so.11.5.19 00002b9f3a645000 2044K ----- /usr/lib64/libidn.so.11.5.19 00002b9f3a844000 4K rw--- /usr/lib64/libidn.so.11.5.19 00002b9f3a845000 28K rw--- [ anon ] 00002b9f3a88d000 316K rw--- [ anon ] 00002b9f3a8dc000 128K r-x-- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so 00002b9f3a8fc000 2048K ----- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so 00002b9f3aafc000 12K rw--- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so 00002b9f3aaff000 36K rw--- [ anon ] 00002b9f3ab08000 76K r-x-- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/oauth.so 00002b9f3ab1b000 2048K ----- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/oauth.so 00002b9f3ad1b000 8K rw--- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/oauth.so 00007fff4a564000 84K rw--- [ stack ] 00007fff4a5fc000 16K r-x-- [ anon ] ffffffffff600000 8192K ----- [ anon ] total 132860K
apc.soはAPCを使う上で必要なのでしょうがないですがoauth.soはそんなに頻繁にOAuthしないので一旦php.iniでコメントアウトしておきます。
PHP-FPMを再起動後、再度pmapしてみます。
pmap 11238 11238: php-fpm: master process (/usr/local/etc/php-fpm.conf) 0000000000400000 8396K r-x-- /usr/local/sbin/php-fpm 0000000000e33000 464K rw--- /usr/local/sbin/php-fpm 0000000000ea7000 120K rw--- [ anon ] 000000001a798000 2252K rw--- [ anon ] 00000000406da000 4K ----- [ anon ] 00000000406db000 10240K rw--- [ anon ] 00000030f0c00000 112K r-x-- /lib64/ld-2.5.so 00000030f0e1c000 4K r---- /lib64/ld-2.5.so 00000030f0e1d000 4K rw--- /lib64/ld-2.5.so 00000030f1000000 1336K r-x-- /lib64/libc-2.5.so 00000030f114e000 2048K ----- /lib64/libc-2.5.so 00000030f134e000 16K r---- /lib64/libc-2.5.so 00000030f1352000 4K rw--- /lib64/libc-2.5.so 00000030f1353000 20K rw--- [ anon ] 00000030f1400000 8K r-x-- /lib64/libdl-2.5.so 00000030f1402000 2048K ----- /lib64/libdl-2.5.so 00000030f1602000 4K r---- /lib64/libdl-2.5.so 00000030f1603000 4K rw--- /lib64/libdl-2.5.so 00000030f1800000 520K r-x-- /lib64/libm-2.5.so 00000030f1882000 2044K ----- /lib64/libm-2.5.so 00000030f1a81000 4K r---- /lib64/libm-2.5.so 00000030f1a82000 4K rw--- /lib64/libm-2.5.so 00000030f1c00000 88K r-x-- /lib64/libpthread-2.5.so 00000030f1c16000 2044K ----- /lib64/libpthread-2.5.so 00000030f1e15000 4K r---- /lib64/libpthread-2.5.so 00000030f1e16000 4K rw--- /lib64/libpthread-2.5.so 00000030f1e17000 16K rw--- [ anon ] 00000030f2000000 80K r-x-- /usr/lib64/libz.so.1.2.3 00000030f2014000 2044K ----- /usr/lib64/libz.so.1.2.3 00000030f2213000 4K rw--- /usr/lib64/libz.so.1.2.3 00000030f2400000 236K r-x-- /lib64/libsepol.so.1 00000030f243b000 2048K ----- /lib64/libsepol.so.1 00000030f263b000 4K rw--- /lib64/libsepol.so.1 00000030f263c000 40K rw--- [ anon ] 00000030f2800000 84K r-x-- /lib64/libselinux.so.1 00000030f2815000 2048K ----- /lib64/libselinux.so.1 00000030f2a15000 8K rw--- /lib64/libselinux.so.1 00000030f2a17000 4K rw--- [ anon ] 00000030f2c00000 28K r-x-- /lib64/librt-2.5.so 00000030f2c07000 2048K ----- /lib64/librt-2.5.so 00000030f2e07000 4K r---- /lib64/librt-2.5.so 00000030f2e08000 4K rw--- /lib64/librt-2.5.so 00000030f3000000 52K r-x-- /lib64/libgcc_s-4.1.2-20080825.so.1 00000030f300d000 2048K ----- /lib64/libgcc_s-4.1.2-20080825.so.1 00000030f320d000 4K rw--- /lib64/libgcc_s-4.1.2-20080825.so.1 00000030f3800000 36K r-x-- /lib64/libcrypt-2.5.so 00000030f3809000 2044K ----- /lib64/libcrypt-2.5.so 00000030f3a08000 4K r---- /lib64/libcrypt-2.5.so 00000030f3a09000 4K rw--- /lib64/libcrypt-2.5.so 00000030f3a0a000 184K rw--- [ anon ] 00000030f4c00000 1204K r-x-- /lib64/libcrypto.so.0.9.8e 00000030f4d2d000 2044K ----- /lib64/libcrypto.so.0.9.8e 00000030f4f2c000 132K rw--- /lib64/libcrypto.so.0.9.8e 00000030f4f4d000 16K rw--- [ anon ] 00000030f5000000 84K r-x-- /lib64/libnsl-2.5.so 00000030f5015000 2044K ----- /lib64/libnsl-2.5.so 00000030f5214000 4K r---- /lib64/libnsl-2.5.so 00000030f5215000 4K rw--- /lib64/libnsl-2.5.so 00000030f5216000 8K rw--- [ anon ] 00000030f6000000 68K r-x-- /lib64/libresolv-2.5.so 00000030f6011000 2048K ----- /lib64/libresolv-2.5.so 00000030f6211000 4K r---- /lib64/libresolv-2.5.so 00000030f6212000 4K rw--- /lib64/libresolv-2.5.so 00000030f6213000 8K rw--- [ anon ] 00000030f6800000 8K r-x-- /lib64/libcom_err.so.2.1 00000030f6802000 2044K ----- /lib64/libcom_err.so.2.1 00000030f6a01000 4K rw--- /lib64/libcom_err.so.2.1 00000030f6c00000 8K r-x-- /lib64/libkeyutils-1.2.so 00000030f6c02000 2044K ----- /lib64/libkeyutils-1.2.so 00000030f6e01000 4K rw--- /lib64/libkeyutils-1.2.so 00000030f7c00000 1228K r-x-- /usr/lib64/libxml2.so.2.6.26 00000030f7d33000 2048K ----- /usr/lib64/libxml2.so.2.6.26 00000030f7f33000 36K rw--- /usr/lib64/libxml2.so.2.6.26 00000030f7f3c000 4K rw--- [ anon ] 00000030f8400000 144K r-x-- /usr/lib64/libk5crypto.so.3.1 00000030f8424000 2044K ----- /usr/lib64/libk5crypto.so.3.1 00000030f8623000 8K rw--- /usr/lib64/libk5crypto.so.3.1 00000030f8800000 32K r-x-- /usr/lib64/libkrb5support.so.0.1 00000030f8808000 2044K ----- /usr/lib64/libkrb5support.so.0.1 00000030f8a07000 4K rw--- /usr/lib64/libkrb5support.so.0.1 00000030f8c00000 584K r-x-- /usr/lib64/libkrb5.so.3.3 00000030f8c92000 2044K ----- /usr/lib64/libkrb5.so.3.3 00000030f8e91000 16K rw--- /usr/lib64/libkrb5.so.3.3 00000030f9400000 176K r-x-- /usr/lib64/libgssapi_krb5.so.2.2 00000030f942c000 2048K ----- /usr/lib64/libgssapi_krb5.so.2.2 00000030f962c000 8K rw--- /usr/lib64/libgssapi_krb5.so.2.2 00000030fa400000 280K r-x-- /lib64/libssl.so.0.9.8e 00000030fa446000 2048K ----- /lib64/libssl.so.0.9.8e 00000030fa646000 24K rw--- /lib64/libssl.so.0.9.8e 0000003100a00000 140K r-x-- /usr/lib64/libpng12.so.0.10.0 0000003100a23000 2048K ----- /usr/lib64/libpng12.so.0.10.0 0000003100c23000 4K rw--- /usr/lib64/libpng12.so.0.10.0 00002aaaaaac4000 40K r-x-- /lib64/libnss_files-2.5.so 00002aaaaaace000 2044K ----- /lib64/libnss_files-2.5.so 00002aaaaaccd000 4K r---- /lib64/libnss_files-2.5.so 00002aaaaacce000 4K rw--- /lib64/libnss_files-2.5.so 00002aaaaaccf000 32768K rw-s- /dev/zero (deleted) 00002aaaacccf000 4K rw-s- /dev/zero (deleted) 00002aac041d0000 8K rw--- [ anon ] 00002aac041d2000 4K rw-s- /dev/zero (deleted) 00002aac041d3000 4K rw-s- /dev/zero (deleted) 00002aac041d4000 4K rw-s- /dev/zero (deleted) 00002aac041d5000 4K rw-s- /dev/zero (deleted) 00002aac041de000 4K rw--- [ anon ] 00002aac041df000 2904K r-x-- /usr/local/mysql/lib/libmysqlclient.so.18.0.0 00002aac044b5000 1020K ----- /usr/local/mysql/lib/libmysqlclient.so.18.0.0 00002aac045b4000 524K rw--- /usr/local/mysql/lib/libmysqlclient.so.18.0.0 00002aac04637000 24K rw--- [ anon ] 00002aac0463d000 132K r-x-- /usr/lib64/libjpeg.so.62.0.0 00002aac0465e000 2044K ----- /usr/lib64/libjpeg.so.62.0.0 00002aac0485d000 4K rw--- /usr/lib64/libjpeg.so.62.0.0 00002aac0485e000 4K rw--- [ anon ] 00002aac0485f000 324K r-x-- /usr/local/lib/libcurl.so.4.2.0 00002aac048b0000 2044K ----- /usr/local/lib/libcurl.so.4.2.0 00002aac04aaf000 12K rw--- /usr/local/lib/libcurl.so.4.2.0 00002aac04ab2000 4K rw--- [ anon ] 00002aac04ab3000 196K r-x-- /usr/lib64/libidn.so.11.5.19 00002aac04ae4000 2044K ----- /usr/lib64/libidn.so.11.5.19 00002aac04ce3000 4K rw--- /usr/lib64/libidn.so.11.5.19 00002aac04ce4000 28K rw--- [ anon ] 00002aac04d2c000 316K rw--- [ anon ] 00002aac04d7b000 128K r-x-- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so 00002aac04d9b000 2048K ----- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so 00002aac04f9b000 12K rw--- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so 00002aac04f9e000 36K rw--- [ anon ] 00007fffbee97000 84K rw--- [ stack ] 00007fffbeffc000 16K r-x-- [ anon ] ffffffffff600000 8192K ----- [ anon ] total 130704K
2Mダイエットできました。
負荷試験
nginxとPHP-FPMを少しキャッシュなどで太らせたいので別のマシンからApacheBenchしてみます。
./ab -n 1000 -c 20 -t 10 http://blog.kubox.info/
# free -m
total used free shared buffers cached
Mem: 497 346 151 0 11 126
-/+ buffers/cache: 208 289
Swap: 1027 9 1017
結構メモリを消費したみたいです。
LoadAverageも結構あがりました。
top - 17:16:48 up 7 days, 2:56, 3 users, load average: 4.12, 1.24, 0.44 Tasks: 107 total, 23 running, 75 sleeping, 8 stopped, 1 zombie Cpu(s): 89.5%us, 4.5%sy, 0.0%ni, 0.0%id, 0.0%wa, 2.5%hi, 3.5%si, 0.0%st Mem: 509792k total, 299092k used, 210700k free, 12056k buffers Swap: 1052248k total, 10232k used, 1042016k free, 102124k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 11823 nginx 16 0 124m 23m 15m R 63.8 4.7 0:02.69 php-fpm 11810 nginx 16 0 124m 23m 15m R 31.9 4.6 0:02.31 php-fpm 11805 nginx 16 0 124m 23m 15m R 25.9 4.6 0:03.59 php-fpm 11824 nginx 16 0 125m 23m 15m R 21.9 4.8 0:01.19 php-fpm 11806 nginx 16 0 124m 23m 15m R 19.9 4.6 0:03.96 php-fpm 11807 nginx 16 0 125m 23m 15m R 15.0 4.8 0:02.25 php-fpm 11825 nginx 16 0 125m 23m 15m R 15.0 4.8 0:01.55 php-fpm 10715 nginx 15 0 21728 2180 756 S 5.0 0.4 0:01.02 nginx 11123 mysql 15 0 452m 50m 5684 S 1.0 10.1 0:01.65 mysqld
エラーがでるまでApacheBenchのスレッドを上げていきます。
同時に先ほどnginxでstatusとnginx_statusを設定したページにアクセスしてプロセスの状態を見ます。
#nginx_status Active connections: 19 server accepts handled requests 815 815 815 Reading: 0 Writing: 19 Waiting: 0
Readingはnginxがリクエストのヘッダーを読み込んでいる数、Writingはレスポンス用のリクエストを生成している数、WaitingはKeepAliveで待っている数です。
KeepAliveは0(無効)にしているので必ず0になります。KeepAliveはDoS攻撃などの耐性が低くなるのでできるだけ無効で運用します。
100スレッド目からエラーが出始めました。
Server Software: nginx
Server Hostname: blog.kubox.info
Server Port: 80
Document Path: /
Document Length: 119439 bytes
Concurrency Level: 100
Time taken for tests: 11.310 seconds
Complete requests: 100
Failed requests: 2
(Connect: 0, Receive: 0, Length: 2, Exceptions: 0)
Write errors: 0
Non-2xx responses: 2
Total transferred: 11742858 bytes
HTML transferred: 11705790 bytes
Requests per second: 8.84 [#/sec] (mean)
Time per request: 11309.898 [ms] (mean)
Time per request: 113.099 [ms] (mean, across all concurrent requests)
Transfer rate: 1013.95 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 10 21 6.5 20 34
Processing: 475 6205 3240.7 6199 11285
Waiting: 129 5687 3298.2 5759 11123
Total: 489 6227 3241.4 6226 11304
Percentage of the requests served within a certain time (ms)
50% 6226
66% 8179
75% 9103
80% 9653
90% 10643
95% 11059
98% 11106
99% 11304
100% 11304 (longest request)
クライアント側のログを見てみます。
[error] 5717#0: *5587 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: IP, server: blog.kubox.info, request: "GET / HTTP/1.0", upstream: "fastcgi://127.0.0.1:9000", host: "hostname"
どうやらnginxが受け取ったリクエストをPHP-FPMに処理を投げたときにconnectionを切られているみたいです。
このエラーの場合はnginxはユーザからリクエストを受け取り、PHP-FPMに処理を投げているのでボトルネックはPHP-FPMだと分かります。
PHP-FPMの設定を見直すとrequest_terminate_timeout= 5が悪さをしているようでタイムアウトを少し長めします。
request_terminate_timeout= 10
150スレッド目で同様のエラーが出力されました。
PHP-FPMのステータスを負荷試験中に見るとmax children reached: 1となっているのでPHP-FPMのプロセスが常時最大に達しているのが確認できました。
pool: www process manager: dynamic accepted conn: 317 listen queue len: 0 max listen queue len: -1 idle processes: 0 active processes: 10 total processes: 10 max children reached: 1
PHP-FPMのプロセスを増やしたいと思います。
ApacheやnginxやPHP-FPMはforkしてプロセス間で負荷分散しつつメモリの消費を節約します。
サーバが落ちる時は大体この辺りの設定ミスです。
実際にどれぐらいforkさせるのかはサービスによって異なりますが物理リソースを無駄なく利用するにはforkで生じるメモリ使用量を把握する必要があります。
メモリのお話については割愛しますが今回のPHP-FPMでいうとforkされたプロセスが使うメモリはおおよそ7M程度です。
物理メモリ100Mは残して起きたいのでさらに20プロセス増やす事にしました。
pm = dynamic pm.max_children = 30 pm.start_servers = 11 pm.min_spare_servers = 10 pm.max_spare_servers = 15 pm.max_requests = 500
PHP-FPMを再起動後メモリの状態を見てみます。
予想に反してあまりメモリを消費しなかったみたいです。
# free -m
total used free shared buffers cached
Mem: 497 400 97 0 52 140
-/+ buffers/cache: 207 290
Swap: 1027 9 1017
プロセス数を3倍にしたので論理的には3倍のアクセスを処理できることになります。
再度ApacheBenchで今度は450スレッドまで引き上げて負荷試験をしてみます。
Server Software: nginx Server Hostname: blog.kubox.info Server Port: 80 Document Path: / Document Length: 119439 bytes Concurrency Level: 350 Time taken for tests: 10.132 seconds Complete requests: 82 Failed requests: 0 Write errors: 0 Total transferred: 10160997 bytes HTML transferred: 10128261 bytes Requests per second: 8.09 [#/sec] (mean) Time per request: 43245.804 [ms] (mean) Time per request: 123.559 [ms] (mean, across all concurrent requests) Transfer rate: 979.37 [Kbytes/sec] received
心なしかレスポンスが良くなりました。
vmstatを見てみるとrがCPUコア数を超えているのでCPUの負荷がかなり高い状態になっています。
メモリはswapは少々発生していますがswap inもoutも発生していないのでとりあえず問題はありません。
procsのbとSWAPのsoが発生し始めると大幅に性能が劣化しますので注意が必要です。
nginxのエラーログとvmstatを見る限りエラーもなしでCPUのみが高負荷なので理想な負荷です。
# 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 10144 73552 54424 147776 0 0 1 3 2 16 0 0 99 0 0 31 0 10144 59592 54424 147776 0 0 0 0 2099 858 57 10 33 0 0 31 0 10144 60948 54424 147792 0 0 0 0 1744 762 94 6 0 0 0 31 0 10144 58468 54424 147868 0 0 0 4 1897 681 94 6 0 0 0 31 0 10144 59212 54424 147876 0 0 0 0 1904 842 94 6 0 0 0 34 0 10144 59708 54428 147948 0 0 0 460 1777 828 94 6 0 0 0 32 0 10144 59832 54428 147944 0 0 0 0 1785 835 94 6 0 0 0 32 0 10144 59212 54428 148020 0 0 0 0 1971 763 93 7 0 0 0 31 0 10144 59832 54428 148024 0 0 0 0 1891 896 94 6 0 0 0 31 0 10144 59708 54428 148100 0 0 0 0 2010 769 94 6 0 0 0 32 0 10144 59832 54436 148080 0 0 0 480 1943 855 93 7 0 0 0 34 0 10144 70760 54436 148232 0 0 0 0 2099 1320 92 9 0 0 0 35 0 10144 69560 54436 148444 0 0 0 0 1020 1727 95 5 0 0 0
Too many open files
800スレッド目からnginxのエラーログにToo many open filesが出力され始めました。
nginxのファイルオープン数の制限に引っかかったようです。
設定ファイルにworker_rlimit_nofile 10240;を追加します。
設定を追加後もエラーが消えないのでどうやらLinuxのファイルディスクリプタに引っかかっているようです。
#vi /etc/security/limits.conf nginx soft nofile 10240 nginx hard nofile 10240
これでエラーはでなくなりました。
最終的に1000スレッドまで達し24時間負荷をかけ続けましたが(これはJmeterで)レスポンスタイムは平均で3秒程度まで落ちたものの安定したアクセスでエラーも出力されていませんでした。
expireを使いユーザのブラウザにキャッシュさせる方法も画像が多いサイトには有効です。
1000スレッド目で負荷を与える側の限界に達したので今回はこれ以上行う事はできませんがApacheと比べて最初はメモリ使用量がやや多いですが、高負荷時は一定のペースを保つので負荷の高いサイトにnginxはオススメです。
その他やったこと
libjpeg-turboに差し替え
libjpeg-truboはlibraryを差し替えるだけで簡単にjpeg系の処理を高速化できます。
http://sourceforge.jp/projects/freshmeat_libjpeg-turbo/
tar -zxvf nasm-2.09.02.tar.gz cd nasm-2.09.02 ./configure make make install tar -zxvf libjpeg-turbo-1.0.90.tar.gz cd libjpeg-turbo-1.0.90 ./configure make make install
/opt/libjpeg-turbo/ にインストールされるので、一旦 /usr/lib/libjpeg* をtmpなどに待避させてから入れ替えます。
mkdir /tmp/libjpeg-backup/ mv /usr/lib/libjpeg* /tmp/libjpeg-backup/ cp -aL /opt/libjpeg-turbo/lib/lib* /usr/lib/ ldconfig
WordPressは接続の度にDBを叩くのでクエリーキャッシュを多めに設定しています。
skip-name-resolve key_buffer_size = 20M max_allowed_packet = 1M table_open_cache = 1024 sort_buffer_size = 1M read_buffer_size = 1M read_rnd_buffer_size = 4M myisam_sort_buffer_size = 8M thread_concurrency = 8 character-set-server = utf8 skip-character-set-client-handshake max_connect_errors=10000 max_connections=300 table_cache=256 thread_cache_size = 30 query_cache_size = 32M wait_timeout = 60 innodb_data_file_path = ibdata1:1024M;ibdata2:2048M:autoextend innodb_buffer_pool_size = 128M innodb_log_file_size = 64M innodb_flush_log_at_trx_commit = 1 innodb_flush_method=O_DIRECT
負荷試験中のMySQLの状態を見てみます。
この3つの値が増え始めるとMySQLの負荷が高くなってくるので上記の設定で予めキャッシュさせておきます。
mysql> SHOW GLOBAL STATUS; Max_used_connections | 2 Opened_tables | 45 Opened_files | 121
オープンソースで構築する仮想ファイアーウォール IPS編
前回の記事でVyattaのF/Wの基本的な使い方を解説いたしましたが、今回はより強固なネットワークを実現させるためにIPSを使ってみます。
VyattaのIPSはLinuxでも頻繁に使われているSnortを使っています。
Vyatta自体は非常に軽量で高速に動作しますがSnort(IPS)を利用する場合はパケットを転送するだけのルータではなく、パケットの中身を見てシグネチャと照らし合わせ、Vyattaで設定したポリシーと照らし合わすなどのプロセスがあるためメモリを多めに積んだマシンが必要になります。
また今回は紹介していないですがNATを利用する場合も同様でパケットを書き換え再度checksumする必要があるためCPUパワーが必要になります。
Snortのおさらい
Snortはオープンソースの侵入検知システム(IDS)と言われるもので、ネットワークに流れるパケットをキャプチャーし不正なパケットを検出します。
通常IDSは不正なパケットを検出し警告を出すにとどまりますが、IPSは検出し遮断します。
VyattaはSnortでこれを実現します。
不正アクセスする手法やシステムの脆弱性は常につきまとうので、Snortは定期的にルールセット(シグネチャ)を更新しています。
Snortにユーザ登録
Snortのシグネチャを定期的に更新するためにSnortにユーザ登録しoink-codeを取得します。
Snortの設定
早速、VyattaでIPSの設定をしてみましょう。
まず、Snortのシグネチャを取得します。
$update ips rules oinkcode ****
Snortのサーバが少し遅いので時間がかかるかもしれません。
updateを先しないとError: Missing Snort Rulesと出たりするので必ず先にUpdateしてください。
ログを見ると更新されたのかわかります。
$show ips update-log
自動アップデートとLANの設定
Snortは定期的にシグネチャを更新する必要があるので自動アップデートの設定を行い定期的にシグネチャを更新したいと思います。
$configure #set content-inspection ips auto-update oink-code **** #set content-inspection ips auto-update update-hour 2
この設定でAM2時に自動的に更新されます。
次にLANのネットワークを予め指定することでSnortによる誤検知を防ぐ事ができます。
$configure #set content-inspection ips modify-rules internal-network 192.168.1.0/24
IPSの設定
VyattaのIPSではパケットをチェックする経路を選択できます。
- 全てのパケットをチェック
- 特定のインタフェースのパケットをチェック
- 特定のゾーン間のパケットをチェック
IPSは負荷が高いのでできるだけパケットチェック対象を絞るべきですが今回は1の全チェックで検証したいと思います。
$configure #set content-inspection inspect-all enable
次にチェックするプロトコルを指定します。これでも同様に全チェックだと負荷が高くなります。ICMPやF/Wで既にdropしているプロトコルなどは省くべきです。
$configure #set content-inspection traffic-filter preset all
ここでshowして設定を確認してみましょう。
$configure #show content-inspection
+inspect-all {
+ enable
+}
+ips {
+ auto-update {
+ oink-code ***
+ update-hour 2
+ }
+ modify-rules {
+ internal-network 192.168.1.0/24
+ }
+}
+traffic-filter {
+ preset all
+}
最後にVyattaでデフォルトルールで設定されている警告レベルの設定を行います。
デフォルトルールには4つのプライオリティが準備されています。
priority-1が最も危険度が高いイベントになりpriority-3が通常の通信でも発生するようなイベントになります。
Otherはルールにマッチしなかった場合に適応されます。
- priority-1
- priority-2
- priority-3
- Other
例えば各priorityには次のようなルールがセットされています。
priority-1 trojan-activity トロイなどによるバックドアを検知します
priority-2 successful-dos DoSを検知します
priority-3 icmp-event Pingを検知します
その他にも多くあるのでドキュメントを参照してください。
show ips rulesでも確認できます。
$ show ips rules
Vyattaではこれらのpriority別に検知時の動作を指定します。
- alert:通信を許可しログにAlertとして記録します
- drop:通信を遮断しログにAlertとして記録します
- pass:通信を許可しそれ以外の動作をしません
- sdrop:通信を遮断しログには記録しません(silent drop)
今回は実験的に全てのpriorityをdropとして指定したいと思います。
$configure #set content-inspection ips actions priority-1 drop #set content-inspection ips actions priority-2 drop #set content-inspection ips actions priority-3 drop #set content-inspection ips actions other pass
そして一旦、ローカルネットワークの設定を削除してみます。
$configure #delete content-inspection ips modify-rules internal-network 192.168.1.0/24 #commit
削除した事で全てのネットワークがIPSの対象になりました。
別のマシンからこのVyattaにPingを打ちます。
$ping 192.168.1.2 PING 192.168.1.1 (192.168.250.1) 56(84) bytes of data. --- 192.168.250.1 ping statistics --- 16 packets transmitted, 0 received, 100% packet loss, time 15086ms
全てPingが失敗したのが確認できたと思います。
この原因を調べてみます。
$show ips log
---------------------------------------------------------------------------
2011-07-22 16:51:15.540325 {ICMP} 192.168.1.2 -> 192.168.1.3
(misc-activity) Misc activity (priority 3)
[1:382:7] ICMP PING Windows
---------------------------------------------------------------------------
2011-07-22 16:51:15.540325 {ICMP} 192.168.1.2 -> 192.168.1.3
(misc-activity) Misc activity (priority 3)
[1:384:5] ICMP PING
---------------------------------------------------------------------------
priority-3のルールによって遮断されていることがわかりました。
Pingを通すためにルールを変更したいと思います。
Pingを防いでいるルールはicmp-info.rulesですので、このルールだけを除外します。
$configure #set content-inspection ips modify-rules exclude-category icmp-info.rules #commit
再度Pingを実行するとPingが通ると思います。
最後に変更したローカルネットワークの設定を元に戻しておいてください。
$configure #set content-inspection ips modify-rules internal-network 192.168.1.0/24
priority-1以外はAlertで大丈夫だと思いますが利用するネットワーク形態によって調節してみてください。
IPS導入時はこまめにログを見てフォールスポジティブとフォールスネガティブが無いかチェックし適宜ポリシーを変更する必要があります。
オープンソースで構築する仮想ファイアーウォール
仮想ファイアーウォールというとイメージしにくいと思いますがVyattaを少し使ってみたので紹介したいと思います。
長くなるので3つぐらいに分けたいと思います。
Vyatta : ビアッタ
Vyattaでできること
- Routing
- NAT
- VPN
- ※F/W
- ※IPS
- Loadbalance
- VRRP
- CUI/GUI(Webベース)
今回紹介するのは※が付いている項目です。
Vyattaとはそもそもなに?
VyattaはDebianベースで作られておりiptablesやsnortなどを組み合わせた“物”です。
Juniperなどのアプライアンス製のF/Wに付いている機能が網羅されている感じです。
ルーティングプロトコルも充実しているので高性能なルータとしても十分機能させることができます。
またVyattaには有料サポートも準備されおりサポートが必要な場合はそちらを使うのもいいと思います。
無料版もVPNクライアントやアンチウイルス機能が無い程度ですので十分に使えます。
イメージの配布もクラウド環境用なども準備されているのですぐに試すことができます。
インストール
メモリ256M、HDD 5Gもあれば十分です。
今回は検証が目的なので利用環境に応じてリソースは変更してください。
インストール自体は非常に簡単なので省略します。
Live Installのままだとリブート後全て消えてしまうのでinstall-imageだけは行ってください。
$install-image
イメージをInstallした後は必ずリブートしてください。色々おかしくなります。
初期設定が終わるまでGUIは使えないのでCLIで進めます。
ユーザ名、パスワード共にvyattaでログインできます。
Linuxライクなプロンプトが表示されていると思いますが、通常のLinuxコマンドもそのまま使えます。
lsコマンドなどを実行するとファイル/ディレクトリリストが表示されると思います。
基本操作
Ciscoなどの製品を使ったことがある人ならすぐになじめると思います。
Vyattaの操作はLinuxコマンドで行うのではなくVyatta専用のコマンドを利用します。
show : showに続く項目の情報を表示する
set : setに続く項目を設定する
save : 設定を起動ファイルに書き込む(設定は再起動まで反映されない)
commit : 設定を反映させる(設定後必ず行う)
$ : operationモード
operationモードでは設定の変更は行えません。
F/Wやインタフェースなどの情報を確認したい場合に利用します。
# : configureモード
operationモード時にconfigureコマンドを実行することでcofigureモードに移行できます。
configureモードでは全ての設定の変更が行えます。
IPの設定
早速IPを割り振ってみましょう。
configureモードになったら次のコマンドを実行します。
$configure #set interface ethernet eth0 address 192.168.1.2/24 #set system gateway-address 192.168.1.1 #commit
これでeth0に192.168.1.2が割り当てられました。
SSHを起動
SSHが使えないと色々不便なのでSSHを起動します。
$configure #set service ssh #commit
ここまでくるとGUIを利用できます。
#set service https #commit
先ほど設定したIPアドレスにアクセスするとGUIモードで設定を変更できます。
NTP/DNS設定
$configure #set system time-zone Asia/Tokyo #set system ntp server ntp.nict.jp #set system name-server 8.8.8.8 #commit
ユーザ追加
plaintext-passwordとなっていますが、入力はプレーンテキストなのでそのままで大丈夫です。
設定後暗号化されます。
levelは適宜変更してください。
$configure #set system login user kojiro #set system login user kojiro level admin #set system login user kojiro authentication plaintext-password hogehoge #commit
F/W設定
初期設定ができたところで、本題のF/Wの設定を行いたいと思います。
F/Wを設定する前に予めIPアドレスのグループを作っておくと後々の運用が楽になります。
VMGuet01というグループを作りIPアドレス172.16.0.10を参加させました。
$configure #set firewall group address-group VMGuet01 #set firewall group address-group VMGuet01 address 172.16.0.10 #commit
VyattaのF/Wは親ノード⇒親ノード内のルール番号(1~1024)⇒許可/拒否⇒マッチ条件 のような流れで設定します。
HTTPという親ノードを作成します
$configure #set firewall name HTTP
ルール番号を指定する。1~1024までならどれも大丈夫ですが1から評価されてゆきマッチした時点でそれ移行を評価しないので100や200など少し間を開けて登録するのがセオリーです。
また、どれにもマッチしなかった場合はF/W全般に言えることですが暗黙の拒否があり全て拒否されます。
今回は内側(LAN)から外側(WAN)に向けた設定をしてみます。
ルールは100から使ってきます。
$configure #set firewall name HTTP rule 100
HTTPノードに対するルール100のアクションを指定します
$configure #set firewall name HTTP rule 100 action accept
HTTPノードに対するルール100のマッチ条件を指定します
$configure #set firewall name HTTP rule 100 source group address-group VMGuet01 #set firewall name HTTP rule 100 destination port 80,443
この設定は送信元がVMGuet01で宛先のポートが80番(HTTP)、443(HTTPS)を許可するという意味になります。
sourceをPortやGlobalIPなどにすることで外側から内側へのアクセス制御も行えます。
commit時にエラーがでる場合などは設定が抜けている事が殆どなのでshowコマンドで設定を確認しながらやっていくといいと思います。
次はIPSを設定してみます。
GoogleのWebアプリケーション脆弱性スキャナ使い方
はてなから引っ越ししました。はてなさん今までお世話になりました。
GoogleがWebアプリケーション脆弱性スキャナskipfishを公開しました。
以前にもGoogleが開発した脆弱性診断ツールratproxyを紹介しました。
ratporxyとskipfishの大きな違いは、ratproxyは受動的な脆弱性診断ツールであったのに対してskipfishは能動的な脆弱性診断ツールです。
skipfishの特徴
- SQLインジェクションの検出
- コマンドインジェクションの検出
- 秒間2000リクエスト以上実行できるパフォーマンスの高さ
- Basic認証、cookieなども渡せる
- 非常にファイルが軽い(1.3M)
- HTMLで結果を出力
使い方
skipfishをダウンロードしてください。
展開後
make ./skipfish -o /var/www/html/skipfish -W /dir/dictionaries/complete.wl http://test.jp/
-o 結果ファイル出力先(空ディレクトリじゃないとエラーになります)
-W テストに使う辞書
-g 最大接続数 (指定推奨)
complete.wlで2時間ぐらいかかりました。
使ってみた感じ
今まで利用した脆弱性診断ツールの中では間違いなく最高速度ですが、辞書が少ない点、辞書式では難しい既存の脆弱性を発見するための仕組みがないことから簡易的な使い方や開発中のアプリケーションのような診断にしか使えないと思います。
現在はまだβ版なので今後の追加機能に期待。

