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

ひかりoneギガ得プラン+RTX1200の性能?

ギガ得プランでRTX1200を使い始めて一ヶ月くらい経ちました。アクセス数やトラッフィクも徐々に増えていき、RTX1200のCPU使用率が90%を超えるようになってきました。
現在の状況は以下のような感じです。

PV => 30万〜40万
全体のHTTPリクエストの数 => 1000万
トラフィック => 80Mbps

HTTPリクエストのうち8〜9割が、300系のリダイレクトなのでこれがかなり無駄してそうです。

まずは、RTX1200の設定から見直してみることにしました。こんな感じでNAPT(静的マスカレード)を使っていました。

回線は、ひかりoneとフレッツを使っています
192.168.99.1 => HGW
192.168.100.2 => サーバ1
192.168.101.2 => サーバ2
ip route default gateway pp 1 filter 1001 gateway 192.168.99.1 filter 1002
lan type lan1 auto port-based-option=divide-network
ip vlan1 address 192.168.100.1/24
ip vlan2 address 192.168.101.1/24
ip lan3 address 192.168.99.2/24
ip lan3 nat descriptor 2
pp select 1
pp always-on on
pppoe use lan2
pppoe auto connect on
pp auth accept pap chap
pp auth myname プロバイダのID パスワード
ppp lcp mru on 1454
ppp ipcp msext on
ppp ccp type none
ip pp mtu 1454
ip pp nat descriptor 1
pp enable 1
ip filter 1001 pass 192.168.100.0/24 * * * *
ip filter 1002 pass 192.168.101.0/24 * * * *
nat descriptor address outer 1 グローバルIPアドレス
nat descriptor address inner 1 192.168.100.1-192.168.100.2
nat descriptor masquerade static 1 1 192.168.100.2 tcp www
nat descriptor masquerade static 1 2 192.168.100.2 tcp 22
nat descriptor masquerade static 1 3 192.168.100.1 udp snmp
nat descriptor masquerade static 1 4 192.168.100.2 tcp ftpdata,21
nat descriptor masquerade static 1 5 192.168.100.2 udp 10161
nat descriptor type 2 masquerade
nat descriptor address outer 2 192.168.99.2
nat descriptor address inner 2 192.168.101.2
nat descriptor masquerade static 2 1 192.168.101.2 tcp www
nat descriptor masquerade static 2 2 192.168.101.2 tcp 22
nat descriptor masquerade static 2 3 192.168.101.2 udp snmp
nat descriptor masquerade static 2 4 192.168.101.2 icmp

NAPTを使っていたのは、ローカル間で通信するのでローカルIPアドレスを振っていたほうがいいと思ったのと、必要なポートのみ解放しておいたほうが安全と思ったためです。

負荷の原因を考えてみて、NAPTの負荷が高いのではと思い、NAPTを使わない方法で考えました。
192.168.50.0/24のネットワークが増えたのは、マスカレードの設定をはずしたため、RTX1200へのSNMPに接続できなくなったためです。今回は監視サーバがあるルータと、ローカルルーティングして対応しました。

ip route default gateway pp 1 filter 1001 gateway 192.168.99.1 filter 1002
ip route 192.168.10.0/24 gateway 192.168.50.2
lan type lan1 auto port-based-option=divide-network
ip vlan1 address グローバルIPアドレス/24
ip vlan2 address 192.168.101.1/24
ip vlan5 address 192.168.50.1/24
ip vlan5 secure filter in 1101 1102
ip vlan5 secure filter out 1201 1202
ip lan3 address 192.168.99.2/24
ip lan3 nat descriptor 1
pp select 1
pp always-on on
pppoe use lan2
pppoe auto connect on
pp auth accept pap chap
pp auth myname プロバイダのID パスワード
ppp lcp mru on 1454
ppp ipcp msext on
ppp ccp type none
ip pp mtu 1454
pp enable 1
ip filter 1001 pass グローバルIPアドレス/32 * * * *
ip filter 1002 pass 192.168.101.0/24 * * * *
ip filter 1101 pass 192.168.10.2 192.168.50.1 icmp
ip filter 1102 pass 192.168.10.2 192.168.50.1 udp * snmp
ip filter 1201 pass 192.168.50.1 192.168.10.2 icmp
ip filter 1202 pass 192.168.50.1 192.168.10.2 udp snmp *
nat descriptor type 1 nat
nat descriptor address outer 1 192.168.99.2
nat descriptor address inner 1 192.168.101.2
nat descriptor static 1 1 192.168.99.2=192.168.101.2 1
syslog notice off
snmp host 192.168.10.2 public
snmp yrifppdisplayatmib2 on

サーバ2にHGWの192.168.99.0/24のネットワークのIPアドレスを直接振りたかったのですが、やり方が分からず静的NATを使うことにしました。ルーティングの設定をしてやればよかったのかな?
そもそも、RTX1200を経由させる必要もない気もします。

