読者です 読者をやめる 読者になる 読者になる

彼女からは、おいちゃんと呼ばれています

ウェブ技術や日々考えたことなどを綴っていきます

MySQL ログのローテーション設定(logrotate)(flush-logs が cron で動かないときの対処を含む)

MySQL Linux

MySQL のログをローテートさせる設定をしていて、下記の現象(詳細は本文参照)にハマりました。やっと解決できたのでメモしておきます。

手動でコマンドを叩いたときはうまくいくのに cron で動かしたときは flush-logs がうまくいかない

環境は CentOS 6.3、MySQL 5.5.28 です。

# yum list centos-release
...
Installed Packages
centos-release.x86_64    6-3.el6.centos.9    @base

# mysql -V
mysql  Ver 14.14 Distrib 5.5.28, for Linux (x86_64) using readline 5.1

MySQL ログのローテーション設定(logrotate)(flush-logs が cron で動かないときの対処を含む)

  • 1. 前提 - MySQL ログ出力設定
  • 2. ローテートファイルの作成
  • 3. mysqladmin のための認証ファイルを作成
  • 4. デバッグモードで実行
  • 5. あれ?うまくいかないので logrotate のログを出力させる
  • 6. /etc/cron.d/0hourly を編集
  • おまけ - ローテーションを実際に実行して確認したいとき

1. 前提 - MySQL ログ出力設定

MySQL のログ設定を下記のように設定して、エラーログとスローログを出力させているとします(クエリーログはすぐに膨大になるので通常時は出さないようにしています)

# /etc/my.cnf

[mysqld]
slow_query_log
slow_query_log_file=/var/log/mysql/slow.log
long_query_time = 0.5
log_queries_not_using_indexes

[mysqld_safe]
log-error = /var/log/mysql/error.log
pid-file = /var/run/mysqld/mysqld.pid

ログ出力用のディレクトリを作成。

# mkdir /var/log/mysql
# chown -R mysql /var/log/mysql
# chmod -R 644 /var/log/mysql

MySQL を再起動して設定を反映させる。

# service mysqld restart

2. ローテートファイルの作成

ローテートさせる設定を書いたファイルを作成して、/etc/logrotate.d に置きます。ひな形が /etc/logrotate.d/mysqld にあったので、これに手を加えて作成しました。

# vi /etc/logrotate.d/mysql-log-rotate
/var/log/mysql/error.log /var/log/mysql/slow.log {
        create 644 mysql mysql
        notifempty
    daily
        rotate 14
        missingok
        nocompress
        dateext
        sharedscripts
    postrotate
    # just if mysqld is really running
    if test -x /usr/bin/mysqladmin && \
       /usr/bin/mysqladmin ping &>/dev/null
    then
       /usr/bin/mysqladmin flush-logs
    fi
    endscript
}

設定値の説明については下記が詳しいです。

