T

Apacheの救急救助手順

最近Apacheの設定なんか知らないという人が増えたのでメモ。急にアクセス数が増えてサーバのロードアベレージが跳ね上がり、レスポンスが返らなくなったサーバについて質問されたとき、どこをどう確認するか、いつもあまり考えずにやってることを記録する。専門ってわけじゃないので、応急処置として何をしているのか見る感じ。対処法はそれぞれのシステムで違うだろうけど。

Apacheのログを見られるなら、それを最初に確認する。意外なことにディスク容量が足りなくて動かなくなっていたり、ファイルシステムの上限設定に引っかかっていて動かないケースも結構あるのだ。それから、セマフォキューが書き込めなくなっていることもあるが、これが一番見つかり難い。Apacheのエラーログに

[emerg] (28)No space left on device: Couldn't create accept lock

こんなのが出ているのにディスクの残り容量やクォータに問題がなければ、セマフォの問題なのでクリアしないといけない。

// rootで
# for i in `ipcs -s | awk '/httpd/ {print $2}'`; do (ipcrm -s $i); done

手慣れたサーバ管理者なら、こんな感じのワンライナーをどこかに保存してあるはずだ。ここを参考にした記憶がある。

MuninやNagiosなどで監視しているなら、最初に必ず確認しよう。証拠も無しにあれこれ手を出したり推測で動き出すのは愚か者に特有の対応だ。もちろんサーバがレスポンスを返せないような状態になったら何の情報も取れなくなっているだろうが、たとえそうであっても、そうなるに至るまでの過程は見える。Apacheのウェブサーバで提供しているサービスなら、たいていCPUが原因で死ぬことはないので、I/O waitとかSWAPの発生、メモリの使用状況とかを真っ先に見る。別に難しい理屈はない。I/O waitが伸びていてSWAPが発生していれば、メモリの使用状況もひどいことになっているはずなので、それらが一致していれば、ああApacheに割り当てられたリソースをシステム側で供給維持しきれなくなって暴動が起きたんだなと理解できる。PHPなんかが動いているpreforkのApacheなら、プロセス数がMaxClientsまで達したかどうかを見るといいかもしれない。達していれば単純に性能の限界だったんだろう。その前にコケていたなら、設定が間違っている。まあ、あんまりきちんと設定されていないサーバでは、大概は達する前にコケている。そんなときは、とりあえずApacheがどれくらいメモリを食うのか調べてみるのも一興。

$ ps ax | grep httpd | grep -v grep | awk '{print $1}' | while read pid
do
cat /proc/$pid/status | grep VmHWM
done

ひゃっほう、Apacheのプロセスたちが物理メモリをピーク時にどれだけ使ったか表示される。これらの数値を見て、まあ大きい方のやつがApacheのMaxClientsの分だけ起動したらどうなるかかけ算すれば、リソースをApacheに食い尽くされないMaxClientsの設定値が見えてくるはずだ。80MBのプロセスがいるなら、まあみんなが最大値になってしまうことはないけれど、そいつをシステムの物理メモリで割った値にちょっと手加減するくらいならなんとかなると見当がつくだろう。

上のやつだとkB単位で個々に結果が表示されるので、どうせなら一気にその合計まで出そう。

$ ps ax | grep httpd | grep -v grep | awk '{print $1}' | while read pid
do
cat /proc/$pid/status | grep VmHWM
done | awk '{sum += $2} END {print sum}'

手元のサーバで実行したら642964だった。kBだから640MB程度。まだまだ大丈夫そうだ。

他にも、サーバに運良くログインできれば、例えばvmstatやsarを実行してみる。

$ 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 307012 212128 118136 2375840    1    1    23   103    8    9 10  2 88  0  0
 1  0 307012 210656 118148 2375848    0    0     4   136  694  606  3  1 97  1  0
 0  0 307012 211416 118152 2375852    0    0     0   124  589  478  4  0 95  0  0
 0  0 307012 206736 118152 2375880    0    0     4    12  785  630  5  1 93  0  0
 0  0 307012 214992 118152 2375888    0    0     8     0  429  297  1  1 98  0  0
 0  0 307012 208428 118152 2375908    0    0     0   168  506  694 22  2 76  0  0
 0  0 307012 211900 118152 2375920    0    0     0   168  809  614  7  3 90  0  0
