どさにっき 2.0 〜2006年7月上旬〜

by やまや
<< = >>

2006年7月1日(土)

qmail の配信能力を極限まで引き出す方法

_ qmail のログを置くファイルシステムをディスクではなくメモリ上に置くことで配送を高速化、だそうで。が、その結果が5分で1万通って、遅くね? 単純計算で1日で300万通弱。Xeon 3GHz dual でたったのそれっぽっち? qmail ってそんなに遅かったっけ?

_ というわけで、ローカルの環境でテストしてみる。実験環境は この前とほぼ同じ。

MSP と MTA2 が同一マシンかつ coLinux といういいかげんな環境にあること、ターゲットが特定少数(MTA2 1台だけ)で不特定多数の宛先に送信しているわけでないことに注意。

_ qmail-1.03: 配送多重度(concurrencyremote=20)や tcpserver の同時接続数制限(-c40)など、設定はほぼデフォルトのまま。ログは splogger で syslog に出力。MSP → MTA1 の時間は、

MTA2% time ./smtp-source -s30 -m10000 -t postmaster@MTA2 MTA1
54.330u 3.240s 4:09.58 23.0%    0+0k 0+0io 0pf+0w
MTA1 → MTA2 は syslog によると7分34秒。うーん、ちょっと遅すぎるなぁ。MSP → MTA1 を SMTP ではなく、qmail の独自プロトコルである QMQP に変えると、
MTA2% time ./qmqp-source -s30 -m10000 -t postmaster@MTA2 MTA1
10.390u 15.770s 4:00.62 10.8%   0+0k 0+0io 0pf+0w
ありゃ、変わんね。4分10秒が4分になっただけ。MTA1 → MTA2 は7分47秒でむしろ遅くなってるが、qmail の構造からしてこれは誤差だろう。

_ qmail-1.03 ログなし: では、 ログの取りかたで高速化したというんだから、いっそすべてのログを /dev/null に捨ててみる。syslog で時間が見られないので、smtp-sink の方で10000通受信したら即座に終了する設定にして、送信開始から smtp-sink 終了までの時間を計測。結果は smtp-source 終了まで2分50秒、smtp-sink 終了までは7分50秒。MSP → qmail は明らかに速くなったけど、qmail → MTA2 は変化なし。SMTP ではなく QMQP だと MSP からは2分半で20秒短いが MTA2 への配送はやっぱり変化なし。

_ というわけで、qmail-smtpd はログを捨てると速くなるけど(tcpserver が遅いのか splogger が遅いのかは不明)、qmail-send の場合はログは律速段階になっていないので捨てても速度にはまったく影響はない、という結果に。もっと高速のマシンだと処理にログの書き込みが追いつかずにボトルネックになることは十分考えられるから、この結果だけでまったくムダとは判断できないけど、Pen3 800MHz 程度のマシンなら気にする必要なしということ。

_ 宛先ホストがひとつしかないから実際の環境とは大きく異なるけど、qmail は 800MHz チューニングなしでも7分半〜8分で1万通を送る能力はあるわけだ。3GHz dual で本気でパラメータをいじれば実環境でも5分どころかもっと短く済むんじゃないかと思うんだけどどうだろ。DNS やネットワークの状況が足をひっぱったり、その他いろんな要因がからんでぜんぜん性能が出ないことはあるけど、それがハードウェア性能に起因するものでないのならば、そんな速いマシンを投入するのはそもそも無駄、ということ。

_ つーか、ログをメモリ上のファイルシステムに吐かせるって……。そんないいかげんなことでいいのかなぁ。まあ、ログを見るつもりがない/必要がないのならそれもアリか。メルマガ配信専用など、何を送るのか事前にわかってる(仮にキューが壊れても再投入が可能な)システムならば、ログよりもむしろキューの方をメモリ上に置いた方がいいかもしれん(通常用途では厳禁)。

_ 参考までに MTA1 を変えて同じことをやってみた結果。

_ postfix-2.3-20060611: この実験では宛先がすべて同一ホストなので、ふつうに送るとコネクションキャッシュが効いてしまって公平な結果が得られない。よって smtp_connection_cache_on_demand = no、smtp_connection_cache_destinations = (空) としてキャッシュを無効にする。配送多重度(smtp_destination_concurrency_limit=20、偶然にも qmail と同じ)やその他のチューニングパラメータはデフォルトのまま。MSP → postfix にかかる時間は、

49.660u 35.050s 1:47.22 79.0%   0+0k 0+0io 0pf+0w
で、postfix → MTA2 は syslog によると3分1秒。コネクションキャッシュがなくても爆速でした。

