mod_rewriteで直リンク対策を簡単にする

外部から勝手にリンクされて困ってしまう場合、特に画像や動画ファイルなど比較的重たいファイルを勝手にリンクされてしまうと帯域を無駄に使ってしまい、困ってしまうことがあると思います。
そこでリファラを使って、直リンクされている場合に403を返すようにmod_rewriteを使ってみます。
今回はそれを簡単にするために、直リンクを許可するドメインをリスト化し、RewriteMapを使って処理したいと思います。

RewriteMapについては下記の記事でも取り上げています。
Apacheのmod_rewriteを使ってフェイルオーバー? – フタなしカンヅメ

Apacheの設定ファイルは以下のようにします。

<IfModule mod_rewrite.c>
RewriteEngine On
# 必ずロックファイルを指定
RewriteLock /tmp/map.lock
# RewriteMapで使う外部プログラムを指定
RewriteMap referercheck-map prg:/usr/local/bin/referer_check.rb
</IfModule>
<Directory />
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} \.(gif|png|jpg|jpeg|bmp)$ [NC]
RewriteCond ${referercheck-map:%{HTTP_REFERER}|NG} ^NG$
RewriteRule ^.*$ / [L,F]
</Directory>

許可ドメインリストを読み取って処理する「referer_check.rb」の中身です。

#!/usr/bin/env ruby
$stdin.sync = 1
$stdout.sync = 1
require 'uri'
CHECK_OK = 'OK'
CHECK_NG = 'NG'
list_file = '/usr/local/etc/referer_list'
referer_list = []
File::open(list_file) do |f|
while line = f.gets
referer_list << line.chomp if line.chomp[0, 1] != "#"
end
end
while true
flag = nil
buffer = $stdin.gets
referer = URI.encode(buffer.strip)
if referer.empty?
flag = true
else
begin
uri = URI.parse(referer)
flag = referer_list.any? {|v| uri.host.include?(v)} if uri.host
rescue Exception
#
end
end
$stdout.puts(flag ? CHECK_OK : CHECK_NG)
end

referer_check.rbには実行権限が必要ですので注意してください。リストファイルは、一行ずつ許可ドメイン(ホスト)を記述します。

example.com
example.co.jp

許可するドメインが増えたら、リストを更新するだけで対応できるので、ちょっと簡単?になった気がします。今回は許可ドメインということでしたが、同じように拒否ドメインリストで対応ってことも可能だと思います。

LighttpdのFD_SETSIZE【続】

以前の記事で以下のようなエラーがでるということを書きましたが、ちょっと嘘ついてたみたいというか、適当すぎたのでよく調べてみました。

2009-08-31 14:05:37: (server.c.1337) [note] sockets enabled again
2009-08-31 14:07:16: (server.c.1383) [note] sockets disabled, connection limit reached

FD_SETSIZEを変更してビルドし直したのですが、またエラーが出るようになりました。色々調べていると、server.max-connectionsなるものがあるらしい。こいつにはまったく手をつけていませんでした。以下のように設定を変更。

# max-fdsは、max-connectionsの2倍以上の値が必要
server.max-fds = 8192
server.max-connections = 4096

max-connectionsは、未設定の場合、(max-fds / 3)の値が適用されるようです。ということは1365くらいになってたってことか。

