Aurora Blue/Green 実践検証 — メンテナンス時に実際どれだけ止まるのかを実測する
目次
はじめに
Aurora PostgreSQL を本番運用していると、メンテナンス時のダウンタイムは避けて通れない課題だ。マイナーバージョンアップ、メジャーバージョンアップグレード、パラメータ変更 — いずれも「どれくらい止まるのか」が事前にわからないと、メンテナンスウィンドウの設計ができない。
AWS は 2023年に Aurora で Blue/Green デプロイメントをサポートし、さらに 2026年2月には AWS JDBC Driver の Blue/Green プラグインを発表した。公式ブログでは「ほぼゼロダウンタイム」を謳っているが、実際のところどうなのか。
本記事では、Aurora PostgreSQL 16.9 のクラスターを使って以下を実測する。
- 通常のフェイルオーバーでどれだけ止まるか
- Blue/Green Switchover(PG 16.9 → 17.6 メジャーアップグレード)でどれだけ止まるか
- DNS の挙動がダウンタイムにどう影響するか
これはシリーズ「Aurora Blue/Green 実践検証」の第1回だ。第2回では AWS JDBC Driver の Blue/Green プラグインを使った場合の改善効果を検証する。
検証環境
| 項目 | 値 |
|---|---|
| リージョン | ap-northeast-1(東京) |
| エンジン | Aurora PostgreSQL 16.9(Blue)→ 17.6(Green) |
| インスタンスクラス | db.r6g.large |
| 構成 | Writer × 1 + Reader × 1 |
| VPC | デフォルト VPC(3 AZ) |
| 接続テスト間隔 | 1秒(psql で SELECT inet_server_addr() を実行) |
前提条件:
- AWS CLI セットアップ済み(
rds:*、ec2:*の操作権限) - psql(PostgreSQL クライアント)
- dig(DNS 問い合わせ用)
セットアップから Switchover まで全工程を再現可能な形で記載する。結果だけ知りたい場合は まとめ にスキップできる。
Step 1: Aurora クラスターの構築
サブネットグループとクラスターの作成
クラスター構築コマンド(サブネットグループ → クラスター → インスタンス)
# サブネットグループ作成(デフォルト VPC の 3 AZ)
aws rds create-db-subnet-group \
--db-subnet-group-name bg-test-subnet-group \
--db-subnet-group-description "Subnet group for Blue/Green deployment test" \
--subnet-ids '["subnet-xxxxx","subnet-yyyyy","subnet-zzzzz"]' \
--region ap-northeast-1
# Aurora PostgreSQL 16.9 クラスター作成
aws rds create-db-cluster \
--db-cluster-identifier bg-test-apg \
--engine aurora-postgresql \
--engine-version 16.9 \
--master-username postgres \
--master-user-password '<your-password>' \
--db-subnet-group-name bg-test-subnet-group \
--storage-encrypted \
--no-deletion-protection \
--region ap-northeast-1
# Writer インスタンス
aws rds create-db-instance \
--db-instance-identifier bg-test-apg-writer \
--db-cluster-identifier bg-test-apg \
--db-instance-class db.r6g.large \
--engine aurora-postgresql \
--no-auto-minor-version-upgrade \
--region ap-northeast-1
# Reader インスタンス
aws rds create-db-instance \
--db-instance-identifier bg-test-apg-reader \
--db-cluster-identifier bg-test-apg \
--db-instance-class db.r6g.large \
--engine aurora-postgresql \
--no-auto-minor-version-upgrade \
--region ap-northeast-1クラスターとインスタンスの作成には約10〜15分かかった。以下のコマンドで両方のインスタンスが available になるまで待つ。
aws rds describe-db-instances \
--filters Name=db-cluster-id,Values=bg-test-apg \
--query 'DBInstances[].{Id:DBInstanceIdentifier,Status:DBInstanceStatus}' \
--region ap-northeast-1available になったら、ローカルから接続するためにセキュリティグループとパブリックアクセスを設定する。
ローカル接続用の設定(SG インバウンドルール + パブリックアクセス)
# クラスターが使用しているセキュリティグループ ID を確認
SG_ID=$(aws rds describe-db-clusters \
--db-cluster-identifier bg-test-apg \
--query 'DBClusters[0].VpcSecurityGroups[0].VpcSecurityGroupId' \
--output text --region ap-northeast-1)
# 自分のパブリック IP を取得
MY_IP=$(curl -s https://checkip.amazonaws.com)
# PostgreSQL ポートを自分の IP からのみ許可
aws ec2 authorize-security-group-ingress \
--group-id "${SG_ID}" \
--protocol tcp --port 5432 \
--cidr "${MY_IP}/32" \
--region ap-northeast-1
# 両インスタンスをパブリックアクセス可能にする
aws rds modify-db-instance \
--db-instance-identifier bg-test-apg-writer \
--publicly-accessible --apply-immediately \
--region ap-northeast-1
aws rds modify-db-instance \
--db-instance-identifier bg-test-apg-reader \
--publicly-accessible --apply-immediately \
--region ap-northeast-1パブリックアクセスの反映には1〜2分かかる。反映後、DNS が パブリック IP を返すようになる。
接続を確認する。
psql -h bg-test-apg.cluster-xxxxx.ap-northeast-1.rds.amazonaws.com \
-U postgres -d postgres -c "SELECT version();"PostgreSQL 16.9 on aarch64-unknown-linux-gnu, compiled by aarch64-unknown-linux-gnu-gcc (GCC) 10.5.0, 64-bitDNS の初期状態を確認する
Aurora のクラスターエンドポイントは CNAME レコードで、Writer インスタンスのエンドポイントを指している。
dig +noall +answer bg-test-apg.cluster-xxxxx.ap-northeast-1.rds.amazonaws.com Abg-test-apg.cluster-xxxxx...rds.amazonaws.com. 60 IN CNAME bg-test-apg-writer.xxxxx...rds.amazonaws.com.
bg-test-apg-writer.xxxxx...rds.amazonaws.com. 60 IN A 172.31.37.206ここで注目すべきは TTL が 60秒 という点だ。フェイルオーバーや Switchover で DNS が切り替わっても、クライアント側のキャッシュが最大60秒間古い IP を返し続ける可能性がある。これがダウンタイムの主要因になる。
Step 2: フェイルオーバーのダウンタイム計測
接続テストスクリプト
1秒間隔でクラスターエンドポイントに接続し、成功/失敗・レイテンシ・接続先 IP を記録するスクリプトを用意した。
connectivity-test.sh(接続テストスクリプト全体)
#!/usr/bin/env bash
set -euo pipefail
ENDPOINT="${1:?Usage: $0 <cluster-endpoint> <password> [interval_ms]}"
PASSWORD="${2:?Password required}"
INTERVAL_MS="${3:-500}"
INTERVAL_SEC=$(echo "scale=3; ${INTERVAL_MS}/1000" | bc)
TOTAL=0; SUCCESS=0; FAIL=0
FIRST_FAIL_TS=""; LAST_FAIL_TS=""
OUTFILE="/tmp/bg-test/connectivity-$(date '+%Y%m%d-%H%M%S').csv"
echo "timestamp,query_num,status,latency_ms,server_ip,error" | tee "${OUTFILE}"
cleanup() {
echo ""
echo "=== Summary ==="
echo "Total: ${TOTAL} | Success: ${SUCCESS} | Failed: ${FAIL}"
if [ -n "${FIRST_FAIL_TS}" ]; then
echo "First failure: ${FIRST_FAIL_TS}"
echo "Last failure: ${LAST_FAIL_TS}"
fi
}
trap cleanup EXIT
export PGCONNECT_TIMEOUT=3
while true; do
TOTAL=$((TOTAL + 1))
TS=$(date '+%Y-%m-%dT%H:%M:%S.%3N')
START_NS=$(date '+%s%N')
RESULT=$(PGPASSWORD="${PASSWORD}" psql -h "${ENDPOINT}" -p 5432 -U postgres -d postgres \
-t -A -c "SELECT inet_server_addr()::text" 2>&1) && STATUS="OK" || STATUS="FAIL"
END_NS=$(date '+%s%N')
LATENCY_MS=$(( (END_NS - START_NS) / 1000000 ))
if [ "${STATUS}" = "OK" ]; then
SUCCESS=$((SUCCESS + 1))
echo "${TS},${TOTAL},OK,${LATENCY_MS},${RESULT}," | tee -a "${OUTFILE}"
else
FAIL=$((FAIL + 1))
ERROR=$(echo "${RESULT}" | tr '\n' ' ' | cut -c1-100)
[ -z "${FIRST_FAIL_TS}" ] && FIRST_FAIL_TS="${TS}"
LAST_FAIL_TS="${TS}"
echo "${TS},${TOTAL},FAIL,${LATENCY_MS},,${ERROR}" | tee -a "${OUTFILE}"
fi
sleep "${INTERVAL_SEC}"
doneフェイルオーバーの実行と結果
バックグラウンドで接続テストを走らせた状態で、Reader をターゲットにフェイルオーバーを実行した。
# 接続テスト開始(バックグラウンド)
./connectivity-test.sh bg-test-apg.cluster-xxxxx.rds.amazonaws.com '<password>' 1000 &
# フェイルオーバー実行
aws rds failover-db-cluster \
--db-cluster-identifier bg-test-apg \
--target-db-instance-identifier bg-test-apg-reader \
--region ap-northeast-1結果は以下の通りだ。
10:01:29.278 #18 OK 124ms 172.31.37.206 ← Blue Writer
10:01:30.406 #19 FAIL 634ms ← 接続失敗開始
10:01:32.050 #20 FAIL 54ms
10:01:33.113 #21 FAIL 55ms
10:01:34.177 #22 FAIL 52ms
10:01:35.238 #23 FAIL 58ms
10:01:36.304 #24 FAIL 53ms
10:01:37.364 #25 FAIL 54ms
10:01:38.427 #26 FAIL 742ms
10:01:40.178 #27 OK 141ms 172.31.37.206 ← 復旧(同じ IP)
... 約36秒間正常 ...
10:02:16.658 #59 FAIL 3113ms ← 2回目の障害(タイムアウト)
10:02:20.780 #60 FAIL 3052ms
10:02:24.841 #61 FAIL 3099ms
10:02:28.949 #62 FAIL 3043ms ← テスト停止(復旧未確認)フェイルオーバーの分析
| 指標 | 値 |
|---|---|
| 接続失敗回数 | 12回 |
| 第1障害期間 | 約10秒(10:01:30〜10:01:40、8回の接続拒否) |
| 第2障害期間 | 約12秒以上(10:02:16〜10:02:28、4回のタイムアウト) |
| 障害パターン | 2つの障害期間に分離 |
注目すべき点が2つある。
- 2つの障害期間が発生した — 最初の約10秒間は接続拒否(即座に失敗)で、フェイルオーバー処理中にインスタンスが接続を受け付けなくなったことが原因だ。その後正常に戻ったが、約36秒後に再び3秒のタイムアウトが連続で発生した。第1障害期間の復旧後は旧 Writer の IP(172.31.37.206)に接続成功し続けていたことから、DNS キャッシュが切れて再解決が走ったタイミングで、エンドポイントの切り替え途中の不安定な状態に当たったと考えられる。
- 復旧後も同じ IP に接続している — フェイルオーバー後、クラスターエンドポイントの CNAME は新しい Writer(旧 Reader)を指すように更新されるが、DNS キャッシュが残っている間は古い IP に接続し続ける。今回は旧 Writer がまだ起動中だったため読み取りクエリは成功したが、書き込みを行うワークロードであれば read-only エラーになっていた可能性がある。これは DNS ベースのフェイルオーバーの根本的な限界だ。
フェイルオーバーでは、DNS キャッシュの影響で障害が2回に分離し、復旧後も誤ったインスタンスに接続し続けるリスクがある。では、Blue/Green デプロイメントではどうなるか。
Step 3: Blue/Green デプロイメントの構築と Switchover
論理レプリケーションの有効化
Blue/Green デプロイメントには論理レプリケーションが必要だ。カスタムパラメータグループを作成してクラスターに適用し、全インスタンスを再起動する。
論理レプリケーション有効化の手順(パラメータグループ作成 → 適用 → 再起動)
# Blue 環境用カスタムパラメータグループ作成
aws rds create-db-cluster-parameter-group \
--db-cluster-parameter-group-name bg-test-apg16-params \
--db-parameter-group-family aurora-postgresql16 \
--description "Custom params for Blue/Green deployment test"
# 論理レプリケーション有効化
aws rds modify-db-cluster-parameter-group \
--db-cluster-parameter-group-name bg-test-apg16-params \
--parameters "ParameterName=rds.logical_replication,ParameterValue=1,ApplyMethod=pending-reboot"
# クラスターに適用
aws rds modify-db-cluster \
--db-cluster-identifier bg-test-apg \
--db-cluster-parameter-group-name bg-test-apg16-params \
--apply-immediately
# 全インスタンスを再起動(pending-reboot → in-sync にするため)
aws rds reboot-db-instance --db-instance-identifier bg-test-apg-writer
aws rds reboot-db-instance --db-instance-identifier bg-test-apg-reader再起動後、パラメータグループのステータスが in-sync になるまで待つ。
aws rds describe-db-clusters \
--db-cluster-identifier bg-test-apg \
--query 'DBClusters[0].DBClusterMembers[].{Id:DBInstanceIdentifier,ParamStatus:DBClusterParameterGroupStatus}' \
--region ap-northeast-1in-sync になったら、論理レプリケーションが有効になっていることを確認する。
psql -h <cluster-endpoint> -U postgres -c "SHOW rds.logical_replication;" rds.logical_replication
-------------------------
onここで1つ注意点がある。カスタムパラメータグループを使っている場合、Green 環境用のパラメータグループも事前に作成しておく必要がある。今回は PG 16 → 17 のメジャーアップグレードなので、aurora-postgresql17 ファミリーのパラメータグループが必要だ。
Green 環境用パラメータグループの作成
aws rds create-db-cluster-parameter-group \
--db-cluster-parameter-group-name bg-test-apg17-params \
--db-parameter-group-family aurora-postgresql17 \
--description "Custom params for PG17 green environment"
aws rds modify-db-cluster-parameter-group \
--db-cluster-parameter-group-name bg-test-apg17-params \
--parameters "ParameterName=rds.logical_replication,ParameterValue=1,ApplyMethod=pending-reboot"Blue/Green デプロイメントの作成
aws rds create-blue-green-deployment \
--blue-green-deployment-name bg-test-upgrade \
--source arn:aws:rds:ap-northeast-1:<account-id>:cluster:bg-test-apg \
--target-engine-version 17.6 \
--target-db-cluster-parameter-group-name bg-test-apg17-params \
--region ap-northeast-1Green 環境のプロビジョニングは3つのフェーズで進行する。進捗は以下のコマンドで確認できる。
aws rds describe-blue-green-deployments \
--blue-green-deployment-identifier bgd-xxxxx \
--query 'BlueGreenDeployments[0].{Status:Status,Tasks:Tasks[].{Name:Name,Status:Status}}' \
--region ap-northeast-1| フェーズ | 内容 | 所要時間(実測) |
|---|---|---|
| CREATING_READ_REPLICA_OF_SOURCE | Blue クラスターのレプリカを作成 | 約18分 |
| DB_ENGINE_VERSION_UPGRADE | PG 16.9 → 17.6 へアップグレード | 約8分 |
| CREATE_DB_INSTANCES_FOR_CLUSTER | Green インスタンスを作成 | 約4分 |
| 合計 | 約30分 |
この30分間、Blue 環境は完全に稼働し続ける。アプリケーションへの影響はゼロだ。
Switchover の実行と結果
Green 環境が AVAILABLE になったら、接続テストを走らせた状態で Switchover を実行する。
aws rds switchover-blue-green-deployment \
--blue-green-deployment-identifier bgd-xxxxx \
--switchover-timeout 300 \
--region ap-northeast-1結果は以下の通りだ。
10:55:55.518 #24 OK 134ms 172.31.26.53 ← Blue Writer (PG 16.9)
10:55:56.658 #25 FAIL 3046ms ← 接続タイムアウト開始
10:56:00.713 #26 FAIL 3055ms
10:56:04.777 #27 FAIL 3050ms
10:56:08.837 #28 FAIL 3049ms
10:56:12.895 #29 FAIL 3045ms
10:56:16.953 #30 FAIL 3045ms
10:56:21.008 #31 OK 249ms 172.31.26.53 ← 一瞬 Blue に接続
10:56:22.263 #32 OK 204ms 172.31.21.178 ← Green Writer (PG 17.6)Switchover の分析
| 指標 | 値 |
|---|---|
| 接続失敗回数 | 6回 |
| ダウンタイム | 約26秒(10:55:56〜10:56:22) |
| 失敗パターン | すべてタイムアウト(各約3秒) |
| IP 遷移 | 172.31.26.53 → 172.31.21.178 |
フェイルオーバーとの違いが明確だ。
- 失敗はすべてタイムアウト — フェイルオーバーでは即座に接続拒否されるケースがあったが、Switchover では全て3秒のタイムアウト。接続拒否ではなくタイムアウトになるのは、Switchover 中に Blue 環境のインスタンスが停止し、TCP レベルで応答しなくなるためだ。
- IP が明確に切り替わる — フェイルオーバーでは同じ IP に接続し続けたが、Switchover では Green 環境の新しい IP に切り替わった。なお、#31 で一瞬 Blue の IP(172.31.26.53)に接続成功しているのは、DNS がまだ更新途中で旧 IP を返し、そのタイミングで Blue インスタンスが一時的に接続を受け付けたためと考えられる。
- バージョンが上がっている — Switchover 後の接続で PG 17.6 が返ってきた。メジャーバージョンアップグレードが完了している。
psql -h bg-test-apg.cluster-xxxxx.rds.amazonaws.com -U postgres \
-c "SELECT version();"PostgreSQL 17.6 on aarch64-unknown-linux-gnuまとめ
検証結果の比較
| 指標 | フェイルオーバー | Blue/Green Switchover |
|---|---|---|
| ダウンタイム | 約10秒 + 約12秒(2回に分離) | 約26秒(連続) |
| 接続失敗回数 | 12回 | 6回 |
| 失敗パターン | 接続拒否 + タイムアウト混在 | タイムアウトのみ |
| バージョン変更 | なし | PG 16.9 → 17.6 |
| 事前準備 | 不要 | 約30分(Green 環境構築) |
| アプリ変更 | 不要 | 不要 |
得られた知見
- DNS TTL 60秒が最大のボトルネック — フェイルオーバーでも Switchover でも、DNS キャッシュが古い IP を返し続けることがダウンタイムの主要因だ。アプリケーション側で DNS キャッシュの TTL を短くするか、IP ベースのルーティングに切り替えることで改善できる可能性がある。
- Blue/Green Switchover のダウンタイムは「長いが予測可能」 — フェイルオーバーでは接続拒否とタイムアウトが混在し2回に分離したのに対し、Switchover は26秒の連続したタイムアウトで予測しやすい。リトライロジックの設計がしやすい。
- Green 環境の構築に30分かかる — メジャーバージョンアップグレードを含む場合、Green 環境の準備に約30分必要だ。この間 Blue 環境は完全に稼働するが、メンテナンスウィンドウの計画には含める必要がある。
- 論理レプリケーションの有効化は事前準備が必要 — Blue/Green デプロイメントには
rds.logical_replication = 1が必須で、これにはカスタムパラメータグループの作成と全インスタンスの再起動が必要だ。本番環境では事前に有効化しておくべきだ。
次回予告
今回の検証では、psql による単純な接続テストで26秒のダウンタイムを観測した。AWS の公式ブログでは、AWS JDBC Driver の Blue/Green プラグインを使うことで「ほぼゼロダウンタイム」を実現できるとしている。
第2回では、このプラグインを実際に使い、HikariCP のリトライのみの場合と比較して、どの程度ダウンタイムが改善されるかを検証する。
クリーンアップ
リソース削除コマンド
# Blue/Green デプロイメントの削除
aws rds delete-blue-green-deployment \
--blue-green-deployment-identifier bgd-xxxxx \
--delete-target \
--region ap-northeast-1
# 旧 Blue 環境(-old1 サフィックス)のインスタンス削除
aws rds delete-db-instance \
--db-instance-identifier bg-test-apg-reader-old1 \
--skip-final-snapshot \
--region ap-northeast-1
aws rds delete-db-instance \
--db-instance-identifier bg-test-apg-writer-old1 \
--skip-final-snapshot \
--region ap-northeast-1
# 旧 Blue クラスター削除
aws rds delete-db-cluster \
--db-cluster-identifier bg-test-apg-old1 \
--skip-final-snapshot \
--region ap-northeast-1
# 新クラスター(Green が昇格したもの)のインスタンス削除
aws rds delete-db-instance \
--db-instance-identifier bg-test-apg-reader \
--skip-final-snapshot \
--region ap-northeast-1
aws rds delete-db-instance \
--db-instance-identifier bg-test-apg-writer \
--skip-final-snapshot \
--region ap-northeast-1
# 新クラスター削除
aws rds delete-db-cluster \
--db-cluster-identifier bg-test-apg \
--skip-final-snapshot \
--region ap-northeast-1
# パラメータグループ削除(クラスター削除後)
aws rds delete-db-cluster-parameter-group \
--db-cluster-parameter-group-name bg-test-apg16-params
aws rds delete-db-cluster-parameter-group \
--db-cluster-parameter-group-name bg-test-apg17-params
# サブネットグループ削除
aws rds delete-db-subnet-group \
--db-subnet-group-name bg-test-subnet-group
# セキュリティグループのインバウンドルール削除
aws ec2 revoke-security-group-ingress \
--group-id sg-xxxxx \
--protocol tcp --port 5432 \
--cidr <your-ip>/32