ニュース

Scribeでログの集約・収集【追記】

あとがきでログを取る際の負荷が高いということを書いたのですが、もう一つのWebサーバ(lighttpd)でもログを取るようにしたところ、あまりにもレスポンスが遅くなってしまったので、Pythonで書き直してみました。といってもサンプルファイルをちょっといじっただけ・・・。

## /usr/local/bin/scribe_httpd2
#!/usr/bin/python
import sys
from scribe import scribe
from thrift.transport import TTransport, TSocket
from thrift.protocol import TBinaryProtocol
if len(sys.argv) == 2:
category = sys.argv[1]
host = '127.0.0.1'
port = 1463
elif len(sys.argv) == 4 and sys.argv[1] == '-h':
category = sys.argv[3]
host_port = sys.argv[2].split(':')
host = host_port[0]
if len(host_port) > 1:
port = int(host_port[1])
else:
port = 1463
else:
sys.exit('usage (message is stdin): scribe_cat [-h host[:port]] category')
socket = TSocket.TSocket(host=host, port=port)
transport = TTransport.TFramedTransport(socket)
protocol = TBinaryProtocol.TBinaryProtocol(trans=transport, strictRead=False, strictWrite=False)
client = scribe.Client(iprot=protocol, oprot=protocol)
transport.open()
while 1:
message = sys.stdin.readline()
log_entry = scribe.LogEntry(dict(category=category, message=message))
result = client.Log(messages=[log_entry])
transport.close()

scribe_httpdを置き換えるだけで動作しますが、lighttpdの場合ログ用のプロセスは終了してくれないようなので自分で終了させる必要がある?ようです。たぶんスクリプトの書き方がおかしいんだと思います。ループしてるところで終了イベントを拾って処理してあげたりする必要があるのかな・・・?よくわからないので下のようにして強引に。

killall scribe_httpd2

これでだいぶレスポンスが早くなりました。

Scribeでログの集約・収集【後編】

いよいよWebサーバのログをScribeを通して処理してみます。examplesディレクトリに入っていた、scribe_catとscribe_ctrlはそのまま使えそうなのでこれを利用してみます。

# cp examples/{scribe_cat,scribe_ctrl} /usr/local/bin/

まず、ログサーバ(ログを集約するサーバ)の設定をします。このサーバに各Webサーバのログが書き込まれていきます。設定ファイルは/usr/local/etc/scribeの下に置くものとします。

# mkdir /usr/local/etc/scribe
# mkdir /var/log/scribe
# touch /usr/local/etc/scribe/scribed.conf

ログサーバの設定ファイル(scribed.conf)の内容は下記になります。

port=1463 # 待ち受けポート
max_msg_per_second=2000000 # 一秒間に受け取るメッセージ数
check_interval=3 # チェックする間隔(Store)
# Web server
<store>
category=www* # wwwではじまるカテゴリを処理
type=buffer # buffer storeを利用
target_write_size=20480 # バッファサイズ(これ以上溜まると書き出す)
max_write_interval=1 # 書き出す間隔(バッファサイズに依存せずに一定期間で処理する間隔)
buffer_send_rate=2 # 処理する間隔
retry_interval=30 # primaryに失敗してからのリトライ間隔
retry_interval_range=10 # リトライ間隔の幅(この幅でランダムに試行する)
<primary>
type=file # file storeを利用
fs_type=std # stdのみサポート
file_path=/home/www/logs # ログを書き出す場所
base_filename=access_log # ログファイル名
max_size=100000000 # ローテート最大サイズ
rotate_period=daily # ローテート間隔(daily=日ごと)
rotate_hour=0 # 何時にローテートするか(0時)
rotate_minute=5 # 何分にローテートするか(5分)
add_newlines=1 # 改行するか(1=する)
create_symlink=true # 最新ファイルへのシンボリックリンクを設定
</primary>
# primaryの書き込みに失敗した場合の予備
<secondary>
type=file
fs_type=std
file_path=/tmp
base_filename=access_log
max_size=3000000
</secondary>
</store>
# 処理されなかったメッセージの保存先
<store>
category=default # defaultにすると処理されなかったものがくる
type=buffer
target_write_size=20480
max_write_interval=1
buffer_send_rate=2
retry_interval=30
retry_interval_range=10
<primary>
type=file
fs_type=std
file_path=/var/log/scribe
base_filename=scribed.log
max_size=1000000
</primary>
<secondary>
type=file
fs_type=std
file_path=/tmp
base_filename=scribed.log
max_size=3000000
</secondary>
</store>

