🙄

【Growi】バックアップスクリプトが全然うまく書けてなかった話

2021/04/07に公開
2

はじめに

以前に下記のような記事を公開しました。

https://zenn.dev/nekoniki/articles/44252c08f725f811be38

簡単に説明すると「GrowiというWikiツールをDockerを使って立てたよ」という話なのですが、最近そのバックアップ設定で盛大にやらかしていた事に気づいたので、その詳細について記事にしたいと思います。

自分と同じようなやらかしをする方が少しでも減ればいいなと思います。

環境

  • CentOS Linux release 7.6.1810
  • Docker 19.03.5

以下のような構成になっていました。

/
├── opt
│   └── docker
│       └── growi
│           ├── backup.sh
│           └── backup
└── home

他にも色々あるのですが、ここでは省略しています。

バックアップスクリプト(当初)

当初backup.shは以下のような内容になっていました。

backup.sh
docker exec {mongodb コンテナ名} mongodump --archive=mongodb.archive --quiet
docker cp {mongodb コンテナ名}:/mongodb.archive /opt/docker/growi/backup/growi_`date "+%Y%m%d"`.archive
find ./ -mtime +7 -name "*.archive" | xargs ls -ltrh
rsync -av /opt/docker/growi/backup/ {マウント先パス}

意図としては

  • Growiのデータが入っているmongodbから毎日dumpを取り、タイムスタンプを付与して/opt/docker/growi/backup.archive形式で保持しておく
  • バックアップは過去7日分あれば十分なので、それ以上経ったものは削除しておく
  • 最後に退避場所として指定したパスにrsyncで同期させる

といった感じです。
これをcrontabで毎日深夜に動かすようにしていました。
※察しのいい方はこの時点で色々やらかしている事に気づくかもしれません。

何が起きたか

一言で表すと 「バックアップが消えずに延々と残り続ける」 状態となっていました。
/opt/docker/growi/backup以下はもちろん、マウント先も毎日.archiveが残っている状態がずーっと続いていたことになります。

Why?

原因は主に2つあります。

xargsのコマンド間違いとfindのパス指定

1つ目は以下のコマンドが原因です。

find ./ -mtime +7 -name "*.archive" | xargs ls -ltrh

find ./ -mtime +7 -name "*.archive"の結果をxargsに渡していますが、肝心のxargsで実行しているのがlsコマンドでした。

xargsについては下記が参考になります。
https://www.atmarkit.co.jp/ait/articles/1801/19/news014.html

これには心当たりがあり、 「ちゃんと削除対象だけが抽出されているか確認するため」 に一旦rm -rfではなくlsに書き換えていました。
それを忘れたままにしてしまい、結果的に 「毎日7日以上更新されていない.archiveファイルをlsする」 という意味のない処理を繰り返していたことになります。

また、地味にfindのパス指定も問題で、シェル単体で実行する分には./のパス指定で構わないのですが、cronで実行する場合はhomeを起点にシェルが実行されるので/opt/docker/growi/backup以外のパスの.archiveを巻き込んで消してしまう可能性があります。
cronのパスの仕様は全く知らなかったので、正直危なかったです。

この処理は以下のようにすると正しいです。

find /opt/docker/growi/backup -mtime +7 | xargs rm -rf

絶対パスでバックアップディレクトリを指定することにしたので、ファイル名で絞り込むのはやめて更新日だけで抽出することにしました。
その上でfindの結果をxargsに渡してrm -rfを実行しています。

rsyncのオプションが足りない

上記の修正を行っても「マウント側」には過去分のバックアップが残り続けてしまいます。
これは以下の記述が原因です。

rsync -av /opt/docker/growi/backup/ {マウント先パス}

rsyncは下記が参考になります。
https://www.atmarkit.co.jp/ait/articles/1702/02/news031.html

バックアップの同期自体は取れているのですが、「同期元にないファイルを同期先から削除する」指定がないため、「/opt/docker/growi/backupからは過去分のバックアップが消えるようになっても、マウント先には過去分が残り続ける」という状態です。

これはrsync--deleteをつける事で解決します。

rsync --delete -av /opt/docker/growi/backup/ {マウント先パス}

見直し後のバックアップスクリプト

ここまでの結果を踏まえて以下のようにbackup.shを修正しました。

backup.sh
docker exec {mongodb コンテナ名} mongodump --archive=mongodb.archive --quiet
docker cp {mongodb コンテナ名}:/mongodb.archive /opt/docker/growi/backup/growi_`date "+%Y%m%d"`.archive
find /opt/docker/growi/backup -mtime +7 | xargs rm -rf
rsync --delete -av /opt/docker/growi/backup/ {マウント先パス}

まとめ

今回はGrowiのバックアップ用のスクリプトについて、バックアップが残り続けるというやらかしをした話をまとめました。
Growiの規模が大きくなってくると、バックアップのサイズも馬鹿にならないのでサーバを圧迫してしまいました。

事故になる前に気づいて修正ができたのは幸いでした。
根本はこのスクリプトを1人で作成して運用していたことです。
コアな部分については複数人で検証の上使用していけば、今回のような凡ミスや知識不足は防げると思うので、いい教訓になりました。

20230814追記

copen様よりコメントいただき、記事中のバックアップスクリプトを一部修正しました。

- docker exec -d {mongodb コンテナ名} mongodump --archive=mongodb.archive
+ docker exec {mongodb コンテナ名} mongodump --archive=mongodb.archive --quiet

旧コマンドでは

でmongodumpを"-d"オプションでデタッチして実行していますが、バックグラウンドで動作しているためmongodumpが完全に終了する前に次のdocker cpが走ってしまうと、不完全なファイルがコピーされます。

の動きとなってしまうためです。

Discussion

copencopen

こちらの内容を参考にさせていただき、GROWIのmongoDBをバックアップしております。
時々、docker cpでコンテナからコピーしたファイルサイズが元のmongodumpしたファイルより小さい現象に悩んでいたのですが、原因が分かりましたのでコメントします。

docker exec -d {mongodb コンテナ名} mongodump --archive=mongodb.archive

でmongodumpを"-d"オプションでデタッチして実行していますが、バックグラウンドで動作しているためmongodumpが完全に終了する前に次のdocker cpが走ってしまうと、不完全なファイルがコピーされます。それを回避するため、

docker exec {mongodb コンテナ名} mongodump --archive=mongodb.archive --quiet

と修正し、フォアグラウンドで実行するように変更しました。

nekonikinekoniki

コメントありがとうございます!
記事中に追記と訂正を反映させていただきました!