Aurora(PostgreSQL) 13→16 Blue/Green アップグレード(Cloneリハーサル版)
作成日: 2026-06-17 担当: SRE(yusaku.ishizawa) 関連ドキュメント:
- Aurora DBクラスタパラメータグループ変更
- 関連 Issue / PR は下記「関連 Issue / PR」を参照
📌 本書は「共通手順(how)」です。 クラスタ名・インスタンス構成・拡張・target version・確認テーブル名などのサービス固有の具体値は サービス別パラメータ を正とします。
- PG16 アップグレード全体の索引・進捗: README
- 本番実施版: procedure-production.md
- 本書中のコマンドは
eligibility-verification(staging)をワークド例として記載しています。別サービスで実施する場合は、該当するservices/<service>.mdの値に読み替えてください。
本書について
- Aurora PostgreSQL を Blue/Green Deployment で 13系 → 16系 にメジャーアップグレードする手順のうち、**本番クラスタの Clone(コピーオンライト複製)を作成して本番同様の流れを素振りする「リハーサル版」**を記載する。
- 目的は、本番実施前に DB 側の手順(パラメータグループ付け替え・logical replication 有効化・Blue/Green 作成・Switchover・拡張更新)を破棄可能な複製で検証し、Runbook を確定させること。
- 対象サービスは
services/配下の各サービス(本書のワークド例はeligibility-verification(staging))。他サービスでも同じ流れで適用でき、固有値はservices/<service>.mdを参照する。 - 本番(production)実施時の差分は末尾「本番実施時の差分・注意」を参照。
関連 Issue / PR
- 親チケット: mental-online-karte #12725 「[DB] eligibility-verification PostgreSQL 16系アップグレード」
- staging 調査・検証の進捗(本リハーサルの記録): mental-online-karte #13098 「[DB][eligibility-verification] staging 調査・検証の進捗(実施済み / 残作業)」
- 実装メモ(カスタム PG 作成・付け替え / logical_replication 有効化 / PG16 ターゲット): mental-online-karte #12905
- カスタム PG / logical_replication が必要な DB の棚卸し・方針決定: mental-online-karte #13044 「[infra][棚卸し] PG16: カスタムPG作成 / logical_replication有効化が必要なDBの棚卸し・方針決定(実機確認)」
- PG16 用 custom parameter group の追加(PR・MERGED): terraform_for_aws #2164 「feat(eligibility-verification/staging): Blue/Green PG16 用 custom parameter group を追加」
前提
- Terraform 側で以下のパラメータグループが作成済みであること(#2164)。
- ソース側 custom CPG:
eligibility-verification(aurora-postgresql13)—microservice-ecsのcluster_parameter_group_custom_enable=trueで作成。 - ターゲット側 CPG/instance PG:
eligibility-verification-pg16(aurora-postgresql16)—template_modules/options/aurora-bluegreen-param-groupsで作成。 - どちらも
rds.logical_replication=1/wal_sender_timeout=0、およびshared_preload_librariesにpg_stat_statementsを含む(いずれもpending-reboot)。 - ⚠️
shared_preload_librariesは上書き型。source の既存値(rdsutilsや、サービスによりpgaudit/pg_cron等)を落とさずにpg_stat_statementsを追加すること(例:rdsutils,pg_stat_statements)。pg_stat_statements単独で設定すると既存 preload を消すリスクがある。 - ⚠️
shared_preload_librariesは source の値を PG16 ターゲット CPG にも揃えること。preload が必要な拡張(pg_cron / pg_partman の bgw / pgaudit 等)を使うサービスは、ターゲット CPG に含めないと Switchover 後に再有効化できない(対象サービスごとに確認。eligibility は pg_stat_statements のみ)。
- ソース側 custom CPG:
- 踏み台(bastion)から SSM ポートフォワードで対象 Aurora に接続できること。
- DB 接続情報(ユーザ・パスワード)は Secrets Manager(
eligibility-verification)から取得する。パスワードはコマンドや出力に出さずPGPASSWORD経由で扱う。 - Aurora の logical replication(cluster パラメータ)反映には 対象インスタンスの再起動が必須(static パラメータ)。
影響
- リハーサルは Clone(独立した複製)に対して実施するため、本番・staging 本体への影響は無い。
- Clone はストレージはコピーオンライトで安価だが、計算インスタンス(Writer)は課金対象。検証後は必ず後始末(削除)すること。
全体の流れ
0. 事前確認(PG設定 / BG可能か / Clone引数値 / target version)
1. Clone作成(cluster + Writer instance)
2. custom CPG を Clone に付け替え → 再起動 → in-sync
3. 踏み台接続 → logical_replication=on を確認
4. Blue/Green 作成前チェック
5. Blue/Green 作成(target=PG16/16.13)→ AVAILABLE まで待機
6. Green 調査(BG同期中・Greenはread-only / ANALYZE)
7. Switchover(名前スワップ・エンドポイント据え置き)
8. Switchover後検証(PG16確認 / ALTER EXTENSION UPDATE / slot / 性能)
9. 後始末(BGレコード削除 → インスタンス → クラスタ削除)共通の環境変数
各ターミナルの先頭で設定しておく。$SOURCE_CL(アップグレード対象の本体=本番では実クラスタ)と $CL(リハーサルで操作する Clone)を明確に分けること。 具体値は対象サービスの services/<service>.md を正とする。下記は eligibility-verification のワークド例。
P=staging-admin # services/<service>.md の「プロファイル(staging)」
R=ap-northeast-1
# アップグレード対象の本体(source)。本番はこれが操作対象、リハーサルは Clone の元
# 値は services/<service>.md の「クラスタ識別子」
SOURCE_CL=eligibility-verification
# リハーサルで操作する Clone(本番では SOURCE_CL をそのまま使う)
CL=eligibility-verification-bgtest-blue⚠️ 本番転用時の事故防止: スナップショット取得(7-0)など「本体に対する操作」は
$SOURCE_CLを使う。リハーサル特有の Clone 作成・BG・後始末は$CL。本番ではCL=$SOURCE_CLとして読み替えるか、各コマンドの対象を$SOURCE_CLに置き換える。
手順
0. 事前確認
DB 内部・PG16 非互換・拡張・接続棚卸し等のアップグレード前 必須確認事項は プレフライト チェックリスト を参照(対象サービスごとに実施)。本手順0 は Clone/BG を回すための最小確認。
0-1. 13系・16系の cluster parameter group 設定を確認
aws rds describe-db-cluster-parameters --db-cluster-parameter-group-name eligibility-verification \
--query "Parameters[?ParameterName=='rds.logical_replication'||ParameterName=='wal_sender_timeout'||ParameterName=='shared_preload_libraries'].[ParameterName,ParameterValue,ApplyMethod]" \
--output table --region $R --profile $P
aws rds describe-db-cluster-parameters --db-cluster-parameter-group-name eligibility-verification-pg16 \
--query "Parameters[?ParameterName=='rds.logical_replication'||ParameterName=='wal_sender_timeout'||ParameterName=='shared_preload_libraries'].[ParameterName,ParameterValue,ApplyMethod]" \
--output table --region $R --profile $P期待値(両方とも):
| rds.logical_replication | 1 | pending-reboot |
| shared_preload_libraries | rdsutils,pg_stat_statements | pending-reboot |
| wal_sender_timeout | 0 | pending-reboot |
shared_preload_librariesはpg_stat_statementsが含まれていること(および source の既存値を落としていないこと)を確認する。値がpg_stat_statements単独になっていたら既存 preload を上書きしている恐れがあるので注意。サービスによりpgaudit/pg_cron等が含まれることもある。
0-2. 本体クラスタが Blue/Green 可能な状態か
aws rds describe-db-clusters --db-cluster-identifier eligibility-verification --region $R --profile $P \
--query "DBClusters[0].{Status:Status,Backup:BackupRetentionPeriod,Engine:EngineVersion,Members:length(DBClusterMembers)}" --output table期待値: Status=available / Backup=7(>0 必須)/ Engine=13.20 / Members=1
Global Database 所属の判定(GlobalWriteForwardingStatus は Global Write Forwarding の状態であり、所属判定としては不十分)。describe-global-clusters のメンバーに対象クラスタが含まれないことを確認する:
aws rds describe-global-clusters --region $R --profile $P \
--query "GlobalClusters[?contains(join(',', GlobalClusterMembers[].DBClusterArn), 'eligibility-verification')].[GlobalClusterIdentifier,Status]" \
--output table期待値: 何も出ない(Global Database に所属していない)。所属している場合は Blue/Green の前提・手順が変わるため別途検討。
0-3. 既存 BG / RDS Proxy が無いか(どちらも出力が空であること)
aws rds describe-blue-green-deployments --region $R --profile $P \
--query "BlueGreenDeployments[?contains(Source,'eligibility-verification')].{Name:BlueGreenDeploymentName,Status:Status}" --output table
aws rds describe-db-proxies --region $R --profile $P --query "DBProxies[].DBProxyName" --output text0-4. Clone / BG の引数になる実値を取得
aws rds describe-db-clusters --db-cluster-identifier eligibility-verification --region $R --profile $P \
--query "DBClusters[0].{Engine:EngineVersion,SubnetGroup:DBSubnetGroup,VpcSG:VpcSecurityGroups[].VpcSecurityGroupId,CPG:DBClusterParameterGroup,Members:DBClusterMembers[].DBInstanceIdentifier,Writer:Endpoint}"
aws rds describe-db-instances --db-instance-identifier eligibility-verification-0 --region $R --profile $P \
--query "DBInstances[0].{Class:DBInstanceClass,DBPG:DBParameterGroups[].DBParameterGroupName,PI:PerformanceInsightsEnabled}"取得する値: DBSubnetGroup(subnet group 名)/ VpcSecurityGroupId(SG)/ DBInstanceClass(インスタンスクラス)。
0-5. target version(16系)がアップグレード先に含まれるか
aws rds describe-db-engine-versions --engine aurora-postgresql --engine-version 13.20 --include-all --region $R --profile $P \
--query "DBEngineVersions[0].ValidUpgradeTarget[?starts_with(EngineVersion,'16')].EngineVersion" --output text期待値: 16.13 が含まれること。
1. Clone(コピーオンライト複製)の作成
NOTE:
restore-db-cluster-to-point-in-timeは--engineを受け付けない(source から継承)。
1-1. Clone クラスタを作成
aws rds restore-db-cluster-to-point-in-time \
--source-db-cluster-identifier eligibility-verification \
--db-cluster-identifier $CL \
--restore-type copy-on-write --use-latest-restorable-time \
--db-subnet-group-name eligibility-verification-subnet \
--vpc-security-group-ids <0-4で取得したSG> \
--region $R --profile $P1-2. Clone に Writer インスタンスを作成(こちらは --engine が必要)
インスタンスクラスは手順0-4 で取得した source と同じクラスに寄せる(所要時間・性能・BG 作成時間を本番相当にしたい場合は必須。手順の素振りだけなら小さめでも可)。
# source と同じインスタンスクラスを取得
CLASS=$(aws rds describe-db-instances --db-instance-identifier ${SOURCE_CL}-0 \
--query "DBInstances[0].DBInstanceClass" --output text --region $R --profile $P)
aws rds create-db-instance \
--db-instance-identifier ${CL}-0 \
--db-cluster-identifier $CL \
--engine aurora-postgresql --db-instance-class "$CLASS" \
--region $R --profile $P手順確認だけなら小さめのクラス(例
db.t3.medium)でも可。所要時間・性能・Blue/Green 作成時間を本番相当に近づけたい場合は source と同じDBInstanceClassを使う。
Status=creating → available になるまで待つ(約9分)。
2. custom CPG を Clone に付け替え → 再起動
2-1. Clone クラスタに custom CPG を付け替え
aws rds modify-db-cluster \
--db-cluster-identifier $CL \
--db-cluster-parameter-group-name eligibility-verification \
--apply-immediately --region $R --profile $P2-2. パラメータグループの反映状態を確認(再起動前は pending-reboot)
aws rds describe-db-clusters --db-cluster-identifier $CL \
--query 'DBClusters[0].DBClusterMembers[].{Instance:DBInstanceIdentifier,Writer:IsClusterWriter,PGStatus:DBClusterParameterGroupStatus}' \
--output table --region $R --profile $PPGStatus=pending-reboot を確認。
Reader がいる構成では Writer / 各 Reader すべてが再起動対象(
PGStatus=pending-rebootの行すべて)。 順序は Reader を先に1台ずつ → 最後に Writer(同時に全部落とすと全断になる)。 ⚠️ Writer を先に再起動しないこと。Writer 再起動で Failover が起きると、まだパラメータ未反映の Reader が新 Writer に昇格して反映漏れが残る。先に Reader を反映させ、最後に Writer を再起動すれば、Failover で昇格しても昇格先 Reader は反映済み。 本リハーサルの Clone は Writer 1台構成のため Writer のみ。
2-3. インスタンスを再起動(Reader → Writer の順)
# Reader がいれば先に1台ずつ(available を待ってから次へ)→ 最後に Writer
# 本リハーサルの Clone は Writer 1台のため Writer のみ
aws rds reboot-db-instance --db-instance-identifier ${CL}-0 --region $R --profile $P2-4. 再起動後に in-sync になったことを確認
aws rds describe-db-clusters --db-cluster-identifier $CL \
--query 'DBClusters[0].DBClusterMembers[].{Instance:DBInstanceIdentifier,Writer:IsClusterWriter,PGStatus:DBClusterParameterGroupStatus}' \
--output table --region $R --profile $PPGStatus=in-sync を確認。
3. 踏み台接続 → logical replication 有効化を確認
3-1. 踏み台 bastion を特定し、SSM ポートフォワード
aws ec2 describe-instances --region $R --profile $P \
--filters "Name=tag:Name,Values=*bastion*" "Name=instance-state-name,Values=running" \
--query "Reservations[].Instances[].{Id:InstanceId,Name:Tags[?Key=='Name']|[0].Value}" --output table
# Clone の Writer エンドポイントへフォワード(ローカル 5432)
aws ssm start-session \
--target <bastion instance id> \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["'"$CL"'.cluster-xxxxxxxx.ap-northeast-1.rds.amazonaws.com"],"portNumber":["5432"],"localPortNumber":["5432"]}' \
--region $R --profile $P3-2. 別ターミナルで psql 接続(パスワードは非表示で PGPASSWORD へ)
CREDS=$(aws secretsmanager get-secret-value --secret-id eligibility-verification \
--query SecretString --output text --region $R --profile $P)
export PGUSER=$(echo "$CREDS" | python3 -c "import sys,json;print(json.load(sys.stdin)['DB_USERNAME'])")
export PGPASSWORD=$(echo "$CREDS" | python3 -c "import sys,json;print(json.load(sys.stdin)['DB_PASSWORD'])")
unset CREDS
psql "host=localhost port=5432 dbname=eligibility_verification sslmode=require"3-3. logical replication 前提を確認
SHOW rds.logical_replication; -- on
SHOW wal_level; -- logical
SHOW wal_sender_timeout; -- 03つすべて期待値(on / logical / 0)であること。これが揃っていないと Blue/Green の論理レプリケーションが成立しない。
3-4. pg_stat_statements Extension の有効化(BG 作成前の DDL)
source(Clone / Blue)が writable なうちに、BG 作成前に有効化しておく(Green は同期中 read-only のため後から作れない)。
-- 有効化確認(installed_version が NULL なら未作成)
SELECT * FROM pg_available_extensions WHERE name = 'pg_stat_statements';
-- 未作成なら有効化(shared_preload_libraries に含まれ、再起動済みであることが前提。再実行に備え IF NOT EXISTS)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
\dx pg_stat_statements3-5. pg_stat_statements のベースライン取得(アップグレード前)
アップグレード後は pg_stat_statements の Query ID が変わるため、事前にベースライン(負荷の高い SQL 一覧)を取得しておき、Switchover 後(手順8-6)と比較する。
-- Aurora PG13(source): total_exec_time / mean_exec_time で上位を確認(現DBに限定)
SELECT
queryid,
LEFT(query, 100) AS query_preview,
calls,
round(total_exec_time::numeric, 2) AS total_ms,
round(mean_exec_time::numeric, 2) AS mean_ms,
rows
FROM pg_stat_statements
WHERE dbid = (SELECT oid FROM pg_database WHERE datname = current_database())
ORDER BY total_exec_time DESC
LIMIT 50;
-- CSV エクスポート
\copy (SELECT queryid, query, calls, total_exec_time, mean_exec_time, rows FROM pg_stat_statements WHERE dbid = (SELECT oid FROM pg_database WHERE datname = current_database()) ORDER BY total_exec_time DESC LIMIT 50) TO '/tmp/pg_stat_baseline.csv' CSV HEADER;
- カラム名は PG13 以降は
total_exec_time/mean_exec_time。PG12 が source の場合(例: RDS fastdoctor-manager-db)はtotal_time/mean_timeに読み替える。- Clone は無トラフィックのため素振り(中身はほぼ内部クエリ)。本番は実トラフィックのある source で、アップグレード前に取得しておくこと(できれば数日分の傾向を把握しておく)。取得した CSV は手順8-6 の Top SQL と突き合わせて性能退行を判断する。
加えて、主要クエリは実行計画(EXPLAIN)も事前に保存しておく(手順8-7 で事後と比較)。
-- アップグレード前: 本番(source)で主要クエリの実行計画を取得して保存
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT ... ; -- ベースライン CSV 上位の主要クエリを順に⚠️
EXPLAIN ANALYZEは実際にクエリを実行する。更新系(INSERT/UPDATE/DELETE)は副作用が出るため、BEGIN; ... ROLLBACK;で囲むか参照系のみに留める。
4. Blue/Green 作成前チェック
# 状態 / バックアップ>0 / Globalでない / インスタンス台数
aws rds describe-db-clusters --db-cluster-identifier $CL \
--query 'DBClusters[0].{Status:Status,Backup:BackupRetentionPeriod,Global:GlobalWriteForwardingStatus,Members:length(DBClusterMembers)}' \
--output table --region $R --profile $P
# 期待: Status=available / Backup=7 / Global=None / Members=1
# custom CPG が in-sync(全メンバー)
aws rds describe-db-clusters --db-cluster-identifier $CL \
--query 'DBClusters[0].DBClusterMembers[].{Instance:DBInstanceIdentifier,Writer:IsClusterWriter,PGStatus:DBClusterParameterGroupStatus}' \
--output table --region $R --profile $P
# PG16 用 Cluster/Instance PG が存在すること(どちらも eligibility-verification-pg16)
aws rds describe-db-cluster-parameter-groups --db-cluster-parameter-group-name eligibility-verification-pg16 \
--region $R --profile $P --query 'DBClusterParameterGroups[0].DBClusterParameterGroupName' --output text
aws rds describe-db-parameter-groups --db-parameter-group-name eligibility-verification-pg16 \
--region $R --profile $P --query 'DBParameterGroups[0].DBParameterGroupName' --output text直前に psql で長時間トランザクション / 進行中の重い DDL が無いことを確認:
SELECT pid, state, now() - xact_start AS xact_age, left(query,80) AS query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL AND state <> 'idle'
ORDER BY xact_age DESC LIMIT 5;4-1. 論理レプリケーションの制限事項を確認(AWS 推奨)
- Blue/Green は Blue→Green の論理レプリケーションで同期するため、論理レプリケーション固有の制限(同期中の DDL は伝播しない、シーケンス値・ラージオブジェクト等の扱い 等)を事前に確認し、必要な措置を講じる。
- 参考: AWS「ブルー/グリーン展開における論理レプリケーション固有の制限事項」。
- 本 Runbook の運用上は 同期中(手順6)に Blue へ DDL / migration を流さないことで多くを回避する。
4-2. default_transaction_read_only をアプリが上書きしていないか監査(AWS 推奨・データ整合性に直結)
- 切替中、AWS は Green ライターの
default_transaction_read_onlyをonにして、昇格完了まで書き込みを防ぐ。 - アプリ/トランザクションが session レベルで
offに上書きしていると、切替中に Green へ書き込みが入り得る。ロールバック時にその書き込みは Blue に存在せず、手動でのデータ不整合解消が必要になる。 - 本当に確認したいのはアプリ側の上書き。
SHOWだけでは監査にならない(Blue 本番は通常offが正常で、onを期待するのは Green / 切替中の AWS 制御文脈)。 - アプリコード・ORM 設定・初期化 SQL・migration・接続プール設定を grep し、以下が無いことを確認する:
SET default_transaction_read_only = offSET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE
-- DBレベルの現在値(Blue 本番は off が正常。これは参考確認)
SHOW default_transaction_read_only;補足: Blue(本番)は
offが正常。Green は作成後 read-only が既定で、Green への write は replication conflict やデータ混入につながるため、Green 側は read-only であることを確認する(手順6)。
4-3. 切替前に CloudWatch メトリクスを確認(AWS 推奨)
切替前に、対象クラスタのアクティビティが許容範囲かを確認する。
- DatabaseConnections: 接続数。切替前のアクティビティ水準の目安。
- DBLoad: Performance Insights 有効時はより正確な負荷指標(本サービスは PI=有効)。
- 長時間/多数のアクティブトランザクションは切替を妨げ得るため、上記
pg_stat_activityでも併せて確認する。 (※ AWS ドキュメントのActiveTransactions+innodb_monitor_enableは MySQL/InnoDB 向け。Aurora PostgreSQL には innodb は無いため、PostgreSQL ではpg_stat_activityでの確認が相当)
# 例: 直近30分の DatabaseConnections(必要に応じ DBLoad も)
aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name DatabaseConnections \
--dimensions Name=DBClusterIdentifier,Value=$CL \
--start-time $(python3 -c "import datetime;print((datetime.datetime.utcnow()-datetime.timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ'))") \
--end-time $(python3 -c "import datetime;print(datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'))") \
--period 300 --statistics Average Maximum --region $R --profile $P --output table5. Blue/Green 作成
CLONE_ARN=$(aws rds describe-db-clusters --db-cluster-identifier $CL \
--query 'DBClusters[0].DBClusterArn' --output text --region $R --profile $P)
aws rds create-blue-green-deployment \
--blue-green-deployment-name eligibility-verification-bgtest \
--source "$CLONE_ARN" \
--target-engine-version 16.13 \
--target-db-cluster-parameter-group-name eligibility-verification-pg16 \
--target-db-parameter-group-name eligibility-verification-pg16 \
--region $R --profile $P返ってきた BlueGreenDeploymentIdentifier(bgd-xxxx)を控える。
BG=bgd-xxxxxxxxxxxxxxxx監視(PROVISIONING → AVAILABLE で作成完了。Green が PG16 で作られ同期開始。数十分かかることあり):
aws rds describe-blue-green-deployments --blue-green-deployment-identifier $BG \
--query "BlueGreenDeployments[0].{Status:Status,Tasks:Tasks}" \
--output json --region $R --profile $P期待値: Status=AVAILABLE / CREATING_READ_REPLICA_OF_SOURCE=COMPLETED / DB_ENGINE_VERSION_UPGRADE=COMPLETED。
アップグレードログの確認(進捗の粒度を追う): describe-blue-green-deployments は Task 状態(PROVISIONING/AVAILABLE)しか出さないため、Green クラスタの describe-events で詳細な進捗(snapshot → volume clone → Upgrading writer → online → upgrade complete)を確認する。スタック時の切り分けにも有用。
# Green クラスタ名(${CL}-green-xxxxxx)を控える
GREEN=$(aws rds describe-db-clusters --region $R --profile $P \
--query "DBClusters[?contains(DBClusterIdentifier,'${CL}-green')].DBClusterIdentifier | [0]" --output text)
aws rds describe-events --source-type db-cluster --source-identifier $GREEN \
--duration 360 --region $R --profile $P \
--query "Events[].[Date,Message]" --output table期待される主なメッセージ(時系列):
... Database cluster engine major version upgrade started. Cluster remains online.
... Upgrade in progress: Creating pre-upgrade snapshot ...
... Upgrade in progress: Cloning volume.
... Upgrade in progress: Upgrading writer.
... Database cluster is online. The primary instance is ready for connections.
... Database cluster engine major version has been upgraded. ... Total time offline: NNN seconds.
--durationは分単位(直近 N 分のイベント)。長く遡る場合は値を増やす(最大 20160=14日)。
所要時間の目安(2026-06-16 リハーサル実測)
RDS イベントによる実測。作成開始(create-blue-green-deployment 実行)から PG16 化完了まで 約33分。
| フェーズ | 所要 | 内容 |
|---|---|---|
| ① Read Replica(Green の雛形)作成 | 約16分 | CREATING_READ_REPLICA_OF_SOURCE(06:44→07:00 UTC) |
| ② PG16 メジャーアップグレード | 約14分 | DB_ENGINE_VERSION_UPGRADE(07:03→07:17 UTC) |
| 合計(作成開始→AVAILABLE) | 約33分 |
- アップグレード中に Green が約12分オフラインになる(イベント
Total time offline: 716.0 seconds)が、オフラインになるのは Green 側だけで Blue(本番)は稼働継続=ユーザー影響なし。
Green 作成時間に影響する要素
Green 作成時間は、Aurora の保持データ量 GB に単純比例するわけではない。AWS 公式ドキュメント上、Aurora Blue/Green の Green 環境は Blue 側の Aurora storage volume を clone して作成され、Green volume は incremental changes(差分)を保持する仕組みのため、初期作成時に全データを丸ごと物理コピーする挙動ではない。
一方で、以下のフェーズは環境差により変動する(本番では下限目安として扱う):
- Green cluster / instance のプロビジョニング
- Green 側のメジャーバージョンアップグレード処理(今回イベントの
DB_ENGINE_VERSION_UPGRADE/ Green offline 区間が最も変動しやすい) - テーブル・インデックス・partition・extension・system catalog 等のオブジェクト数
- Blue/Green 作成後の書き込み量と Switchover 前の replication lag
- PG16 化後の ANALYZE / 統計再作成(テーブル数・データ量に依存)
特に Switchover は、AWS 公式上 replica lag が 0 になるまで待つため、本番では書き込み量・長時間トランザクション・進行中 DDL の有無が所要時間に影響する。
根拠の強さの区別
- 公式ドキュメントで確認できる事実: Green は Aurora storage volume の clone で作られ差分のみ保持/Green は Blue と同期し続ける/Switchover は replica lag 0 まで待つ/pg_upgrade は dump/restore 方式ではない/ANALYZE は統計収集処理。
- 実務上の見立て(断定でない): 「メジャーアップグレード時間は GB よりテーブル/インデックス/カタログ量が効きやすい」は pg_upgrade の性質からの推定(AWS は Aurora 内部のアップグレード実装詳細を完全公開していないため「効きやすい」と表現)。
参考:
- AWS: Blue/Green deployments — considerations / overview / switching / Aurora cloning(copy-on-write)
- PostgreSQL: pg_upgrade / Upgrading / ANALYZE
6. Green 調査(Blue/Green 同期中)
Green は Switchover まで read-only(Blue からの論理レプリケーションの受け側)。 この段階でできるのは
SHOW(read)とANALYZE VERBOSE(リハーサルで Green 上で実行可能であることを実機確認済み) まで。ALTER EXTENSION等の DDL は Switchover 後に実施する。ALTER EXTENSIONなどの DDL は Switchover 後でないとcannot execute ... in a read-only transactionで失敗する。
6-1. Green の Writer エンドポイントへ SSM ポートフォワード(ローカル 15432)
Green クラスタ名は ${CL}-green-xxxxxx の形。describe-db-clusters で確認のうえフォワードする。
aws ssm start-session \
--target <bastion instance id> \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["<green cluster endpoint>"],"portNumber":["5432"],"localPortNumber":["15432"]}' \
--region $R --profile $P6-2. psql で Green に接続(port 15432)し、PG16 として起動しているか確認
SHOW server_version; -- 16.13
SELECT version();
SHOW rds.logical_replication; -- on
SHOW wal_level; -- logical
SHOW wal_sender_timeout; -- 0
SHOW shared_preload_libraries; -- rdsutils,...,pg_stat_statements,writeforward,...,rds_blue_greenGreen では AWS 管理の
writeforward/rds_blue_green等が自動付与される(想定どおり)。
6-3. Blue とデータが一致しているか確認
\dt
SELECT COUNT(*) FROM ocr_results;
SELECT COUNT(*) FROM prompt_definitions;
SELECT COUNT(*) FROM _prisma_migrations;6-4. メジャーアップグレード後の統計取り直し(Green で ANALYZE)
\dx pg_stat_statements -- Version 1.8 / Default 1.10(UPDATE は Switchover 後)
ANALYZE VERBOSE;Switchover 前に Green で ANALYZE しておくと、切替の瞬間から PG16 プランナに良い統計が揃い、切替直後の性能事故を防げる(Blue=本番には無負荷)。
6-5. BG 全体の状態確認(異常なし / 各メンバー AVAILABLE)
aws rds describe-blue-green-deployments --blue-green-deployment-identifier $BG \
--query "BlueGreenDeployments[0].{Status:Status,StatusDetails:StatusDetails,Switchover:SwitchoverDetails[].{Member:SourceMember,St:Status}}" \
--output json --region $R --profile $P期待値: Status=AVAILABLE / StatusDetails=null(Replication degraded なし)/ 各メンバー AVAILABLE。
レプリケーションラグを SQL で見る場合、論理スロットは publisher=Blue(source)側にあるため Blue 側で確認する(Green で
pg_replication_slots(logical) を見ても 0 行)。 Aurora は AWS 管理のため SQL でスロットが見えないこともあり、その場合は CloudWatchOldestReplicationSlotLag/AuroraReplicaLagを見る。 なおswitchoverは lag=0 を自動で待ってから切替を進める(--switchover-timeout既定 300 秒)ため、手動ラグ確認は「念のため」の位置づけ。
7. Switchover(切り替え)
低トラフィック時間帯・メンテ枠で実施する(本番)。書き込み断は一瞬(実測 約2秒)だが、再接続・DNS 伝播の影響を最小化するため。
⚠️ 外部 DB クライアント(Trocco / ReTool / ReDash / BI / バッチ / Airflow / 手元スクリプト等)の接続先も要修正。 これらは DB を直接参照しており、アプリのように据え置きエンドポイントで自動追従しないものは Switchover で旧 Blue(
-old1)へ残り続ける。管理しきれていない接続もあるため、事前棚卸し(→ 後述の「外部DBクライアント棚卸し」)に加え、Switchover 後に Blue(-old1)側の現行接続(pg_stat_activity)や full query log(log_connections/pgaudit)を参照し、切替が必要なツールを実地に洗い出すこと。
- cluster / reader endpoint 利用=原則自動追従(要再接続確認)/ instance endpoint・IP 直指定=修正必須/ 不明=owner 確認まで GO しない。
7-0. Switchover 前スナップショット取得(本番必須 / リハーサルは任意)
ロールバック用に、Blue=PG13(13.20)の source クラスタを Switchover の前に手動スナップショット取得する。 スナップショットはエンジンバージョンを保持するため、これは PG13 のスナップショット=障害時は PG13 として復元できる(→ 8-9)。
⚠️ Switchover 後は名前が入れ替わる(
$SOURCE_CL=新 PG16/旧 PG13=$SOURCE_CL-old1)。PG13 を確実に確保するには、必ず Switchover 前に$SOURCE_CL(=この時点では Blue=PG13)を取得する。本番では Switchover に近いタイミングで取得し、Blue の最新状態を確保する。
対象は本体(source)= $SOURCE_CL(本番: eligibility-verification / リハーサル: eligibility-verification-bgtest-blue)。Clone ($CL) ではなく $SOURCE_CL を指定する(本番事故防止)。
SNAP=${SOURCE_CL}-pre-pg16-$(date -u +%Y%m%d%H%M)
aws rds create-db-cluster-snapshot \
--db-cluster-snapshot-identifier "$SNAP" \
--db-cluster-identifier "$SOURCE_CL" \
--region $R --profile $P
# available まで待機
aws rds wait db-cluster-snapshot-available \
--db-cluster-snapshot-identifier "$SNAP" --region $R --profile $P
# エンジンバージョンが 13.x であることを確認(=Blue=PG13 を取得できている)
aws rds describe-db-cluster-snapshots --db-cluster-snapshot-identifier "$SNAP" \
--query "DBClusterSnapshots[0].{Snap:DBClusterSnapshotIdentifier,Engine:EngineVersion,Status:Status,Created:SnapshotCreateTime}" \
--output table --region $R --profile $Pスナップショット名・取得時刻・エンジンバージョンを Issue に記録する(#12725 9-1)。復元手順は 8-9。
7-1. 最終ゲート確認
aws rds describe-blue-green-deployments --blue-green-deployment-identifier $BG \
--query "BlueGreenDeployments[0].{Status:Status,StatusDetails:StatusDetails,Sw:SwitchoverDetails[].Status}" \
--output json --region $R --profile $P期待値: Status=AVAILABLE / StatusDetails=null / Sw=["AVAILABLE","AVAILABLE"]。
レプリカラグがゼロ付近であることを確認(Green クラスタの CloudWatch AuroraReplicaLag):
aws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name AuroraReplicaLag \
--dimensions Name=DBClusterIdentifier,Value=$GREEN \
--start-time $(python3 -c "import datetime;print((datetime.datetime.utcnow()-datetime.timedelta(minutes=15)).strftime('%Y-%m-%dT%H:%M:%SZ'))") \
--end-time $(python3 -c "import datetime;print(datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'))") \
--period 60 --statistics Average Maximum --region $R --profile $P --output table
switchoverは lag=0 を自動で待つが、事前にゼロ付近を確認しておくと、本番で書き込みが多い時間帯に lag 解消待ちが長引く兆候を早めに掴める。SQL で見るなら publisher=Blue 側(手順6-5)。
あわせて、手順4で確認した以下を切替直前に再確認する(AWS 推奨):
- ANALYZE 済み(手順6-4)/論理レプリケーション制限事項に抵触する操作をしていない(手順4-1)
- アプリが
default_transaction_read_onlyをoffに上書きしていない(手順4-2) - CloudWatch
DatabaseConnections/DBLoad・pg_stat_activityのアクティビティが許容範囲(手順4-3) - DNS キャッシュ TTL が 5 秒以下(下記)
7-2. 切り替え実行
aws rds switchover-blue-green-deployment --blue-green-deployment-identifier $BG \
--switchover-timeout 300 \
--region $R --profile $PStatus=SWITCHOVER_IN_PROGRESS を確認。
--switchover-timeout(秒・既定 300)は lag=0 を待つ最大時間。超過すると Switchover は失敗扱いになり、両環境に変更を加えず自動ロールバックされる(→ 8-9)。書き込みが多い本番では値を見直す。
切替で 名前がスワップされる(Green が source の名前を取得し、旧 source は
-old1サフィックスになる)。エンドポイント名は据え置きのため、アプリの接続先設定は変更不要(接続は一瞬切断 → 自動再接続)。書き込み断は実測 約2秒(リハーサル時のイベント:
The write downtime during the switchover lasted approximately 2 seconds)。Switchover 自体も開始→完了で約40秒(09:02:15→09:02:55 UTC)。なお DNS 伝播には追加で時間がかかる場合がある。
⚠️ DNS キャッシュ TTL(切替前の前提条件・特に本番) Switchover はエンドポイント名を据え置いたまま DNS の指す先を新環境(旧 Green)へ張り替える仕組み。そのため、ネットワーク/クライアント側の DNS キャッシュ TTL が 5 秒を超えないようにすること(5 秒は Aurora DNS ゾーンの既定値)。 DNS キャッシュが長いと、切替後もしばらく古い接続先を参照し、接続エラーや旧環境(
-old1)への接続リスクが残る。そのため、アプリ・OS・JVM・コネクションプールの DNS キャッシュ TTL を事前に確認する。 確認ポイント:アプリ/コネクションプーラ/OS リゾルバ/JVM(networkaddress.cache.ttl)等の DNS キャッシュ設定が 5 秒以下になっているか。
7-3. 完了確認
aws rds describe-blue-green-deployments --blue-green-deployment-identifier $BG \
--query "BlueGreenDeployments[0].Status" --output text --region $R --profile $P
# SWITCHOVER_COMPLETED8. Switchover 後の検証
8-0. Switchover イベントの確認(ログで切替を裏取り)
describe-events で名前スワップ完了と書き込み断時間を確認する。
aws rds describe-events --source-type db-cluster --source-identifier $CL \
--duration 60 --region $R --profile $P \
--query "Events[].[Date,Message]" --output table期待される主なメッセージ:
... Switchover from DB cluster <blue> to <green> started.
... The DB cluster <green> is now accepting read and write operations ... write downtime during the switchover lasted approximately N seconds.
... Switchover ... completed. Renamed <blue> to <blue>-old1 and <green> to <blue>.8-1. 新プライマリ(PG16)に SSM ポートフォワード(ローカル 5432)
切替後は元の名前 $CL が新 PG16 プライマリを指す。
aws rds describe-db-clusters --db-cluster-identifier $CL \
--query 'DBClusters[0].Status' --output text --region $R --profile $P # available
aws ssm start-session \
--target <bastion instance id> \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["'"$CL"'.cluster-xxxxxxxx.ap-northeast-1.rds.amazonaws.com"],"portNumber":["5432"],"localPortNumber":["5432"]}' \
--region $R --profile $Ppsql で接続(手順 3-2 と同じ)。
8-2. PG16 確認 と 統計の最終確認
SHOW server_version;
SELECT version();
SELECT current_database(), current_user, inet_server_addr(), inet_server_port();
SELECT relname, last_analyze, last_autoanalyze, n_live_tup
FROM pg_stat_user_tables ORDER BY n_live_tup DESC LIMIT 20;
-- 主要テーブルの last_analyze が空なら再実行
ANALYZE VERBOSE;8-3. pg_stat_statements 拡張を PG16 既定版へ更新(writable になったので実行可)
ALTER EXTENSION pg_stat_statements UPDATE;
\dx pg_stat_statements -- Version が 1.10 になっていること8-4. 更新が必要な拡張が残っていないか
SELECT name, installed_version, default_version FROM pg_available_extensions
WHERE installed_version IS NOT NULL AND installed_version <> default_version ORDER BY name;
-- 0 行=全拡張が PG16 既定版に揃っている8-5. レプリケーション slot の確認(BG 用 slot が消えていること)
SELECT slot_name, slot_type, active, restart_lsn, confirmed_flush_lsn FROM pg_replication_slots;
-- 0 行(BG レプリケーションが解かれたため)が正8-6. 性能確認(負荷の高い SQL ランキング)
SELECT substring(query,1,100) AS query, calls,
round(mean_exec_time::numeric,2) AS mean_ms, round(total_exec_time::numeric,2) AS total_ms, rows
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 20;total_exec_time 降順で重いクエリを把握し、手順3-5 で取得したベースライン(/tmp/pg_stat_baseline.csv)と突き合わせて退行が無いか確認する。怪しいものは EXPLAIN (ANALYZE, BUFFERS) でプラン比較(→ 8-7)。 (アップグレードで Query ID が変わるため、queryid そのものではなくクエリ本文・呼び出し回数・mean/total 時間で比較する。リハーサルの Clone は実トラフィックが無いため内部・検証クエリしか並ばない=素振り。本番では実アプリのクエリで実施する。)
8-7. 実行計画の事前/事後比較(性能退行の特定)
PG13→16 は Optimizer(プランナ)に多数の改善が入っており、同じクエリでも実行計画が変わることがある。主要クエリについて、手順3-5 で保存した事前 EXPLAIN と、事後の EXPLAIN を比較する。
-- アップグレード後(新プライマリ): 同じ主要クエリの実行計画を取得
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT ... ; -- 3-5 と同一クエリ比較ポイント:
Seq Scan↔Index Scan(またはその逆)の変化Nested Loop↔Hash Join↔Merge Joinの変化actual timeの増減rows(推定行数 vs 実行行数)の乖離
参考: メジャーバージョン間の主な Optimizer 変更
| バージョン | 主な変更 | 影響 |
|---|---|---|
| PG13 | インクリメンタルソート / B-tree 重複排除 | ソートを含むクエリのプラン変化 |
| PG14 | Memoize 結合戦略の追加 | Nested Loop 結合のプラン変化 |
| PG15 | hash_mem_multiplier 既定 2.0 | ハッシュ結合のメモリ使用量が増える |
| PG16 | ウィンドウ関数最適化 | ウィンドウ関数を使うクエリのプラン変化 |
任意の上級策: Aurora の apg_plan_mgmt(Aurora Query Plan Management) で、アップグレード前にプランベースラインをキャプチャし、アップグレード後のプラン安定性を確保する方法もある(拡張の有効化・運用オーバーヘッドを伴うため、対象クエリが多く退行リスクが高い場合に検討)。 参考: Aurora PostgreSQL Query Plan Management
8-8. アップグレード後の復旧・確認チェックリスト(該当する場合のみ)
サービスによって使う拡張・構成が異なるため、対象サービスで該当する項目のみ実施する。事前に「対象サービスが何を使っているか」を確認しておく(拡張は \dx / pg_extension、preload は shared_preload_libraries、Auto Scaling は application-autoscaling describe-scalable-targets)。
| 項目 | タイミング | 該当条件 / 内容 |
|---|---|---|
| 全 Extension の更新 | 後 | \dx の各拡張を PG16 既定版へ(8-3/8-4)。ALTER EXTENSION ... UPDATE |
| pg_partman / pg_cron 再有効化・動作確認 | 後(前提は事前) | 使用サービスのみ。bgw/cron は shared_preload_libraries 必須=PG16 ターゲット CPG に事前に含める。ジョブ定義(cron.job 等)の残存も確認 |
| pg_repack 再作成 | 後 | 使用サービスのみ。ALTER EXTENSION UPDATE で揃わなければ drop→create |
| Auto Scaling ポリシー再設定 | 後 | Reader/Auto Scaling 利用サービスのみ。Switchover でクラスタ実体が入れ替わるためターゲットが外れることがある→再設定/確認 |
| pg_stat_statements リセット | 後 | PG16 移行後だけの統計にするため一度リセット(下記「性能計測の開始」参照) |
| アプリエラーログ監視 | 後(〜数日) | サービス共通。切替後の異常検知(本番は実トラフィックで最低3日) |
アップグレード後の性能計測の開始(pg_stat_statements リセット)
なぜ必要か: リセットしないと、PG13 時代の通常 SQL/BG 作成前・Green 検証中・migration・Switchover 直後の確認 SQL が混ざり、「PG16 移行後に本当に遅くなった SQL」が見えにくくなる。一度リセットして PG16 後だけの統計にする。 pg_stat_statements_reset() が消すのは SQL 実行統計のみ(テーブルデータ・アプリには影響しない)。
手順(本番ではいきなりリセットせず、保存してから):
- 必要に応じて reset 前の Top SQL を保存する
pg_stat_statements_reset()を実行する- reset 時刻を記録する(
pg_stat_statements_info) - PG16 移行後の通常トラフィックを一定期間蓄積する
- Top SQL を確認し、PG13 時点のベースライン(手順3-5)と比較する
-- 1. reset 前の Top SQL を保存(任意)
SELECT now() AS captured_at, queryid, LEFT(query,200) AS query_preview,
calls, total_exec_time, mean_exec_time, rows
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 50;
-- 2. 統計をリセット
SELECT pg_stat_statements_reset();
-- 3. reset 時刻を確認
SELECT * FROM pg_stat_statements_info;
-- 4〜5. 一定期間(数時間〜数日)通常トラフィックを蓄積後、Top SQL を確認し 3-5 のベースラインと比較参考: PostgreSQL: pg_stat_statements(pg_stat_statements_reset() / pg_stat_statements_info(リセット時刻 stats_reset 等)/ カラム定義)
前提(手順0/前提に紐づく):
shared_preload_librariesが必要な拡張(pg_cron / pg_partman の bgw / pgaudit 等)は、source のshared_preload_librariesを確認し、PG16 ターゲット CPG に必ず揃えること。含めないと Switchover 後に再有効化できない。
eligibility-verification の場合(実機確認済み・2026-06): インストール拡張は
plpgsqlのみ(pg_stat_statements は別途 CREATE)。pg_repack / pg_partman / pg_cron は未使用(installed_versionが NULL)。Auto Scaling ポリシー無し・Reader 無し(Writer 1台)。 → 実際にやるのは ①拡張更新(pg_stat_statements)②(任意)pg_stat_statements リセット ③アプリエラーログ監視 の3つのみ。pg_partman / pg_cron / pg_repack / Auto Scaling は 該当なし。
8-9. ロールバック判断基準と手順
正本は #12725「13. ロールバック方針」。本節はその要約。本番では #12725 を参照すること。 なお Clone リハーサルのロールバックは「Clone を削除するだけ」(本番への影響なし)なので、以下は主に本番向け。
段階別の方針:
| タイミング | 対応 |
|---|---|
| Switchover 前(Green 検証中) | BG を削除すれば Blue 本番に影響なし(--delete-target で Green も破棄=中止) |
| Switchover 中(タイムアウト超過) | AWS 側で自動ロールバック。両環境に変更なし(--switchover-timeout 既定 300 秒)。結果を確認 |
| Switchover 後 | PG16 → 13 にその場で戻すことは不可。重大障害時は インシデント対応として 旧Blue相当から復元 / 事前スナップショット復元 / PITR / 差分破棄 / 手動補正 を判断 |
ロールバック判断基準(#12725 §13・継続時間を満たしたら判断へ):
- 主要 API エラー率 > 1% が 5分以上継続
- レスポンスタイムが通常時の 3倍以上が 10分以上継続
- DB 接続エラーが 5分以上 / 主要機能が利用不能 が 3分以上 / 書き込み失敗が 3分以上継続
Switchover 後の復元(Aurora はクラスタ単位。RDS 単体の restore-db-instance-from-db-snapshot とは異なる)。詳細な本番ロールバック手順は末尾「付録: スナップショットからの復元(本番ロールバック詳細)」を参照:
# 事前スナップショットから復元クラスタを作成
aws rds restore-db-cluster-from-snapshot \
--db-cluster-identifier <rollback-cluster-id> \
--snapshot-identifier <pre-upgrade-snapshot-id> \
--engine aurora-postgresql --region $R --profile $P
# あるいは PITR
aws rds restore-db-cluster-to-point-in-time \
--source-db-cluster-identifier <source-cluster-id> \
--db-cluster-identifier <rollback-cluster-id> \
--restore-to-time <timestamp> --region $R --profile $P
# → その後 create-db-instance でインスタンスを付ける⚠️ 注意:
- Switchover 後に新 PG16 へ入った書き込みは、旧 Blue / 事前スナップショットには存在しない → ロールバック時はデータ差分の再実行・補正を手動判断(無停止ロールバックではない)。
- snapshot/PITR 復元は 新規クラスタ作成=復旧時は 接続先切替(Secrets Manager)・ECS 再起動・動作確認 が必要。
- 本番は Switchover 前に手動スナップショット取得(#12725 9-1・必須) と 監視担当者・ロールバック判断者の待機 を前提にする。
9. 後始末(課金停止・必須)
Switchover 後の後始末では
delete-blue-green-deploymentに--delete-targetを付けない(--delete-targetは Switchover 前に Green を破棄して中止する場合用)。
# 9-1. BG レコードの削除(DB 本体は残る)
aws rds delete-blue-green-deployment --blue-green-deployment-identifier $BG --region $R --profile $P
# 9-2. インスタンス削除(新・旧 両方)
aws rds delete-db-instance --db-instance-identifier ${CL}-0 --skip-final-snapshot --region $R --profile $P
aws rds delete-db-instance --db-instance-identifier ${CL}-0-old1 --skip-final-snapshot --region $R --profile $P
# 9-3. クラスタ削除(新・旧 両方)
aws rds delete-db-cluster --db-cluster-identifier $CL --skip-final-snapshot --region $R --profile $P
aws rds delete-db-cluster --db-cluster-identifier ${CL}-old1 --skip-final-snapshot --region $R --profile $P最終確認: bgtest を含むリソースが残っていないこと。
aws rds describe-db-clusters --region $R --profile $P \
--query "DBClusters[?contains(DBClusterIdentifier,'bgtest')].DBClusterIdentifier" --output text
aws rds describe-db-instances --region $R --profile $P \
--query "DBInstances[?contains(DBInstanceIdentifier,'bgtest')].DBInstanceIdentifier" --output textリハーサルで確認できた要点
- Green は Blue/Green 同期中 read-only。
ALTER EXTENSION等の DDL は Switchover 後でないと失敗(ANALYZEのみ特別に許可)。 - ラグ確認は Blue(source)側 or CloudWatch。
switchoverは lag=0 を自動待機する。 - 後始末の
delete-blue-green-deploymentは Switchover 後は--delete-targetなし。 - Switchover はエンドポイント名を維持するため、アプリの接続先設定は変更不要。
本番メンテナンス枠の時間設計(1時間枠前提・どこまでを Switchover 前にやるか)
前提: 本番のメンテ枠が 1時間しか取れない場合、その枠は Switchover と事後確認に集中し、時間のかかる準備は枠の前に終わらせておく。
ダウンタイムが出るのは Switchover だけ(だから準備は枠前にできる)
- Blue/Green 作成・Green の PG16 化・Green 検証・ANALYZE は、本番 Blue にダウンタイムを発生させない(Green は別環境として作られ、Blue は稼働継続。AWS 公式: production に影響を与えず staging を作る方式)。
- ダウンタイムは Switchover 時のみ(AWS 公式: 通常1分未満・workload 依存。本リハーサル実測の書込断は約2秒)。
- ただし Blue/Green 作成中は 論理レプリケーション開始に伴う負荷・lag が出るので「ダウンタイムではないが監視対象」。
- → よって Green 作成(実測 約33分)と検証はメンテ枠の“前”に完了させておける。
スナップショットは「枠の最後に取り始めない」
- Aurora の手動スナップショットは継続的・増分バックアップ方式で DB 停止や性能中断は伴わないが、
availableになるまでの待ち時間は読めない(データ量・変更量・AWS 側状況で変動)=ダウンタイム要因ではなく“作業時間の不確実性”要因。 - ⚠️ Switchover 直前に
create-db-cluster-snapshotを“開始”しない。available 待ちで1時間枠を食い潰す。 - メンテ枠の前(またはごく早い段階)で取得を開始し、
availableを確認してから Switchover する(手順7-0)。 - ⚠️ ただし取りすぎても rollback 時のデータ欠損範囲が広がるため、「Switchover にできるだけ近いが枠を圧迫しない時刻」に取り、available 確認後に切替。
- available にならなければ Switchover を延期 / 中止。
メンテ枠の前にやること(時間がかかる準備)
- プレフライト確認(別ドキュメント)/custom CPG 付け替え+Writer 再起動(手順2)=瞬断あり。これも枠前の別メンテで実施推奨
- Blue/Green 作成 → Green PG16 化完了待ち(実測 約33分)→ Green 検証・ANALYZE(手順5〜6)
- replication lag 確認 / DDL・migration 凍結 / 監視準備(手順4)
- Switchover 前スナップショット(PG13 Blue)を取得開始 →
available確認(手順7-0)
メンテ枠(1時間)の中でやること
- snapshot が
available/ BGStatus=AVAILABLE/StatusDetails=null/ replica lag ゼロ付近 / 長時間tx なし / アプリ正常 を最終ゲート確認(手順7-1) - Switchover 実行(手順7-2・
--switchover-timeout) - PG16 疎通・アプリ主要 API/OCR read-write 確認(手順8)
ALTER EXTENSION pg_stat_statements UPDATE等の事後 DDL(手順8-3)- メトリクス監視・ロールバック判断基準に抵触しないか確認(手順8-6 / 8-9)
時間設計の例
T-数時間 : Blue/Green 作成・Green 検証・ANALYZE 完了
T-60〜-30: PG13 Blue snapshot 取得開始
T-30 : snapshot available 確認(未 available なら Switchover 判断を保留)
T-15 : 最終ゲート確認
T 0 : Switchover(書込断は通常1分未満)
T+5〜+15 : PG16 確認 / アプリ確認
T+15〜+30: ALTER EXTENSION / メトリクス確認
T+30〜+60: 監視 / rollback 判断基準に抵触していないかSwitchover 後に問題が出たら(fix-forward 優先・旧Blue切戻し・snapshot の関係)
| 手段 | 位置づけ | 補足 |
|---|---|---|
| PG16 で fix-forward | 第一選択 | データ欠損なし。原因調査・修正は要 |
旧 Blue(-old1/PG13)へ切り戻し | 緊急退避候補 | 標準のワンクリック逆 Switchover ではない。接続先切替(Secret DATABASE_URL)・ECS 再起動・旧Blue が writable か確認が必要。Switchover 後に PG16 へ入った書込は旧Blue に無い(差分は破棄/手動補正) |
| 事前 snapshot 復元 | 最終保険 | 旧Blue 切戻しが失敗(read-only 解除不可・誤削除・状態不明 等)した場合の復元元。古い・復元に時間・接続先切替要(→ 付録) |
| PITR | 個別判断 | 新クラスタ作成。Switchover 後の source 取り違えに注意($SOURCE_CL は PG16) |
旧Blueがあるから snapshot 不要、にはならない。旧Blue は「稼働リソース」(操作ミス・削除・read-only の影響を受ける)であり、snapshot は「明示的な復元ポイント+証跡」。旧Blue=第一の緊急退避候補/snapshot=最後の保険。旧Blue は Switchover 時点までのデータ=snapshot より新しいが、Switchover 後の書込は含まない(無損失ロールバックではない)。詳細は 8-9 / 付録。
本番実施時の差分・注意
リハーサル(Clone)で「DB 側の手順」は実証済み。本番(production)では以下が追加で必要。
- 対象は本番クラスタ本体。実トラフィックがあるため メンテ枠・低トラフィック帯・Featureチームへの周知・監視 mute を行う。
- DNS キャッシュ TTL を 5 秒以下に(Aurora DNS ゾーン既定値)。ネットワーク/クライアント(アプリ・コネクションプーラ・OS リゾルバ・JVM の
networkaddress.cache.ttl等)が DNS を長くキャッシュすると、Switchover 後もアプリが旧 Blue(-old1)へ書き込みを送り続ける。事前に各クライアントの設定を確認しておく。 - CPG 付け替え+再起動(手順2)は本番 DB に瞬断が発生する。事前メンテ枠で実施。Reader がある構成では Writer→各 Reader を1台ずつ再起動(構成を必ず事前確認)。
- BG 同期中(手順6)は Blue(本番)への DDL / migration を凍結する。
- Switchover 後(手順8)は 実トラフィックでの性能監視を最低3日実施し、アプリ動作(OCR read/write 等)を確認する。
- Terraform 整合確認(Switchover 後):
- ⚠️ Switchover 後、Terraform コードが PG13 のままの状態では
terraform apply禁止(PG13 へ戻す/replace される恐れ)。 - 先に
rds_engine_versionを 16 系へ更新、加えて parameter group の family(aurora-postgresql16)・PG16 parameter group 参照を修正する。 terraform planで downgrade や replace(ForceNew)が出ないことを確認してから apply する。更新しないと「16→13」ドリフトになる。
- ⚠️ Switchover 後、Terraform コードが PG13 のままの状態では
- 後始末は 新 PG16 を残し、旧 PG13(
-old1)は数日安定確認後に削除する。 rds.logical_replication/wal_sender_timeoutを恒久維持するか戻すかは #13044 で判断。
付録: スナップショットからの復元(本番ロールバック詳細)
8-9 の復元コマンドは概要。実運用では「元クラスタを巻き戻す」のではなく、事前スナップショット(PG13)から新しい Aurora クラスタを作成し、アプリの接続先を切り替える。以下は本番ロールバックの詳細手順。
⚠️ 最重要: Switchover 後は
$SOURCE_CL(例eligibility-verification)が PG16 新本番を指す(旧 PG13 は-old1)。PG13 を復元したいのに$SOURCE_CLを source/対象にしないこと。標準は PG13 手動スナップショット(7-0)からの復元、PITR はインシデント対応時の個別判断(source の取り違えに注意)とする。
1. 事前スナップショットの情報を確認(PG13 であること)
SNAP=<pre-upgrade-snapshot-id>
aws rds describe-db-cluster-snapshots --db-cluster-snapshot-identifier "$SNAP" \
--query "DBClusterSnapshots[0].{Snapshot:DBClusterSnapshotIdentifier,Engine:Engine,EngineVersion:EngineVersion,Status:Status,KmsKeyId:KmsKeyId}" \
--output table --region $R --profile $P期待値: Engine=aurora-postgresql / EngineVersion=13.x / Status=available。13.x であることを必ず確認(PG16 ならロールバック用ではない)。
2. PG13 として復元クラスタを作成
ROLLBACK_CL=${SOURCE_CL}-rollback-pg13-$(date -u +%Y%m%d%H%M)
aws rds restore-db-cluster-from-snapshot \
--db-cluster-identifier "$ROLLBACK_CL" \
--snapshot-identifier "$SNAP" \
--engine aurora-postgresql \
--engine-version 13.20 \
--db-subnet-group-name <original-subnet-group> \
--vpc-security-group-ids <original-sg-id> \
--db-cluster-parameter-group-name <pg13-cluster-parameter-group> \
--region $R --profile $PPG13 用 cluster parameter group を指定すること(例: PG13 Blue の custom CPG
eligibility-verification。family がaurora-postgresql13であることが前提)。subnet group / security group も元と同じものを指定する。
3. 復元クラスタが available になるまで待つ
aws rds wait db-cluster-available --db-cluster-identifier "$ROLLBACK_CL" --region $R --profile $P4. Writer インスタンスを作成(クラスタ復元だけでは instance が無い)
aws rds create-db-instance \
--db-instance-identifier "${ROLLBACK_CL}-0" \
--db-cluster-identifier "$ROLLBACK_CL" \
--engine aurora-postgresql \
--db-instance-class <original-instance-class> \
--db-parameter-group-name <pg13-instance-parameter-group> \
--region $R --profile $P
<original-instance-class>は元の本番と同じクラスが安全(例db.r6g.large/db.t4g.medium等)。
5. インスタンスが available になるまで待つ
aws rds wait db-instance-available --db-instance-identifier "${ROLLBACK_CL}-0" --region $R --profile $P6. 接続・データ確認(PG13 として起動しているか)
aws rds describe-db-clusters --db-cluster-identifier "$ROLLBACK_CL" \
--query "DBClusters[0].{Endpoint:Endpoint,Reader:ReaderEndpoint,Engine:EngineVersion,Status:Status}" \
--output table --region $R --profile $PSHOW server_version; -- 13.x
SELECT current_database(), current_user;
SELECT COUNT(*) FROM _prisma_migrations;
SELECT COUNT(*) FROM ocr_results;
SELECT COUNT(*) FROM prompt_definitions;7. アプリの接続先切替(Secrets Manager / ECS)
snapshot 復元は新クラスタ作成=endpoint が変わり、アプリは自動では向かない(クラスタ名を変えても endpoint の
cluster-<hash>部分は新クラスタで別物になるため、必ず接続先変更が必要)。
接続の事実(eligibility-verification・コード確認済み):
- アプリ(Prisma)は
DATABASE_URLのみで接続(schema.prisma:url = env("DATABASE_URL"))。 DATABASE_URLは単一 Secreteligibility-verification(JSON)内のキーで、値はpostgresql://<DB_USERNAME>:<DB_PASSWORD>@<cluster-endpoint>/<DB_NAME>。DB_HOST(ECS env)はアプリ未使用(Terraform が設定するが Prisma は参照しない)→ 変更不要。- ⚠️ Secret 値・DB_HOST はいずれも Terraform 管理(
jsonencode(secret_values))。手動変更は次のterraform applyで上書きされて元の endpoint に戻る → 復旧中は terraform apply を凍結し、方針確定後に Terraform を復元先へ整合させる。
手順:
- rollback クラスタの endpoint を取得し、Secret の
DATABASE_URLを新 endpoint で書き換える(認証情報は維持・パスワードは argv に出さずファイル経由)。
SECRET_ID=eligibility-verification
ROLLBACK_EP=$(aws rds describe-db-clusters --db-cluster-identifier "$ROLLBACK_CL" \
--query 'DBClusters[0].Endpoint' --output text --region $R --profile $P)
TMP=$(mktemp); chmod 600 "$TMP"
aws secretsmanager get-secret-value --secret-id $SECRET_ID --query SecretString --output text \
--region $R --profile $P \
| ROLLBACK_EP="$ROLLBACK_EP" DBNAME="<DB_NAME>" python3 -c \
"import os,sys,json; d=json.load(sys.stdin); d['DATABASE_URL']=f\"postgresql://{d['DB_USERNAME']}:{d['DB_PASSWORD']}@{os.environ['ROLLBACK_EP']}/{os.environ['DBNAME']}\"; print(json.dumps(d))" > "$TMP"
aws secretsmanager put-secret-value --secret-id $SECRET_ID --secret-string file://"$TMP" \
--region $R --profile $P
shred -u "$TMP" 2>/dev/null || rm -f "$TMP"- ECS を強制再デプロイ(タスクは起動時に Secret を読むため。再起動しないと反映されない)。
aws ecs update-service --cluster <ecs-cluster> --service <service> \
--force-new-deployment --region $R --profile $P- 疎通確認:新タスクが起動・healthy、主要 API・OCR read/write が成功すること(必要に応じ ALB ヘルスチェックも)。
SG 要件: 手順2の復元で
--vpc-security-group-idsに元と同じ DB 用 SG を指定していること(既存の「ECS → DB」SG 許可ルールがそのまま効く)。別 SG だと疎通しないので、その場合は SG ルール追加が必要。スキーマ/マイグレーション: 復元は snapshot 時点の PG13 スキーマ=再マイグレーション不要(Prisma は同一スキーマ)。Switchover 後に PG16 で実行された migration / 書き込みは復元先に無い(下記 RPO)。
注意(データ差分・RPO)
- snapshot 復元は DB を過去時点に戻す操作。Switchover 後に PG16 へ入った書き込みは PG13 snapshot に存在しない。
- そのため rollback 判断時は 「どの時点までのデータを捨てるか(RPO)」を明示的に判断する。無損失ロールバックではなく、データ差分補正を伴うインシデント対応である。
- PITR を使う場合の source 指定に注意: Switchover 後の
$SOURCE_CLは PG16 新本番。PG13 復元用途では不用意に$SOURCE_CLを source にしない(旧 PG13 は-old1)。標準は PG13 手動 snapshot からの復元、PITR は個別判断。