scribedの起動スクリプトは下記のようなものを作りました。

#!/bin/sh
#
# Scribed     Startup script for the scribe daemon
#
# chkconfig: - 83 13
# description: Scribed.
#
# processname: scribed
# config: /usr/local/etc/scribe/scribed.conf
# pidfile: /var/run/scribed.pid
#
# Source function library
. /etc/rc.d/init.d/functions
if [ -f /etc/sysconfig/scribed ]; then
. /etc/sysconfig/scribed
fi
if [ -z "$SCRIBED_CONF_PATH" ]; then
SCRIBED_CONF_PATH="/usr/local/etc/scribe/scribed.conf"
fi
prog="scribed"
lockfile="/var/lock/subsys/$prog"
scribed="/usr/local/bin/scribed"
scribe_ctrl="/usr/local/bin/scribe_ctrl"
RETVAL=0
start() {
echo -n $"Starting $prog: "
$scribed -c $SCRIBED_CONF_PATH > /dev/null 2>&1 &
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
success
touch $lockfile
fi
echo
return $RETVAL
}
stop() {
echo -n $"Stopping $prog: "
killproc $scribed
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f $lockfile
return $RETVAL
}
status() {
$scribe_ctrl status
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status
;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
RETVAL=1
esac
exit $RETVAL

次にWebサーバ(ログの送信元サーバ)の設定をします。設定ファイルの保存先や起動スクリプトなどはログサーバと同じです。今回は念のためにログをネットワーク先(ログサーバ)とローカル二つに保存することにします。

port=1463
max_msg_per_second=2000000
check_interval=3
# Web server
<store>
category=www*
type=multi # multi storeネットワークとローカルで保存
target_write_size=20480
max_write_interval=1
# ネットワーク保存
<store0>
type=network # ネットワーク転送する
remote_host=192.168.1.2 # 転送先ホスト
remote_port=1463 # 転送先ポート
</store0>
# ローカル保存
<store1>
type=file
fs_type=std
file_path=/home/www/logs
base_filename=access_log
max_size=100000000
rotate_period=daily
rotate_hour=0
rotate_minute=5
add_newlines=0
create_symlink=true
</store1>
</store>
<store>
category=default
type=buffer
target_write_size=20480
max_write_interval=1
buffer_send_rate=2
retry_interval=30
retry_interval_range=10
<primary>
type=file
fs_type=std
file_path=/var/log/scribe
base_filename=scribed.log
max_size=1000000
</primary>
<secondary>
type=file
fs_type=std
file_path=/tmp
base_filename=scribed.log
max_size=3000000
</secondary>
</store>

設定は以上です。両方のサーバでscribedが正常に起動できるか確認してください。次にWebサーバとなるApacheのログ出力をファイルから、Scribeへの出力に切り替えます。とりあえず、エラーログはそのままファイルへと出力、アクセスログをScribeに出力することにします。

パイプを使いScribeに渡すことになりますが、scribe_catにそのまま渡すだけではうまく動きませんでした。どうやらパイプを渡す先のプログラムをプロセス上にあげておく必要があるようです。(おそらくログ出力の負荷を減らすため?)
scribe_httpdというシェルプログラムを作り、その中で標準出力を受けながらループすることにしました。

## /usr/local/bin/scribe_httpd
## このシェルプログラムからscribe_catへ渡す
#!/bin/sh
while /bin/true
do read line
echo $line | /usr/local/bin/scribe_cat $1
usleep 100000
done

上記のシェルプログラムを使い、パイプ経由でログを出力します。

# httpd.confまたはextra/httpd-vhosts.confなど
<VirtualHost *:80>
ServerName www.example.com
DocumentRoot "/home/www.example.com/public_html"
# ErrorLogはファイルに出力
ErrorLog "/home/www/logs/www.example.com/error_log"
# CustomLogはパイプを使ってScribeに出力
CustomLog "| /usr/local/bin/scribe_httpd www.example.com" combined
</VirtualHost>

ちゃんとScribe経由で出力されたのですが一つ問題が・・・。負荷がけっこう高いようです。scribe_httpdがログ出力の個数分プロセスに立ち上がるようなので、バーチャルホストの数やアクセスが増えたらたいへんそうです。CPUの負荷が高いようなので適当に作ったシェルスクリプトがやばいんでしょうか?毎回scribe_catを呼び出すというようなことをせずに、直接Scribeにログを渡すような形にしてやれば、負荷もけっこう下がりそうです。

無事に成功?ということでこれで終わりにしたいと思います。いまさらですが、ScribeでWebサーバのログを集約するのはあまり向かないような気も・・・。しばらく運用してみて問題がおきたら報告していきたいと思います。

Scribeでログの集約・収集【中編】

前編ではScribeのインストールと動作確認までおこないました。実際にWebサーバのログをScribeで処理していくわけですが、その前にネットワークを経由して正常にログの受け渡しができるかどうかを調べていきます。

別のサーバにもScribeをインストールして試したほうがいいのですが、今回はローカル内にポートを変えて二つのscribedを起動して検証します。

## ログサーバ側と仮定 ###
## 今回は設定サンプルのcentralというのを使ってみる 
$ mkdir /tmp/scribetest
$ /usr/local/bin/scribed examples/example2central.conf
## Webサーバ側と仮定 ###
## 別のシェルから同じように設定ファイルを変えて立ち上げる
$ mkdir /tmp/scribetest2
$ /usr/local/bin/scribed examples/example2client.conf
## 別シェルからWebサーバ(ポート1464)に向けて、メッセージを送信
## -h オプションで送信先を指定できる
$ echo "web server message." | ./examples/scribe_cat -h localhost:1464 log
## Webサーバ側で下記のようなメッセージが出力される
## ログサーバにメッセージが送信された
[Thu Jul 30 19:37:50 2009] "Successfully sent <1> messages to remote scribe server <localhost:1463>"
## メッセージを受信できたか調べる
$ cat /tmp/scribetest/log/log_current
web server message.
## ログサーバ(ポート1463)を停止してみる
# ./examples/scribe_ctrl stop 1463
## 次に停止してるログサーバに向けてメッセージを送信
$ echo "hello tokyo." | ./examples/scribe_cat -h localhost:1463 log
## 次のようなメッセージを出力
[Thu Jul 30 19:40:38 2009] "Failed to send <1> messages to remote scribe server <localhost:1463> error <No more data to read.>"
Thrift: Thu Jul 30 19:40:38 2009 TSocket::open() error on socket (after poll) <Host: localhost Port: 1463>Connection refused
[Thu Jul 30 19:40:38 2009] "failed to open connection to remote scribe server <localhost:1463> thrift error <socket open() error: Connection refused>"
[Thu Jul 30 19:40:38 2009] "[log] Opened file </tmp/scribetest2/log/log_00000> for writing"
[Thu Jul 30 19:40:38 2009] "[log] choosing new retry interval <29> seconds"
[Thu Jul 30 19:40:38 2009] "[log] Changing state from <STREAMING> to <DISCONNECTED>"
## ログサーバを復帰させる
$ /usr/local/bin/scribed examples/example2central.conf
[Thu Jul 30 19:42:36 2009] "Opened connection to remote scribe server <localhost:1463>"
[Thu Jul 30 19:42:36 2009] "[log] Changing state from <DISCONNECTED> to <SENDING_BUFFER>"
[Thu Jul 30 19:42:36 2009] "[log] successfully read <1> entries from file </tmp/scribetest2/log/log_00000>"
[Thu Jul 30 19:42:36 2009] "Successfully sent <1> messages to remote scribe server <localhost:1463>"
[Thu Jul 30 19:42:36 2009] "[log] No more buffer files to send, switching to streaming mode"
[Thu Jul 30 19:42:36 2009] "[log] Changing state from <SENDING_BUFFER> to <STREAMING>"
## 復帰したログサーバにメッセージが届いているか確認
$ cat /tmp/scribetest/log/log_current
web server message.
hello tokyo.

無事に成功しました。これでネットワーク経由でログメッセージのやり取りが可能だということがわかりましたので、次は実際にWebサーバのログをやり取りしていきたいと思います。

Scribeでログの集約・収集【前編】

前回の記事で静的ファイルを別サーバから配信することにしましたが、今度はアクセスログがサーバ毎に分散してしまうので、少々やっかいだなと思いました。
出来るだけ正確でリアルタイムに近い形でログを収集できればいいなという感じです。

まず、考えていたのは一定期間毎に一つのサーバにログを収集していく方法です。せっかくlsyncdも入れているので、rsyncで収集するのが一番簡単そうです。しかし、lsyncdでそのままログファイルをミラーしてしまうと、ログが書き込まれる度に送信しちゃうわけで。。。この辺りはどうにか出来るとは思いますが、みんなはどうやっているのかちょっと調べてみました。

とりあえず、検索してみたら下記のページが見つかりました。
ログ集約・収集について【syslog – 集約】 – プログラマ 福重 伸太朗 〜基本へ帰ろう〜

どうやら、「集約」と「収集」では意味合いが違う模様。たしかにそうだなと思い、妙に納得してしまうのでありました。こちらに書いてあったのは、syslogやsyslog-ngを使う方法のようです。
「集約」を目的にするとどうしても精度が落ちてしまうらしい、当然に集約するためにログサーバなどに送信するわけで、データが正しく送れる保証はないということです。
それに比べて「収集」は、ローカルに保存されたデータを定期的にログサーバなどに送信するといった感じでしょうか。精度の高いデータを必要する場合は、こちらがいいようです。今回はアクセスログで、ログ解析などを必要とするためにある程度の精度が必要です。

ここでふと思い出したのが、Scribeというものです。知っている方も多いと思いますがFacebookで利用されているログを集約・収集するためのツールです。調べてみた所、けっこういいんじゃないかと思ったので導入してみることにしました。

・中間程度の信頼性がある。通信が失敗したときはローカルに保存し、次に成功したときに自動的に送信してくれる。
・メッセージ構造がシンプル。カテゴリとメッセージ本体のみで保存される。

とりあえず、使ってみることにします。
Thriftというものを使っているらしく、まずこちらを入れてみます。
# ThriftもFacebookが開発したもので、RPCフレームワークらしいです
Apache Thrift

その前にBoost1.36以上に依存しているため新しいものを入れます。今回はRPMで入れる事にしました。yumではインストール出来ませんので、下記サイトを参考にSRPMからインストールします。
Boost 1.37.0のRPMパッケージ作成メモ – torutkの日記

## http://rpm.pbone.net/index.php3/stat/26/dist/32/size/29387914/name/boost-1.37.0-3.fc11.src.rpm からダウンロード
# rpm -ivh boost-1.37.0-3.fc11.src.rpm
## 依存パッケージをインストール
# yum install libicu-devel
## http://www.02.246.ne.jp/~torutk/linux/centos5/packages.html#SEC23 ここにあるファイルで下記ディレクトリのファイルを置き換える
$ /usr/src/redhat/SPECS
$ /usr/src/redhat/SOURCES
# rpmbuild -bb /usr/src/redhat/SPECS/boost.spec
# rpm -Uvh boost-devel-1.37.0-1.i386.rpm boost-1.37.0-1.i386.rpm
## libeventをyumでインストール
## イベント駆動型アーキテクチャを実装するためのライブラリだそうです
# yum install libevent libevent-devel
$ tar zxvf thrift.tgz
$ cd thrift
$ ./bootstrap.sh
## しかし下記のようなエラー
configure.ac:50: error: possibly undefined macro: AC_PROG_MKDIR_P
If this token and others are legitimate, please use m4_pattern_allow.
See the Autoconf documentation.
## autoconfが古いと出るみたいなので2.6以上にします
## http://rpm.pbone.net/index.php3?stat=26&dist=42&size=959985&name=autoconf-2.61-2.el4.pp.noarch.rpm からダウンロード
# rpm -Uvh autoconf-2.61-2.el4.pp.noarch.rpm
## thriftディレクトリで再度実行
$ ./bootstrap.sh
$ ./configure && make
# make install
## Thirftの追加パッケージfb303というにも依存しているらしいので
## fb303もインストールしておきます
$ cd thrift/contrib/fb303
$ ./bootstrap.sh
$ ./configure && make
# make install

次に下記のページからScribe本体をダウンロードします。
Scribe | Free Development software downloads at SourceForge.net

$ tar zxvf scribe-version-2.01.tar.gz
$ cd scribe
$ ./bootstarp.sh
$ ./configure && make
# make install

これでインストール完了です。早速使ってみることにします。

## ビルドしたディレクトリ内にある設定ファイルのサンプルを使って起動
$ /usr/local/bin/scribed examples/example1.conf
## こんなエラーが出ました
/usr/local/bin/scribed: error while loading shared libraries: libthrift.so.0: cannot open shared object file: No such file or directory
## Thriftのライブラリが読み込めていないようです
# echo "/usr/local/lib" >> /etc/ld.so.conf
# /sbin/ldconfig
## 再度実行し、成功すると下記のようなメッセージ
$ /usr/local/bin/scribed examples/example1.conf
Thrift: Thu Jul 30 12:44:18 2009 libevent 1.1a method epoll

この状態ではまだデーモンとして立ち上げず、別のシェル上からログメッセージを送信してみます。Scribeのビルドディレクトリ内のexamplesの中にサンプルのプログラムが入っているみたいです。
scribe_catはメッセージを追記、scribe_ctrlはサーバの状態を確認するもののようです。pythonで出来てました。

## 最後のtestの部分がいわゆるカテゴリ
$ echo "hello japan." | ./examples/scribe_cat test
## するとscribedがこんなメッセージを出力しました
[Thu Jul 30 12:48:30 2009] "Exception < boost::filesystem::create_directory: No such file or directory: "/tmp/scribetest/test" > trying to create directory"
[Thu Jul 30 12:48:30 2009] "[test] Failed to open file </tmp/scribetest/test/test_00000> for writing"
[Thu Jul 30 12:48:30 2009] "[test] Opened file </tmp/test/test_00000> for writing"
## どうやら/tmp/scribetestというディレクトリを作っておく必要があるらしい
## 作ってなかったので、/tmp/test以下に自動保存された
$ mkdir /tmp/scribetest
## 自動的にscribedが認識し自動保存されたログを書き込んだ模様
[Thu Jul 30 12:49:38 2009] "[test] Opened file </tmp/scribetest/test/test_00000> for writing"
[Thu Jul 30 12:49:38 2009] "[test] Changing state from <DISCONNECTED> to <SENDING_BUFFER>"
[Thu Jul 30 12:49:38 2009] "[test] successfully read <1> entries from file </tmp/test/test_00000>"
# ちゃんと書き込まれている
$ cat /tmp/scribetest/test/test_current
hello japan.

とりあえず、無事に動いているようでインストール成功しました。長くなりそうなので記事を別けて掲載します。次の記事ではネットワーク経由でログを出力させてみようと思います。

MovableTypeのインポートに注意

MovableTypeに他のブログからのバックアップデータを復元しようとしたところ、ファイルが大きすぎるというエラーが発生しました・・・。
確かに20MB近くあるのでけっこうデカイです。

mt-config.cgiを下記のように変更してみました。

## とりあえず50MBくらいにした
CGIMaxUpload 50000000

しかしまだインポートできません。今度はちゃんとアップロードは出来ているようで、完了しましたも出るのになぜだろう。
どうやら改行コードの問題でした。改行コードはLFじゃないとだめらしい。ファイルを調べてみるとCRLFになってました。

vimでファイルを開き、改行コードを変更してアップロードしたところ、成功しました。めでたしめでたし。

## Unix改行コード(LF)に変更
:set fileformat=unix
:wq

lsyncdで簡単サーバー間ミラーリング

サーバ間のデータミラーリングについて調べてみました。
僕がやりたかったのは「別のサーバにデータをコピーしたい」ということでした。
もう一つ条件があります。「簡単」にやりたい。

データのバックアップのためとか、冗長化という意味合いもあると思うのですが、画像などの静的ファイルのみ別のサーバから配信することにします。
ちょっとやっかいなことが、静的ファイルを生成するタイミングがバラバラということです。そして、すぐに別のサーバから配信されるようにしたいというのもありました。

NFSrsync、Unisonなどを使おうと考えていましたが、リアルタイムとなるとけっこう難しいものがあると思います。NFSだとちょっと設定や安定性が不安というのも・・・。あと遠隔地など物理的に距離があると難しそうです。

rsyncやUnisonでリアルタイムに出来るのかな?と思いましたが、調べてみるとlsyncdというものを見つけました。
これはrsyncを使ったものなのですが、Linuxのinotify機能というものを使い、ファイル更新時にリアルタイムでrsyncを実行する事が出来るそうです。なんとスバラシイのでしょうか。

inotify機能について
404 – エラー: 404

今回はこのlsyncdを使ってみようと思います。
使い方はいたって簡単です。まず二つのサーバ(ミラー元とミラー先)にrsyncをインストールします。CentOSを使っていたのでyumを使いました。

# yum install rsync 

まずはミラー先のサーバを設定します。

# /usr/local/etc/rsyncd.conf を作成。設定内容は下記。
# /etc/rsyncd.conf がある場合はそちらのほうがいいかも。
# chrootしない。/etc/localtime が参照できないため、ログの時間がズレる
# chroot先に/etc/localtime を作ってもOK
use chroot = no
# ログの保存先
log file = /var/log/rsyncd.log
# モジュール名
[www]
# rsyncを動かすときのユーザとグループ
uid = nobody
gid = nobody
# ミラー先のパス
path = /home/rsync
# ローカルのみ許可
hosts allow = 192.168.1.0/24
# 書き込み許可
read only = no

パスワードの設定などもできますが使用する場合は、読み込みの権限など気をつけた方がいいかもしれません。
あとは、rsyncdに設定ファイルを読み込ませて起動します。起動ファイルがない場合は下記参照。

#!/bin/sh
# Rsyncd This shell script takes care of starting and stopping the rsync daemon
# description: Rsync is an awesome replication tool.
#
# chkconfig: 345 87 87
# description: Rsync is an awesome replication tool.
# Source function library.
. /etc/rc.d/init.d/functions
[ -f /usr/bin/rsync ] || exit 0
start() {
action "Starting rsyncd: " /usr/bin/rsync --daemon --config=/usr/local/etc/rsyncd.conf
}
stop() {
action "Stopping rsyncd: " killall rsync
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
sleep 1
start
;;
*)
echo "Usage: rsyncd {start|stop|restart}"
exit 1
esac
exit 0

次にミラー元サーバの設定をします。下記からlsyncdをダウンロードします。

lsyncd –

Lsyncd (Live Syncing Daemon) synchronizes local directories with a remote targets – Google Project Hosting

$ tar zxvf lsyncd-1.26.tar.gz
$ cd lsyncd-1.26
$ ./configure && make
# make install
# 展開したディレクトリの中にlsyncd.conf.xmlというサンプルがあるので、
# その設定ファイルを/usr/local/etc/lsyncd.conf.xml に保存
# cp lsyncd.conf.xml /usr/local/etc/

lsyncdの設定をします。設定ファイルはXMLで記述するみたいです。

# オプションが下記のようになっていたので、コメントアウトして次のように変更してみました
#<option text="-lt%r"/>
# rsyncのオプションが使えるらしい
<option text="-avz"/>
<option text="--delete"/>
# <directory>から</directory>のセットで、同期ディレクトリを設定
# いくつでも増やせるみたい
<directory>
# 同期元ディレクトリ
<source path="/home/rsync"/>
# 同期先のサーバとモジュール名を指定
# 192.168.0.2::www/hoge/dir こんな感じにモジュール名の後にもパスが書けます
<target path="192.168.0.2::www"/>
</directory>

あとは起動するとすぐに同期(ミラーリング)が始まる模様。起動ファイルは下記のページを参考にちょっといじってみました。
リアルタイムミラーリングツール導入(lsyncd+rsyncd) – Fedoraで自宅サーバー構築

#!/bin/bash
#
# lsyncd
#
# chkconfig: - 99 20
# description: lsyncd auto start script
start() {
/usr/local/bin/lsyncd --conf=/usr/local/etc/lsyncd.conf.xml
}
stop() {
/bin/kill -9 `/sbin/pidof lsyncd`
until [ -z $(/sbin/pidof lsyncd) ]; do :; done
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
pid=`pidof lsyncd`
if [ $? -eq 0 ]; then
echo "lsyncd (pid $pid) is running..."
else
echo "lsyncd is not running"
fi
;;
*)
echo "Usage: lsyncd {start|stop|restart|status}"
exit 1
esac
exit $?

見事にリアルタイムで同期されていました。安定性などはまだ分かりませんがしばらく使ってみようと思います。
今回はローカル環境でしたので、認証や暗号などは一切しませんでしたが気になる方は、sshなどと併用してみるのもアリだと思います。

SQLiteの日付にハマる

この前初めてSQLiteを使ってみたのですが思わぬところでハマってしまいました。

SQLiteではよくある話のようで日付処理に関する問題です。
型の概念がほとんどないらしく、数値と文字列の二つで管理されている?ようです。テーブルを見てみるとDATE型とか表示されるので一見、型があるように思うのですが・・・。

当然日付に関しても数値か文字列でしか管理されないというわけです。
なので、挿入する際に数値か文字列のどちからで統一しておかないと、めんどうなことになりそうです。
日付文字列に関してのフォーマットもあまり柔軟ではないようです。

例えば関数に渡すとき

# YYYY-MM-DD HH:MM:SS これならOK
1999-02-01 23:01:01
# YYYY-M-D H:M:S これだとだめ
1999-2-1 23:1:1

julianday関数で数値化して格納するか、文字列のフォーマットを統一して格納するという感じでしょうか・・・?
文字列のフォーマットを統一する際は、アブリ側でやることになるのでしょうか?PHPならば、mktime、sprintf、strftime、strtotime関数などが使えそうです。RubyならDateクラスなどに同様のメソッドがあるみたい?です。

参考
shiromaru しろまる: SQliteで日付の演算ができるみたいだ
rakuto.net – rakuto Resources and Information. This website is for sale!

ひかりoneギガ得プランの性能とかメールサーバ

前回の記事で無事?に接続出来たわけなので、さっそく回線速度を測ってみました。画像は・・・キャプするの忘れました;;

上りが300Mbps
下りが500Mbps

計測したサイトの限界が500Mbpsと書いてあったので、500Mbps以上出るのかもしれません。予想以上の速度におどろきを隠せません!かなり地域差がでるのでしょうか?
付属のCat5eUTPケーブルを使うと速度が出ないと書いてありましたが、僕の場合は関係なかったみたいです。粗悪品が多いのかな?
そういえば100円ショップでもUTPケーブル売ってました。1m〜3mくらいがあったかな・・・。

実質固定IPアドレスなのでサーバを立てようと思う人もいると思いますが、ちょっと注意が必要かもしれません。
最近は当たり前かもしれないですが、メールを送信する際にSMTPサーバにつながりません。OP25Bというやつです。

そこでプロバイダのメールサーバにリレーしようかと思いました。僕はauone-netなので、まずサイトに接続してメールアドレスを発行しました。
それでSMTPサーバのアドレスを確認したわけですが、こいつを指定してもつながらないわけです・・・。SMTPAuthというやつか?!とか色々考えたのですが、そもそもtelnetで叩いてみても反応がないです。(もちろん、KDDIの回線から)

こんなの
SMTP: msa.ac.auone-net.jp

調べたら中継用のサーバがちゃんと用意されているらしい。サポート対象外らしいです。下のページの一番下のところにありました。
Outbound Port25 Blocking: 迷惑メール対策 | au one net 会員向けサービス | au one net

Sendmailのリレーサーバの指定方法です。

# Sendmail.mc
# 編集後にsendmailをリスタートするだけでOK
define(`SMART_HOST',`smtp:ここに中継用サーバのアドレスを入れる')
# 送信テスト
echo hogehoge | mail -s test [email protected]

参考猫ぐらし: sendmailのデフォルトリレー先の設定

ひかりoneギガ得プランでRTX1200

ひかりoneギガ得プランというのが登場していました。
すごいです・・・家庭でギガですよ!

ということで、さっそく契約してみました。
ひかりoneホームで、プロバイダはauoneというのにしました。旧dionらしい。
それで色々調べてみたわけですが、ルータを指定のものから替えたいという方がけっこう多いらしくて、ちょっと試してみます。
ついでに気になっていたYamaha RTX1200も購入してみました。

f:id:happytar0:20090726120400j:image
これが指定ルータの「NEC Aterm BL190HW」通称HGW
f:id:happytar0:20090726134201p:image
ギガ対応ルータの「Yamaha RTX1200」

調べてみるとMACアドレスを見ているらしい。まず最初にONUからHGWに繋いでみました。何も設定せずに繋がりました。ブラウザからルータの設定画面を見てみると確かにDHCPになってます。

次にDHCPでRTX1200に繋いでみました。やっぱり無理でした・・・。show status dhcpcとかで見てみると取得中みたいな感じで止まっていました。DHCP以外にも見ているという書き込みがあったので、パケットキャプチャで見てみる事にしました。

最初どうやればいいのかな?と思ったのですが、ONUとHGWの間にハブをはさめればいいだけみたい。Wiresharkで見てみると、確かにDHCPで通信してました。パッとみた感じ他に気になる所はなかったような気がしました。

RTシリーズで使える「dhcp client client-identifier」これを使えばごまかせるかもしれないという書き込みがあったのでやってみました。結果はだめでした・・・。本当のMACアドレス?を見ているらしい。

参考FAQ for YAMAHA RT Series / ADSL Internet

あとは、マニュアルを見ていたら「dhcp client option」というのがありました。これはDHCPサーバに送るメッセージを指定することができるらしい。パケットキャプチャした内容を参考に、色々いじってみたのですがこれもだめでした。

RTシリーズはMACアドレスは変更できないらしいので打つ手なし?とりあえず、HGWにあったDMZを使い、ブリッジのようにして使う事にしました。

さて、終わりにするかと思ったのですが、よく考えてみるとMACアドレスを書き換えれば本当に繋がるのか試していませんでした・・・。それで今調べてみたのですが・・・衝撃の事実が!!

ちょうどMacBookを使っていたのでこんな感じにHGWのMACアドレスに書き換えてみました。

ifconfig en0 lladdr ff:ff:ff:ff:ff:ff

ちゃんとDHCPで取ってきました・・・でも前のIPアドレスと違う?!どういうこと?MACアドレスは間違ってないはず・・・。

f:id:happytar0:20090726133652p:image
こんなのに変わってた。
f:id:happytar0:20090726133637p:image
急いで前のIPアドレスを直接入れてみた。ちゃんと繋がった。

どっちも使えました・・・。実質固定IPアドレスと聞いていたのですが、実はIPアドレスを自分で選択できちゃうような太っ腹な感じなのでしょうか?!
ちなみにHGWに繋ぎ替えたら前のIPアドレスとってきましたw

それとDHCPのリース時間で繋がらなくなるらしいのですが、それは分からないです・・・めんどうで調べていませんが、一日一回ひかりone側からHGWの更新チェックが入るようなので、そこで引っかかるのかな?ちなみに更新チェックの時間はKDDIのサポートページから変更できるみたいです。
初期設定では10時〜11時になっていましたが、チェック無効はなかったようです。

はじめてのSQLite

はじめてSQLiteさわってみました。

さくらのワンコイン共有サーバ使っているのですがMySQLのDBは一つしか作れないのですね・・・。
不安定で遅い!というイメージだったので敬遠していたところがあったのですがMySQLより速い?らしい。よく考えたら軽い実装なのだから当たり前なのかな?

サーバの設定ではデフォで使えるようになっていた(PHP)ので、さっそくDBを作ってみよう!と思ったのだけど、よく考えたら安いプランでシェルが使えなかった。WebベースのSQLiteManagerというのを入れてみました。phpmyadminみたいなものみたいです。

無事にDBを作ってテーブルを流し込みました。型とか大丈夫なのかな?と心配でしたが、すんなりいって一安心。SQLiteは型の概念がほとんどないらしい。

ここまではよかったのですが、ここからちょっと引っかかりました。
PHPで昔作ったものでADODBというライブラリを使っていたのですが、DSNをSQLiteに書き換えても上手く動きません。

sqlite:///home/hogehoge/sqlite/hoge.db
SQL logic error or missing database

こんなエラーが出ました。パーミッション?SQL?と色々いじってみたのですが解決せず・・・。でもよく考えるとSQLiteManagerでは動いていた気がしました。ADODBのソースを見てみる事に・・・。

どうも独自に内部でDSNを処理しているらしく、parse_url関数の部分でエラーになってるっぽいです。ホスト名の指定がないせいで、正しいURLと解釈されないのが原因でした。絶対パスで指定するのが間違ってるのかな・・・?

sqlite://localhost/home/hogehoge/sqlite/hoge.db

こんな感じに直してみたらまだエラー。さらにソースを覗いてみると

if (empty($argHostname) && $argDatabasename) $argHostname = $argDatabasename;

どうもホスト名が空じゃないとだめらしい・・・。これは無理かなと思い、別の方法を考えることに。

$con =& NewADOConnection('sqlite');
$con->Connect('/home/hogehoge/sqlite/hoge.db');

これで動きました!これが一番簡単みたい。でもどうしてもDSNで指定したいと思い、ソースを眺めてたらこんな記述がありました。

if (($at2 = strpos($origdsn,'@/')) !== FALSE) {
// special handling of oracle, which might not have host
$fakedsn = str_replace('@/','@adodb-fakehost/',$fakedsn);
}

どうやら@を使えばホスト名を指定しなくてもOKらしいです。結局これで解決しました。

# ちなみに@の後に/を二つ書かないと一つ目は消されるみたい
sqlite://@//home/hogehoge/sqlite/hoge.db

そんなこんなで小一時間かかりました。
そういえば、NOW()なんてのも動かないんですね・・・。