_ sendmail-8.13.6: 設定はほとんどデフォルトのまま。MSP → sendmail は

8.780u 21.000s 27:44.94 1.7%    0+0k 0+0io 0pf+0w
で、sendmail → MTA2 も同じだけ時間がかかって27分45秒。弁護してあげたいんだけど、さすがにひと桁違うと言葉がないですな。ちなみに、実行中の load avg は常時5以下でこいつがいちばん低かった(ほかは10を越える)。

_ なお、ここまでの速度が必要なのは xSP か大企業か spammer かのいずれかなので、一般人は気にする必要はございません。つーか、重要なのは速さよりも確実さだということを忘れちゃいけない。よほど特殊な用途でもないかぎり、ログをメモリに置いて高速化なんてのはダメ。


2006年7月2日(日)

きのうのつづき

_ あああああ、そっかそういうことか。合点した。 きのうの qmail のログ取りうんちゃらの件だが、 Linux の syslogd が fsync() 呼びまくりで遅いという話に関連してるのか。なるほど、それじゃきのうの試験に FreeBSD を使ったのがそもそもおかしかったんだな。「qmail のログ出力が遅い」のではなく、「そのログを受ける syslogd が遅い」のが元凶なんだから、異なる実装の syslogd を使ってたら比較にならんわ。FreeBSD の syslogd は Linux のと違って fsync() しないから、ログ出力はほとんど負荷にならんはずだもの (*1)この考察はあくまで Linux などで使われている sysklogd の話であって、それ以外の syslogd 実装では事情は異なるということを頭に入れとかなくちゃいけない。

_ ……つーか、ならば sysklogd で fsync() をやめさせる(方法はちゃんと man に書いてある)とか、sysklogd を使わずに syslog-ng を使うとか、qmail なんだから multilog でログを取る(すでに 書いてるようだが)とかの方向に行くべきであって、メモリ上にログを置くってのはかなり間違った方向に試行錯誤してるような気がする。

_ もうひとつ、こんどは合点がいかないこと。 きのうの sendmail の結果だと、1時間で2万通ちょいしか送れないことになるんだけど、これ遅すぎないか。わしが以前動かしてた sendmail のサーバは、UltraSPARC IIe 500MHz でピーク時は1時間あたり3〜5万通ぐらい処理してまだ余裕があったような記憶があるんだが……。当時から sendmail と postfix の両方を使ってたけど、そのときの認識は、性能を比較すると断然 postfix に軍配はあがるけど10倍近い差がつくほど大きなものじゃない、てな感じだったと思う。だいぶ前のことだから正確なことは思い出せないし、環境が大きく違うので単純な比較はできないし、そもそも記憶自体が間違ってるという可能性も否定できないんだけど。


(*1): いま man を確認してみたら、FreeBSD の syslogd はカーネルログだけは fsync() するようだ。それも設定で抑制できるけど、逆に設定で kern.* 以外も fsync() するようにはできないようだ。

2006年7月3日(月)

無題

_ 暑いのに、蒸れるのに、自転車つーきん。シャツの替えは持っていったけど、足りん。次はパンツの替えも必要だ。つーか、暑いんだからやめれ。

_ で、帰りも自転車で往復 50km 弱。つかれた。←バカ

空っぽメール

_ from も to も subject も本文もまったく空っぽのメールが送られてくる話うちもたくさん来てる。たしかにフィルタリングするのはやっかい。なんかよけいな情報がくっついてくるものはそれを条件に弾けばいんだけど、なにもない、ってのを条件にするのはむずかしいんだよね。

_ 中身が空っぽだから宣伝じゃないんだろうけど、じゃあ何を目的に送っているのか。

書きかけで送信したような文面、送信先を間違えたような文面のメールを受け取ると、「間違いメールが届きましたよ」と親切心から返事してしまうことがある。迷惑メール中の「不要な場合はこちらへ」という文面を真に受けて返信してしまう人もいる。これらはいずれも、迷惑メール業者が、「生きている」、使用している人がいる有効なメールアドレスを集める手口なのである。空白メールや数字メールに対しても同じ対応をする人がいるのではないか。
と、この記事ではアドレス収集説を唱えているわけだが、ちょっと違うと思う。というか、
幸いなことに、今回の空白メールは、送信者欄(From:)が空白で、返信先などの指定もなかったので返信しようにもできなかった。
空メールを送る方にも空っぽじゃ返信しようがないことはわかってるはずでしょ。

_ わしはメールアドレス収集というより、収集したアドレスのクリーニングが目的なんじゃないかと思ってるんだがどうだろう。