効果はあまり実感できなかったのですが、CPU使用率は80%くらいになりました。1〜2割くらいよくなった感じでしょうか。思ったより負荷に弱いのか、僕の設定が悪いのか・・・。

参考
異なるセグメント間のLANのルータ設定 − Master of IP Network − @IT

FAQ for YAMAHA RT Series / IP Packet Filter

RubyのNet::HTTPでハマる

前回の記事でmod_rewriteのRewriteMapを使い、なんちゃってフェイルオーバーを作ってみましたが、どうもたまにヘルスチェックをおこなうrubyのスクリプトが落ちるわけです・・・。
エラーはこんな感じ。

/usr/lib/ruby/1.8/timeout.rb:54:in `open': execution expired (Timeout::Error)
from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
from /usr/lib/ruby/1.8/timeout.rb:56:in `timeout'
from /usr/lib/ruby/1.8/timeout.rb:76:in `timeout'
from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
from /usr/lib/ruby/1.8/net/http.rb:553:in `do_start'
from /usr/lib/ruby/1.8/net/http.rb:542:in `start'
from /usr/local/etc/shells/healthcheck.rb:30

rescueで捕捉しているのにどうしてだろう?と思っていたのですが、下記のような記事を発見。どうやら今のやり方では、Timeout::Errorの例外は捕捉してくれないようです。
Net::HTTPの例外補足方法 – OVERT MEMO

さっそく以下のように修正。

      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 Exception # ここを修正
#
end

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書き換え | ゆーすけぶろぐ

新型MacBookPro(Mid2009)のメモリとHDDを交換

かれこれ3年くらいMacBookの黒を使っていたわけですが、最近すこぶる調子が悪いです。とにかく熱い、夏だから余計に気になります。中のファンも壊れて2回ほど交換しました。
そして最近でたMacBookProの13インチがずっと気になっていて、ついに買うことを決意しました。

f:id:happytar0:20090809211900j:image
新型MacBookPro 13インチ

幸いなことにソフマップで新古品を11万で買えました。ついでにHDDとメモリも交換することにしました。パーツは秋葉原で購入した、SSDPQI X25-Mとサムスンの2Gメモリ2枚です。

f:id:happytar0:20090811063631p:image
SSDPQI X25-M Intel SSDのOEMらしい
f:id:happytar0:20090811172430p:image
SamsungのDDR3 SO-DIMM 1066MHz 2GB

交換は思ったより簡単で、底のネジ10本を全てはずすだけで底の蓋がきれいにはずれます。ただし、ネジが小さいので精密ドライバーが必要になります。あと、HDDを交換する場合はT6のトルクスドライバーが必要になるので注意です。

底の蓋のはずし方
新MacBook Pro 17インチのHDD交換方法 – iPod LOVE
マニュアル(ちゃんと交換の仕方が載ってます)
http://manuals.info.apple.com/ja_JP/MacBook_Pro_13inch_Mid2009_J.pdf

f:id:happytar0:20090811063754p:image

底の蓋をはずしたところ

まずは、HDDを交換してみます。
f:id:happytar0:20090809212600j:image
HitachiのHDDがついてました
f:id:happytar0:20090809213600j:image
HDD上部についてるブラケットのネジ2本をはずすと、あっさりとHDDがとれるので、横についてるコネクタもはずします

f:id:happytar0:20090809213900j:image
HDDの横4カ所についているネジをT6のトルクスドライバー使い、はずします
f:id:happytar0:20090809214200j:image
新しいHDD(SSD)の横に付け替えたところ
f:id:happytar0:20090809214400j:image
元の位置に戻します、しっかりブラケットで固定

次にメモリを交換します。
f:id:happytar0:20090809214500j:image
メモリの側面にあるレバーを押すと、斜めに飛び出す
f:id:happytar0:20090811064000p:image
そのまま引き抜いて全部はずしたところ

f:id:happytar0:20090809214700j:image
ついていたものもSamsung製でした
f:id:happytar0:20090811064112p:image
新しいメモリを斜めに押し込みます
f:id:happytar0:20090809214800j:image
そのまま下に押すだけで終わり

無事に交換できました。付属のインストールCDでセットアップしようとしたところ、インストール先のHDDが表示されなくて焦りました。
f:id:happytar0:20090809220100j:image

ディスクユーティリティを使い、HDDをフォーマットしておく必要があるみたいです。
f:id:happytar0:20090809220200j:image
上部のメニューから起動
f:id:happytar0:20090809220500j:image
MacOS拡張(ジャーナリング)を選択、名前はMacintoshHDとした
f:id:happytar0:20090809220600j:image
無事にインストール先が表示された

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

自宅サーバでセカンダリ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を使ってセカンダリにすることにしました。
以上です。思ったより簡単に出来てしまいましたが管理していくのがちょっとたいへんそうですね・・・。がんばります。