Skip to content

Aurora(PostgreSQL) 13→16 Blue/Green アップグレード(Cloneリハーサル版)

作成日: 2026-06-17 担当: SRE(yusaku.ishizawa) 関連ドキュメント:


📌 本書は「共通手順(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-verificationaurora-postgresql13)— microservice-ecscluster_parameter_group_custom_enable=true で作成。
    • ターゲット側 CPG/instance PG: eligibility-verification-pg16aurora-postgresql16)— template_modules/options/aurora-bluegreen-param-groups で作成。
    • どちらも rds.logical_replication=1 / wal_sender_timeout=0、および shared_preload_librariespg_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 のみ)。
  • 踏み台(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 のワークド例。

bash
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 設定を確認

bash
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_librariespg_stat_statements が含まれていること(および source の既存値を落としていないこと)を確認する。値が pg_stat_statements 単独になっていたら既存 preload を上書きしている恐れがあるので注意。サービスにより pgaudit / pg_cron 等が含まれることもある。

0-2. 本体クラスタが Blue/Green 可能な状態か

bash
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 のメンバーに対象クラスタが含まれないことを確認する:

bash
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 が無いか(どちらも出力が空であること)

bash
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 text

0-4. Clone / BG の引数になる実値を取得

bash
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系)がアップグレード先に含まれるか

bash
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 クラスタを作成

bash
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 $P

1-2. Clone に Writer インスタンスを作成(こちらは --engine が必要)

インスタンスクラスは手順0-4 で取得した source と同じクラスに寄せる(所要時間・性能・BG 作成時間を本番相当にしたい場合は必須。手順の素振りだけなら小さめでも可)。

bash
# 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=creatingavailable になるまで待つ(約9分)。


2. custom CPG を Clone に付け替え → 再起動

2-1. Clone クラスタに custom CPG を付け替え

bash
aws rds modify-db-cluster \
    --db-cluster-identifier $CL \
    --db-cluster-parameter-group-name eligibility-verification \
    --apply-immediately --region $R --profile $P

2-2. パラメータグループの反映状態を確認(再起動前は pending-reboot

bash
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

PGStatus=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 の順)

bash
# Reader がいれば先に1台ずつ(available を待ってから次へ)→ 最後に Writer
# 本リハーサルの Clone は Writer 1台のため Writer のみ
aws rds reboot-db-instance --db-instance-identifier ${CL}-0 --region $R --profile $P

2-4. 再起動後に in-sync になったことを確認

bash
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

PGStatus=in-sync を確認。


3. 踏み台接続 → logical replication 有効化を確認

3-1. 踏み台 bastion を特定し、SSM ポートフォワード

bash
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 $P

3-2. 別ターミナルで psql 接続(パスワードは非表示で PGPASSWORD へ)

bash
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 前提を確認

sql
SHOW rds.logical_replication;  -- on
SHOW wal_level;                -- logical
SHOW wal_sender_timeout;       -- 0

3つすべて期待値(on / logical / 0)であること。これが揃っていないと Blue/Green の論理レプリケーションが成立しない。

3-4. pg_stat_statements Extension の有効化(BG 作成前の DDL)

source(Clone / Blue)が writable なうちに、BG 作成前に有効化しておく(Green は同期中 read-only のため後から作れない)。