それでよくよくソースコードを見てみると、今回の場合は、そもそもがFD_SETSIZEを変更してビルドする必要はなかったようです。
event-handlerという設定があるのですが、こちらはデフォルトで「0」が設定されているようなので、以下の条件に一致します。

        if (srv->srvconf.event_handler->used == 0) {
/* choose a good default
*
* the event_handler list is sorted by 'goodness'
* taking the first available should be the best solution
*/
srv->event_handler = event_handlers[0].et;
if (FDEVENT_HANDLER_UNSET == srv->event_handler) {
log_error_write(srv, __FILE__, __LINE__, "s",
"sorry, there is no event handler for this system");
return -1;
}
} else {

event_handlersの最初の値が使われるようです。

        struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] =
{
/* - poll is most reliable
* - select works everywhere
* - linux-* are experimental
*/
#ifdef USE_POLL
{ FDEVENT_HANDLER_POLL,           "poll" },
#endif
#ifdef USE_SELECT
{ FDEVENT_HANDLER_SELECT,         "select" },
#endif
#ifdef USE_LINUX_EPOLL
{ FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" },
#endif
#ifdef USE_LINUX_SIGIO
{ FDEVENT_HANDLER_LINUX_RTSIG,    "linux-rtsig" },
#endif
#ifdef USE_SOLARIS_DEVPOLL
{ FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
#endif
#ifdef USE_FREEBSD_KQUEUE
{ FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
{ FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue" },
#endif
{ FDEVENT_HANDLER_UNSET,          NULL }
}; 

Linux環境だったので、poll、select、linux-sysepoll、linux-rtsigから選択できる感じなのでしょうか?デフォルトでは、pollが使用されるようですが、カーネルが2.6以上の場合は、epollを使うと効率的らしいです。
Server.event-handlerDetails – Lighttpd – lighty labs
ということは、デフォルトでpollを使っているならば、初期値は以下の4096が適用される感じになるはず。

        if (srv->event_handler == FDEVENT_HANDLER_SELECT) {
/* select limits itself
*
* as it is a hard limit and will lead to a segfault we add some safety
* */
srv->max_fds = FD_SETSIZE - 200;
} else {
srv->max_fds = 4096;
}

さらに、max-fdsを設定しているならば、以下のように設定された値がそのまま使われるようです。

                if (use_rlimit && srv->srvconf.max_fds) {
/* set rlimits */
rlim.rlim_cur = srv->srvconf.max_fds;
rlim.rlim_max = srv->srvconf.max_fds;
if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) {
log_error_write(srv, __FILE__, __LINE__,
"ss", "couldn't set 'max filedescriptors'",
strerror(errno));
return -1;
}
}
if (srv->event_handler == FDEVENT_HANDLER_SELECT) {
srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200;
} else {
srv->max_fds = rlim.rlim_cur;
}

ということなので、event-handlerを特に設定していない状態で、selectを指定していないのであれば、FD_SETSIZEを変更して、ビルドし直す必要はなかった・・・でした。
あとは、max-workerなるものもあるようで、負荷が増えてきたらこいつも設定してあげるといいのじゃろか。

LighttpdのFD_SETSIZE

Lighttpdを利用していて、エラーログに以下のようなエラーメッセージが表示されるようになりました。

2009-08-31 14:05:37: (server.c.1337) [note] sockets enabled again
2009-08-31 14:07:16: (server.c.1383) [note] sockets disabled, connection limit reached

調べてみるとFD_SETSIZEを変更してビルドし直せばいいらしい。ちなみにFD_SETSIZEとは、ファイルディスクリプタのデフォルトサイズで、ファイルを入出力したりソケットを開いたりすると増えていって、この設定したサイズを上回るとエラーになるようです。

以下のようにビルドし直して、様子を見てみることにしました。

$ CFLAGS="-DFD_SETSIZE=4096" ./configure && make
# make install

参考
高負荷、大量アクセスなサイトで Lighttpd を使う場合の注意点 :: drk7jp

続編記事
LighttpdのFD_SETSIZE【続】 – フタなしカンヅメ

Apache2でPassengerを使ってみる

Apache2のモジュールでお手軽にRailsを動かせるというPassengerをインストールしてみることにしました。そこでちょっとハマった。

Apacheは最初からインストール済みで、まずはRubyGemsを入れるところから始めました。yumで入るかと思ったのですが、初期の設定では無理なようでソースから入れました。(リポジトリを追加したりすればできそう)

次にgemでPassengerをインストール。

# gem install passenger

あとは、rootになってpassenger-install-apache2-moduleを実行するだけでOKだそうです。しかしApacheが見つからないというエラー。どうやら/usr/local/apache2にインストールしていたせいみたい。
ドキュメントには、export APXS2=/usr/local/apache2/bin/apxsを実行すれば大丈夫と書いてありました。やってみるとApacheのディレクトリをちゃんと見てくれるようになりました。
しかし、途中のビルドで以下のようなエラーが・・・

(in /usr/lib/ruby/gems/1.8/gems/passenger-2.2.4)
** Invoke apache2 (first_time)
** Invoke ext/apache2/mod_passenger.so (first_time)
** Invoke ext/apache2/libpassenger_common.a (first_time, not_needed)
** Invoke ext/apache2/libpassenger_common/Utils.o (first_time, not_needed)
** Invoke ext/common/Utils.cpp (first_time, not_needed)
** Invoke ext/common/Utils.h (first_time, not_needed)
** Invoke ext/apache2/libpassenger_common/Logging.o (first_time, not_needed)
** Invoke ext/common/Logging.cpp (first_time, not_needed)
** Invoke ext/common/Logging.h (first_time, not_needed)
** Invoke ext/apache2/libpassenger_common/SystemTime.o (first_time, not_needed)
** Invoke ext/common/SystemTime.cpp (first_time, not_needed)
** Invoke ext/common/SystemTime.h (first_time, not_needed)
** Invoke ext/apache2/libpassenger_common/CachedFileStat.o (first_time, not_needed)
** Invoke ext/common/CachedFileStat.cpp (first_time, not_needed)
** Invoke ext/common/CachedFileStat.h (first_time, not_needed)
** Invoke ext/apache2/libpassenger_common/Base64.o (first_time, not_needed)
** Invoke ext/common/Base64.cpp (first_time, not_needed)
** Invoke ext/common/Base64.h (first_time, not_needed)
** Invoke ext/apache2/ApplicationPoolServerExecutable (first_time, not_needed)
** Invoke ext/common/ApplicationPoolServerExecutable.cpp (first_time, not_needed)
** Invoke ext/common/ApplicationPool.h (first_time, not_needed)
** Invoke ext/common/Application.h (first_time, not_needed)
** Invoke ext/common/StandardApplicationPool.h (first_time, not_needed)
** Invoke ext/common/ApplicationPoolStatusReporter.h (first_time, not_needed)
** Invoke ext/common/MessageChannel.h (first_time, not_needed)
** Invoke ext/common/SpawnManager.h (first_time, not_needed)
** Invoke ext/common/PoolOptions.h (first_time, not_needed)
** Invoke ext/common/StringListCreator.h (first_time, not_needed)
** Invoke ext/common/FileChangeChecker.h (first_time, not_needed)
** Invoke ext/common/SystemTime.h (not_needed)
** Invoke ext/common/CachedFileStat.hpp (first_time, not_needed)
** Invoke ext/apache2/libboost_oxt.a (first_time, not_needed)
** Invoke ext/apache2/libboost_oxt/boost/once.o (first_time, not_needed)
** Invoke ext/boost/src/pthread/once.cpp (first_time, not_needed)
** Invoke ext/apache2/libboost_oxt/boost/exceptions.o (first_time, not_needed)
** Invoke ext/boost/src/pthread/exceptions.cpp (first_time, not_needed)
** Invoke ext/apache2/libboost_oxt/boost/thread.o (first_time, not_needed)
** Invoke ext/boost/src/pthread/thread.cpp (first_time, not_needed)
** Invoke ext/apache2/libboost_oxt/oxt/system_calls.o (first_time, not_needed)
** Invoke ext/oxt/system_calls.cpp (first_time, not_needed)
** Invoke ext/oxt/thread.hpp (first_time, not_needed)
** Invoke ext/oxt/spin_lock.hpp (first_time, not_needed)
** Invoke ext/oxt/backtrace.hpp (first_time, not_needed)
** Invoke ext/oxt/system_calls.hpp (first_time, not_needed)
** Invoke ext/oxt/macros.hpp (first_time, not_needed)
** Invoke ext/oxt/tracable_exception.hpp (first_time, not_needed)
** Invoke ext/oxt/detail/tracable_exception_disabled.hpp (first_time, not_needed)
** Invoke ext/oxt/detail/spin_lock_pthreads.hpp (first_time, not_needed)
** Invoke ext/oxt/detail/tracable_exception_enabled.hpp (first_time, not_needed)
** Invoke ext/oxt/detail/spin_lock_portable.hpp (first_time, not_needed)
** Invoke ext/oxt/detail/backtrace_disabled.hpp (first_time, not_needed)
** Invoke ext/oxt/detail/backtrace_enabled.hpp (first_time, not_needed)
** Invoke ext/oxt/detail/spin_lock_gcc_x86.hpp (first_time, not_needed)
** Invoke ext/apache2/libboost_oxt/oxt/tracable_exception.o (first_time, not_needed)
** Invoke ext/oxt/tracable_exception.cpp (first_time, not_needed)
** Invoke ext/oxt/thread.hpp (not_needed)
** Invoke ext/oxt/spin_lock.hpp (not_needed)
** Invoke ext/oxt/backtrace.hpp (not_needed)
** Invoke ext/oxt/system_calls.hpp (not_needed)
** Invoke ext/oxt/macros.hpp (not_needed)
** Invoke ext/oxt/tracable_exception.hpp (not_needed)
** Invoke ext/oxt/detail/tracable_exception_disabled.hpp (not_needed)
** Invoke ext/oxt/detail/spin_lock_pthreads.hpp (not_needed)
** Invoke ext/oxt/detail/tracable_exception_enabled.hpp (not_needed)
** Invoke ext/oxt/detail/spin_lock_portable.hpp (not_needed)
** Invoke ext/oxt/detail/backtrace_disabled.hpp (not_needed)
** Invoke ext/oxt/detail/backtrace_enabled.hpp (not_needed)
** Invoke ext/oxt/detail/spin_lock_gcc_x86.hpp (not_needed)
** Invoke ext/apache2/libboost_oxt/oxt/backtrace.o (first_time, not_needed)
** Invoke ext/oxt/backtrace.cpp (first_time, not_needed)
** Invoke ext/oxt/thread.hpp (not_needed)
** Invoke ext/oxt/spin_lock.hpp (not_needed)
** Invoke ext/oxt/backtrace.hpp (not_needed)
** Invoke ext/oxt/system_calls.hpp (not_needed)
** Invoke ext/oxt/macros.hpp (not_needed)
** Invoke ext/oxt/tracable_exception.hpp (not_needed)
** Invoke ext/oxt/detail/tracable_exception_disabled.hpp (not_needed)
** Invoke ext/oxt/detail/spin_lock_pthreads.hpp (not_needed)
** Invoke ext/oxt/detail/tracable_exception_enabled.hpp (not_needed)
** Invoke ext/oxt/detail/spin_lock_portable.hpp (not_needed)
** Invoke ext/oxt/detail/backtrace_disabled.hpp (not_needed)
** Invoke ext/oxt/detail/backtrace_enabled.hpp (not_needed)
** Invoke ext/oxt/detail/spin_lock_gcc_x86.hpp (not_needed)
** Invoke ext/apache2/libboost_oxt/oxt/thread.o (first_time, not_needed)
** Invoke ext/oxt/thread.cpp (first_time, not_needed)
** Invoke ext/oxt/thread.hpp (not_needed)
** Invoke ext/oxt/spin_lock.hpp (not_needed)
** Invoke ext/oxt/backtrace.hpp (not_needed)
** Invoke ext/oxt/system_calls.hpp (not_needed)
** Invoke ext/oxt/macros.hpp (not_needed)
** Invoke ext/oxt/tracable_exception.hpp (not_needed)
** Invoke ext/oxt/detail/tracable_exception_disabled.hpp (not_needed)
** Invoke ext/oxt/detail/spin_lock_pthreads.hpp (not_needed)
** Invoke ext/oxt/detail/tracable_exception_enabled.hpp (not_needed)
** Invoke ext/oxt/detail/spin_lock_portable.hpp (not_needed)
** Invoke ext/oxt/detail/backtrace_disabled.hpp (not_needed)
** Invoke ext/oxt/detail/backtrace_enabled.hpp (not_needed)
** Invoke ext/oxt/detail/spin_lock_gcc_x86.hpp (not_needed)
** Invoke ext/apache2/libpassenger_common.a (not_needed)
** Invoke ext/apache2/libboost_oxt.a (not_needed)
** Invoke ext/apache2/mod_passenger.o (first_time)
** Invoke ext/apache2/mod_passenger.c (first_time, not_needed)
** Execute ext/apache2/mod_passenger.o
gcc -Iext -Iext/common -fPIC -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE -I/usr/include/apr-1 -I/usr/include/apr-1 -I/usr/local/apache2/include -D_REENTRANT -I/usr/local/include -Wall -g -DPASSENGER_DEBUG -DBOOST_DISABLE_ASSERTS -o ext/apache2/mod_passenger.o -c ext/apache2/mod_passenger.c
** Invoke ext/apache2/Configuration.o (first_time)
** Invoke ext/apache2/Configuration.cpp (first_time, not_needed)
** Invoke ext/apache2/Configuration.h (first_time, not_needed)
** Execute ext/apache2/Configuration.o
g++ -Iext -Iext/common -fPIC -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE -I/usr/include/apr-1 -I/usr/include/apr-1 -I/usr/local/apache2/include -D_REENTRANT -I/usr/local/include -Wall -g -DPASSENGER_DEBUG -DBOOST_DISABLE_ASSERTS -o ext/apache2/Configuration.o -c ext/apache2/Configuration.cpp
/usr/local/apache2/include/apr_file_info.h:192: error: ‘apr_ino_t’ does not name a type
rake aborted!
Command failed with status (1): [g++ -Iext -Iext/common -fPIC -DLINUX=2 -D_...]
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:995:in `sh'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:1010:in `call'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:1010:in `sh'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:1094:in `sh'
/usr/lib/ruby/gems/1.8/gems/passenger-2.2.4/misc/rake/cplusplus.rb:31:in `compile_cxx'
/usr/lib/ruby/gems/1.8/gems/passenger-2.2.4/Rakefile:310
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `call'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `execute'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `each'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `execute'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:597:in `invoke_with_call_chain'
/usr/lib/ruby/1.8/monitor.rb:238:in `synchronize'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:590:in `invoke_with_call_chain'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:607:in `invoke_prerequisites'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:604:in `each'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:604:in `invoke_prerequisites'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:596:in `invoke_with_call_chain'
/usr/lib/ruby/1.8/monitor.rb:238:in `synchronize'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:590:in `invoke_with_call_chain'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:607:in `invoke_prerequisites'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:604:in `each'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:604:in `invoke_prerequisites'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:596:in `invoke_with_call_chain'
/usr/lib/ruby/1.8/monitor.rb:238:in `synchronize'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:590:in `invoke_with_call_chain'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:583:in `invoke'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2051:in `invoke_task'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `each'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2023:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2001:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:1998:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/bin/rake:31
/usr/bin/rake:19:in `load'
/usr/bin/rake:19

どうやらもう一つ環境変数として、APR_CONFIGも書き出しておかないといけないみたい。

# export APR_CONFIG=/usr/local/apache2/bin/apr-1-config

これで無事にインストール完了しました。

参考
404 Not Found

CentOSでyum update

yum updateでパッケージを更新しようとしたところ、ドバドバっと色々表示されて、挙げ句にエラーが出ました。こんなに更新してないはずは・・・。

--> Processing Dependency: /usr/lib/python2.4 for package: gamin-python
--> Processing Dependency: /usr/lib/python2.4 for package: libxslt-python
--> Processing Dependency: /usr/lib/python2.4 for package: libxml2-python
--> Finished Dependency Resolution
gamin-python-0.1.7-8.el5.i386 from installed has depsolving problems
--> Missing Dependency: /usr/lib/python2.4 is needed by package gamin-python-0.1.7-8.el5.i386 (installed)
libxslt-python-1.1.17-2.el5_2.2.i386 from installed has depsolving problems
--> Missing Dependency: /usr/lib/python2.4 is needed by package libxslt-python-1.1.17-2.el5_2.2.i386 (installed)
libxml2-python-2.6.26-2.1.2.8.i386 from updates has depsolving problems
--> Missing Dependency: /usr/lib/python2.4 is needed by package libxml2-python-2.6.26-2.1.2.8.i386 (updates)
Error: Missing Dependency: /usr/lib/python2.4 is needed by package gamin-python-0.1.7-8.el5.i386 (installed)
Error: Missing Dependency: /usr/lib/python2.4 is needed by package libxslt-python-1.1.17-2.el5_2.2.i386 (installed)
Error: Missing Dependency: /usr/lib/python2.4 is needed by package libxml2-python-2.6.26-2.1.2.8.i386 (updates)

いったん全てクリアしてみることに。

# yum clean all
# yum update

すごい大量に更新パッケージが表示されました。いったい何が起きたのか。とりあえず、そのまま更新して問題ないようです。

参考
Linux備忘録&ちょっと休憩しましょ:rpmforgeをセットしなおしたら – livedoor Blog(ブログ)

Awstatsでlogresolvemerge.plを使う

Awstatsで複数のログファイルをマージするために、付属のlogresolvemerge.plを使っていましたが、ある時を境にエラーが出るようになりました。
記録するのを忘れてしまったのですが、以下のようなエラーだったと思います。

Error Command for pipe logresolvemerge.pl

logresolvemerge.plを直接実行してみたところ、「ファイルを開きすぎです」と出てきました。そんなにファイル開いてるのか?と思ったのですが、開くファイル数や現在開いているファイル数などは、以下のようにして調べることができるようです。

# cat /proc/sys/fs/file-max
366343
# cat /proc/sys/fs/file-nr
2112	0	366343

file-maxの数字を書き換えることで、上限を変更できるそうです。

書き換えたりしたものの改善せず、よく考えているとlogresolvemerge.plには以下のようにして指定していました。

LogFile="/opt/awstats/tools/logresolvemerge.pl /home/www/logs/www.* |"

これだと毎回全てのログファイルを読みにいってしまいますので、以下のように変更してみました。

LogFile="find /home/www/logs/www.* -mtime -1 -type f -print | xargs /opt/awstats/tools/logresolvemerge.pl $1 |"

参考
404 Not Found

iptablesで20と21番ポートを空けたのに繋がらない

この前iptablesを設定したときに、FTPのポートも空けたはずなのですが、Passive(パッシブ)モードで繋がりません。繋がらないというか、LISTコマンドとかでリストを表示しようとすると固まるといった感じです。

## こんな感じで固まる
ftp> ls
229 Entering Extended Passive Mode (|||11524|)

どうやらiptablesで必要なモジュールを読み込んでおく必要があるようです。

# modprobe ip_conntrack_ftp
# modprobe ip_nat_ftp

これで解決しました。自動で読み込むように以下のようにiptablesの設定を変更。

## /etc/sysconfig/iptables-configのファイルを編集
IPTABLES_MODULES="ip_conntrack_netbios_ns ip_conntrack_ftp ip_nat_ftp"

ip_conntrack_netbios_nsは、最初から書いてありましたが、はずしてもよかったかもしれないです。

参考
[linux] iptablesでftpを通す – Memorandum

Apacheのmod_rewriteを使ってフェイルオーバー?

Apachemod_rewriteを使って、フェイルオーバーみたいなことができないかと思い、ちょっとやってみることにしました。

フェイルオーバーといったら大げさな感じですが、もうちょっと本格的にやるならmod_proxy_balancerを使ったほうがいいと思うので。。。

現在下記のような構成で運用しているサーバがありますが、静的ファイルを配信するサーバBが落ちている場合やオフになっている場合などは、サーバBから配信したいという感じです。

サーバA -> CGIの処理や比較的軽いHTMLを処理
サーバB -> 主に画像などの重たい静的ファイルを処理

かなり限定的な環境だと思うのですが、サーバAで一度リクエストを受け、画像ファイルだけをリダイレクトでサーバBに転送しています。
なので、サーバBが落ちていたりした場合、サーバBに転送せずにサーバAで返却したいというわけです。サーバAでも同じデータを持っている必要があります。

調べたところRewriteMapを使うことで外部プログラムを呼び出すことができるようです。今回はこれを使ってみます。
サーバが稼働しているか定期的にヘルスチェックをする必要がありますが、今回はサーバAにリクエストされた際に、外部プログラムを通して5秒間隔でサーバBにHTTPリクエストをおこなってチェックしてみます。

外部プログラムはRubyを使ってみます。外部プログラムの呼び出しは起動時に一回のみおこなわれ、STDIN、STDOUTを通して処理されるとのことで、オーバーヘッドはあまり気にする必要はなさそうです。
こんな感じで作ってみました。

## /usr/local/bin/healthcheck.rb
#!/usr/bin/env ruby
# Buffered I/Oを使わない
$stdin.sync = 1
$stdout.sync = 1
require 'net/http'
STATUS_DOWN = 'DOWN'
STATUS_UP = 'UP'
STATUS_NULL = 'NULL'
CHECK_PERIOD = 5 # ヘルスチェックの間隔(秒)
HTTP_TIMEOUT = 2 # HTTPコネクションと読み込みのタイムアウト(秒)
lastcheck = 0
laststatus = STATUS_NULL
while true
buffer = $stdin.gets
hostname, port, path = buffer.chomp.split(/:/, 3)
nowcheck = Time.now.to_i
if (lastcheck + CHECK_PERIOD) > nowcheck
$stdout.puts laststatus
else
response = nil
if hostname
begin
http = Net::HTTP.new(hostname, port ? port : 80)
http.open_timeout = HTTP_TIMEOUT
http.read_timeout = HTTP_TIMEOUT
http.start {|p| response = p.head(path ? path : '/') }
rescue
#
end
end
if response and response.code.to_i == 200
$stdout.puts laststatus = STATUS_UP
else
$stdout.puts laststatus = STATUS_DOWN
end
lastcheck = nowcheck
end
end

注意:上記のスクリプトにはバグがあります。
RubyのNet::HTTPでハマる – フタなしカンヅメ

Apacheの設定ファイルは下記のように書きました。

<IfModule mod_rewrite.c>
RewriteEngine On
# 必ずロックファイルを指定
RewriteLock /tmp/map.lock
# RewriteMapで使う外部プログラムを指定
RewriteMap healthcheck-map prg:/usr/local/bin/healthcheck.rb
</IfModule>
<Directory />
RewriteEngine On
# RewriteMapを呼び出す時は下記のように指定
# RewriteRuleでも呼び出せます
RewriteCond %{REQUEST_FILENAME} \.(gif|png|jpg|jpeg|bmp)$ [NC]
RewriteCond ${healthcheck-map:192.168.1.3:80:/} ^UP$ [NC]
RewriteRule (.*) http://example.com/$2 [L,R]
</Directory>
RewriteCond ${healthcheck-map:192.168.1.3:80:/} ^UP$ [NC]
- healthcheck-map => 使用するRewriteMapの名前
- 192.168.1.3 => ヘルスチェックするホスト名またはIPアドレス
- 80 => ポート番号
- / => チェック先のリクエストパス
のように「:」で区切って呼び出します

一番ハマったのが、RewriteMapで外部プログラムを指定する部分。いろいろいじっても下記のようなエラーが・・・。

map lookup FAILED: map=healthcheck-map key=localhost:80:/

原因は、ディレクティブの中で、RewriteEngine Onを指定していなかったせいでした。そんなんあり?

参考サイト
mod_rewriteでサーバーの負荷が高いときだけリダイレクトする – inspfightmanの日記
Apache module mod_rewrite
mod_rewrite – RewriteMap – とみぞーノート
rubyスクリプトでRewriteMapを書く際の注意点 — Stack-Style
mod_rewrite:RewriteMapで柔軟なURL書き換え | ゆーすけぶろぐ

自宅サーバでセカンダリDNSを構築する

前回の記事でプライマリのDNSを構築したので、次は自宅サーバでセカンダリDNSを構築しようかと思います。その前にネームサーバを構築してみて、ちょっとアレ?と思った部分があったので、その事を書いておきます。

外部ドメインを引いてみようと思い、下記のようなコマンドを打ってみたところ帰ってきませんでした。

## digコマンドを打ってみた
$ dig @127.0.0.1 www.yahoo.co.jp

なぜだろう?と思ったところ、recursionの設定がnoになっていたせいでした。これは再帰的に問い合わせるかどうかの設定なのですが、外向けに設定されているネームサーバでは、第三者からの問い合わせにも応答してしまうため、本来はアクセス制限をしたほうがいいようです。recursionをyesに変更したところ、無事に問い合わせることができました。

セカンダリDNSを構築

セカンダリDNSもbind9を使い、構築していきます。インストールまでは前回の記事と同じです。
設定内容だけちょっとだけ異なりますがほとんど同じなので簡単です。

## /var/named/chroot/etc/named.confを編集
options {
listen-on port 53 { any; };
directory       "/var/named";
dump-file       "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
allow-query     { any; }; # 外部に公開
version "unknown";
};
logging {
channel default_debug {
file "data/named.run";
severity dynamic;
};
};
include "/etc/rndc.key";
controls {
inet 127.0.0.1 port 953
allow { 127.0.0.1; } keys { "rndckey"; };
};
view external {
match-clients { any; };
recursion no;
zone "." {
type hint;
file "named.ca";
};
zone "example.com" {
type slave; # セカンダリなのでslaveにする
file "slaves/example.com.zone"; # slavesディレクトリの下に保存
masters { 192.168.x.1; }; # プライマリDNS(マスタ)を指定
};
};

ちなみに自宅サーバだけでは心もとなかったので、Linodeの無料DNSを使ってセカンダリにすることにしました。
以上です。思ったより簡単に出来てしまいましたが管理していくのがちょっとたいへんそうですね・・・。がんばります。

lsyncdが勝手に止まってた

この前設定したlsyncdですが、いつの間にか勝手に止まってました。なぜだろう?と思いログを見てみると下記のようなエラーで止まっていた。

rsync: failed to connect to 192.168.1.2: Connection refused (111)
rsync error: error in socket IO (code 10) at clientserver.c(107) [sender=2.6.8]
Sun Aug  9 04:02:11 2009: Forked binary process returned non-zero return code: 10
Sun Aug  9 04:02:11 2009: ERROR: Initial rsync from /home/www/vhosts/ to 192.168.1.2::www failed.

どうやら転送先に接続できなくて強制終了しているみたい。転送先では正常にrsyncdは起動していました。気になったのは時間です。4時2分?ログローテートの際にrsyncdを再起動しているため偶然止まったのかな・・・?
色々調べてみると、「rsyncで転送中に転送先と通信ができなくなったときに強制終了する」ということがわかりました。しかし、一回接続出来なかったくらいで勝手に止まっても困るところです。

どんな処理しているのかソースコードを眺めていると下記の記述を発見しました。

/**
* Global Option: if true, ignore rsync errors on startup.
*                (during normal operations they have to be ignored eitherway,
*                 since rsync may also fail due e.g. the directory already
*                 beeing deleted when lsyncd wants to sync it.)
*/
int flag_stubborn = 0;

どうやらオプションで、stubbornというオプションがあるらしい。こいつを設定すれば勝手に止まるようなことがなくなるかも?試しに設定して実験してみることに。

## /usr/local/etc/lsyncd.conf.xml を編集
<settings>
# グローバルオプションなので、settings要素の間に記述する
<stubborn/> # これを追加する
</settings>

適当に1Gほどの空ファイルを作成し、lsyncdで同期中に転送先のrsyncdを止めてみました。エラーは出力されますが、強制終了することなく動いているようです。rsyncdを動かすと、再度転送が始まるのかと思いきや、転送はされませんでした。さらに適当なファイルを作成してみたところ、前回中途半端だった転送途中のファイルも含めて、しっかり転送されることを確認。転送途中のファイル転送が再開されないのがちょっと予定外でしたが、とりあえずこれで大丈夫そうです。

## ddで空のファイルを作成
$ dd if=/dev/zero of=blank bs=1024 count=1000000