2011/11/04

Logrotateはいつログを圧縮するか、あるいは、postrotateはいつ実行されるか

日々刻々、サーバにログはたまり続け、ディスク容量を圧迫する場合がある。これを回避するため、ログファイルをrename (mv)、圧縮(gzip)し、保存期間を過ぎたものを削除する、ローテートと呼ばれる操作を行う必要がある。RHELやCentOSでは、logrotateというコマンドでこれを行う。

このlogrotateの設定ファイルは、/etc/logrotate.confおよび、その中から読み出される/etc/logrotate.d/*である。詳しくは、logrotate(8)を参照のこと。

さて、このlogrotateを使って、複数のサーバのログを、NFSなどの共有ディスク(仮に/var/oldlogsとする)に集約する場合を考えてみる。まず、複数のサーバのログが同ファイル名のだと一箇所に集約したときに上書きされてしまう可能性があるので、例えば、/var/log/some/log-ホスト名の様にしなければならない。

設定ファイル中で、ディレクティブolddir directoryでその共有ディスクに移動させる方法を思いつくかもしれない。
/var/log/some/log-ホスト名 {
      olddir /var/oldlogs
}
しかし、これは上手くいかない。というのは、logrotateでは、ローテート後のファイルは、元ファイルと同じデバイス上にしか置けない、という制限があるからだ。
# cat /etc/logrotate.d/test
/var/log/some/log-ホスト名 {
        olddir /var/oldlog
}
# ls /var/log/some
log-ホスト名
# logrotate -f /etc/logrotate.d/test
error: /etc/logrotate.d/test:3 olddir /var/oldlog and log file /var/log/some/log-ホスト名 are on different devices
# 
これを回避するために、ローテート後に共有ディスクへのコピーを実行することを思いつくだろう。設定ファイル中でpostrotate/endscript ディレクティブを使えば実現できる。このディレクティブを用いると、mv直後にシェルスクリプトを実行させることができるためだ。
# cat /etc/logrotate.d/test
/var/log/some/log-ホスト名 {
        rotate 4
        postrotate
                cp -p /var/log/some/log-ホスト名.1 /var/oldlog
        endscript
}
# logrotate -f /etc/logrotate.d/test
# ls /var/log/some
log-ホスト名.1
# ls /var/oldlog
log-ホスト名.1  lost+found
#
しかし、これにもまだ問題が出る場合がある。ディレクティブcompressを使って、ログファイルを圧縮する場合だ。次の様にして上手くいくだろうか?
# cat /etc/logrotate.d/test
/var/log/some/log-ホスト名 {
        rotate 4
        compress
        postrotate
                cp -p /var/log/some/log-ホスト名.1.gz /var/oldlog
        endscript
}
#
これは上手くいかない。
# ls /var/log/some/log-ホスト名*
/var/log/some/log-ホスト名
# ls /var/oldlog/log-ホスト名*
ls: /var/oldlog/log-ホスト名*: No such file or directory
# logrotate -f /etc/logrotate.d/test
cp: cannot stat `/var/log/some/log-ホスト名.1.gz': No such file or directory
error: error running postrotate script for /var/log/some/log-ホスト名
# ls /var/log/some/log-ホスト名*
/var/log/some/log-ホスト名.1.gz
# ls /var/oldlog/log-ホスト名.*
ls: /var/oldlog/log-ホスト名*: No such file or directory
#
なぜか?それは、実行タイミングにある。logrotateは、まずmvし、postrotate/endscript ディレクティブのシェルスクリプトを実行した後、gzipで圧縮するため、シェルスクリプト実行時には、まだ*.1.gzというファイルは存在せず、圧縮前の*.1のままである。

これは、postrotate/endscript ディレクティブの代わりにlastaction/endscript ディレクティブを使えば解決できる。
# cat /etc/logrotate.d/test
/var/log/some/log-ホスト名 {
        rotate 4
        compress
        lastaction
                cp -p /var/log/some/log-ホスト名.1.gz /var/oldlog
        endscript
}
# ls /var/log/some/log-ホスト名*
/var/log/some/log-ホスト名
# ls /var/oldlog/log-ホスト名*
ls: /var/oldlog/log-ホスト名*: No such file or directory
# logrotate -f /etc/logrotate.d/test
# ls /var/log/some/log-ホスト名*
/var/log/some/log-ホスト名.1.gz
# ls /var/oldlog/log-ホスト名*
/var/oldlog/log-ホスト名.1.gz
#
このままでは、共有ディスクには一世代しか保存されない。また、一度コピーに失敗すると、復旧できない。これらを解決するには、dateextディレクティブとrsyncコマンドを用いる。
# cat /etc/logrotate.d/test
/var/log/some/log-ホスト名 {
        rotate 4
        compress
        dateext
        lastaction
                rsync -a /var/log/some/log-ホスト名-*.gz /var/oldlog
        endscript
}
# ls /var/log/some/log-ホスト名*
/var/log/some/log-ホスト名
# ls /var/oldlog/log-ホスト名*
ls: /var/oldlog/log-ホスト名*: No such file or directory
# logrotate -f /etc/logrotate.d/test
# ls /var/log/some/log-ホスト名*
/var/log/some/log-ホスト名-20111104.gz
# ls /var/oldlog/log-ホスト名*
/var/oldlog/log-ホスト名-20111104.gz
#