From 4b3333d49aee98f1d04a34d1a474dd1e0b46f623 Mon Sep 17 00:00:00 2001 From: Rikuoh Date: Sun, 12 Nov 2023 19:04:57 +0900 Subject: [PATCH] =?UTF-8?q?=E6=8A=95=E7=A8=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/post/test.md | 61 ------------------- .../あるはずのバックアップが全部消えていた.md | 61 +++++++++++++++++++ 2 files changed, 61 insertions(+), 61 deletions(-) delete mode 100644 content/post/test.md create mode 100644 content/post/あるはずのバックアップが全部消えていた.md diff --git a/content/post/test.md b/content/post/test.md deleted file mode 100644 index 3e2c30e..0000000 --- a/content/post/test.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: "あると思っていたバックアップが一つもなかった" -date: 2023-11-11T20:16:27+09:00 -draft: true -tags: ['tech', 'diary'] ---- - -![](/img/222.png) - -自分だけは、と思っていても起きる時は起きる。いつまでもあると思うな親と金とバックアップ。Cloudflare R2にcronで毎日アップロードしていたMastodonインスタンスのデータベースが、いつの間にか全部消えていた。AP実装においてデータベースはすべてである。これをなくしたらもはや取り返しはつかない。ドメインもなにもかも変えて一からやり直すしかない。 - -だからこそインスタンスの運営者はデータベースの保全に気を配る。わざわざオブジェクトストレージにアップロードしているのもサーバ本体の破損にデータベースが巻き込まれるのを防ぐためで、ローカルにバックアップを置くよりもクラウド上の方が安全との判断からだ。にも拘らず、ちょっとした行き違いが重なると瞬時に破滅の刃が首元まで迫ってくる。かつて僕が書いたバックアップの自動スクリプトの例を下記に記す。 - -```bash -#!/bin/bash - -echo "Backup begin..." -cd /home/ユーザ/インスタンスのディレクトリ/ -docker exec mastodon-db-1 pg_dump -Fc -U user db | gzip -c >> backup.gz -chmod 744 ./backup.gz -echo "Success!" - -su - ユーザ << bash - echo "Uploading to Cloudflare R2..." - cd /home/ユーザ/プロジェクトディレクトリ/ - npx wrangler r2 object put "あんたのバケット名/$(date +\%Y\%m\%d_\%H-\%M-\%S).gz" --file=/home/ユーザ/インスタンスのディレクトリ/backup.gz - rm /home/ユーザ/インスタンスのディレクトリ/backup.gz -bash -``` - -実際のところ、このスクリプト自体にはさほど問題はない。もちろん万全を期すならエラー時の条件分岐を仕込んでメールで通知するなど他にやりようはあったとはいえ、今回の問題はもっと単純な仕様の読み落としによるものだった。スクリプトの後半、`wrangler`でCloudflare R2にファイルをアップロードする公式的なやり方が、意外にもバックアップ全消しの引き金を司っていた。 - -日々、流れ込む投稿やユーザ情報を蓄えるデータベースは肥大化の定めから逃れられない。インスタンス設立当初は100MBにも満たなかったバックアップはすぐに200MBを越え、現時点では300MBをも上回る。 **しかし、wranglerがサポートする最大アップロード容量は315MBまでだったのだ。** 君ら、知っていたか? 僕は知らなかったよ。空バケットの画面に堂々と書いてあるのにな。データベースが315MBを越えたある日以来――それがいつなのかはもはや知る由もないが――毎日行われる予定のアップロード処理は途中で止まっていたことになる。 - -一方、バックアップを預かるCloudflare R2のバケット側では、一週間以上古いファイルを自動で削除する設定が施してあった。むやみにストレージをあふれさせるわけにはいかないので、これは特段におかしい措置ではない。ところが、新しいバックアップが来ない状態で定期削除が実行されると、1日経過するたびに既存のファイルが消されていき最終的にはすべてのバックアップが消滅する。結果、記事冒頭の画像の通り、まさにもぬけの殻と相成った。 - -翻ってスクリプトファイルに戻ると、最後にローカル側のバックアップを削除している。必要なファイルはクラウド上にアップロードされているはずなので、ローカルに残していても意味はない。そもそもサーバの生存をあてにしない前提のスクリプトなので、これ自体も悪手とまでは言えない。ストレージ容量も貴重ゆえ非効率な多重化は避けたい。 - -以上のように措置の一つ一つは必ずしも大きな問題ではなかったのに、わずかな過ちがドミノ倒しのごとく連鎖して最終的にすべてのバックアップを失ってしまった。なお、ずいぶんすっとぼけた前振りを延々としてきたが、インシデント発覚時点でMastodonインスタンスは全然普通に動いていたので現実の障害には至っていない。 - -さしあたり、容量制限によって機能を果たせない`wrangler`に代わり`rclone`を採用してスクリプトの内容を刷新した。さらに、ローカル側にも古いファイルを一定期間置く形に改めた。こうすればローカルかオブジェクトストレージのどちらかに一応バックアップが残るだろう。 - -```bash -#!/bin/bash - -echo "Backup begin..." -cd /home/ユーザ/インスタンスのディレクトリ/ -docker exec mastodon-db-1 pg_dump -Fc -U mastodon_user mastodon_db | gzip -c >> "/home/ユーザ/backup/$(date +\%Y\%m\%d_\%H-\%M-\%S).gz" -echo "Success!" - -echo "Syncing..." -find /home/mastodon/backup/ -mtime +7 -name "*.gz" | xargs rm -f -rclone sync copy /home/ユーザ/backup/ r2:mastodon-backup - -echo "Finish!" -``` - -## おまけ -そもそもCloudflare R2上のバックアップファイルを定期的に消していたのは一定以上のデータ格納量を上回ると課金されるためだが、今回の件とは無関係にインスタンスのメディアファイルをずっと預けっぱなしにしていたのでどのみち金を払う羽目になった。それにしてもおぞましい金額だ。メルカリで使い古しの参考書などを売って100円ほど銭を稼がなければならない。 - - diff --git a/content/post/あるはずのバックアップが全部消えていた.md b/content/post/あるはずのバックアップが全部消えていた.md new file mode 100644 index 0000000..ec875d2 --- /dev/null +++ b/content/post/あるはずのバックアップが全部消えていた.md @@ -0,0 +1,61 @@ +--- +title: "あるはずのバックアップが全部消えていた" +date: 2023-11-12T19:04:27+09:00 +draft: false +tags: ['tech', 'diary'] +--- + +![](/img/222.png) + +自分だけは、と思っていても起きる時は起きる。いつまでもあると思うな親と金とバックアップ。Cloudflare R2にcronで毎日アップロードしていたMastodonインスタンスのデータベースが、いつの間にか全部消えていた。AP実装においてデータベースはすべてである。これをなくしたらもはや取り返しはつかない。 + +だからこそインスタンスの運営者はデータベースの保全に気を配る。わざわざオブジェクトストレージにアップロードしているのもサーバ本体の破損にデータベースが巻き込まれるのを防ぐためで、ローカルよりもクラウド上の方が安全との判断からだ。にも拘らず、ちょっとした行き違いが重なると瞬時に死の刃が首元まで迫ってくる。かつて僕が書いた自動化スクリプトの例を以下に記す。 + +```bash +#!/bin/bash + +echo "Backup begin..." +cd /home/ユーザ/インスタンスのディレクトリ/ +docker exec mastodon-db-1 pg_dump -Fc -U user db | gzip -c >> backup.gz +chmod 744 ./backup.gz +echo "Success!" + +su - ユーザ << bash + echo "Uploading to Cloudflare R2..." + cd /home/ユーザ/プロジェクトディレクトリ/ + npx wrangler r2 object put "あんたのバケット名/$(date +\%Y\%m\%d_\%H-\%M-\%S).gz" --file=/home/ユーザ/インスタンスのディレクトリ/backup.gz + rm /home/ユーザ/インスタンスのディレクトリ/backup.gz +bash +``` + +実際のところ、このスクリプト自体にさほど問題はない。万全を期すならメールかSlackボットで通知させるなど他にやりようはあったとはいえ、今回の原因はもっと単純な仕様の読み落としによるものだった。スクリプトの後半部分、`wrangler`でCloudflare R2にファイルをアップロードする公式的なやり方が、意外にもバックアップ全消しの引き金を司っていた。 + +日々、流れ込む投稿やユーザ情報を蓄えるデータベースは肥大化の定めから逃れられない。インスタンス設立当初には10MBにも満たなかったバックアップはすぐに100MBを越え、現在では300MBをも上回る。 **しかし、wranglerがサポートする最大アップロード容量は315MBまでだったのだ。** データベースのサイズが315MBを越えたある日以降――それがいつなのかはもはや知る由もないが――毎日行われる予定のアップロード処理は途中で止まっていたことになる。 + +一方、バックアップを預かるCloudflare R2のバケット側では、一週間以上古いファイルを自動で削除する設定が施してあった。むやみにストレージをあふれさせるわけにはいかないのでこれは特段におかしい措置ではない。ところが、新しいバックアップが来ない状態で定期削除が実行され続けると、1日経過するたびに既存のファイルが古い方から消されていき最終的にすべてのバックアップが消滅する。結果、記事冒頭の画像の通り、まさにもぬけの殻と相成った。 + +また、自動化スクリプトでは最後にローカル側のバックアップを削除している。必要なファイルはクラウド上にアップロードされているはずなので、ローカルに残していても意味はない。元よりサーバの生存をあてにしない前提のスクリプトゆえこれ自体も悪手とまでは言えない。ストレージ容量の節約を図る狙いもあった。 + +以上のように処理の一つ一つには必ずしも大きな落ち度はなかったのに、想定外にもたらされた制限がドミノ倒しのごとく連鎖してバックアップを全部失ってしまった。なお、ずいぶんすっとぼけた前振りを延々としてきたが、インシデント発覚時点で僕のMastodonインスタンスは全然普通に動いていたので現実の障害には至っていない。 + +さしあたり、容量制限によって機能を果たせない`wrangler`に代わり`rclone`を採用してスクリプトの内容を刷新した。さらに、ローカル側にも古いファイルを一定期間置く形に改めた。こうすればローカルとオブジェクトストレージの両方に一応バックアップが残るだろう。 + +```bash +#!/bin/bash + +echo "Backup begin..." +cd /home/ユーザ/インスタンスのディレクトリ/ +docker exec mastodon-db-1 pg_dump -Fc -U mastodon_user mastodon_db | gzip -c >> "/home/ユーザ/backup/$(date +\%Y\%m\%d_\%H-\%M-\%S).gz" +echo "Success!" + +echo "Syncing..." +find /home/ユーザ/backup/ -mtime +7 -name "*.gz" | xargs rm -f +rclone sync copy /home/ユーザ/backup/ r2:mastodon-backup + +echo "Mastodone." +``` + +## おまけ +そもそもCloudflare R2上のバックアップファイルを定期的に消していたのは一定以上のデータ格納量を上回ると課金されるためだが、今回の件とは無関係にMastodonインスタンスのメディアファイルをずっと預けっぱなしにしていたのでどのみち金を払う羽目になった。それにしてもおぞましい金額だ。メルカリで使い古しの参考書を売って100円ほど銭を稼がなければならない。 + +