.......

$ sar 
(略)
10時00分01秒       CPU     %user     %nice   %system   %iowait    %steal     %idle
10時10分01秒       all     10.96      0.20      2.01      0.25      0.02     86.57
10時20分01秒       all     11.01      0.27      1.92      0.36      0.02     86.42
10時30分01秒       all     11.55      0.23      2.11      0.32      0.02     85.77
10時40分01秒       all     11.27      0.20      1.86      0.21      0.02     86.44
10時50分01秒       all     11.05      0.38      1.73      0.22      0.02     86.61
11時00分01秒       all      7.74      0.20      1.55      0.14      0.02     90.35
11時10分01秒       all     10.51      0.21      2.31      0.34      0.02     86.61
平均値:        all      9.86      0.24      1.58      0.39      0.02     87.90

上は一時的に死にかけたけど健康を取り戻しつつあるサーバの例。vmstatのswapの欄で頻繁にsiとかsoが出ていたり、swpdの値が増え続けているなら非常にまずい状態だ。ApacheのMaxClientsを下げてでもリソース消費を抑えないとサーバにログインさえできなくなるかもしれない。OOM Killerに大事な身内を殺される前になんとかしないといけない。またsarでiowaitの増減を確認すると問題の起きる時間帯がうまく特定できる。

娘が自らオムツを外して床におしっことうんこをするというチャレンジに成功したので、掃除や着替えに忙しく中断していたら、何を書こうとしていたのか思い出せない。ええと、最近のクラウンド環境とかだと、物理サーバの頃よりますますI/O性能に引っ張られてしまう傾向にあるから、まずはこれらの確認が大事になる。

それから、忘れてはいけないのがserver-statusで、SSHでトンネル掘ってでも見に行くべきだ。レスポンスが遅くなっているページがあればすぐに見つかるし、例えばApacheからImageMagickのプロセスを呼んでそのまま刺さりっぱなしになっているリクエストなどが見つかり、そいつがプロセスを占有しているのが問題の原因だったりすることもある。それから、現在のスロット(preforkだったら各プロセス)のステータスがリアルタイムで表示されるのも面白い。

CC__.WWCC..WCK.KKKKW..WWKKW.....................................
................................................................

例えば、Kはデータの送出が終わってもKeepAliveで一応待っている状態のやつ、Wがデータを送出中、Cは接続終了処理中で、「.」は接続を待ち受けているやつだ。もしレスポンスが悪くて困っているなら、場合によってはいっそKeepAliveを切ってしまってもいいだろう。感覚としては、PC向けのサービスで帯域の利用状況が問題になってはいないケースなら切ってしまうことの方が多い。

とりあえず、初動としてはまずはこんな感じの対応をする。いずれも安定稼働しているサーバのメンテでやっていることなので、それ以外は別の対処があるだろうし、アプリケーション側で出来ることもたくさんあるが、夜中の連絡で緊急出動する場合はそこまでやっていられないだろう。

mod-pagespeedの不審な挙動

Google様によるApacheモジュール mod-pagespeedについて。

