はじめに
以前、WordPressのデータベースをAmazon S3へバックアップするプラグインを作りました。
- WordPress.org: https://wordpress.org/plugins/ozeki-database-backup-for-s3/
- GitHub: https://github.com/ozekihiroshi/secure-s3-storage-for-wordpress
最初は、
WordPress database
↓
SQL dump
↓
gzip
↓
Amazon S3
という構成でした。
しかし「サイトを復旧する」というところまで考えると、すぐに問題にぶつかりました。
データベースのバックアップが成功したことと、WordPressサイトを復旧できることは同じではない。
記事本文や設定が戻っても、画像やPDFなどのメディアファイルが失われていれば、サイトは元には戻りません。
そこで現在のプラグインでは、DBだけでなく wp-content/uploads もS3へバックアップできるようにしました。
ただ、今回考えたいのは機能追加そのものではありません。
バックアップとは「何を保存したか」ではなく、「事故の後にどう戻すか」から設計すべきではないか。
ということです。
1. DBをバックアップしただけではWordPressは戻らない
WordPressサイトを大まかに分解すると、少なくとも次の要素があります。
WordPress site
├── Database
├── wp-content/uploads
├── plugins
├── themes
├── wp-config.php
└── Web / PHP / server configuration
DBには投稿、ユーザー、設定、メディアのメタデータなどがあります。
一方、画像やPDFなどの実体は通常 wp-content/uploads 以下にあります。
したがって、
Database restored
だけでは、
WordPress site restored
にはなりません。
例えば、
記事本文 → 戻った
画像へのURL → 戻った
画像ファイル本体 → ない
という状態が起こり得ます。
ページは表示されても画像だけ404です。
これでは「復旧した」とは言いにくい。
DBバックアップから始めたプラグインをuploadsまで広げた理由はここにあります。
2. バックアップは「保存」ではなく「復旧」から考える
バックアップを考えるとき、
何を保存するか
から始めがちです。
しかし事故時に必要なのは、
何が起きたとき
どの時点へ
どこまで
どれくらいの時間で
戻したいか
です。
ここでRPOとRTOという考え方が出てきます。
RPO (Recovery Point Objective)
どこまでのデータ損失を許容するか
RTO (Recovery Time Objective)
どれくらいの時間で復旧したいか
例えば、
DBは最大1日分の損失まで許容
uploadsは正常な最新世代まで必要
復旧は2時間以内
という要求なら、それに応じてバックアップ頻度、保存方法、復元手順を決める必要があります。
単に backup.zip が存在するだけでは、復旧できるとは限りません。
3. AWSにもバックアップ機能がある。それでもなぜアプリ側で取るのか
AWSには優れたバックアップ機能があります。
Amazon RDSには、automated backups、manual snapshots、point-in-time recovery (PITR) があります。AWS Backupを使えば、複数サービスのバックアップをポリシーで管理することもできます。
それでもWordPress側からSQL dumpをS3へ保存する意味はあると考えています。
目的が少し違うからです。
AWS側のバックアップはインフラストラクチャとして非常に強力です。
一方、WordPress側からSQL dumpを作れば、
WordPress
↓
application-level logical backup
↓
SQL
↓
S3
という、アプリケーション単位で扱いやすいバックアップになります。
SQLファイルなら、
- 別のMySQL/MariaDB環境へ持っていく
- 開発環境で確認する
- 別サーバへ移行する
- WordPress単位で保持状況を確認する
といった用途にも使いやすくなります。
つまり、
AWS側のバックアップとWordPress側のバックアップは、必ずしも二者択一ではない。
ということです。
参考:
- Amazon RDS backup / restore
https://docs.aws.amazon.com/AmazonRDS/latest/gettingstartedguide/managing-backup-restore.html - AWS Backup for Amazon RDS
https://docs.aws.amazon.com/aws-backup/latest/devguide/rds-backup.html
4. レプリケーションはバックアップではない
データベースの冗長化やReplicaは可用性を高めるために重要です。
しかし、
冗長化とバックアップは目的が違う
と考えた方がよいです。
例えば誤って、
DELETE FROM wp_posts;
を実行し、その変更が正規の更新としてレプリカにも伝播すれば、
Primary → 壊れた
Replica → 同じ変更が反映された
となります。
アプリケーションの不具合による破壊的更新も同様です。
レプリケーションは主に「現在のサービスを止めにくくする」ための仕組みです。
バックアップには、
過去の正常な状態へ戻るための復旧点を持つ
という別の役割があります。
もちろんRDSのPITRなど、AWSにはそのための仕組みもあります。
大切なのは、
High Availability
≠
Backup
≠
Disaster Recovery
として混同しないことです。
5. 「自分で壊す」ことも事故である
バックアップというと、
サーバ故障
マルウェア
ランサムウェア
不正アクセス
を想像しがちです。
しかし実際には、自分自身が壊すこともあります。
例えば、
SQLを間違えた
ファイルを削除した
プラグイン更新で壊れた
migrationに失敗した
設定変更を誤った
スクリプトが想定外のファイルを消した
などです。
悪意がなくてもデータは壊れます。
だから、
最新版のコピーをもう一つ持つだけでは不十分
です。
必要なのは、
現在
↑
1日前
↑
3日前
↑
1週間前
↑
1か月前
のような複数の復旧ポイントです。
6. マルウェア対策でも「過去へ戻れること」は重要
感染や改ざんに気づいたとき、現在の状態しか残っていなければ、そこから正常なデータを切り分けなければなりません。
一方、侵害前と判断できる復旧ポイントがあれば、
侵害前のDB
+
侵害前のuploads
へ戻せる可能性があります。
ただし、
感染を確認した時刻
≠
侵入された時刻
かもしれません。
またDBやuploadsを戻しても、管理者アカウント、APIキー、SSH鍵、wp-config.php、plugin/theme、cron、Webサーバ設定、OSなどに侵害の痕跡が残っている可能性があります。
したがって、
バックアップから戻すことと、インシデント対応が完了することは同じではありません。
侵入経路を塞ぎ、必要な認証情報を更新し、永続化の有無を確認したうえで、正常と判断できる復旧ポイントを使う必要があります。
それでも「過去へ戻れる選択肢」があることには大きな意味があります。
7. uploadsは「zipを一つ作ればよい」のか
小規模なサイトなら、
wp-content/uploads
↓
zip
↓
S3
でも一つの方法です。
しかしuploadsが大きくなると、
- ZIP生成用のディスク容量
- PHPの実行時間
- 大きなファイル
- 中断時のやり直し
- 何が正常に保存されたかの確認
といった問題が出ます。
そこで現在の実装では、uploads内のファイルを個別のS3 objectとして扱っています。
概念的には、
uploads
↓
file enumeration
↓
sorted inventory
↓
SHA-256
↓
S3 upload
↓
verification
↓
completion marker
という流れです。
大きなファイルにはmultipart uploadを使います。
ここで重視したのは、
「何かがS3に置かれている」
ではなく、
どのファイルを対象とし、何が保存され、最後まで検証されたか
を残すことです。
8. completion markerを最後に作る理由
例えば1万ファイルをバックアップするとして、
9,950 files uploaded
50 files failed
だったとします。
S3には大量のファイルがあるので、一見するとバックアップが存在するように見えます。
しかし、これは完全なバックアップではありません。
そのため現在の実装では、
inventory
+
objects
+
verification
が完了してから、最後にcompletion markerを作ります。
概念的には、
completion marker exists
↓
このジョブは最後まで検証処理を通過した
という意味です。
もちろん、これだけで「必ず復元できる」と証明できるわけではありません。
最終的にはrestore testが必要です。
ただ、処理途中のバックアップと完了したバックアップを区別できることは、復旧時の判断をかなり減らします。
9. checksumを取る理由
現在のメディアバックアップでは、各ファイルについてSHA-256を計算し、inventoryへ記録しています。
これは、
copyした
だけではなく、
copyしたものを検証できる
ようにするためです。
復元後にも同じSHA-256を計算すれば、バックアップ時点のファイルと一致しているかを比較できます。
つまり、
backup
↓
restore
↓
verify
までを一つの流れとして考えています。
10. 現時点では「完全自動リストア」までは実装していない
ここは現在のプラグインの状態として明確にしておきます。
現在のバージョンでは、
- DB backup
- uploads backup
- inventory
- SHA-256
- upload verification
- completion marker
まで実装しています。
一方、メディアについて本番向けの自動restore commandはまだ提供していません。
つまり、
完全なWordPress復元を目標にバックアップ側を拡張しているが、復元操作まで完全自動化したわけではない
という状態です。
これは重要な区別です。
「バックアップがある」と「復元手順が完成している」は別だからです。
今後、本当に復旧を重視するなら、
restore procedure
restore verification
recovery drill
まで必要になります。
11. 復旧時間を短くするには「事故後に考えない」
障害時に時間がかかるのは、データ転送そのものとは限りません。
むしろ、
何が残っているのか
どれを戻すのか
どの時点が正常なのか
このバックアップは完全なのか
DBとuploadsの時点は整合しているのか
を調べる時間が大きくなります。
つまりRTOを短くするには、
事故が起きてから復旧方法を考えない
ことが重要です。
平常時に、
バックアップ形式
保存場所
世代数
復旧手順
検証方法
必要な権限
を決める。
可能なら別環境へのrestore testも行う。
ここから先は、バックアップ機能というより運用設計です。
12. バックアップ自体を消されないようにする
S3へ保存したから安全、とも限りません。
本番WordPressを操作できる認証情報が、同じ権限でバックアップの削除までできるなら、
本番を破壊
+
バックアップも削除
ということが理論上可能です。
本格的に考えるなら、
- S3 Versioning
- Lifecycle
- S3 Object Lock
- 別AWSアカウントへのコピー
- 最小権限IAM
なども検討対象になります。
S3 Versioningは過去のobject versionを保持でき、誤操作やアプリケーション障害からの復旧に利用できます。
S3 Object LockはVersioningを有効にしたbucketでWORM型の保護を行い、設定した保持期間中の削除や上書きに対する保護を追加できます。
参考:
- S3 data protection
https://docs.aws.amazon.com/AmazonS3/latest/userguide/data-protection.html - S3 Object Lock
https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html
ここで重要なのは、
本番を壊せる権限と、バックアップを消せる権限をできるだけ分離する
ことです。
13. 「全部消して作り直す」以外の選択肢を持つ
事故が起きると、
全部消して最初から構築し直した方が安全ではないか
という判断になることもあります。
侵害範囲が分からない場合には、そのような対応が必要なケースもあります。
一方、
操作ミスによるDB破壊
特定時点以降のデータ不整合
uploadsの誤削除
プラグイン更新による障害
などなら、正常な復旧ポイントが明確であれば、その時点へ戻した方が早い場合があります。
そのためにも、
どの時点の
何が
どこに
どの状態で保存されているか
が分かる必要があります。
バックアップは単なる保険ではなく、
復旧時の選択肢を増やすもの
でもあります。
14. どこまで取れば「WordPressの完全バックアップ」なのか
DBとuploadsを保存すれば、WordPressの重要な部分をかなり保護できます。
しかし厳密には、それだけで環境全体を完全再現できるとは限りません。
例えば、
plugins
themes
wp-config.php
Web server settings
PHP settings
Cron
OS packages
DNS
TLS certificates
AWS infrastructure
などがあります。
これらはすべて同じ方法でバックアップする必要はありません。
例えば、
- plugins/themesはGitや配布元から再取得する
- infrastructureはTerraform/CloudFormationで再構築する
- secretsは専用の仕組みで管理する
という考え方もできます。
つまり、
何が失われたら困るか、何は再生成できるか、何は別の仕組みで戻せるか
を整理する必要があります。
uploadsを追加したのも、この「復旧に必要なもの」を考えた結果です。
まとめ
WordPressのS3バックアップをDBだけで終わらせず、uploadsまで広げた理由は、
バックアップ対象を増やしたかったから
ではありません。
本当に考えたかったのは、
このサイトを事故後に戻せるか
ということでした。
DBのバックアップが成功しても、画像が失われていればサイトは戻りません。
レプリカがあっても、誤操作が伝播すれば過去には戻れません。
S3にファイルがあっても、処理途中の不完全なバックアップなら復旧には使えません。
そしてバックアップがあっても、復元手順を一度も試していなければ、本番障害時に初めて手順を考えることになります。
そのため、バックアップを設計するときには、
何を保存するか
より先に、
何が起きたとき
どの時点まで
何を
どのくらいの時間で
どうやって戻すか
を考える。
その結果として必要なバックアップが決まる。
現在のプラグインも、まだ「完全自動復旧」まで到達しているわけではありません。
しかし、
DB
+
uploads
+
inventory
+
checksum
+
verification
+
completion marker
まで拡張したことで、単なる「保存」から「復旧」を考えるための形には近づいてきました。
バックアップの価値は正常時にはほとんど見えません。
実際に価値が出るのは、何かを失ったときです。
だからこそ、
バックアップは、保存できたかではなく、戻せるかで評価する。
これを基準に考えていきたいと思っています。