【Growi】バックアップスクリプトが全然うまく書けてなかった話
はじめに
以前に下記のような記事を公開しました。
簡単に説明すると「GrowiというWikiツールをDockerを使って立てたよ」という話なのですが、最近そのバックアップ設定で盛大にやらかしていた事に気づいたので、その詳細について記事にしたいと思います。
自分と同じようなやらかしをする方が少しでも減ればいいなと思います。
環境
CentOS Linux release 7.6.1810Docker 19.03.5
以下のような構成になっていました。
/
├── opt
│ └── docker
│ └── growi
│ ├── backup.sh
│ └── backup
└── home
他にも色々あるのですが、ここでは省略しています。
バックアップスクリプト(当初)
当初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については下記が参考になります。
これには心当たりがあり、 「ちゃんと削除対象だけが抽出されているか確認するため」 に一旦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は下記が参考になります。
バックアップの同期自体は取れているのですが、「同期元にないファイルを同期先から削除する」指定がないため、「/opt/docker/growi/backupからは過去分のバックアップが消えるようになっても、マウント先には過去分が残り続ける」という状態です。
これはrsyncに--deleteをつける事で解決します。
rsync --delete -av /opt/docker/growi/backup/ {マウント先パス}
見直し後のバックアップスクリプト
ここまでの結果を踏まえて以下のように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
こちらの内容を参考にさせていただき、GROWIのmongoDBをバックアップしております。
時々、docker cpでコンテナからコピーしたファイルサイズが元のmongodumpしたファイルより小さい現象に悩んでいたのですが、原因が分かりましたのでコメントします。
でmongodumpを"-d"オプションでデタッチして実行していますが、バックグラウンドで動作しているためmongodumpが完全に終了する前に次のdocker cpが走ってしまうと、不完全なファイルがコピーされます。それを回避するため、
と修正し、フォアグラウンドで実行するように変更しました。
コメントありがとうございます!
記事中に追記と訂正を反映させていただきました!