フタなしカンヅメ

徒然なるままに @happytar0

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サーバのログを集約するのはあまり向かないような気も・・・。しばらく運用してみて問題がおきたら報告していきたいと思います。