バックアップとリストア手順¶
この文書は、production で state を持つデータが現在どう保護されているか を整理した正本です。単なる理想形ではなく、実際に repo にある workflow / script / just recipe / infra role と、2026-05-26 時点の確認結果を基準にまとめています。
まずここだけ読めばよい¶
現在のバックアップ体制は、まず次の 3 分類で理解すると全体像を掴みやすくなります。
- 自動で守れているもの
Vendure PostgreSQL と WordPress MariaDB は、どちらも 毎時 backup / 30 日保持 まで実運用へ反映済みです。失敗時は Slack alert を出し、WordPress は VPS local、 Postgres は GitHub Actions artifact に短期 fallback を残します。 - 意図的に backup 対象外としているもの
Redis と Storefront maintenance KV は、再構築・再投入前提の運用 state なので、formal backup の対象にしていません。 - 将来検討として残しているもの
Vendure asset と WordPress media は、primary storage のみで運用しており、 secondary backup / versioning / replication はまだ入れていません。
この文書の使い分け¶
| 目的 | まず参照する節 | 補足 |
|---|---|---|
| 現在の保護状況を把握したい | production バックアップ状況サマリ | 何が自動 backup 対象で、何が対象外かを確認する |
| 定期演習として restore drill を実施したい | 図で見る restore drill と各系統の Restore drill 節 |
staging を上書きする actual 実行かどうかを先に確認する |
| 実際に DB を復元したい | 各系統の リストア 節 |
復元先 DB は上書きされるため、対象環境を取り違えない |
| 契約上の窓口や応答条件を確認したい | 保守・サポート計画書 | 本書は契約条件の正本ではない |
実行前チェックリスト¶
restore drill / 本番復元のどちらでも、実行前に次を確認します。
- 対象環境(staging / production)
- 使用する backup file 名
- 復元先 DB が 上書き対象 であること
- 実行時点のコードと DB schema の整合性
- 実行開始/終了時刻、所要時間、結果を記録する担当者
- 本番 dump を扱う場合の機密情報取り扱い
Production DB 操作ガード¶
production の DB state を変更し得る標準入口は、直前 backup または明示 confirm を要求します。
repo の wrapper を迂回した raw ssh / flyctl / psql / docker compose 直叩きは、このガードの対象外です。
| 対象 | 標準入口 | production での挙動 |
|---|---|---|
| WordPress WP-CLI | just wp-cli-vps "<command>" production |
read-only allowlist 外の command は ritsubi-wordpress-backup.service 成功後に実行 |
| WordPress PHP snippet | just wp-php-vps-file <file> production |
PHP の書き込み有無は判定せず、常に ritsubi-wordpress-backup.service 成功後に実行 |
| WordPress SSH shell | just wp-ssh production production-shell |
confirm 必須。DB 操作が必要なら wp-cli-vps / wp-php-vps-file を優先 |
| Vendure migration / drift repair | just migrate-fly production / just vendure-drift-* production |
just backup-postgres-production 成功後に実行 |
| Vendure drift sync (data 書き込み含む) | production では廃止。staging のみ just vendure-drift-sync staging / just vendure-drift-sync-baseline staging |
production は fixture repair を経由し得る一括 sync を使わない。just vendure-drift-audit production の結果を見て、backup 成功後に対象を絞った audited repair を実行 |
| Vendure deploy release command | just deploy-fly production ... / just manual-deploy-vendure production / GitHub Actions production deploy |
Fly release command 前に just backup-postgres-production を実行 (manual-deploy 経由は image build と並列に backup を起動し、flyctl deploy の手前で wait。手動 backup 済みの再実行では precompleted_backup=<dump名またはR2 URI> を渡す) |
| Vendure production DB proxy | just proxy-postgres production auto production-db-proxy |
confirm 必須、proxy 開始前に just backup-postgres-production を実行 |
| Vendure Fly console | just fly-console-vendure production production-console |
confirm 必須。DB 操作は監査済み recipe を優先 |
| production fixture sync | sync-vendure-test-customers* / sync-vendure-reservation-products* |
production では fail-closed |
production バックアップ状況サマリ¶
| 対象 | 正本 | 自動バックアップ | 現在の状態 | 復元導線 |
|---|---|---|---|---|
| Vendure PostgreSQL | Fly app ritsubi-postgres-db |
GitHub Actions backup-postgres-prd.yml -> R2 postgres/ |
正常: 毎時 backup / 720 時間保持。2026-05-26 に latest dump の pg_restore -l と manual backup success を確認 |
pg_restore + just proxy-postgres <env> |
| WordPress MariaDB | production VPS 上の MariaDB | ritsubi-wordpress-backup.timer -> R2 wordpress/production/db/ |
正常: 毎時 backup / 720 時間保持。2026-05-26 に DB container dump へ修正し、timer / R2 / local fallback / alert を確認 | gunzip -c ... から wp-cli db import - へ流し込む |
| Vendure asset | R2 ritsubi-ec-assets-prod |
なし | 現行方針: primary storage のみ(独立バックアップは将来検討) | 同一バケット上のオブジェクト再取得のみ |
| WordPress media | R2 ritsubi-wp-media-prod |
なし | 現行方針: primary storage のみ(独立バックアップは将来検討) | 同一バケット上のオブジェクト再取得のみ |
| Storefront maintenance state | Cloudflare KV ec-storefront-config の storefront:maintenance |
なし | 対象外: 手動再作成前提 | Cloudflare KV へ JSON 再投入 |
| Redis session / shared cache | Fly Redis ritsubi-redis-prod |
formal backup は未定義 | 対象外: セッション/共有キャッシュのみ、再構築前提 | 再ログイン / cache warm-up |
[!IMPORTANT] production の DB backup は 毎時 cadence + 720 時間保持(30 日)へ反映済みです。Vendure PostgreSQL は GitHub Actions success + manual backup success、WordPress MariaDB は production VPS 上の timer active + manual upload success まで確認しました。backup 失敗時の Slack alert は
BACKUP_NOTIFY_WEBHOOK_URLを正本にします。
図で見る production バックアップ体制¶
flowchart LR
subgraph prod["production で state を持つもの"]
pg["Vendure PostgreSQL\nFly app: ritsubi-postgres-db"]
wpdb["WordPress MariaDB\nproduction VPS"]
asset["Vendure asset\nR2: ritsubi-ec-assets-prod"]
media["WordPress media\nR2: ritsubi-wp-media-prod"]
redis["Redis session / shared cache"]
kv["Storefront maintenance KV"]
end
gha["GitHub Actions\n毎時 :05"]
timer["systemd timer\n毎時 :15"]
backup_bucket["R2 backup bucket\nritsubi-ecommerce-backup\n30 日保持"]
alert["Slack alert\n:warning:"]
fallback["短期 fallback\nWP: VPS local 72h\nPG: Actions artifact 3d"]
rebuild["再構築 / 再投入前提"]
future["将来検討\nsecondary backup なし"]
pg --> gha --> backup_bucket
wpdb --> timer --> backup_bucket
gha -. failure .-> alert
timer -. failure .-> alert
gha -. R2 upload failure .-> fallback
timer -. R2 upload failure .-> fallback
asset --> future
media --> future
redis --> rebuild
kv --> rebuild
この図の読み方は次のとおりです。
- 実際に backup されているのは DB 2 系統だけです。
- DB backup の失敗は通知対象です。通知 webhook は AWS Secrets Manager
b2b-ecommerce/prod/infraのBACKUP_NOTIFY_WEBHOOK_URLを使います。 - asset / media は失われてよいのではなく、現時点では primary storage のみで受容している領域です。
- Redis / KV は重要度が低いのではなく、正本データではないため再構築方針を採っています。
要件目標と現在の到達状況¶
要件上の目標は
docs/01-requirements/consolidated/requirements-specification.md
の RPO 1 時間 / RTO 30 分です。現在の到達状況は次のとおりです。
| 項目 | 目標 | 現在の到達状況 |
|---|---|---|
| Vendure PostgreSQL | RPO 1 時間相当 | 達成。毎時 HH:05 UTC の custom dump と 720 時間保持で、復元点は最大 1 時間以内。RTO 30 分は 初回 actual restore drill 成功(24 秒) まで確認済み |
| WordPress MariaDB | RPO 1 時間相当 | 達成。毎時 HH:15 UTC の gzip SQL dump と 720 時間保持で、復元点は最大 1 時間以内。RTO 30 分は 初回 actual restore drill 成功(31 秒) まで確認済み |
[!NOTE] Vendure asset / WordPress media は 2026-05-26 時点では primary storage のみを許容し、独立バックアップは将来検討とします。Redis と Storefront maintenance KV は再構築前提の運用 state であり、restore 前提の backup target には含めません。
Alert と障害時 fallback¶
DB backup の alert は、backup 処理そのものが失敗した事実を operator に出すためのものです。 復元可能性の判定は、alert だけでなく R2 object の存在、dump の読込検証、必要に応じた restore drill で確認します。
| 対象 | Alert 条件 | fallback | 保持期間 |
|---|---|---|---|
| Vendure PostgreSQL | dump 生成失敗、R2 upload 失敗、R2 retention cleanup 失敗 | R2 upload 失敗時に GitHub Actions artifact へ dump を保存 | 3 日 |
| WordPress MariaDB | dump 生成失敗、R2 upload 失敗、R2 retention cleanup 失敗 | VPS local /var/backups/ritsubi-wordpress/ に gzip dump を保存 |
72 時間 |
通知 payload は :warning: を使い、Ritsubi Backup Alert として投稿します。
webhook URL は repo に書かず、AWS Secrets Manager b2b-ecommerce/prod/infra の
BACKUP_NOTIFY_WEBHOOK_URL を正本にします。GitHub Actions の汎用 failure finalizer が使う
SLACK_WEBHOOK_URL は fallback として扱います。
障害対応の初動は次の順序で確認します。
- Slack alert の対象(Postgres / WordPress)と state(
failed/degraded)を読む。 - R2 に同時刻の backup object があるか確認する。
- R2 upload failure の場合は fallback を確認する。Postgres は Actions artifact、 WordPress は VPS local backup を見る。
- dump が存在しても、Postgres は
pg_restore -l、WordPress はgunzipとwp_optionsの存在確認で、壊れた復元点ではないことを確認する。
Postgres Actions artifact の確認:
gh run list --workflow backup-postgres-prd.yml --limit 5
gh run view <run-id> --json conclusion,artifacts
gh run download <run-id> -n postgres-backup-local-fallback -D tmp/postgres-backup-local-fallback
WordPress local fallback の確認:
just wp-ssh production production-shell
sudo ls -lh /var/backups/ritsubi-wordpress/
図で見る restore drill¶
flowchart LR
dry_run["事前 rehearsal\nRESTORE_DRILL_DRY_RUN=true\njust restore-drill all staging latest"]
backups["R2 backup bucket\npostgres/ と wordpress/production/db/"]
actual["actual 実行\njust restore-drill all staging latest restore-drill-staging"]
stg_pg["staging Vendure DB\nschema recreate + restore + 件数確認"]
stg_wp["staging WordPress DB\nimport + URL rewrite + verify"]
record["記録\nbackup file / 所要時間 / 結果"]
dry_run --> actual
backups --> actual
actual --> stg_pg
actual --> stg_wp
stg_pg --> record
stg_wp --> record
初回の actual 実行では、次を確認済みです。
- Postgres:
ritsubi_vendure_20260410143247.dumpを staging へ復元し、24 秒 - WordPress:
wordpress-db-20260410T141501Z.sql.gzを staging へ復元し、31 秒 - ただし 月次の継続実績はまだ 1 回分なので、以後はこの記録を積み上げます
1. Vendure PostgreSQL(Fly.io Docker Postgres)¶
1.1 現行バックアップ導線¶
- GitHub Actions
backup-postgres-prd.ymlが毎時5 * * * *(UTC)で起動する。 - workflow は AWS Secrets Manager から
production_vendureとproduction_infraを読み込む。 - workflow は production DB と同じ major の
postgresql-client-17を PGDG から入れる。pg_dumpversion mismatch 時だけscripts/ops/backup-postgres-fly.shが Docker fallback (postgres:<major>-alpine) を使う。 scripts/ops/backup-postgres-fly.shが以下を実行する。fly proxyで production DB appritsubi-postgres-dbに接続pg_dump -Fcで custom format dump (*.dump) を作成- Cloudflare R2 バケット
ritsubi-ecommerce-backupのpostgres/プレフィックスへ upload scripts/ops/backup-postgres-fly.shは upload 後にpostgres/配下を走査し、 720 時間(30 日)より古い dump を削除する。- 手動実行の入口は
just backup-postgres-production。workflow と同じscripts/ops/backup-postgres-fly.shを使う。snapshot-staging-vendure-dbは staging 専用の R2 snapshot 入口であり、production 用 recipe は存在しない。 - R2 upload / retention cleanup / dump 生成が失敗した場合は
BACKUP_NOTIFY_WEBHOOK_URL(またはSLACK_WEBHOOK_URL)へ:warning:付きで通知する。GitHub Actions 上の R2 upload 失敗では local dump を削除せず、postgres-backup-local-fallbackartifact として 3 日だけ保存する。
1.2 現在の確認結果¶
- 2026-05-26 時点で直近の
backup-postgres-prd.ymlは success 継続。 - R2
postgres/の latest dump としてritsubi_vendure_20260526024412.dumpの生成を確認した。 postgres:17-alpineのpg_restore -lでritsubi_vendure_20260526011213.dumpを読み、custom format dump と TOC を確認した。just backup-postgres-productionを手動実行し、R2 upload と期限切れ object cleanup が成功した。- R2 upload / retention cleanup / dump 生成失敗時は
BACKUP_NOTIFY_WEBHOOK_URL(またはSLACK_WEBHOOK_URL)へ:warning:付きで通知する。 - R2 upload 失敗時は local dump を削除せず、GitHub Actions 上では
postgres-backup-local-fallbackartifact として 3 日保持する。
1.3 確認コマンド¶
直近 run の成否:
gh run list --workflow backup-postgres-prd.yml --limit 5
R2 上の最新 dump:
SECRETS_CONFIG=production_infra SECRETS_INCLUDE_SHARED=0 ./scripts/ops/with-env.sh -- bash -lc '
set -euo pipefail
endpoint="${BACKUP_R2_ENDPOINT:-https://${BACKUP_R2_ACCOUNT_ID}.r2.cloudflarestorage.com}"
env -u AWS_PROFILE -u AWS_REGION -u AWS_DEFAULT_REGION \
AWS_ACCESS_KEY_ID="$BACKUP_R2_ACCESS_KEY_ID" \
AWS_SECRET_ACCESS_KEY="$BACKUP_R2_SECRET_ACCESS_KEY" \
AWS_REGION=auto AWS_DEFAULT_REGION=auto \
aws --region auto s3 ls "s3://${BACKUP_R2_BUCKET_NAME:-ritsubi-ecommerce-backup}/postgres/" \
--endpoint-url "$endpoint" | tail -n 5
'
1.3.1 一回限りの production migration / data 補正で守る順序¶
release image を再 deploy せず、手元から production DB に一回限りの migration / data 補正を当てる場合でも、次の順序を崩さない。
just backup-postgres-productionjust vendure-drift-migrate production- 必要なら
just vendure-drift-audit production - 対象行の確認 SQL またはスモークテスト
[!IMPORTANT]
just vendure-migrate-run production_vendureは手元から shared env の DB に直接つなぐ導線ではない。 Fly private hostname (*.internal) を解決できず失敗しうるため、production の手動 migration はvendure-drift-migrateの proxy 導線を使う。
1.4 リストア¶
backup-postgres-fly.sh が生成するのは plain SQL ではなく custom format dump
(*.dump) なので、復元は psql ではなく pg_restore を使う。
- R2 から対象 dump をローカルへ取得する。
SECRETS_CONFIG=production_infra SECRETS_INCLUDE_SHARED=0 ./scripts/ops/with-env.sh -- bash -lc '
set -euo pipefail
endpoint="${BACKUP_R2_ENDPOINT:-https://${BACKUP_R2_ACCOUNT_ID}.r2.cloudflarestorage.com}"
env -u AWS_PROFILE -u AWS_REGION -u AWS_DEFAULT_REGION \
AWS_ACCESS_KEY_ID="$BACKUP_R2_ACCESS_KEY_ID" \
AWS_SECRET_ACCESS_KEY="$BACKUP_R2_SECRET_ACCESS_KEY" \
AWS_REGION=auto AWS_DEFAULT_REGION=auto \
aws --region auto s3 cp \
"s3://${BACKUP_R2_BUCKET_NAME:-ritsubi-ecommerce-backup}/postgres/<file>.dump" \
"./<file>.dump" \
--endpoint-url "$endpoint"
'
- 復元先に応じて proxy を張る。
# staging
just proxy-postgres staging
# production は confirm 必須。recipe が直前 backup を作ってから proxy を開く
just proxy-postgres production auto production-db-proxy
DATABASE_URLから対象 DB の user / dbname を確認し、pg_restoreを実行する。
# ローカル
pg_restore -h localhost -p 5432 -U postgres -d vendure \
--clean --if-exists --no-owner --no-privileges ./<file>.dump
# staging (proxy: 15433)
pg_restore -h localhost -p 15433 -U <staging-user> -d <staging-dbname> \
--clean --if-exists --no-owner --no-privileges ./<file>.dump
復元後は、少なくとも次を確認します。
- 必要なテーブルが復元されていること
channel件数など最低限の件数確認が取れること- 対象環境でアプリケーション接続エラーが出ていないこと
1.5 Restore drill¶
Postgres の restore drill は、production backup を staging
DB へ実際に戻す演習として just restore-drill に統一しました。
事前 rehearsal(non-destructive):
RESTORE_DRILL_DRY_RUN=true just restore-drill postgres staging latest
actual 実行(staging DB を上書き):
just restore-drill postgres staging latest restore-drill-staging
recipe は次を自動実行します。
- R2
postgres/から対象 dump(既定: 最新)を取得する - staging Fly Postgres へ proxy を張る
publicschema を再作成してpg_restoreを流し込むinformation_schema.tablesとchannel件数で最低限の復元確認を行う
演習記録には、実行開始/終了時刻、使用した backup file、所要時間、詰まった点 を残してください。
[!NOTE] 2026-04-10 に
just restore-drill all staging latest restore-drill-stagingを actual 実行し、ritsubi_vendure_20260410143247.dumpで staging restore success を確認しました。publictable 125 /channel1、所要 24 秒で完了しています。
1.6 現時点の漏れ¶
- medium: restore drill の 初回 actual 証跡 は得られたが、月次の定例実行履歴は まだ 1 回分のみ。RTO 30 分の継続達成は、今後の月次実績で追跡する。
2. WordPress / MariaDB on VPS¶
[!NOTE] 2026-05-26 に production restore と backup 修復を実施しました。 障害前に有効な production DB backup として確認できた最新は
wordpress-db-20260517T221501Z.sql.gzで、その後の 2026-05-18 08:15 JST 以降の backup はmysqldump不在により 20 byte 程度の空 gzip でした。復旧後は DB container のmariadb-dump/mysqldumpを使う実装へ変更し、R2 / local fallback / Slack alert まで確認済みです。
2.1 現行バックアップ導線¶
- production VPS に
ritsubi-wordpress-backup.timerを配備する。 - timer は毎時
*:15:00 UTCにapps/wordpress-cms/scripts/backup-wordpress-vps.shを起動する。 - script は WordPress container の
wp-cli db exportではなく、DB container のmariadb-dump/mysqldumpで dump を作る。 - dump を先に VPS ローカルの
/var/backups/ritsubi-wordpress/wordpress-db-*.sql.gzへ保存し、その後 Cloudflare R2ritsubi-ecommerce-backup/wordpress/production/db/へ upload する。 export 結果が空、またはwp_optionsを含む WordPress SQL dump と判定できない場合は、 upload 前に失敗させて壊れた復元点を R2 へ追加しない。 - R2 upload / retention cleanup に失敗した場合は、ローカル backup を残したうえで
BACKUP_NOTIFY_WEBHOOK_URL(またはSLACK_WEBHOOK_URL)へ通知し、service を fail させる。通知 webhook が未設定の場合でも systemd journal には失敗を残す。 - dump の R2 保持期間は
BACKUP_RETENTION_HOURS=720。script 自体が 720 時間(30 日)より古いwordpress-db-*.sql.gzを削除する。 ローカル backup の既定保持期間はBACKUP_LOCAL_RETENTION_HOURS=72(3 日)で、 R2 障害時の短期復旧点として扱う。
2.2 現在の確認結果¶
- 2026-05-26 の復旧では
wordpress-db-20260517T221501Z.sql.gzを production へ import し、home/siteurl、page=12、product_detail=3、campaign=3、announcement=1を確認した。 - 復旧直前の安全 backup は production VPS の
/home/ubuntu/restore-safety/pre-restore-wordpress-20260526T015610Z.sql.gzに残した。 - 復旧後の R2
wordpress/production/db/ではwordpress-db-20260526T022112Z.sql.gzなどの非空 dump を確認した。 wordpress-db-20260526T022112Z.sql.gzはgunzipで展開でき、CREATE TABLE13 件とwp_optionsを確認した。ritsubi-wordpress-backup.timerは active。manual run と timer run の両方で R2 upload success を確認した。- R2 upload failure の疑似試験では service が失敗し、VPS local backup を残し、 Slack alert が投稿されることを確認した。
- staging には production と同等の
ritsubi-wordpress-backup.timer//etc/ritsubi/wordpress-backup.env/ R2wordpress/staging/db/は存在しない。 staging は restore drill の復元先であり、production backup の正本ではない。
2.2.1 2026-05-26 WordPress backup 不全からの教訓¶
障害発生前に利用可能だった production backup は、R2 上では
wordpress-db-20260517T221501Z.sql.gz まででした。2026-05-18 08:15 JST 以降の
R2 object は 20 byte 程度の空 gzip で、復元点として使えませんでした。
根本原因は、WordPress container の wp-cli db export が内部で mysqldump を必要とする一方、
runtime container に mysqldump が存在しなかったことです。復旧後の script は DB container の
mariadb-dump / mysqldump を使い、次の条件を満たさない dump は R2 upload へ進めません。
- gzip 前の SQL が空ではないこと
wp_optionsを含む WordPress DB dump であること- R2 upload 前に VPS local backup として保存できていること
同種の不全を疑う場合は、R2 object の size だけではなく、実際に gunzip して
wp_options と CREATE TABLE を確認してください。
2.3 確認コマンド¶
timer と直近ログ:
just wp-backup-status-vps production
just wp-backup-run-vps production
R2 上の最新 dump:
SECRETS_CONFIG=production_infra SECRETS_INCLUDE_SHARED=0 ./scripts/ops/with-env.sh -- bash -lc '
set -euo pipefail
endpoint="${BACKUP_R2_ENDPOINT:-https://${BACKUP_R2_ACCOUNT_ID}.r2.cloudflarestorage.com}"
env -u AWS_PROFILE -u AWS_REGION -u AWS_DEFAULT_REGION \
AWS_ACCESS_KEY_ID="$BACKUP_R2_ACCESS_KEY_ID" \
AWS_SECRET_ACCESS_KEY="$BACKUP_R2_SECRET_ACCESS_KEY" \
AWS_REGION=auto AWS_DEFAULT_REGION=auto \
aws --region auto s3 ls "s3://${BACKUP_R2_BUCKET_NAME:-ritsubi-ecommerce-backup}/wordpress/production/db/" \
--endpoint-url "$endpoint" | tail -n 5
'
AccessDenied が出る場合:
- AWS Secrets Manager
b2b-ecommerce/prod/infraのBACKUP_R2_ACCESS_KEY_ID/BACKUP_R2_SECRET_ACCESS_KEYを更新する。 just wp-deploy-vps production backup verify=falseで/etc/ritsubi/wordpress-backup.envを再配布する。just wp-backup-run-vps productionを再実行し、journal にUploaded WordPress backup to ...が出ることを確認する。
2.4 リストア¶
- R2 から対象 dump (
wordpress-db-*.sql.gz) をローカルへ取得する。 - 復元先ホストで
docker composeとwp-cliが正常であることを確認する。 - 注意: 復元先の既存 WordPress DB は上書きされる。
production へ復元:
gunzip -c wordpress-db-20260402T190000Z.sql.gz \
| just wp-cli-vps "db import -" production
just wp-cli-vps "db import -" production は import の前に
ritsubi-wordpress-backup.service を実行し、直前 backup が失敗した場合は import へ進みません。
staging へ復元:
gunzip -c wordpress-db-20260402T190000Z.sql.gz \
| ssh ubuntu@116.80.84.4 "cd /opt/wordpress && sudo docker compose -f docker-compose.yml run --rm -T wp-cli --quiet db import -"
復元後は、少なくとも次を確認します。
home/siteurlが対象環境の URL になっていること- 商品件数など最低限の目視確認が取れること
just wp-verify-vps <env>相当の検証で公開 URL と compose 状態を確認できること
[!IMPORTANT] WordPress の定期バックアップは DB only です。media 自体は
cms-assets.ritsubi-platform.com/ritsubi-wp-media-prodを正本とするため、DB restore は attachment 情報と URL の復元に限定されます。
2.5 Restore drill¶
WordPress の restore drill も、production backup を staging
VPS へ戻す演習として just restore-drill に統一しました。
事前 rehearsal(non-destructive):
RESTORE_DRILL_DRY_RUN=true just restore-drill wordpress staging latest
actual 実行(staging WordPress DB を上書き):
just restore-drill wordpress staging latest restore-drill-staging
recipe は次を自動実行します。
- R2
wordpress/production/db/から対象 dump(既定: 最新)を取得する - staging VPS の WordPress DB へ
wp-cli db import -で投入する cms.ritsubi-platform.com/cms-assets.ritsubi-platform.comを staging domain へsearch-replaceするhome/siteurlを staging URL へ戻すjust wp-verify-vps stagingで compose / cloudflared / 公開 URL を検証する
演習記録には、実行開始/終了時刻、使用した backup file、所要時間、URL 置換結果 を残してください。
[!NOTE] 2026-04-10 に同じ
just restore-drill all staging latest restore-drill-stagingの actual 実行でwordpress-db-20260410T141501Z.sql.gzを staging VPS へ復元しました。home/siteurlはともにhttps://cms-staging.ritsubi-platform.com、商品件数は 3、 所要 31 秒でした。
2.6 現時点の漏れ¶
- medium: restore drill の 初回 actual 証跡 は得られたが、月次の定例実行履歴は まだ 1 回分のみ。RTO 30 分の継続達成は、今後の月次実績で追跡する。
3. Vendure asset(Cloudflare R2)¶
production の商品画像・PDF 等は ritsubi-ec-assets-prod(配信ドメイン:
ec-assets.ritsubi-platform.com)を正本とする。
現在 repo 上で確認できるのは以下までです。
- R2 バケットが primary storage であること
- Storefront / API 側が
VENDURE_ASSET_URLとしてそのバケットを参照すること - DB backup 用の R2 bucket (
ritsubi-ecommerce-backup) とは分離されていること
一方で、次は repo 管理下に存在しません。
- secondary backup bucket
- bucket versioning / object lock / replication
- 定期 export / cross-account copy
- asset restore drill
2026-05-26 時点の運用判断では、上記は 将来検討 とし、現時点の必須バックアップ対象には含めません。したがって Vendure asset は、primary storage のみを前提に運用する受容リスク として扱います。
4. WordPress media(Cloudflare R2 offload)¶
production の WordPress media は ritsubi-wp-media-prod(配信ドメイン:
cms-assets.ritsubi-platform.com)を正本とする。
wordpress-media-offload.md と wordpress-vps-deployment.md は、WordPress DB
backup が DB only であり、media は R2
offload を正本とする前提を明示しています。
したがって、現在の位置づけは次のとおりです。
- WordPress DB backup は attachment metadata の保護
- media 実体は
ritsubi-wp-media-prodに依存 - media 実体の secondary backup / versioning / replication は repo 上で未定義
- VPS deploy は
/opt/wordpress/uploadsを code sync /--deleteの対象から外す。uploadsはwp-content/uploadsの bind mount であり、repo 側から上書きしない。
2026-05-26 時点の運用判断では、上記は 将来検討 とし、現時点の必須バックアップ対象には含めません。したがって WordPress media も、primary storage のみを前提に運用する受容リスク として扱います。
5. 再構築前提の運用 state¶
次は production で state を持つが、現行方針では formal backup target ではないものです。
| 対象 | 現行方針 | 備考 |
|---|---|---|
Redis (ritsubi-redis-prod) |
session / shared cache は再構築前提 | セッション失効や cache miss は起こり得るが、正本データではない |
| Storefront maintenance KV | storefront:maintenance を手動再投入 |
単一キーの運用 state |
| AWS Secrets Manager | secret の正本 | アプリデータ backup ではないが、DR には必須 |
- Vendure 実装では Redis は
sessionCacheStrategyとcacheStrategyにのみ使う。 - job queue は
DefaultJobQueuePluginの in-memory 運用であり、Redis 永続キューは使っていない。
[!NOTE] 一部文書には Redis AOF / Fly Volume snapshot をバックアップ戦略として書いている箇所がありますが、現在の実装と運用正本では Redis backup は不要 と判断します。Redis 消失時の影響はセッション失効と cache warm-up に留まり、restore 前提のバックアップ対象ではありません。
6. Staging 環境の snapshot 管理¶
Staging は「dogfood 可能な既知の良好状態」に手動でリセットできるよう、
staging 専用スナップショット 機能を用意しています。Production backup とは別の
R2 prefix (postgres/staging-snapshot/) に保存し、最新 5 件を自動ローテーションします。
スナップショット取得¶
# 現在の staging DB を R2 に保存(保持件数: 5 件)
just snapshot-staging-vendure-db
# オプション: ラベル付きで保存(ファイル名に付与される)
just snapshot-staging-vendure-db label=before-dogfood-2026-05-28
一覧確認¶
just list-staging-snapshots
復元(デストラクティブ)¶
# dry-run で確認
RESTORE_STAGING_DRY_RUN=true just restore-staging-snapshot
# 実際に復元(latest スナップショット)
RESTORE_STAGING_CONFIRM=restore-staging-snapshot just restore-staging-snapshot
# 特定のスナップショットを指定して復元
RESTORE_STAGING_CONFIRM=restore-staging-snapshot \
just restore-staging-snapshot backup=staging-20260528T032435Z.dump
いつ snapshot を取るか¶
| タイミング | 操作 |
|---|---|
| dogfood や大きな staging 変更作業の前 | just snapshot-staging-vendure-db label=before-<purpose> |
| SMILE 商品データをインポートした直後 | just snapshot-staging-vendure-db label=after-smile-import |
| staging reset → migrate → seed 後 | just snapshot-staging-vendure-db label=clean-baseline |
制約・注意事項¶
- このスナップショットは staging 専用 です。production の RPO/RTO 保証には影響しません。
- スナップショット取得には
staging_vendure(DATABASEURL)とproduction_infra(BACKUP_R2*) の secrets が必要です(AWS Secrets Manager)。 - 復元後は検索インデックスが古くなる場合があります。
必要に応じて
just deploy-storefront-stagingで Worker を再デプロイしてください。
7. 現時点で残るギャップ¶
| 優先度 | ギャップ | 影響 |
|---|---|---|
| medium | restore drill の初回 actual は成功したが、月次実績はまだ 1 回分のみ | RTO 30 分を継続的に満たせるという運用 evidence がまだ薄い |
| medium | Vendure asset / WordPress media の独立バックアップは未整備 | primary R2 storage の削除・破損に対する復元点がない |
[!NOTE] Vendure asset / WordPress media の独立バックアップは未整備ですが、2026-05-26 時点では必須対応から外し、将来検討事項として扱います。
8. 優先して塞ぐ順序¶
- 月次点検へ
just restore-drill all staging latest restore-drill-stagingを正式に組み込み、2 回目以降の所要時間を継続記録する。 - Postgres / WordPress それぞれの月次実績を蓄積し、RTO 30 分の継続達成を実績値として更新する。
- Vendure asset / WordPress media の secondary backup を入れる場合は、DB backup とは別設計で R2 versioning / replication / cross-account copy のどれを正本にするか決める。
注意事項¶
- バージョン整合性: DB のスキーマ構造はコードベースと一致している必要がある。本番 dump を古いコードの staging に戻すと migration 差分で失敗し得る。
- 機密情報: 本番 dump には実顧客情報が含まれる。ローカル・staging へ持ち出す場合は、個人情報保護方針に従い、必要に応じて masking を行う。