sql
-- 有効化確認(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_statements

3-5. pg_stat_statements のベースライン取得(アップグレード前)

アップグレード後は pg_stat_statements の Query ID が変わるため、事前にベースライン(負荷の高い SQL 一覧)を取得しておき、Switchover 後(手順8-6)と比較する。

sql
-- 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_timePG12 が source の場合(例: RDS fastdoctor-manager-db)は total_time / mean_time に読み替える。
  • Clone は無トラフィックのため素振り(中身はほぼ内部クエリ)。本番は実トラフィックのある source で、アップグレード前に取得しておくこと(できれば数日分の傾向を把握しておく)。取得した CSV は手順8-6 の Top SQL と突き合わせて性能退行を判断する。

加えて、主要クエリは実行計画(EXPLAIN)も事前に保存しておく(手順8-7 で事後と比較)。

sql
-- アップグレード前: 本番(source)で主要クエリの実行計画を取得して保存
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT ... ;  -- ベースライン CSV 上位の主要クエリを順に

⚠️ EXPLAIN ANALYZE実際にクエリを実行する。更新系(INSERT/UPDATE/DELETE)は副作用が出るため、BEGIN; ... ROLLBACK; で囲むか参照系のみに留める。


4. Blue/Green 作成前チェック

bash
# 状態 / バックアップ>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 が無いことを確認:

sql
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_onlyon にして、昇格完了まで書き込みを防ぐ。
  • アプリ/トランザクションが session レベルで off に上書きしていると、切替中に Green へ書き込みが入り得る。ロールバック時にその書き込みは Blue に存在せず、手動でのデータ不整合解消が必要になる。
  • 本当に確認したいのはアプリ側の上書きSHOW だけでは監査にならない(Blue 本番は通常 off が正常で、on を期待するのは Green / 切替中の AWS 制御文脈)。
  • アプリコード・ORM 設定・初期化 SQL・migration・接続プール設定を grep し、以下が無いことを確認する:
    • SET default_transaction_read_only = off
    • SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE
sql
-- 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 での確認が相当)
bash
# 例: 直近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 table

5. Blue/Green 作成

bash
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

返ってきた BlueGreenDeploymentIdentifierbgd-xxxx)を控える。

bash
BG=bgd-xxxxxxxxxxxxxxxx

監視(PROVISIONINGAVAILABLE で作成完了。Green が PG16 で作られ同期開始。数十分かかることあり):

bash
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)を確認する。スタック時の切り分けにも有用。

bash
# 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 内部のアップグレード実装詳細を完全公開していないため「効きやすい」と表現)。

参考:


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 で確認のうえフォワードする。

bash
aws ssm start-session \
    --target <bastion instance id> \
    --document-name AWS-StartPortForwardingSessionToRemoteHost \
    --parameters '{"host":["<green cluster endpoint>"],"portNumber":["5432"],"localPortNumber":["15432"]}' \
    --region $R --profile $P

6-2. psql で Green に接続(port 15432)し、PG16 として起動しているか確認

sql
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_green

Green では AWS 管理の writeforward / rds_blue_green 等が自動付与される(想定どおり)。

6-3. Blue とデータが一致しているか確認

sql
\dt
SELECT COUNT(*) FROM ocr_results;
SELECT COUNT(*) FROM prompt_definitions;
SELECT COUNT(*) FROM _prisma_migrations;

6-4. メジャーアップグレード後の統計取り直し(Green で ANALYZE)

sql
\dx pg_stat_statements   -- Version 1.8 / Default 1.10(UPDATE は Switchover 後)
ANALYZE VERBOSE;

Switchover 前に Green で ANALYZE しておくと、切替の瞬間から PG16 プランナに良い統計が揃い、切替直後の性能事故を防げる(Blue=本番には無負荷)。

6-5. BG 全体の状態確認(異常なし / 各メンバー AVAILABLE)

bash
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 でスロットが見えないこともあり、その場合は CloudWatch OldestReplicationSlotLag / 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 を指定する(本番事故防止)。

bash
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. 最終ゲート確認

bash
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):

bash
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_onlyoff に上書きしていない(手順4-2)
  • CloudWatch DatabaseConnections / DBLoadpg_stat_activity のアクティビティが許容範囲(手順4-3)
  • DNS キャッシュ TTL が 5 秒以下(下記)

7-2. 切り替え実行

bash
aws rds switchover-blue-green-deployment --blue-green-deployment-identifier $BG \
    --switchover-timeout 300 \
    --region $R --profile $P

Status=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. 完了確認

bash
aws rds describe-blue-green-deployments --blue-green-deployment-identifier $BG \
    --query "BlueGreenDeployments[0].Status" --output text --region $R --profile $P
# SWITCHOVER_COMPLETED

8. Switchover 後の検証

8-0. Switchover イベントの確認(ログで切替を裏取り)

describe-events で名前スワップ完了と書き込み断時間を確認する。

bash
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 プライマリを指す。

bash
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 $P

psql で接続(手順 3-2 と同じ)。

8-2. PG16 確認 と 統計の最終確認

sql
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 になったので実行可)

sql
ALTER EXTENSION pg_stat_statements UPDATE;
\dx pg_stat_statements   -- Version が 1.10 になっていること

