🐷

本当にあった怖い話(バックアップが取れてない)

2021/04/15に公開

序章

あるプロジェクト(仮に ProjectZとしよう)ではWebアプリケーションを開発していた。
このWebアプリケーションではデータを保存するためのDBとしてPostgreSQLを使用することにした。
環境を構築するにあたり、プロジェクトメンバーのAさんは次のように言った。

Aさん: 「万が一に備えてDBのバックアップをとっておけるようにしましょう」

事前準備

Aさんのいうことはもっともである。
通常運用では使用しなくても、DBのバックアップは取っておくべきである。
例えば運用作業中に誤ってデータを破損させてしまったり、アプリケーションのバグによって予期せぬデータが書き込まれた場合に元に戻せなくてはならず、DBバックアップはその最終手段として非常に有用であるからである。

ProjectZではpsqlコマンドでDBのバックアップを取得後、クラウドサービス上のオブジェクトストレージに保存する事にした。
クラウドサービスとして今回はAzure(※)を選択したため、バックアップの保存先としてはAzure Blob Storageを使用する事にした。
※) AWSやGCPなどなんでも良い

実装

pg_dumpコマンドでDBバックアップを取得し、その後azコマンドでAzure Blob Storageで保存する作業を定期的に実行するので、スクリプト化する事にした。
また、保存したバックアップを復元するリカバリースクリプトと、復元データが正しいことを検証するためのチェックスクリプトも作成した。

backup.sh
# 引数やエラー処理などは一部省略

DATE=`date "+%Y%m%d"`
pg_dump -Fc -h projectz.$ENV.postgres.server.com projectz > project-backup-$DATE.dump
az login --service-principal --username <user> --password <passwd>
az storage blob upload --name project-backup-$DATE --file project-backup-$DATE.dump
echo "ok."
recover-check.sh
# 引数やエラー処理などは一部省略

DATE=$1
az login --service-principal --username <user> --password <passwd>
az storage blob download --name project-backup-$DATE --file project-backup-$DATE.dump

# create temporary postgres for check and restore data
az postgres server create --name recover-check-projectz
pg_restore -C -h restore-check-projectz.postgres.database.azure.com -d postgres project-backup-$DATE.dump

# start application and test access by curl
./start-app.sh --db=restore-check-projectz.postgres.database.azure.com
curl localhost

Aさん: 「backup.shを実行後、Azure Blob Storage上にバックアップファイルがあるし、それを使ってrecopver-check.shを走らせると、ちゃんとcurlも通っているしこれで大丈夫だろう。」
Aさん: 「定期的に実行したいからCronを使って自動実行できるようにしたら便利だな」

そこでAさんは下のようにしてCronで自動実行できるようにした。

$crontab -e
0 17 * * * /myscript/backup.sh

Aさん: 「Azure Blob Storageの中を見るとちゃんとCronで実行されてバックアップデータのファイル名が表示されているな。問題なし!」

AさんはCronが実行され、Azure Blob Storage上にバックアップファイルがあることを確認し、DBのバックアップを取得するというタスクを終了状態にさせた。

異変

それから幾許か経ったある日のこと、ProjectZの開発環境で運用手順ミスによりデータが欠損した。
「こんな時こそバックアップデータの出番だ。」 Aさんは意気揚々と答えた。
このプロジェクトではバックアップスクリプトのテストも兼ねて開発環境でもDBバックアップスクリプトを走らせてバックアップを取得していたので、それを使って元に戻そうとしたのである。

バックアップの日付を選択し、元に戻そうとリカバリースクリプトを走らせたが、なんとエラーが返ってきた。

え? なんで??

よくよく調べてみるとなんとバックアップファイルの中身がなく、ファイルサイズが0のファイルがAzure Blob Storageに保存されていたのだった。

何が起こったのか

なぜBlob Storage上にバックアップファイルが正常に保存されていなかったのか?
その原因について説明しよう。

まず、スクリプト自体は問題なかった。
手動でバックアップスクリプトやリカバリースクリプトなどは正常に実行できており、期待通りの動作であった。
問題だったのは、Cronでスクリプトを動かした際の考慮不足だったのである。
手動実行時には別の運用手順書内でENVという名の環境変数を設定していたのだが、Cronで動かす際に環境情報が設定されておらず、pg_dumpコマンドが失敗していたのである。
さらに、スクリプトがそこで終了せずazコマンドまで動作したため、空のディレクトリがazコマンドによってBlob Storage上にアップロードされたのである。
今回はファイル名があることのみ確認していたため、azコマンドが動いた時点で成功したと勘違いしてしまったのだった。

対処

ProjectZではまず暫定的な対処として、backup.sh内でENVという環境変数がなくても動くように修正した。
また、このような問題は別原因でも起こりうると考え、保存されたデータを確認する際にデータサイズも合わせて確認するよう運用手順を更新した。

教訓

今回の事例からどうすべきだったか考えよう。

まず、スクリプト化したこととそれをCronを使って自動実行すること自体はよかった。
ただ、問題だったのはめんどくさがってテストを省略したことである。
作成したものを過大評価せずちゃんとテストすることが必要であった。
なお、ProjectZで使用していたリカバリー・チェックスクリプトはDBの構築から始めるため(※)、少し時間がかかってしまっていた。
そのため、不精してしまったのであるが、これがよくなかった。
基本的に毎回全てをテストする必要はないが、最初の一回くらいは本番運用を想定して一通りテストすべきである。
※) 経費削減のため、常時テスト用DBを起動していないため

また、今回は幸いにも開発環境で問題に気づけ大事にはならなかった。
こういうこともあるので可能であれば本番で使っているものを開発でも使えるようにしていきたいものである。

今回は環境変数の設定ミスだったので一度修正すればよかったが、それ以外でも例えばツールのバージョンアップなどで起こるかもしれない。
普段使ってないものほど定期的にメンテナンスすることを心掛けよう!

Discussion