_ そのサーバに存在しない宛先アドレスを RCPT で指定しても、MTA が 5xx の応答を返すとはかぎらない。yahoo は、存在しない宛先を指定すると RCPT ではなく、メッセージをすべて送り終わった後の "." でエラー応答を返すんだよね、たしか。なので、何でもいいから DATA を送らないとアドレスが存在するかどうかわからない。あるいは、aol は RCPT だけで DATA を送らずに接続を切るとアドレス収集とみなしてブラックリストに載せやがるので、それを避けるには DATA を送っておく必要がある。yahoo や aol 以外に対しても、この最小限の DATA を送った結果が空メールなのではないかと思ってるんだけど、どうだろうか。

_ qmail など存在しない宛先でも常に 250 を返すサーバを相手にする場合、アドレスが生きているか死んでいるかを調べるには、バウンスメールを解析する必要がある。つまり、何でもいいから DATA を送ってバウンスさせる必要がある。実際にそれをやってみたらバウンスしないで実際のメールボックスに入っちゃったものが空メールだ、というのも考えられるんだけど、この場合はバウンスを受け取る必要がある、つまり from を偽装できない。なので、これはちょっと弱いと思う。

_ まあ、あくまで推測でしかなく、ほんとはどんな目的なのかは送ってる当人に聞かなきゃわからんのだが。対策としては、宛先が存在するしないにかかわらず、たとえば50バイト以下のメールはすべて拒否する、とかいう設定を入口 MTA にしておけばいいと思う(MTA の後段にいる spam フィルタでそういう設定をしても意味はない)。空メールを受けとらず、プローブにすべて失敗応答を返すことで、存在するアドレスでも存在しないものとしてリストから消えてくれることが期待される(アドレスクリーニング説が正しければの話だが)。

_ もっとも、そんな設定が簡単にできる MTA なんてのはわしは知らん。Postfix はアクセスポリシー委譲の機能を使えば可能だろうが、自分でスクリプトを書かなきゃならんのでめんどくさい。Exim は非常に柔軟な acl を設定できるので、もしかしたら標準機能だけでできるかもしれん(が、あまり詳しくないので実際のところは知らん)。


2006年7月4日(火)

2.3-RC3

_ あ、FreeBSD の ports/mail/postfix-current が Postfix 2.3-RC3 になってる。とうとう milter 対応版 Postfix が ports でインストールできるようになった。めでたい。っていうか、もう RC なのか。大丈夫なのかなー。milter と TLS はまだ枯れてないような気が。それ以外は問題ないだろうけど。

_ ports からインストールする際の注意点。Postfix 2.3 では Cyrus SASL 以外に Dovecot の SASL も使えるようになってるけど、だからといって、ports のオプションで WITH_DOVECOT=true とする必要はない。WITHOUT_DOVECOT でも Dovecot の SASL はちゃんと使えるし、むしろ Cyrus SASL と共存できなくなるという副作用がある(共存して使うことはあまりないと思うけど)。

_ ちうわけで、野良ビルドだったうちの Postfix も ports のものに戻しました。

application/x-httpd-php

_ hoge.php というスクリプトに対して PATH_INFO つきでリクエストすると hoge.php/fuga/piyo という URL になるわけだが、これかっちょわるいよねー、hoge/fuga/piyo にしたいよねー、という要望を見ることがある。

_ で、それに対するよくある答。

  1. mod_negotiation をロードして、
  2. AddType application/x-httpd-php .php と設定して、
  3. Options +MultiViews と設定すると、
拡張子の .php なしでアクセスしても mod_negotiation がよきにはからってくれるから hoge/fuga/piyo で使えるよっ。

_ 間違いです。

