今回はEC2インスタンスのOSとしてUbuntuを選択。
CentOS系のAmazon Linuxだとデフォルトで入るバージョンが色々古くてめんどくさいからね。でもapt-get
をyum
に変えたらだいたい動くんじゃないかと期待。
wal-eとはもともとHerokuがAWS上でpostgresqlのバックアップを取るために開発していたPythonスクリプト。EC2上で走っているPostgreSQLのデータをS3に保存してくれる。
S3にデータ送信すること自体がscp
などを使えず厄介なので、そこを解決してくれているところもあるのだが、単にs3cmd
等でバックアップを取るのと違ってデータを圧縮して送ってくれていたり、今あるバックアップ一覧をコマンド一発で見れたりする部分が良いと思う。それ以上には詳しく調べてないけど、HerokuはAWS上でPostgreSQLを動かすノウハウは一番あるのではないかと言うようなサービスなので、なんとなく良さそうに思えるw
wal-eのインストール
最新を入れたい人は、gitのソースコードから入れる。
git clone https://github.com/wal-e/wal-e.git
cd wal-e
sudo python setup.py install
ちょっと古くてもいい人は、以下のようにやると良い。(pip派はpip使って下さい)
sudo apt-get install python-setuptools
sudo apt-get install python-dev libevent-dev
sudo easy_install wal-e
ちなみに、postgresql
もapt-get
で入れた。そしたらVersion9.1.8が入った。最新の9.2が使いたかったら自分でやること。
$ sudo apt-get install postgresql
$ psql --version
psql (PostgreSQL) 9.1.8
wal-e用設定
https://github.com/wal-e/wal-e にそのまま従って設定
sudo su -
umask u=rwx,g=rx,o=
mkdir -p /etc/wal-e.d/env
echo XXXXXXXXX > /etc/wal-e.d/env/AWS_ACCESS_KEY_ID
echo xxxxxxxxxxxxxxxxxxxxx > /etc/wal-e.d/env/AWS_SECRET_ACCESS_KEY
echo 's3://your-bucket-name/backups' > /etc/wal-e.d/env/WALE_S3_PREFIX
chown -R root:postgres /etc/wal-e.d
ここyour-bucket-nameはs3のUS Standardに作る。(wal-eは現状us-eastでしか動かない。Issueは出来ているのでもうすぐ出来るようになるかもしれないけれど。)
さらに以下のブログによると悪意のあるデータ削除対策としてVersioningを有効にすると良いらしい。
http://blog.opbeat.com/2013/01/07/postgresql-backup-to-s3-part-one/
wal-eでバックアップを取る
まず必要な環境をInstall
sudo apt-get install daemontools pv lzop mbuffer
wal-eでログを取るためにはwal_levelがarchive以上が必要。
wal_levelはminimal(デフォルト), archive, hot_standbyの順に上がっていく。
http://www.postgresql.jp/document/9.1/html/runtime-config-wal.html
今回はarchiveでやるが、hot_standbyにするときは、
http://blog.gomiso.com/2012/02/13/adventures-in-scaling-part-3-postgresql-streaming-replication/
を参考にすると良さそう。
#------------------------------------------------------------------------------
# WRITE AHEAD LOG
#------------------------------------------------------------------------------
wal_level = archive # hot_standby in 9.0 is also acceptable
archive_mode = on
archive_command = '/usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e wal-push %p'
archive_timeout = 300
と設定して、リスタート!
sudo /etc/init.d/postgresql restart
なお、archive_timeout
は少し大きめの5分間隔にした。本当にデータを失いたくないなら、hot_stanbyにするべきだし、archive-modeで1分は短すぎてパフォーマンス的に嬉しくない気がする。詳しくはpostgresqlのドキュメント参照。
archive_command
では念のため、envdir
やwal-e
コマンドをfull pathで指定しておくと良いと思う。自分はこのコマンドが走るときだけ、/usr/local/bin
がPATHに入ってないことに気づかずにだいぶハマった。なお、archive_command
における%p
は格納されるファイルの絶対パスを表す。
http://www.postgresql.jp/document/9.1/html/runtime-config-wal.html#GUC-ARCHIVE-COMMAND
一応archive_command
に設定したwal-push
がちゃんと動くか確かめておこう。(0000000100000000000000B4
の部分は各自の環境で、ls /var/lib/postgresql/9.1/main/pg_xlog
した結果の中から一つ選ぶ。)
sudo su - postgres
envdir /etc/wal-e.d/env wal-e wal-push /var/lib/postgresql/9.1/main/pg_xlog/0000000100000000000000B4
うまくいってそうだったらbackup-push
してみる。
sudo su - postgres
envdir /etc/wal-e.d/env wal-e backup-push /var/lib/postgresql/9.1/main/
envdir /etc/wal-e.d/env wal-e backup-list # ちゃんととれたか確認
ここで、/var/lib/postgresql/9.1/main/
はPostgreSQLのデータが入ったディレクトリのこと。postgresql.conf
に以下の様な記述があるはず。
data_directory = '/var/lib/postgresql/9.1/main'
ちなみに自分たちは、これを追加したEBS上に設定しなおしている。
バックアップタスクをcronに登録
sudo su - postgres
crontab -e
とし、以下のように打つ。
0 4 * * * /usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e backup-push /var/lib/postgresql/9.1/main/
これで毎日午前4時にバックアップがとられる。
(dateコマンドを打ってTimezoneがJSTになっていることを確認すること)
JSTにするには以下のようにすれば良い。
sudo mv /etc/localtime /etc/localtime.default
sudo ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
バックアップからのリカバリ
バックアップからのリカバリはこれが参考になる。
https://gist.github.com/ruckus/2293434
とりあえずリカバリ実験用に無理やりpostgresをkillする。
sudo pkill -9 postgres
ここからリカバリするには、
sudo su - postgres
cp /usr/share/postgresql/9.1/recovery.conf.sample /var/lib/postgresql/9.1/main/recovery.conf
emacs /var/lib/postgresql/9.1/main/recovery.conf
でrestore_command
にwal-fetchを設定する。
restore_command = '/usr/bin/envdir /etc/wal-e.d/env /usr/local/bin/wal-e wal-fetch %f %p'
これで、sudo /etc/init.d/postgresql start
やpg_ctl start
などでPostgreSQLを再起動させたらWALが残っているところまで(ほぼ全部)復元され、サービスが再開される。
なお、DBがrecoveryされたら、recovery.conf
はrecovery.done
という名前になる。
データそのものが失われてしまっていた場合
ハードディスクが飛んでデータも消えちゃったことを想定し、
rm -rf /var/lib/postgresql/9.1/main/
する。
これは、
umask u=rwx,g=,o=
envdir /etc/wal-e.d/env wal-e backup-fetch /var/lib/postgresql/9.1/main/ LATEST
としてbackup-push
したデータから基本的な構成を復元した後、先ほどの手順に従ってrecovery.conf
を作成してpostgresqlを再起動したら、wal_push
されs3にアーカイブが残っている部分まで復元できる。
不要なアーカイブログとチェックポイントのクリーンアップ
過去のbackup-push
のデータや、wal-push
で送られたアーカイブログは際限なく溜まっていく。S3なのでこれらのファイルを削除しないという方針でいいかもしれないが、DBを復元する際に必要のない、直前のbackup-push
より前のものは削除してしまっても問題ない。
これを行うためには、以下のコマンドを叩けば良い。
envdir /etc/wal-e.d/env wal-e backup-list | tail -n1 | cut -f1 | xargs envdir /etc/wal-e.d/env wal-e delete --confirm before
もう少し慎重に、例えば、3つ前まで残しておきたいというときは、tail -n1
をtail -n3 | head -n1
に変えればよい。
ハマったポイント
設定に書くときは、wal-e
を/usr/local/bin/wal-e
にしないとダメだったという話。エラーから検索してたどり着く人もいるかもと思って書いておく。
NOTICE: pg_stop_backup cleanup done, waiting for required WAL segments to be archived
WARNING: pg_stop_backup still waiting for all required WAL segments to be archived (60 seconds elapsed)
HINT: Check that your archive_command is executing properly. pg_stop_backup can be canceled safely, but the database backup will not be usable without all the WAL segments.
WARNING: pg_stop_backup still waiting for all required WAL segments to be archived (120 seconds elapsed)
HINT: Check that your archive_command is executing properly. pg_stop_backup can be canceled safely, but the database backup will not be usable without all the WAL segments.
これは調べても、ディスク容量が足りないことやpermissionの設定が間違っているのではといった情報しか出てこないのだが、どれも当てはまらなかった。ここで、走らせるarchive_command
を
archive_command = 'date >> test.txt && echo %p >> test.txt && envdir /etc/wal-e.d/env wal-e wal-push %p || echo $? >> test.txt'
とし、/var/lib/postgresql/9.1/main/test.txt
を確認することで、エラーが出た時の終了コードが111となっていることがわかり、daemontoolsのenvdirの項目を見ると、envdirはエラーコード111で死ぬことがわかり、原因を特定できた。
実際は以下の様なエラーが出ていたみたい。
envdir: fatal: unable to run wal-e: file does not exist
これで、
envdir /etc/wal-e.d/env wal-e wal-push %p
を
envdir /etc/wal-e.d/env /usr/local/bin/wal-e wal-push %p
に変えればいいことがわかった。
bashrc的なものはshを読み込んだ時しか動かないので、手元で走らせても動くけど、postgresが走らせるときには動かないみたいなことが起こるんだなと思った。気づけば当たり前なんだけど・・・。