Hatena::ブログ(Diary)

(ひ)メモ このページをアンテナに追加 RSSフィード

2013-06-19 (Wed)

monitor-aws-status - AWSのService Health Dashboardに異変があったらIRCで通知してくれるヤツ

AWSで障害が起こったらナルハヤで知りたいよねってことで、AWSのService Health Dashboard

RSSフィードを提供してるので、それを定期的にチェックしてIRCに通知してくれるスクリプトを AnyEvent で書きました。(要ikachan)

  • 通知を受けたいregionは、%Target_Region のコメントを外してください
  • 通知を受けたくないserviceは、%Ignore_Service に登録してください
  • 通知する IRC のチャンネル名は、load_config んところのを適宜書き換えてください
  • IRC以外への通知(メール送るとか)はちょっと書けばできるでしょう
  • Service Health Dashboard以外のフィードを監視するのもちょっと書けばできるでしょう

2013-06-13 (Thu)

Apacheでreverse proxyするときに、受けのパスに応じて異なるタイムアウト値を設定する方法

Apacheでreverse proxyするときに、バックエンドは同じなんだけど受けるパスに応じて別なタイムアウト値を設定しようとしてハマったのでそのメモです。

以下、Apache 2.2.22 でのお話です。2.4でどうなってるか、どなたかご存知でしたら教えてください><


まず思いつくのはこんな設定だと思います。

ProxyTimeout 7
ProxyPass    /3sec/  http://127.0.0.1:9999/  timeout=3
ProxyPass    /5sec/  http://127.0.0.1:9999/  timeout=5

バックエンド (127.0.0.1:9999) は、こんなのを動かしておけば十分です。

$ while true; do echo listen...; nc -l 9999; done

これでクライアントでアクセスしてみると…

$ time curl http://example.com/5sec/foo/bar >/dev/null
real    0m5.011s
user    0m0.000s
sys     0m0.000s

$ time curl http://example.com/3sec/foo/bar >/dev/null
real    0m5.010s
user    0m0.000s
sys     0m0.000s

/5sec/ の方は期待した通り 5 秒で(タイムアウトして)レスポンスが返ってきてますが、/3sec/ の方は 3 秒ではなく 5 秒かかっています。


これの原因は、Apache の proxy はバックエンドへの接続や設定を管理するのに「Worker」というモノを使っていて、Worker は ProxyPass で指定されたバックエンドの URL (スキーマからパスまでも含む) を一意キーとして管理、共用されているためです。

このへんはドキュメントにちゃんと書かれています。

つまり、/3sec/ で受けても /5sec/ で受けても同じ Worker に処理されることになり、その設定値は最後に現れたもので上書かれるので、/3sec/ でアクセスしてもそのタイムアウトは 5 秒になるわけです。


これを理解していれば、受けのパスに応じてタイムアウト値を変えるには、ProxyPassのURLを違えて別々の Worker が作らるようにすればいいことに気が付くでしょう。

例えば、以下のように片方だけ127.0.0.1localhostに変えると、

ProxyTimeout 7
ProxyPass    /3sec/  http://localhost:9999/  timeout=3
ProxyPass    /5sec/  http://127.0.0.1:9999/  timeout=5

期待したタイムアウトになります。ハッピー

$ time curl http://example.com/5sec/foo/bar >/dev/null
real    0m5.011s
user    0m0.000s
sys     0m0.000s

$ time curl http://example.com/3sec/foo/bar >/dev/null
real    0m3.009s
user    0m0.000s
sys     0m0.000s

もしもっとバリエーションを増やしたければ、

  • バックエンドが0.0.0.0 (INADDR_ANY) をlistenしているのなら、ホスト名を0.0.0.0やeth0のアドレスにする
  • バックエンドが複数ポートをlistenするようにして、ポートを違える
  • loに127.0.0.2をIP Aliasして、ホスト名を127.0.0.2にする
  • http://127.0.0.1/dummy/../ のようにしてパスを違える (バックエンド側で、パスを正規化してからディスパッチするようなロジックになってないとおかしなことになるので要注意)

といった方法があります。

ひとつ留意しておきたいのは、本質的には同じバックエンドなので、本来ならば Worker を共有すべきであり、ここで紹介した方法は深遠な理由がある場合の苦肉の策である、という点です。

さてここまでがおハマりその1です。


もうひとつのおハマりは、ProxyPass の代わりに ProxyPassMatch を使った場合です。

  ProxyTimeout    7
  ProxyPassMatch ^/3sec/(.*)$  http://localhost:9999/$1  timeout=3
  ProxyPass       /5sec/       http://127.0.0.1:9999/    timeout=5

reverse proxyの動作としては、先に見たものと同じ挙動になるはずの設定です。

が、

$ time curl http://example.com/5sec/foo/bar >/dev/null
real    0m5.011s
user    0m0.000s
sys     0m0.000s

$ time curl http://example.com/3sec/foo/bar >/dev/null
real    0m7.010s
user    0m0.000s
sys     0m0.000s

ProxyPassMatch を使った /3sec/ の方が、3 秒ではなく 7 秒かかってしまっています。

この設定のケースでは、

という一意キーを持った Worker が作られます。

mod_proxy は ProxyPass, ProxyPassMatch の設定に従って書き換えた URL と、この一意キーを比較して、適切な Worker を選出します。このへんの処理は、modules/proxy/proxy_util.c の ap_proxy_get_worker の下の方で行われています。

http://example.com/3sec/foo/bar というアクセスが来た場合、ProxyPassMatchの設定に従って http://loaclhost:9999/foo/bar に書き換えられた URL と、Workerのキー(worker->name)が比較されるのですが、先に見たように合致するはずの Worker のキーは「http://localhost:9999/$1」となっていて、「$1」がじゃまでマッチせずに終わってしまいます。

マッチする Worker がなかった場合は、デフォルトの Worker というのがいて、このデフォルト Worker が処理を引き受けることになります。そしてこのデフォルト Worker のタイムアウトの設定が ProxyTimeout 7 なので、/3sec/foo/bar へのアクセスは 3 秒ではなく 7 秒でタイムアウトして返ってきているわけです。

つまり、ProxyPassMatch でバックリファレンス($1とか)入りの URL を指定した場合、Worker は作られるがまず使われることはないということになります。


こちらからは以上です

関係あるかも

2003 | 11 | 12 |
2004 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 10 | 11 | 12 |
2005 | 01 | 02 | 03 | 05 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 12 |
2012 | 01 | 02 | 03 | 06 | 08 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 10 |
2015 | 01 | 02 | 07 | 10 |
2016 | 01 | 05 | 10 | 12 |
2017 | 07 |
2018 | 05 |