_ こういう設定をした場合、クライアントが Accept: text/* のようなリクエストをした場合(application/* が含まれない場合)、406 Not Acceptable というエラーになる。w3m ユーザに見せるつもりはないのね。406 の場合はエラーページに他のバリアントが表示されるけど、そこにも text/html ではなく application/x-httpd-php という疑似タイプを持つとして表示されてしまう。つーか、MultiViews は拡張子なしでアクセスさせるための機能じゃないよ。選択可能な複数のリソースをひとつの URL で提供するときに使うものであって、選択の余地なくひとつの hoge.php しかない場合に使うべきもんじゃありません。

_ 正しくは、

  1. hoge.php ではなく実際に hoge というファイル名にしてしまって、
  2. httpd.conf なり .htaccess なりで
    <Files hoge>
        SetHandler php-script
    </Files>
    
  3. mod_negotiation は不要
とする。

_ 逆に、こういうアホ設定のために 406 のエラーを受け取ってしまった w3m ユーザのみなさまは、~/.w3m/config で accept_media に application/* を追加しておきましょう。そんなところにアクセスできなくてもいいや、と割り切るのもアリですが。

_ application/x-httpd-php にかぎらず、application/x-httpd-cgi とか text/x-server-parsed-html とか、過去の遺物なんだからもう捨てようよ。こいつらはあくまで疑似 MIME タイプでしかなくて、本来はそうやって扱うべきものじゃないんだから。つーか、Apache 2 の開発初期には、1.x で使われてたこいつらは 2.x では廃止するとかなんとか言ってたような気がするんだけど違ったっけ? ……えーと、 これか。


2006年7月9日(日)

それでも mod_negotiation を使いたいのならば

_ この前のつづき

_ そういえば w3cは、たとえば image/gif のファイルを将来 image/png に変更するような場合でも URI を変更しなくてもいいように、ファイルの種類をあらわす拡張子を URI に含めないことを推奨してたような気が。実際、w3c はドキュメントも画像も拡張子がついてないし。なので、 選択の余地なくひとつのうんぬんはとりあえず撤回してみる。

_ それはさておき。

_ この前の hoge.php を実際に hoge に変更してしまうという方法で Web 上の動作としてはまったく問題ないんだけど、ファイル名を見ただけじゃ何のファイルかわからん、という欠点はある。編集するにも、エディタの拡張子ごとのカスタマイズが効かないし。というわけで、application/x-httpd-hoge という擬似 MIME タイプを使わず、かつファイル名は hoge.php のままで hoge とアクセスさせる方法。

_ 誰もが思いつくのは、mod_rewrite だな。でも、ドキュメントには mod_rewrite は URL 操作のスイスアーミーナイフである、とあった (*1)。でも、ワインの栓を抜くのにアーミーナイフを使うのは美しくない。ワインオープナーが使えるのならばそっちを使うべきだ。じゃあ、エレガントにワインの栓を抜くにはどうすればいいか。

_ 406 Not Acceptable なんてエラーが出るのは、hoge という URI と hoge.php というファイルの間の関連づけに application/x-httpd-hoge などという擬似 MIME タイプを使ってるから。ちゃんと text/html(あるいは image/gif なり application/rss+xmlなり)のちゃんとしたタイプで関連づけてやればいい。そういうしくみはちゃんと用意されている。

_ まず前提として mod_negotiation をロードしておく。そして、hoge というファイルに以下のように書く。見ればわかると思うが、text/html の hoge.php を呼べ、という意味。

URI: hoge.php
Content-Type: text/html
さらに、この hoge というファイルが URI とファイルとの関連づけをおこなうファイルであることを apache に教えてやる。
<Files hoge>
    SetHandler type-map
</Files>
以上。これで、hoge/fuga/piyo へのアクセスで hoge.php が呼ばれる。この場合、Accept: に text/html がないリクエストをしてきたクライアントに 406 エラーを返すが、hoge.php がほんとに text/html を返すスクリプトなのであればそれで問題ない。application/x-httpd-hoge はネゴシエーションに使われる MIME タイプと実際の MIME タイプが異なるのが問題なのであって、一致しているのならば 406 を返すのがむしろ正しい動作といえる。hoge.php が場合によって異なる MIME タイプで結果を返すのならばこの方法はよろしくない。

_ なお、上記は mod_negotiation を使うが、Options MultiViews は不要。また、type-map ハンドラのかわりに application/x-type-map という擬似 MIME タイプもあるが、例によって 406 問題を引き起こすので使うな。

_ あるいは、以下がいちばんカンタンな解決方法かもしれない。ただし、1.3.x では不可。

Options +MultiViews
MultiViewsMatch Handlers
MultiViews は通常は登録済みの MIME タイプにしか対象としないので、application/x-httpd-hoge という擬似 MIME タイプを登録することでごまかしていたわけだが、ハンドラも探すようにしてやれば腐った MIME タイプは必要はない。ただ、これはコンテンツネゴシエーションという意味ではあまり美しい方法ではないと思う(スクリプトが自力で Accept*: の解析をするのなら話は別だが)。正しくネゴシエーションをおこなう type-map か、 まったくネゴシエーションしない方法のどちらかがおすすめ。いずれの方法を使うにしろ、application/x-httpd-hoge は下の下策。

_ なお前回も含め、例として php を使ってるけど、cgi でもまったく同じなので念のため。


(*1): 2.0.55 までのドキュメントにはそう書いてあったんだが、2.0.58、2.2.x では消えてるな。

<< = >>
やまや