僕は次のようにしていますが、このあたりはお好みで。

  • スローログの解析とかをやるときにいちいち解凍するのが面倒なので、圧縮しない(nocompress)
  • ログの参照、解析がやりやすいように、他のユーザーにも参照権限を与える(create 644 mysql mysql
  • サフィックスは xxx.log.1 形式よりも xxx.log-20130320 みたいに日付が付く方がよい(dateext)
  • flush-logs の実行は、ログファイルごとに行うのではなく、1回のみ行う(sharedscripts)

3. mysqladmin のための認証ファイルを作成

ローテートした後に「/usr/bin/mysqladmin flush-logs」するので、user と password を書いたファイルを作成します。

これについても /etc/logrotate.d/mysqld の中に説明が載っていました。説明にも書いているように、root ユーザしか扱えないようにする権限設定も忘れずに。

# vi /root/.my.cnf
[mysqladmin]
password = xxxxxxxx
user= root
# chmod 600 /root/.my.cnf

4. デバッグモードで実行

/etc/logrotate.d/ 配下に置いているファイルは、cron が実行してくれるのですが、設定に誤りがないか確認するため、一度、手動でデバッグモードで実行しておくと良いです。

# logrotate -dv /etc/logrotate.d/mysql-log-rotate
...(エラーがないか確認する)

メモ: logrotate の使い方

# logrotate --help
使い方: logrotate [OPTION...] <configfile>
  -d, --debug               Don't do anything, just test (implies -v)
  -f, --force               Force file rotation
  -m, --mail=command        Command to send mail (instead of `/bin/mail')
  -s, --state=statefile     Path of state file
  -v, --verbose             Display messages during rotation

Help options:
  -?, --help                Show this help message
  --usage                   Display brief usage message

5. あれ?うまくいかないので logrotate のログを出力させる

と、ここまでで設定は完了で、あとは cron が毎日 logrotate してくれるよ、と他のいろんな記事には書かれていたので、うまくいく人は下記はすっ飛ばして良いと思います。

ですが、自分の環境だと、どうもうまくいかない。。。具体的にどういうことかというと、下記のように、ログが slow.log や error.log に書き込まれずに、サフィックスに日付が付いたログファイルのほうへ書き込まれてしまうのでした。

# ls -l /var/log/mysql
-rw-r--r-- 1 mysql         0  3月 14 03:16 2013 error.log
-rw-r--r-- 1 mysql       102  3月 14 00:12 2013 error.log-20130314
-rw-r--r-- 1 mysql         0  3月 14 03:16 2013 slow.log
-rw-r--r-- 1 mysql      8029  3月 14 21:41 2013 slow.log-20130314

これは flush-logs がされていない様子。。。手動でコマンドを叩くと、slow.log や error.log に書き込まれるようになりました(flush-logs の挙動については下記が詳しいです)

# /usr/bin/mysqladmin flush-logs

でも OK だったし、

# logrotate /etc/logrotate.d/mysql-log-rotate

でも OK でした。

なぜ、cron からの実行だとダメなのか??原因を調べるために、logrotate のログを出力させます。

# vi /etc/cron.daily/logrotate
-/usr/sbin/logrotate /etc/logrotate.conf >/dev/null 2>&1
+/usr/sbin/logrotate -v /etc/logrotate.conf >/var/log/logrotate 2>&1

で、一日待って、出力されていたログが下記。

# cat /var/log/logrotate
...
running postrotate script
/usr/bin/mysqladmin: connect to server at 'localhost' failed
error: 'Access denied for user 'root'@'localhost' (using password: NO)'
error: error running non-shared postrotate script for /var/log/mysql/error.log of '/var/log/mysql/error.log '

mysqladmin の認証ができていない様子。。。

6. /etc/cron.d/0hourly を編集

mysqladmin の認証ができていない = 先に作成した /root/.my.cnf を読み込めていないのではないかと予想し、cron の設定を見てみたところ、それらしい箇所を見つけたので編集しました(環境によっては、/etc/cron.d/0hourly ではなく「/etc/cron.d/dailyjobs」のようです)

# vi /etc/cron.d/0hourly
-HOME=/
+HOME=/root

これでうまくいくようになりました。

(2013年3月22日 追記)

コメント&はてぶコメントで教えていただきましたが、/etc/cron.d/0hourly を編集せずに、/etc/logrotate.d/mysql-log-rotate の方を編集して、下記のようにしても良さそうです。

/usr/bin/mysqladmin --defaults-extra-file=/root/.my.cnf flush-logs

しても良さそうというか、/etc/cron.d/0hourly を編集することで他の箇所に影響を与えてしまうリスクもあるので、--default-extra-file を指定する方が良いですね。

id:sh2 さん、id:manabusakai さん、ありがとうございました!

おまけ - ローテーションを実際に実行して確認したいとき

ローテーションを実際に実行して確認したい場合は、/var/lib/logrotate.status の日付を編集してから実行すればよいようです。

(2013年3月22日 追記)

こちらもはてぶコメントで教えていただきましたが、下記のように -f オプションをつければ、処理を強制できるので、次の日まで待たなくても動作確認ができます。

# logrotate -df /etc/logrotate.d/mysql-log-rotate

ひろやん(id:hiboma)ありがとうございました!