(1)フルパス(http://から始まる)のリソースへのパスを先頭に「/」がない相対パスに書き換える。

mod_rewriteでパスを書き換えているシステムでこれをやられると、画像とかCSSとかの元のリソースに辿り着けないのでエラーになる。UserDirを使っている場合のことを考えているのかもしれないが、いまどきこの仕様は困ったものだ。

(2)画像をプリロードする。

上のに組み合わせて、謎のリソース名を生成するので、プリロードしても無意味なブラウザ以外からのアクセスだと困るんじゃないかと思う。試してないけど。どのみち、リソース名を書き換えてブラウザ側のキャッシュを利用しようとする強引な手法なので、例えば携帯ブラウザみたいな拡張子に意味のあるシステムだと使えないだろう。

というわけで、困った存在のmod-pagespeedを本格的に消しました、

mod_pagespeed

Google謹製mod_pagespeedを入れてみた。それだけ。

MacBook Pro

MacBookのバッテリーが突然妊娠してしまい、バッテリーを外しても突然電源が落ちるようになってしまったので、思い切ってMacBook Proを購入した。奇妙なことに、MacBook Airの新型モデルが発売したその日の出来事である。

さすがに処理は体感できるレベルで速いし、特に文句もないのだが、持ち運び中に手元で開発できるようにApacheを設定していたらsegmentaion faultで落ちまくる。調べていて途中で面倒くさくなってgdbでプロセスをアタッチしてみたらよけいに面倒くさくなり困ったが、結局犯人は

(1)WebDAV関連モジュール

(2)PHPの設定ファイル

だった。(1)は使わないので単純にLoadModuleしないようにして、(2)は/etc/php.ini.defaultを/etc/php.iniに変更して保存した。妙なものだ。

変なCSRF対策

au端末のみアクセス可能なサイトでクロスサイトリクエストフォージェリー対策が必要になったのだが、ワンタイムトークンとかのよくある手段だとおもしろくないので、何かないかと考えてみた。

auの端末からのリクエストには、端末の契約情報に紐づいた一意のID(サブスクライバID)がリクエストヘッダとして送信される。そこで、こんなのを作ってみた。

(1)リモートアドレスでauのデートウェイからのリクエスト以外は弾く
(2)POSTリクエストを受け付けるURIはサブスクライバIDで異なる

まあ(1)はよくあることなのでいいとして、(2)はこんな感じになっている。公開するURLは「http://example.com/form」とかいう形式になっている。au端末からそのURLへのリクエストがあると、サーバ側で「https://example.com/xxxxxxxxxx/」にリダイレクトする。この「xxxxxxxxxx」はサブスクライバIDとサーバ側に保持している特定のsaltとなる文字列(例えば創世記の全文)から生成されたハッシュ値になっている。リクエストしてきた端末のサブスクライバIDから生成したハッシュ値とURIのハッシュ値が一致すればリクエストを通して、そうでなければ弾く。リダイレクト処理が入るので「http://example.com/form」へのPOSTリクエストはリクエストボディーの内容が消えたGETのリクエストになってしまうから、もし第三者がこのURLに対してCSRFを仕掛けてきても無効化される。ハッシュ値付きの正しいURLにPOSTリクエストを投げるようにしたくても、端末が送信するサブスクライバIDとsalt値とハッシュ値の生成ルールが分からなければ犠牲者の端末専用のURLが推測できないため攻撃用のフォームを作ることができない。

あとは.htaccessなどで存在しないディレクトリへのアクセスをリライトしてしまえばおしまい。

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

とかいう感じ。いやっほう。

あんまり見たことないやり方なので破り方があるかどうかちゃんと検証できてない気がするけど、思いつかないので実戦投入した。

携帯サイトのセキュリティ対策(ざっくり)

携帯サイト構築で忘れがちなセキュリティ対策を記録しておく。もちろん、外部に公開されているサーバで余計なポートが開放されていたり要らないサービスが起動していたりするのは論外。

SSL

SSLを利用している場合、基本的にSSLv3とTLS1以外を利用することはないので切っておく。特にNull暗号をオフにしておく必要がある。

Apacheだとこんな感じ。

SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:!LOW:!SSLv2:!EXP:!eNULL:!aNULL

Poundだとこんな感じ。

ListenHTTPS
  Ciphers "ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:!LOW:!SSLv2:!EXP:!eNULL:!aNULL"

Apache

リクエストヘッダのHost部分を書き換えられることを前提とした設定をしていないケースが多いと思われる。VirtualHostの先頭にデフォルトの設定を入れて対応する。mod_rewriteは入っているものとみなす。

Apacheのバージョンが低い場合のTRACEメソッドの禁止も追加。


  DocumentRoot /var/www/html
  ServerName default.host.name
  RewriteEngine on
  RewriteCond %{REQUEST_URI} !^/server-status
  RewriteRule .* - [F]
  #TRACEメソッドの禁止
  RewriteCond %{REQUEST_METHOD} ^TRACE
  RewriteRule .* - [F]

Aliasで指定されたiconsやmanualは不要なので削除。

#Alias /icons/ "/var/www/icons/"
#   
#
以下このディレクティブの最後まで。

PHP

基本的な設定はされているものとして、忘れがちなのをいくつか。php.iniの場合は

バージョン情報を晒さない。

expose_php = Off

リモートファイルをスクリプトとして読み込ませない。

allow_url_fopen = Off

リモートファイルをスクリプトとして読み込ませない。

display_errors = Off

続く、かも。

HadoopのnamespaceIDでエラー

Hadoopでときどき「Incompatible namespaceIDs in /tmp/hadoop-user/dfs/data」みたいなエラーが出る。既知の問題らしく、namespaceのIDを記述したファイルを見ても同じだったりするので困るが、潔く

$ $HADOOP_HOME/bin/stop-all.sh
$ rm -rf /tmp/hadoop-user/* ←パスは設定ファイルに依存、間違えたら知らないよ
$ $HADOOP_HOME/bin/hadoop namenode -format
$ $HADOOP_HOME/bin/start-all.sh

で再フォーマットすると直る。

なかなか便利なトラブルシューティング集を見つけた。

ハードウェア変更

この数日、サーバの負荷が急に増えたのでハードウェアを変更しました。これまでのマシンをプロキシにして、バックエンドにもう少しスペックの高いマシンを置いてコンテンツをそちらに移設しています。

新しいマシンはHPのProliant ML115 G5、AMD Phenom IIプロセッサにメモリ8GB、HDDに至っては合計2.5TB、さらにSSDが32GBという構成にESXiで3つのOSが動作しています。これまでのHPのデスクトップ用Celeronマシンとはかなり違いますが、こちらも安定して動いてくれることを願います。

猫大好きです。今日も二人で過ごしています。

TRACEなどのリクエストメソッドの禁止

ほぼ同じ内容のエントリを見つけたけど、よく忘れるのでここにメモしておく。

最近の傾向として、ウェブアプリケーションの導入前にセキュリティ審査が入ることが多くなったのだが、その際にApacheのバージョンが2.0.55より前の場合は

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK|OPTIONS)
    RewriteRule .* - [F]
</IfModule>

こんな感じでmod_rewriteを使ってTRACE(とかTRACKとかOPTIONS)メソッドを蹴るようにしないといけない。まあ、TRACKはIISでサポートされているけどApacheにはない(RFC2616のHTTP1.1の仕様にもない)のでいいんだけれども。

Apacheが2.0.55以降だと

TraceEnable Off

で済んでしまう。

CouchDBによる次世代データストレージ

CouchDBによる次世代データストレージ」と題された文章があったので以下に訳出。著者はCouchDBの開発者

原文はこちら

『CouchDBによる次世代ストレージ』

コンピュータの世界は常に、速く、進化している。しかし、データを保存するやり方は数十年も(!)前に作り出されたものだ。現在のニーズに適応するために、古いシステムに対するワイルドなソリューションがなされてきた。このような実践はハッキングと呼ばれている。このようなソリューションは多くの人々にとって有用であるが、その一方で、根本的な欠陥が行き渡っている。つまり、それらはハッキングであり、システムの設計と実装の根本となるアイデアに背を向けているのだ。

CouchDBは現代のデータストレージに必要とされるものを満たすよう設計されている。サポートするべきレガシーは存在せず、ハッキングの必要なしに現在の、そして未来のシステムで動作することができる。もし「The four pillars of data management」にあるようにデータを保存、閲覧、安全化、共有する方法をお探しなら、CouchDBはその全てを備えている。つまり

保存…ローバストでACID対応のストレージエンジン

閲覧…データを効果的にフィルター、フォーマット、体系化するViewエンジン

共有…効率のいい、インクリメンタルで双方向的なレプリケーション

セキュリティ…分散セキュリティとバリデーションモデル

お手元のデータベースでは上のどれだけがサポートされているだろうか。

CouchDBはマルチコアCPUの複数台構成による分散処理を直接サポートしている。しかもデフォルトで。これからそれをご覧に入れよう。

いつ?

2007年6月12日、チューリッヒのWebtuesdayで。そのときお会いしましょう。

これだけ読んでもわかったようなわからないような感じなのでもうちょっと追いかけてみる。