たごもりすメモ

コードとかその他の話とか。

Linuxサーバのディスク容量減少アラートが飛んできた!ってときにどう対処するか

完全に このエントリ のネタパクりです。すいません。

何に使われてるかわかったもんじゃないマシンとか開発用サーバとかだと超巨大なバイナリとか置いてあるかもしれませんが、プロダクション用のサーバでそういうことは無いとしましょう。
その場合、原因はだいたい以下のどれかです。www/appとdbが別マシンに分かれてる場合は更に絞り込めますね。

  • wwwサーバやappサーバ
    • ログ
      • 圧縮してあるが保存世代数が多くて厳しいケース
      • 圧縮し忘れてるケース
      • 圧縮どころかローテーションすら忘れてて1ファイルどかんと存在するケース
      • ローテーションがうまくいかなくて deleted ファイルなケース
    • tmpデータなど(app)
    • キャッシュサーバのディスクキャッシュ
  • dbサーバ
    • データ実体 (ib_data)
    • バイナリログ

ログの場合でも、ディスク上のどこにログが書かれてるかは色々なパターンがある可能性がありますね。社会は厳しい。現状ベースの調査がいちばんです。

ということで、まず df します。dfしたときに nfs mount してたりすると後々の調査でそこを除外したりする必要があるので注意します。

# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda2             225G  140G   74G  66% /
tmpfs                 7.8G  751M  7.0G  10% /dev/shm

だいたい何GB使っているかを把握します。その上で、各ディレクトリごとに確認をします。まずざっくりトップレベルから。nfs mountしてたりするとこれが物凄く時間かかったりするので注意するか nfs mount しているディレクトリは除外するなどしましょう。

# du -sch /*
7.7M	/bin
34M	/boot
788M	/dev
100M	/etc
1.4G	/home
220M	/lib
26M	/lib64
4.0K	/media
4.0K	/misc
4.0K	/mnt
24M	/opt
0	/proc
292M	/root
35M	/sbin
4.0K	/selinux
74M	/service
0	/sys
168K	/tmp
124G	/usr
101M	/var
127G	total

これけっこう時間かかりますが、サブディレクトリへの調査を含めた2度目からは必要な情報がディスクキャッシュに載ってるので割と早く帰ってきます。心配しないようにしましょう。
で、このケースだと /usr 以下が多いですねってことで、これはもしは /usr/local/apache2/logs 以下かな……みたいなことを起動しているデーモンとあわせて調査することになります。以下みたいな。

# cd /var/log
# du -sch ./*

普通に圧縮済みログファイルの世代数が多い場合、ローカルに置いておくべき適正な世代数を考え、それ以外は削除するなどの作業をします。他サーバに既にバックアップがある場合にはローカルにそれほど多く置いておく必要は無いでしょう。また圧縮が行われていない場合には圧縮します。
このあたり、とりあえずの作業が終わったら必ず logrotate の設定も見直しましょう。じゃないとまた後日同じ作業をやるハメになります。logrotateの設定ファイル /etc/logrotate.d 以外にも、各ユーザの crontab や /etc/cron.d になぜか独自の方法で rotate 設定が仕込まれている可能性もあるので注意が必要です。社会は厳しい。
rotate設定がそもそも行われていない場合についてはちょっと後でまとめて。

DBサーバのバイナリログも似たような処理を取りますが、どこから保存しておくかはどこからデータ復旧をかけるかに依存するので、そのへんはちゃんとチェックしましょう。消しちゃいけないものを消すと恐ろしいことになる……かもしれません。

また上述の du の合計値(total)と df -h のUsedの値が大きくズレている場合、既に削除されているファイルをどれかのプロセスが掴んでいてディスク上にまだ存在してしまっている、という状況の可能性があります。
このときは以下のコマンドの出力を見て確認します。*1

# lsof | grep deleted
tail      29123     root    3r      REG                8,2 14179692293             40503744 /usr/local/apache2/logs/old/accesslog.20130312 (deleted)

このケースだと14GBくらいのファイルを tail が掴んでますね。tailを終了させるかkillってやればこのファイル実体が消えて df のUsedが小さくなるでしょう。

また logrotate 設定を忘れていて超巨大な1ファイルがディスクを埋め尽くしている場合もあります。既に deleted だったりした場合はどうやっても救えないので、残念ですがdaemonをkillってディスクを解放してあげましょう。

deletedでない場合、とりあえず圧縮できるかどうかを考えます、が、その前に rotate して該当のファイルへの書き込みを止め、切り離してやる必要があります。
logrotate の設定をここで即座にやってもいいですが、圧縮までやってしまうと後がちょっと面倒なので、とりあえず手動で切り替えます。

# mv accesslog accesslog.1
# apachectl graceful   # apacheの場合
# nginx -s reload     # nginxの場合

これで超巨大なログファイル accesslog.1 への書き込みは止まり accesslog が新規に削除され、そちらへの書き込みが始まっているはずです。

この時点で accesslog.1 の容量と、該当パーティションの空き容量を確認します。経験的にアクセスログファイルを圧縮すると、どんなに頑張ってもだいたい 1/10 くらいのサイズが限界です。なので空き容量が圧縮対象ファイルサイズの1/10以下の場合は残念ですが諦めて削除してしまう方が良いでしょう。あるいはどうしても消せない場合はネットワーク経由で他所に逃がしてやります。( gzip -c と nc などを組合せると良いでしょう。)

その後、logrotate の設定がすぐに行える場合は実施します。適切に設定されている他のサーバからパクってくるなどの方法が良いでしょう。おそらく。

# vi /etc/logrotate.d/fxxxkin_daemon
# logrotate -f /etc/logrotate.d/fxxxkin_daemon

logrotate の設定が終わったら必ず logrotate -f path で実行してみて、結果を確認すること。ファイルのリネームと新ファイルへのログ書き込みが行われていれば成功です。これはトラブル対応以外の通常の手順でも同じです。どこかがおかしかったら旧ファイルに書き込みが続いてるはずですから、書き込み先が切り替わったかどうかのチェックは絶対に行ってください。*2

まあだいたいこんな感じですね!

*1:いま某サーバで例を探してたらすげー偶然にも起動しっぱなしの tail -f を発見したwwwwwww

*2:実例として postrotate の記述内容がおかしく正常に切り替わらない、など起きてます。