8-4. 更新が必要な拡張が残っていないか

sql
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 が消えていること)

sql
SELECT slot_name, slot_type, active, restart_lsn, confirmed_flush_lsn FROM pg_replication_slots;
-- 0 行(BG レプリケーションが解かれたため)が正

8-6. 性能確認(負荷の高い SQL ランキング)

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 を比較する。

sql
-- アップグレード後(新プライマリ): 同じ主要クエリの実行計画を取得
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT ... ;  -- 3-5 と同一クエリ

比較ポイント:

  • Seq ScanIndex Scan(またはその逆)の変化
  • Nested LoopHash JoinMerge Join の変化
  • actual time の増減
  • rows(推定行数 vs 実行行数)の乖離
参考: メジャーバージョン間の主な Optimizer 変更
バージョン主な変更影響
PG13インクリメンタルソート / B-tree 重複排除ソートを含むクエリのプラン変化
PG14Memoize 結合戦略の追加Nested Loop 結合のプラン変化
PG15hash_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 実行統計のみ(テーブルデータ・アプリには影響しない)。

手順(本番ではいきなりリセットせず、保存してから):

  1. 必要に応じて reset 前の Top SQL を保存する
  2. pg_stat_statements_reset() を実行する
  3. reset 時刻を記録する(pg_stat_statements_info
  4. PG16 移行後の通常トラフィックを一定期間蓄積する
  5. Top SQL を確認し、PG13 時点のベースライン(手順3-5)と比較する
sql
-- 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_statementspg_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 とは異なる)。詳細な本番ロールバック手順は末尾「付録: スナップショットからの復元(本番ロールバック詳細)」を参照:

bash
# 事前スナップショットから復元クラスタを作成
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 を破棄して中止する場合用)。

bash
# 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 を含むリソースが残っていないこと。

bash
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-onlyALTER EXTENSION 等の DDL は Switchover 後でないと失敗(ANALYZE のみ特別に許可)。
  • ラグ確認は Blue(source)側 or CloudWatchswitchover は lag=0 を自動待機する。
  • 後始末の delete-blue-green-deploymentSwitchover 後は --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時間)の中でやること

  1. snapshot が available / BG Status=AVAILABLE / StatusDetails=null / replica lag ゼロ付近 / 長時間tx なし / アプリ正常 を最終ゲート確認(手順7-1)
  2. Switchover 実行(手順7-2・--switchover-timeout
  3. PG16 疎通・アプリ主要 API/OCR read-write 確認(手順8)
  4. ALTER EXTENSION pg_stat_statements UPDATE 等の事後 DDL(手順8-3)
  5. メトリクス監視・ロールバック判断基準に抵触しないか確認(手順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」ドリフトになる。
  • 後始末は 新 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 であること)

bash
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=available13.x であることを必ず確認(PG16 ならロールバック用ではない)。

2. PG13 として復元クラスタを作成

bash
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 $P

PG13 用 cluster parameter group を指定すること(例: PG13 Blue の custom CPG eligibility-verification。family が aurora-postgresql13 であることが前提)。subnet group / security group も元と同じものを指定する。

3. 復元クラスタが available になるまで待つ

bash
aws rds wait db-cluster-available --db-cluster-identifier "$ROLLBACK_CL" --region $R --profile $P

4. Writer インスタンスを作成(クラスタ復元だけでは instance が無い)

bash
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 になるまで待つ

bash
aws rds wait db-instance-available --db-instance-identifier "${ROLLBACK_CL}-0" --region $R --profile $P

6. 接続・データ確認(PG13 として起動しているか)

bash
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 $P
sql
SHOW 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 は単一 Secret eligibility-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 を復元先へ整合させる。

手順:

  1. rollback クラスタの endpoint を取得し、Secret の DATABASE_URL新 endpoint で書き換える(認証情報は維持・パスワードは argv に出さずファイル経由)。
bash
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"
  1. ECS を強制再デプロイ(タスクは起動時に Secret を読むため。再起動しないと反映されない)。
bash
aws ecs update-service --cluster <ecs-cluster> --service <service> \
    --force-new-deployment --region $R --profile $P
  1. 疎通確認:新タスクが起動・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 は個別判断。