Amazon RDS Blue/Green Deployments で Amazon Aurora PostgreSQL のメジャーバージョンアップをしてみた

PostgreSQL固有の制約事項があるので注意しよう
2024.02.06

短いダウンタイムでメジャーバージョンアップを行いたい

こんにちは、のんピ(@non____97)です。

皆さんはAmazon Aurora PostgreSQLを短いダウンタイムでメジャーバージョンアップしたいなと思ったことはありますか? 私はあります。

インプレースアップグレードで行うと、数十分ほどダウンタイムが発生します。よりダウンタイムを短くしようとするとDMSやPostgreSQLの機能で論理レプリケーションをして、DNSレコードを切り替えるといった方法があります。しかし、それは中々準備が大変です。

そんな時に役立つのがRDS Blue/Green Deploymentsです。ワークロードにもよりますが、RDS Blue/Green Deploymentsを活用することでダウンタイムを1分ほどに抑えることが可能です。

RDS Blue/Green Deploymentsの詳細は以下記事やAWS公式ドキュメントをご覧ください。

RDS Blue/Green DeploymentsがGAしたタイミングではRDS for MySQLとAurora MySQLのみでしたが、去年、RDS for PostgreSQLとAurora PostgreSQLでもサポートされました。

実際にRDS Blue/Green Deploymentsを使ってメジャーバージョンアップを試してみます。

いきなりまとめ

  • RDS Blue/Green Deploymentsを使うことで、ダウンタイムが1分ほどで切り替えができる
    • 論理レプリケーションのラグや書き込み量によって実際のダウンタイムは変動する
  • RDS Blue/Green DeploymentsはSecrets Managerの統合を未サポート
  • バージョンアップをする際は、Green側でのレプリカが作成完了した後にDBエンジンのバージョンアップを行う
    • 検証ではGreen環境のAurora DBクラスターの作成が完了するまで40分かかった
  • DDLの変更を行うと、BlueからGreenへのレプリケーションが停止する
    • Blue/Green Deploymentsを再作成する必要がある
  • Aurora PostgreSQL 16もRDS Blue/Green Deploymentsに対応している
  • RDS Blue/Green Deployments切り替え後、新Blueに対してAWS CDKで更新することは可能

やってみた

検証環境

検証環境は以下のとおりです。

Amazon RDS Blue:Green Deployments で Amazon Aurora PostgreSQL のメジャーバージョンアップをしてみた検証環境構成図

検証環境はAWS CDKでデプロイしました。使用したコードは以下リポジトにあります。

Aurora PostgreSQL 14.10から15.5にアップデートします。2024/2/6時点での14.10からのアップグレードターゲットは以下のとおりです。

$ aws rds describe-db-engine-versions \
  --engine aurora-postgresql \
  --engine-version 14.10 \
  --query 'DBEngineVersions[*].ValidUpgradeTarget[*].{EngineVersion:EngineVersion}' \
  --output text
15.5
16.1

最新の情報は以下AWS公式ドキュメントをご覧ください。

ちなみに、Aurora DBクラスターでSecrets Managerの統合を設定しているとSecrets Manager はブルー/グリーンデプロイの作成機能をサポートしていません。この DB クラスターにブルー/グリーンデプロイを作成するには、まず、この DB クラスターを変更して Secrets Manager の統合をオフにする必要があります。とエラーになります。注意しましょう。

Secrets Manager はブルー:グリーンデプロイの作成機能をサポートしていません

EC2インスタンスにpsqlをインストール

EC2インスタンスにpsqlをインストールします。GreenのAurora DBクラスターはPostgreSQL 15にするので、PostgreSQL 15のクライアントをインストールします。

$ sudo dnf install postgresql15
Last metadata expiration check: 0:35:35 ago on Mon Feb  5 04:31:27 2024.
Dependencies resolved.
================================================================================================
 Package                         Architecture Version                   Repository         Size
================================================================================================
Installing:
 postgresql15                    x86_64       15.5-1.amzn2023.0.1       amazonlinux       1.6 M
Installing dependencies:
 postgresql15-private-libs       x86_64       15.5-1.amzn2023.0.1       amazonlinux       142 k

Transaction Summary
================================================================================================
Install  2 Packages

Total download size: 1.8 M
Installed size: 6.9 M
Is this ok [y/N]: y
Downloading Packages:
(1/2): postgresql15-private-libs-15.5-1.amzn2023.0.1.x86_64.rpm 1.1 MB/s | 142 kB     00:00
(2/2): postgresql15-15.5-1.amzn2023.0.1.x86_64.rpm               11 MB/s | 1.6 MB     00:00
------------------------------------------------------------------------------------------------
Total                                                           7.7 MB/s | 1.8 MB     00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                                        1/1
  Installing       : postgresql15-private-libs-15.5-1.amzn2023.0.1.x86_64                   1/2
  Installing       : postgresql15-15.5-1.amzn2023.0.1.x86_64                                2/2
  Running scriptlet: postgresql15-15.5-1.amzn2023.0.1.x86_64                                2/2
  Verifying        : postgresql15-private-libs-15.5-1.amzn2023.0.1.x86_64                   1/2
  Verifying        : postgresql15-15.5-1.amzn2023.0.1.x86_64                                2/2

Installed:
  postgresql15-15.5-1.amzn2023.0.1.x86_64  postgresql15-private-libs-15.5-1.amzn2023.0.1.x86_64

Complete!

Aurora DBクラスターへの接続確認

Aurora DBクラスターへ接続します。認証情報はSecrets Managerに保存されています。

$ get_secrets_value=$(aws secretsmanager get-secret-value \
    --secret-id AuroraSecret7ACECA7F-IzECtgHudC8Q \
    --region us-east-1 \
    | jq -r .SecretString)

$ export PGHOST=$(echo "${get_secrets_value}" | jq -r .host)
$ export PGPORT=$(echo "${get_secrets_value}" | jq -r .port)
$ export PGDATABASE=$(echo "${get_secrets_value}" | jq -r .dbname)
$ export PGUSER=$(echo "${get_secrets_value}" | jq -r .username)
$ export PGPASSWORD=$(echo "${get_secrets_value}" | jq -r .password)

$ psql
psql (15.5, server 14.10)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.

testDB=> \dt
Did not find any relations.

testDB=> \l
                                                 List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    | ICU Locale | Locale Provider |   Access privileges
-----------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            |
 rdsadmin  | rdsadmin | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | rdsadmin=CTc/rdsadmin
 template0 | rdsadmin | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | =c/rdsadmin          +
           |          |          |             |             |            |                 | rdsadmin=CTc/rdsadmin
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | =c/postgres          +
           |          |          |             |             |            |                 | postgres=CTc/postgres
 testDB    | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            |
(5 rows)

DBにサンプルデータを投入

DBにサンプルデータを投入します。投入するデータはISOが割り振っている地理情報です。

$ curl https://ftp.postgresql.org/pub/projects/pgFoundry/dbsamples/iso-3166/iso-3166-1.0/iso-3166-1.0.tar.gz \
  -s \
  -o iso-3166-1.0.tar.gz

$ ls -l
total 40
-rw-rw-r--. 1 ssm-user ssm-user 39677 Feb  5 05:12 iso-3166-1.0.tar.gz

$ tar zxvf iso-3166-1.0.tar.gz
iso-3166/
iso-3166/iso-3166.sql

$ createdb -U postgres iso

$ psql -U postgres -f iso-3166/iso-3166.sql iso
BEGIN
SET
CREATE TABLE
COPY 242
CREATE TABLE
COPY 3995
COMMIT
ANALYZE
ANALYZE

投入後、DBやテーブルを確認します。

$ psql iso
psql (15.5, server 14.10)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.

iso=> \dt
           List of relations
 Schema |    Name    | Type  |  Owner
--------+------------+-------+----------
 public | country    | table | postgres
 public | subcountry | table | postgres
(2 rows)

iso=> \d country
                Table "public.country"
   Column   |  Type   | Collation | Nullable | Default
------------+---------+-----------+----------+---------
 name       | text    |           | not null |
 two_letter | text    |           | not null |
 country_id | integer |           | not null |
Indexes:
    "country_pkey" PRIMARY KEY, btree (two_letter)
Referenced by:
    TABLE "subcountry" CONSTRAINT "subcountry_country_fkey" FOREIGN KEY (country) REFERENCES country(two_letter)

iso=> \d subcountry
                Table "public.subcountry"
      Column      | Type | Collation | Nullable | Default
------------------+------+-----------+----------+---------
 country          | text |           | not null |
 subcountry_name  | text |           | not null |
 subdivision      | text |           |          |
 subcountry_level | text |           |          |
Indexes:
    "subcountry_country_subcountry_name_key" UNIQUE CONSTRAINT, btree (country, subcountry_name)
Foreign-key constraints:
    "subcountry_country_fkey" FOREIGN KEY (country) REFERENCES country(two_letter)

iso=> SELECT constraint_type, * FROM information_schema.table_constraints;
 constraint_type | constraint_catalog | constraint_schema |            constraint_name             | table_catalog | table_schema | table_name | constraint_type | is_deferrable | initially_deferred | enforced
-----------------+--------------------+-------------------+----------------------------------------+---------------+--------------+------------+-----------------+---------------+--------------------+----------
 PRIMARY KEY     | iso                | public            | country_pkey                           | iso           | public       | country    | PRIMARY KEY     | NO            | NO                 | YES
 UNIQUE          | iso                | public            | subcountry_country_subcountry_name_key | iso           | public       | subcountry | UNIQUE          | NO            | NO                 | YES
 FOREIGN KEY     | iso                | public            | subcountry_country_fkey                | iso           | public       | subcountry | FOREIGN KEY     | NO            | NO                 | YES
 CHECK           | iso                | public            | 2200_20491_1_not_null                  | iso           | public       | country    | CHECK           | NO            | NO                 | YES
 CHECK           | iso                | public            | 2200_20491_2_not_null                  | iso           | public       | country    | CHECK           | NO            | NO                 | YES
 CHECK           | iso                | public            | 2200_20491_3_not_null                  | iso           | public       | country    | CHECK           | NO            | NO                 | YES
 CHECK           | iso                | public            | 2200_20498_1_not_null                  | iso           | public       | subcountry | CHECK           | NO            | NO                 | YES
 CHECK           | iso                | public            | 2200_20498_2_not_null                  | iso           | public       | subcountry | CHECK           | NO            | NO                 | YES
(8 rows)

countrysubcountryというテーブルが作成されています。

それぞれの中身は以下のようなレコードが入っています。

country

iso=> SELECT * FROM country LIMIT 10;
        name         | two_letter | country_id
---------------------+------------+------------
 Afghanistan         | AF         |          4
 Albania             | AL         |          8
 Algeria             | DZ         |         12
 American Samoa      | AS         |         16
 Andorra             | AD         |         20
 Angola              | AO         |         24
 Anguilla            | AI         |        660
 Antarctica          | AQ         |         10
 Antigua and Barbuda | AG         |         28
 Argentina           | AR         |         32
(10 rows)

iso=> SELECT count(*) FROM country;
 count
-------
   242
(1 row)

subcountry

iso=> SELECT * FROM subcountry LIMIT 10;
 country | subcountry_name |      subdivision      | subcountry_level
---------+-----------------+-----------------------+------------------
 AD      | AN              | Andorra la Vella      |
 AD      | CA              | Canillo               |
 AD      | EE              | Escaldes-Engordany    |
 AD      | EN              | Encamp                |
 AD      | JL              | Sant Julià de Lòria   |
 AD      | MA              | La Massana            |
 AD      | OR              | Ordino                |
 AE      | AJ              | Ajman                 |
 AE      | AZ              | Abu Z¸aby [Abu Dhabi] |
 AE      | DU              | Dubayy [Dubai]        |
(10 rows)

iso=> SELECT count(*) FROM subcountry;
 count
-------
  3995
(1 row)

作成されたテーブルにレコードを追加します。まず、countryテーブルに私の王国non-97 kingdomを追加します。

iso=> SELECT MAX(country_id) FROM country;
 max
-----
 894
(1 row)

iso=> INSERT INTO public.country values ('non-97 kingdom', 'NK', 10000);
INSERT 0 1

iso=> SELECT * FROM country WHERE two_letter='NK';
      name      | two_letter | country_id
----------------+------------+------------
 non-97 kingdom | NK         |      10000
(1 row)

subcountryにもレコードを追加します。countrycountryテーブルのtwo_letterの外部キーです。

iso=> INSERT INTO public.subcountry values ('NK', current_timestamp, current_timestamp);
INSERT 0 1

iso=> INSERT INTO public.subcountry values ('NK', current_timestamp, current_timestamp);
INSERT 0 1


iso=> SELECT * FROM public.subcountry WHERE country='NK';
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
(2 rows)

マテビューも作成しておきます。

iso=> CREATE MATERIALIZED VIEW nk_view AS
  SELECT
    country, subcountry_name, subdivision, subcountry_level
  FROM
    public.subcountry
  WHERE
    country='NK';
SELECT 2

iso=> SELECT * FROM nk_view;
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
(2 rows)

Blue/Green Deploymentsの作成

Blue/Green Deploymentsの作成をします。

作成手順や注意点、準備事項は以下AWS公式ドキュメントにまとまっています。

Blue/Green Deploymentsの作成をするにあたって、事前に論理レプリケーションを有効にする必要があります。

ブルー/グリーンデプロイ用 Aurora PostgreSQL クラスターの準備

Aurora PostgreSQL DB クラスターのブルー/グリーンデプロイを作成する前に、以下を実行するようにしてください。

  • クラスターを、論理レプリケーション (rds.logical_replication) が有効になっているカスタム DB クラスターパラメータグループに関連付けます。ブルー環境からグリーン環境へのレプリケーションには、論理レプリケーションが必要です。論理レプリケーションを有効にする手順については、「Aurora PostgreSQL DB クラスターの論理レプリケーションの設定」を参照してください。
  • 論理レプリケーションを有効にした後は、必ず DB クラスターを再起動して変更を有効にしてください。ブルー/グリーンデプロイでは、ライターインスタンスが DB クラスターパラメータグループと同期している必要があり、同期していない場合は作成に失敗します。詳細については、「Aurora クラスター内の DB インスタンスの再起動」を参照してください。
  • DB クラスターがブルー/グリーンデプロイと互換性のあるバージョンの Aurora PostgreSQL を実行していることを確認してください。互換性のあるバージョンの一覧については、「Aurora PostgreSQL によるブルー/グリーンデプロイ」を参照してください。
  • DB クラスターが外部レプリケーションのソースでもターゲットでもないことを確認します。詳細については、「ブルー/グリーンデプロイの一般的な制約事項」を参照してください。
  • DB クラスターのすべてのテーブルにプライマリキーがあることを確認します。PostgreSQL の論理レプリケーションでは、プライマリキーのないテーブルに対する UPDATE または DELETE オペレーションは許可されません。

ブルー/グリーンデプロイの準備

論理レプリケーションが有効(rds.logical_replicationon)であることを確認します。

iso=> SELECT * FROM pg_settings WHERE name IN ('wal_level','rds.logical_replication','max_replication_slots','max_wal_sender','max_logical_replication_worker','max_worker_processes');
          name           | setting | unit |                category                |                              short_desc                              | extra_desc |  context   | vartype |       source       | min_val | max_val |         enumvals          | boot_val | reset_val |            sourcefile             | sourceline | pending_restart
-------------------------+---------+------+----------------------------------------+----------------------------------------------------------------------+------------+------------+---------+--------------------+---------+---------+---------------------------+----------+-----------+-----------------------------------+------------+-----------------
 max_replication_slots   | 20      |      | Replication / Sending Servers          | Sets the maximum number of simultaneously defined replication slots. |            | postmaster | integer | configuration file | 0       | 262143  |                           | 10       | 20        | /rdsdbdata/config/postgresql.conf |        182 | f
 max_worker_processes    | 8       |      | Resource Usage / Asynchronous Behavior | Maximum number of concurrent worker processes.                       |            | postmaster | integer | configuration file | 0       | 262143  |                           | 8        | 8         | /rdsdbdata/config/postgresql.conf |         22 | f
 rds.logical_replication | on      |      | Customized Options                     | Enables logical decoding.                                            |            | postmaster | bool    | configuration file |         |         |                           | off      | on        | /rdsdbdata/config/postgresql.conf |        168 | f
 wal_level               | logical |      | Write-Ahead Log / Settings             | Sets the level of information written to the WAL.                    |            | postmaster | enum    | configuration file |         |         | {minimal,replica,logical} | replica  | logical   | /rdsdbdata/config/postgresql.conf |        175 | f
(4 rows)

有効でない場合はパラメーターグループを変更します。なお、こちらのパラメーターを変更内容を反映させるためにはDBインスタンスを再起動させる必要があります。レプリカが存在していていたとしてもフェイルオーバーによるダウンタイムが発生するので注意しましょう。

Aurora DBクラスターを選択してブルー/グリーンデプロイの作成をクリックします。

Blue:Green Deploymentsの作成

注意事項を確認して確認をクリックします。

Aurora PostgreSQL 論理レプリケーションの制限事項

Blue/Green Deployments識別子やGreenのDBエンジンバージョン、DBクラスターパラメーターグループ、DBパラメーターグループを選択します。

ブルー:グリーンデプロイ db-cluster の作成

選択できるDBエンジンの一覧は以下のとおりです。

選択可能なDBエンジン

2024/2/5時点のAWS公式ドキュメントではAurora PostgreSQL 16についての記述はありませんが、16.1からサポートしていそうですね。

Blue:Green Deployments with Aurora PostgreSQL

抜粋 : Blue/Green Deployments - Amazon Aurora

ステージング環境の作成をクリックすると、Blue/Green Deploymentsが作成が開始されました。

RDS が、ブルー:グリーンデプロイ blue-green とグリーンデータベース を作成しています

作成開始直後だからかGreenの情報はまだ確認できていません。

blue-greenのプロビジョニング

Greenクラスターのエンジンバージョンが14_10

ステータスを確認すると、「リードレプリカの作成」と「DBエンジンのバージョンアップ」の処理があることが分かります。現在はリードレプリカの作成中のようです。

グリーン環境ステータス

15分ほど待つと、GreenのAurora DBクラスターが作成ステータスが進んでいました。

Greenクラスターが利用可能_接続とセキュリティ

現時点ではGreenのDBエンジンバージョンもBlueのDBエンジンバージョンも同じ14.10です。

Greenクラスターが利用可能_設定

さらに10分ほど待つと、GreenのAurora DBクラスターのバージョンアップが開始していました。

DBエンジンのバージョンアップの開始

ふと、バージョンアップ中の更新もレプリケーションされるのか気になりました。BlueのAurora DBクラスターで以下のようにレコードを追加します。

iso=> INSERT INTO public.subcountry values ('NK', current_timestamp, current_timestamp);
INSERT 0 1

iso=> SELECT * FROM public.subcountry WHERE country='NK';
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
 NK      | 2024-02-05 06:28:00.036196+00 | 2024-02-05 06:28:00.036196+00 |
(3 rows)

15分ほど待つと、GreenのAurora DBクラスターのバージョンが指定した15.5にバージョンアップしていました。

DBエンジンのバージョンアップが完了したことを確認

Green環境のステータスでも「DBエンジンのバージョンアップ」が完了となっていますね。

DB エンジンのバージョンアップ

最近のイベントは以下のとおりです。Blueについてのイベントか、Greenについてのイベントか分かりやすいです。

最近のイベント

詳細は以下のとおりです。トータルで40分ほどかかりました。

DB 識別子 ロール 時間 システムノート
db-cluster (Blue) プライマリ February 05, 2024, 13:30 (UTC+09:00) DB cluster created
db-instance-writer (Blue) プライマリ February 05, 2024, 13:33 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer (Blue) プライマリ February 05, 2024, 13:34 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer (Blue) プライマリ February 05, 2024, 13:35 (UTC+09:00) DB instance created
db-instance-writer (Blue) プライマリ February 05, 2024, 13:36 (UTC+09:00) CloudWatch Logs Export enabled for logs [postgresql]
db-instance-writer (Blue) プライマリ February 05, 2024, 13:38 (UTC+09:00) Monitoring Interval changed to 60
db-instance-writer (Blue) プライマリ February 05, 2024, 13:38 (UTC+09:00) Performance Insights has been enabled
db-instance-writer (Blue) プライマリ February 05, 2024, 13:54 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-cluster (Blue) プライマリ February 05, 2024, 13:55 (UTC+09:00) Reset master credentials
db-instance-writer (Blue) プライマリ February 05, 2024, 13:55 (UTC+09:00) DB instance shutdown
db-instance-writer (Blue) プライマリ February 05, 2024, 13:55 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer (Blue) プライマリ February 05, 2024, 13:56 (UTC+09:00) DB instance restarted
db-cluster-green-ak5mgh (Green) プライマリ February 05, 2024, 15:12 (UTC+09:00) DB cluster created
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:16 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:17 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:17 (UTC+09:00) The parameter rds.logical_replication was set to a value incompatible with logical replication. It has been adjusted from 0 to 1. Please drop all logical replication slots before disabling logical decoding.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:17 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:19 (UTC+09:00) DB instance created
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:20 (UTC+09:00) Replication has stopped.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:20 (UTC+09:00) CloudWatch Logs Export enabled for logs [postgresql]
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:22 (UTC+09:00) Replication for the Read Replica resumed
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:23 (UTC+09:00) Performance Insights has been enabled
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:23 (UTC+09:00) Monitoring Interval changed to 60
db-cluster-green-ak5mgh (Green) プライマリ February 05, 2024, 15:24 (UTC+09:00) Database cluster engine version upgrade started.
db-cluster-green-ak5mgh (Green) プライマリ February 05, 2024, 15:24 (UTC+09:00) Upgrade in progress: Performing online pre-upgrade checks.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:25 (UTC+09:00) DB instance shutdown
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:25 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-cluster-green-ak5mgh (Green) プライマリ February 05, 2024, 15:26 (UTC+09:00) Upgrade in progress: Creating pre-upgrade snapshot [preupgrade-db-cluster-green-ak5mgh-14-10-to-15-5-2024-02-05-06-24].
db-cluster-green-ak5mgh (Green) プライマリ February 05, 2024, 15:28 (UTC+09:00) Upgrade in progress: Cloning volume.
db-cluster-green-ak5mgh (Green) プライマリ February 05, 2024, 15:30 (UTC+09:00) Upgrade in progress: Upgrading writer.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:35 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:35 (UTC+09:00) DB instance shutdown
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:35 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:36 (UTC+09:00) DB instance shutdown
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:36 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:36 (UTC+09:00) DB instance restarted
db-cluster-green-ak5mgh (Green) プライマリ February 05, 2024, 15:37 (UTC+09:00) Updated to use DBParameterGroup aurorastack-auroradbclusterparametergroup150c75b72f-olcenkiixqad
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:37 (UTC+09:00) Replication has stopped.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:37 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:38 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-cluster-green-ak5mgh (Green) プライマリ February 05, 2024, 15:39 (UTC+09:00) Database cluster engine major version has been upgraded.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:39 (UTC+09:00) The parameter max_wal_senders was set to a value incompatible with replication. It has been adjusted from 10 to 20.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:40 (UTC+09:00) Replication for the Read Replica resumed
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 15:40 (UTC+09:00) Updated to use DBParameterGroup aurorastack-auroradbparametergroup158895769d-slpjow9ruia7
bgd-hy5ntvomddiv89r8 ブルー/グリーンデプロイ February 05, 2024, 15:40 (UTC+09:00) Blue/green deployment tasks completed. You can make more modifications to the green environment databases or switch over the deployment.

ちなみに、GreenのAurora DBクラスター作成中、BlueのAurora DBクラスターへの接続が途切れるということはありませんでした。

レプリケーションされているか確認

レプリケーションされているか確認します。

GreenのAurora DBクラスターに接続します。

$ get_secrets_value=$(aws secretsmanager get-secret-value \
    --secret-id AuroraSecret7ACECA7F-IzECtgHudC8Q \
    --region us-east-1 \
    | jq -r .SecretString)

$ export PGHOST=db-cluster-green-ak5mgh.cluster-cicjym7lykmq.us-east-1.rds.amazonaws.com
$ export PGPORT=$(echo "${get_secrets_value}" | jq -r .port)
$ export PGDATABASE=$(echo "${get_secrets_value}" | jq -r .dbname)
$ export PGUSER=$(echo "${get_secrets_value}" | jq -r .username)
$ export PGPASSWORD=$(echo "${get_secrets_value}" | jq -r .password)

$ psql iso
psql (15.5)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.

iso=> SELECT * FROM public.subcountry WHERE country='NK';
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
 NK      | 2024-02-05 06:28:00.036196+00 | 2024-02-05 06:28:00.036196+00 |
(3 rows)

iso=> SELECT * FROM nk_view;
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
(2 rows)

バージョンアップ中に追加したレコードも反映されていますね。マテビューはBlue側でリフレッシュしていないので、2件のままで問題ありません。

論理レプリケーションのサブスクリプションを確認します。4つのスロットがあることが分かります。

iso=> SELECT oid, subdbid, subname, subowner, subenabled, subbinary, substream, subslotname, subsynccommit, subpublications FROM pg_subscription;
  oid  | subdbid |                        subname                        | subowner | subenabled | subbinary | substream |                      subslotname                       | subsynccommit |                     subpublications
-------+---------+-------------------------------------------------------+----------+------------+-----------+-----------+--------------------------------------------------------+---------------+---------------------------------------------------------
 36879 |   14717 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_14717 |    24586 | t          | f         | f         | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_14717 | off           | {rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_14717}
 36880 |   16384 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_16384 |       10 | t          | f         | f         | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16384 | off           | {rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_16384}
 36881 |   16401 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_16401 |    24586 | t          | f         | f         | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16401 | off           | {rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_16401}
 36882 |   20512 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_20512 |    24586 | t          | f         | f         | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_20512 | off           | {rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_20512}
(4 rows)

Blue側から論理レプリケーションのパブリケーションを確認します。スロットと対応しているDBや、レプリケーションしているテーブルを確認できます。

iso=> SELECT * FROM pg_publication;
  oid  |                        pubname                        | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate | pubviaroot
-------+-------------------------------------------------------+----------+--------------+-----------+-----------+-----------+-------------+------------
 20550 | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_20512 |    20540 | t            | t         | t         | t         | t           | f
(1 row)

iso=> SELECT * FROM aurora_stat_logical_wal_cache();
                          name                          | active_pid | cache_hit | cache_miss | blks_read | hit_rate |     last_reset_timestamp
--------------------------------------------------------+------------+-----------+------------+-----------+----------+-------------------------------
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_14717 |            |       848 |          0 |       848 | 100.00%  | 2024-02-05 06:04:41.934204+00
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16384 |            |       840 |          0 |       840 | 100.00%  | 2024-02-05 06:04:41.998953+00
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16401 |            |       840 |          0 |       840 | 100.00%  | 2024-02-05 06:04:42.150032+00
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_20512 |            |       840 |          0 |       840 | 100.00%  | 2024-02-05 06:04:42.262146+00
(4 rows)

iso=> SELECT * FROM pg_show_replication_origin_status();
 local_id | external_id | remote_lsn | local_lsn
----------+-------------+------------+-----------
(0 rows)

iso=> SELECT * FROM pg_replication_slots;
                       slot_name                        |  plugin  | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn | wal_status | safe_wal_size | two_phase
--------------------------------------------------------+----------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+---------------------+------------+---------------+-----------
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_14717 | pgoutput | logical   |  14717 | postgres | f         | f      |            |      |        20997 | 0/417D0F0   | 0/4180270           | reserved   |               | f
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16384 | pgoutput | logical   |  16384 | rdsadmin | f         | f      |            |      |        20997 | 0/417D0F0   | 0/41801E8           | reserved   |               | f
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16401 | pgoutput | logical   |  16401 | testDB   | f         | f      |            |      |        20997 | 0/417D0F0   | 0/4180270           | reserved   |               | f
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_20512 | pgoutput | logical   |  20512 | iso      | f         | f      |            |      |        20997 | 0/417D0F0   | 0/4180270           | reserved   |               | f
(4 rows)

iso=> SELECT * FROM pg_publication_tables;
                        pubname                        | schemaname | tablename
-------------------------------------------------------+------------+------------
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_20512 | public     | country
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_20512 | public     | subcountry
(2 rows)

Blue側でレコードを追加すると、Greenに反映されることを確認します。

Blue

iso=> INSERT INTO public.subcountry values ('NK', current_timestamp, current_timestamp);
INSERT 0 1

iso=> INSERT INTO public.subcountry values ('NK', current_timestamp, current_timestamp);
INSERT 0 1

Green

iso=> SELECT * FROM public.subcountry WHERE country='NK';
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
 NK      | 2024-02-05 06:28:00.036196+00 | 2024-02-05 06:28:00.036196+00 |
 NK      | 2024-02-05 08:04:24.233622+00 | 2024-02-05 08:04:24.233622+00 |
 NK      | 2024-02-05 08:04:27.876364+00 | 2024-02-05 08:04:27.876364+00 |
(5 rows)

即座に反映されました。

論理レプリケーションされない処理をしてみる

論理レプリケーションされない処理を試しにやってみます。

まず、Blue側でマテビューの更新をしてもGreenに反映されないことを確認します。

マテリアライズドビューはグリーン環境では自動的に更新されません。

ブルー環境でマテリアライズドビューを更新しても、グリーン環境では更新されません。スイッチオーバー後、マテリアライズドビューの更新をスケジュールできます。

ブルー/グリーンデプロイの PostgreSQL 論理レプリケーションの制約事項

Blue側でマテビューのリフレッシュをします。リフレッシュするとGreenに反映されないとメッセージが表示されました。親切です。

Blue

iso=> SELECT * FROM nk_view;
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
(2 rows)

iso=> REFRESH MATERIALIZED VIEW nk_view;
WARNING:  command will not be replicated to the green instance: "REFRESH MATERIALIZED VIEW"
REFRESH MATERIALIZED VIEW

iso=> SELECT * FROM nk_view;
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
 NK      | 2024-02-05 06:28:00.036196+00 | 2024-02-05 06:28:00.036196+00 |
 NK      | 2024-02-05 08:04:24.233622+00 | 2024-02-05 08:04:24.233622+00 |
 NK      | 2024-02-05 08:04:27.876364+00 | 2024-02-05 08:04:27.876364+00 |
(5 rows)

Green側でマテビューを確認します。リフレッシュの結果が反映されていないことが分かります。

iso=> SELECT * FROM nk_view;
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
(2 rows)

なお、Green側でマテビューのリフレッシュはできませんでした。

iso=> REFRESH MATERIALIZED VIEW nk_view;
ERROR:  cannot execute REFRESH MATERIALIZED VIEW in a read-only transaction

iso=> SELECT * FROM nk_view;
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
(2 rows)

次にテーブル定義を変更します。

CREATE TABLE や CREATE SCHEMA などのデータ定義言語 (DDL) ステートメントは、ブルー環境からグリーン環境にはレプリケートされません。

Aurora がブルーの環境で DDL の変更を検出すると、グリーンデータベースはレプリケーションが低下した状態になります。

ブルー環境での DDL の変更を緑の環境にレプリケートできないことを通知するイベントを受け取ります。ブルー/グリーンデプロイとすべてのグリーンデータベースを削除してから再作成する必要があります。そうしないと、ブルー/グリーンデプロイに切り替えることができません。

ブルー/グリーンデプロイの PostgreSQL 論理レプリケーションの制約事項

今回はsubcountryテーブルにregionというカラムを追加します。ALTER TABLEはGreenにレプリケーションされないとメッセージが表示されました。

Blue

iso=> ALTER TABLE public.subcountry ADD COLUMN region text;
WARNING:  command will not be replicated to the green instance: "ALTER TABLE"
ALTER TABLE

iso=> \d subcountry
                Table "public.subcountry"
      Column      | Type | Collation | Nullable | Default
------------------+------+-----------+----------+---------
 country          | text |           | not null |
 subcountry_name  | text |           | not null |
 subdivision      | text |           |          |
 subcountry_level | text |           |          |
 region           | text |           |          |
Indexes:
    "subcountry_country_subcountry_name_key" UNIQUE CONSTRAINT, btree (country, subcountry_name)
Foreign-key constraints:
    "subcountry_country_fkey" FOREIGN KEY (country) REFERENCES country(two_letter)
Publications:
    "rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_20512"

Greenを確認します。確かに追加したregionというカラムが反映されていません。

Green

iso=> \d subcountry
                Table "public.subcountry"
      Column      | Type | Collation | Nullable | Default
------------------+------+-----------+----------+---------
 country          | text |           | not null |
 subcountry_name  | text |           | not null |
 subdivision      | text |           |          |
 subcountry_level | text |           |          |
Indexes:
    "subcountry_country_subcountry_name_key" UNIQUE CONSTRAINT, btree (country, subcountry_name)
Foreign-key constraints:
    "subcountry_country_fkey" FOREIGN KEY (country) REFERENCES country(two_letter)

AWS CDKによる更新ができるか

Blue/Green Deploymentsが存在する状態でAWS CDKによる更新ができるか確認します。試しにDBクラスターとDBインスタンスのメンテナンスウィンドウを変更します。

$ npx cdk diff
Stack AuroraStack
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Resources
[~] AWS::RDS::DBCluster Aurora/Default Aurora2CBAB212
 └─ [~] PreferredMaintenanceWindow
     ├─ [-] Sat:17:00-Sat:17:30
     └─ [+] Sun:17:00-Sun:17:30
[~] AWS::RDS::DBInstance Aurora/Default/Writer AuroraWriter14FF9353 may be replaced
 └─ [~] PreferredMaintenanceWindow (may cause replacement)
     ├─ [-] Sat:17:30-Sat:18:00
     └─ [+] Sun:17:30-Sun:18:00


✨  Number of stacks with differences: 1

$ npx cdk deploy

✨  Synthesis time: 17.99s

AuroraStack:  start: Building 62022da364f861e7b04c71879f6ba59f5bf5a0469b50a1e33c90b79d85ce8e07:<AWSアカウントID>-us-east-1
AuroraStack:  success: Built 62022da364f861e7b04c71879f6ba59f5bf5a0469b50a1e33c90b79d85ce8e07:<AWSアカウントID>-us-east-1
AuroraStack:  start: Publishing 62022da364f861e7b04c71879f6ba59f5bf5a0469b50a1e33c90b79d85ce8e07:<AWSアカウントID>-us-east-1
AuroraStack:  success: Published 62022da364f861e7b04c71879f6ba59f5bf5a0469b50a1e33c90b79d85ce8e07:<AWSアカウントID>-us-east-1
AuroraStack: deploying... [1/1]
AuroraStack: creating CloudFormation changeset...

 ✅  AuroraStack

✨  Deployment time: 47.54s

Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/AuroraStack/e6e64230-c3de-11ee-84dd-12d3827feafb

✨  Total time: 65.53s

変更後のDBクラスターとDBインスタンスのメンテナンスウィンドウは以下のとおりです。Blueのもののみ変更されていますね。

$ aws rds describe-db-clusters \
  --query 'DBClusters[].{DBClusterIdentifier : DBClusterIdentifier, PreferredMaintenanceWindow : PreferredMaintenanceWindow}'
[
    {
        "DBClusterIdentifier": "db-cluster",
        "PreferredMaintenanceWindow": "sun:17:00-sun:17:30"
    },
    {
        "DBClusterIdentifier": "db-cluster-green-ak5mgh",
        "PreferredMaintenanceWindow": "sat:17:00-sat:17:30"
    }
]

$ aws rds describe-db-instances \
  --query 'DBInstances[].{DBInstanceIdentifier : DBInstanceIdentifier, PreferredMaintenanceWindow : PreferredMaintenanceWindow}'
[
    {
        "DBInstanceIdentifier": "db-instance-writer",
        "PreferredMaintenanceWindow": "sun:17:30-sun:18:00"
    },
    {
        "DBInstanceIdentifier": "db-instance-writer-green-8n9hio",
        "PreferredMaintenanceWindow": "sat:17:30-sat:18:00"
    }
]

Blue/Green Deploymentsの切り替え

それでは、Blue/Green Deploymentsの切り替えを行います。

切り替え手順や切り替え時に内部で行われている処理、注意点、ベストプラクティスは以下AWS公式ドキュメントにまとまっています。

切り替え中のダウンタイムと継続してレプリケーションされているのか確認をしたいので、Blue側でレコードの追加を行い、Green側でレコード件数を表示します。

Blue

$ get_secrets_value=$(aws secretsmanager get-secret-value \
    --secret-id AuroraSecret7ACECA7F-IzECtgHudC8Q \
    --region us-east-1 \
    | jq -r .SecretString)

$ export PGHOST=$(echo "${get_secrets_value}" | jq -r .host)
$ export PGPORT=$(echo "${get_secrets_value}" | jq -r .port)
$ export PGDATABASE=iso
$ export PGUSER=$(echo "${get_secrets_value}" | jq -r .username)
$ export PGPASSWORD=$(echo "${get_secrets_value}" | jq -r .password)

$ while true; do
  date +"%H:%M:%S.%3N " | tr -d "\n"
  echo "INSERT INTO public.subcountry values ('NK', current_timestamp, current_timestamp);" | psql -t
  sleep 1
done

08:44:55.216 INSERT 0 1
08:44:56.266 INSERT 0 1
08:44:57.323 INSERT 0 1
08:44:58.382 INSERT 0 1
08:44:59.435 INSERT 0 1
08:45:00.482 INSERT 0 1
08:45:01.546 INSERT 0 1
08:45:02.638 INSERT 0 1

Green

$ get_secrets_value=$(aws secretsmanager get-secret-value \
    --secret-id AuroraSecret7ACECA7F-IzECtgHudC8Q \
    --region us-east-1 \
    | jq -r .SecretString)

$ export PGHOST=db-cluster-green-ak5mgh.cluster-cicjym7lykmq.us-east-1.rds.amazonaws.com
$ export PGPORT=$(echo "${get_secrets_value}" | jq -r .port)
$ export PGDATABASE=iso
$ export PGUSER=$(echo "${get_secrets_value}" | jq -r .username)
$ export PGPASSWORD=$(echo "${get_secrets_value}" | jq -r .password)

$ while true; do
  date +"%H:%M:%S.%3N " | tr -d "\n"
  echo "SELECT COUNT(*) FROM public.subcountry WHERE country='NK';" | psql -t
  sleep 1
done

08:44:58.694      5

08:44:59.747      5

08:45:00.803      5

08:45:01.879      5

08:45:02.947      5

08:45:04.024      5

はい、いくら待てどもBlueで追加したレコードがGreen側に反映されません。

論理レプリケーションの状態を確認します。

Blue

iso=> SELECT * FROM pg_publication;
  oid  |                        pubname                        | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate | pubviaroot
-------+-------------------------------------------------------+----------+--------------+-----------+-----------+-----------+-------------+------------
 20550 | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_20512 |    20540 | t            | t         | t         | t         | t           | f
(1 row)

iso=> SELECT * FROM pg_replication_slots;
                       slot_name                        |  plugin  | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn | wal_status | safe_wal_size | two_phase
--------------------------------------------------------+----------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+---------------------+------------+---------------+-----------
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_14717 | pgoutput | logical   |  14717 | postgres | f         | t      |       7472 |      |        33902 | 0/4344CD8   | 0/4347188           | reserved   |               | f
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16384 | pgoutput | logical   |  16384 | rdsadmin | f         | t      |       7488 |      |        33902 | 0/4344CD8   | 0/4347188           | reserved   |               | f
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16401 | pgoutput | logical   |  16401 | testDB   | f         | t      |       7489 |      |        33902 | 0/4344CD8   | 0/4347188           | reserved   |               | f
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_20512 | pgoutput | logical   |  20512 | iso      | f         | f      |            |      |        33448 | 0/4334808   | 0/4337A88           | reserved   |               | f
(4 rows)

iso=> SELECT * FROM pg_publication_tables;
                        pubname                        | schemaname | tablename
-------------------------------------------------------+------------+------------
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_20512 | public     | country
 rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_20512 | public     | subcountry
(2 rows)

Green

iso=> SELECT oid, subdbid, subname, subowner, subenabled, subbinary, substream, subslotname, subsynccommit, subpublications FROM pg_subscription;
  oid  | subdbid |                        subname                        | subowner | subenabled | subbinary | substream |                      subslotname                       | subsynccommit |                     subpublications
-------+---------+-------------------------------------------------------+----------+------------+-----------+-----------+--------------------------------------------------------+---------------+---------------------------------------------------------
 36879 |   14717 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_14717 |    24586 | t          | f         | f         | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_14717 | off           | {rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_14717}
 36880 |   16384 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_16384 |       10 | t          | f         | f         | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16384 | off           | {rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_16384}
 36881 |   16401 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_16401 |    24586 | t          | f         | f         | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_16401 | off           | {rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_16401}
 36882 |   20512 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_20512 |    24586 | t          | f         | f         | rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_20512 | off           | {rds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_pub_20512}
(4 rows)

isoDBをレプリケーションしているrds_us_east_1_nkicdmh5hx7lonqhyyks76nd3q_bg_slot_20512active_pidが空になっていますね。

また、Blue/Green Deploymentsのイベントを確認すると、以下のイベントが記録されていました。

DB 識別子 ロール 時間 システムノート
db-instance-writer (Blue) プライマリ February 05, 2024, 17:07 (UTC+09:00) Data definition language (DDL) changes aren't supported for blue/green deployments. These changes aren't replicated from the blue environment to the green environment, and switchover will be blocked. Your green databases now have a status of REPLICATION_DEGRADED. Delete and recreate your blue/green deployment and avoid future DDL changes. See the engine logs for the timestamp 2024-02-05-08 for more information. The log_min_messages DB parameter must be set to WARNING (the default) or lower for DDL warning messages to be saved in the engine log files.
db-instance-writer-green-8n9hio (Green) プライマリ February 05, 2024, 17:07 (UTC+09:00) Replication has been degraded.

Blue側でテーブルにカラムを追加したことが原因でレプリケーションが停止してしまったようです。

イベントメッセージに従いBlue/Green Deploymentsを一度削除して作り直します。

ブルー:グリーンデプロイを削除しますか?

削除を実行すると、GreenのAurora DBクラスターの削除も開始されました。

削除中

GreenのAurora DBクラスターの削除完了後、再度Blue/Green Deploymentsを作成します。

再作成後のBlueとGreenの論理レプリケーションの状態は以下のとおりです。

Blue

iso=> SELECT * FROM pg_publication;
  oid  |                        pubname                        | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate | pubviaroot
-------+-------------------------------------------------------+----------+--------------+-----------+-----------+-----------+-------------+------------
 20572 | rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_pub_20512 |    20562 | t            | t         | t         | t         | t           | f
(1 row)

iso=> SELECT * FROM pg_replication_slots;
                       slot_name                        |  plugin  | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn | wal_status | safe_wal_size | two_phase
--------------------------------------------------------+----------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+---------------------+------------+---------------+-----------
 rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_slot_14717 | pgoutput | logical   |  14717 | postgres | f         | t      |      21175 |      |        49632 | 0/456FAC0   | 0/4571660           | reserved   |               | f
 rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_slot_16384 | pgoutput | logical   |  16384 | rdsadmin | f         | t      |      21185 |      |        49632 | 0/456FAC0   | 0/4571660           | reserved   |               | f
 rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_slot_16401 | pgoutput | logical   |  16401 | testDB   | f         | t      |      21186 |      |        49632 | 0/456FAC0   | 0/4571660           | reserved   |               | f
 rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_slot_20512 | pgoutput | logical   |  20512 | iso      | f         | t      |      21187 |      |        49632 | 0/456FAC0   | 0/4571660           | reserved   |               | f
(4 rows)

iso=> SELECT * FROM pg_publication_tables;
                        pubname                        | schemaname | tablename
-------------------------------------------------------+------------+------------
 rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_pub_20512 | public     | country
 rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_pub_20512 | public     | subcountry
(2 rows)

Green

iso=> SELECT * FROM pg_stat_subscription;;
 subid |                        subname                        | pid | relid | received_lsn |      last_msg_send_time       |     last_msg_receipt_time     | latest_end_lsn |        latest_end_time
-------+-------------------------------------------------------+-----+-------+--------------+-------------------------------+-------------------------------+----------------+-------------------------------
 36879 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_14717 | 680 |       | 0/44590E0    | 2024-02-05 10:09:48.742687+00 | 2024-02-05 10:09:48.743167+00 | 0/44590E0      | 2024-02-05 10:09:48.742687+00
 36880 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_16384 | 702 |       | 0/44590E0    | 2024-02-05 10:09:48.743101+00 | 2024-02-05 10:09:48.743222+00 | 0/44590E0      | 2024-02-05 10:09:48.743101+00
 36881 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_16401 | 703 |       | 0/44590E0    | 2024-02-05 10:09:48.74285+00  | 2024-02-05 10:09:48.743245+00 | 0/44590E0      | 2024-02-05 10:09:48.74285+00
 36882 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_20512 | 704 |       | 0/44590E0    | 2024-02-05 10:09:48.743162+00 | 2024-02-05 10:09:48.743296+00 | 0/44590E0      | 2024-02-05 10:09:48.743162+00
(4 rows)

iso=> SELECT oid, subdbid, subname, subowner, subenabled, subbinary, substream, subslotname, subsynccommit, subpublications FROM pg_subscription;
  oid  | subdbid |                        subname                        | subowner | subenabled | subbinary | substream |                      subslotname                       | subsynccommit |                     subpublications
-------+---------+-------------------------------------------------------+----------+------------+-----------+-----------+--------------------------------------------------------+---------------+---------------------------------------------------------
 36879 |   14717 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_14717 |    24586 | t          | f         | f         | rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_slot_14717 | off           | {rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_pub_14717}
 36880 |   16384 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_16384 |       10 | t          | f         | f         | rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_slot_16384 | off           | {rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_pub_16384}
 36881 |   16401 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_16401 |    24586 | t          | f         | f         | rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_slot_16401 | off           | {rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_pub_16401}
 36882 |   20512 | rds_us_east_1_4udpsqnc36ykdcc2xrkm3rimy4_bg_sub_20512 |    24586 | t          | f         | f         | rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_slot_20512 | off           | {rds_us_east_1_5rohy2hsgaxinidn3ody3p7rve_bg_pub_20512}
(4 rows)

Blue側でレコードの追加を行い、Green側でレコード件数を表示します。

Blue

$ get_secrets_value=$(aws secretsmanager get-secret-value \
    --secret-id AuroraSecret7ACECA7F-IzECtgHudC8Q \
    --region us-east-1 \
    | jq -r .SecretString)

$ export PGHOST=$(echo "${get_secrets_value}" | jq -r .host)
$ export PGPORT=$(echo "${get_secrets_value}" | jq -r .port)
$ export PGDATABASE=iso
$ export PGUSER=$(echo "${get_secrets_value}" | jq -r .username)
$ export PGPASSWORD=$(echo "${get_secrets_value}" | jq -r .password)

$ while true; do
  date +"%H:%M:%S.%3N " | tr -d "\n"
  echo "INSERT INTO public.subcountry values ('NK', current_timestamp, current_timestamp);" | psql -t
  sleep 1
done

11:39:31.878 INSERT 0 1
11:39:32.930 INSERT 0 1
11:39:33.984 INSERT 0 1
11:39:35.042 INSERT 0 1
11:39:36.096 INSERT 0 1
11:39:37.160 INSERT 0 1
.
.
(以下略)
.
.

Green

$ get_secrets_value=$(aws secretsmanager get-secret-value \
    --secret-id AuroraSecret7ACECA7F-IzECtgHudC8Q \
    --region us-east-1 \
    | jq -r .SecretString)

$ export PGHOST=db-cluster-green-ak5mgh.cluster-cicjym7lykmq.us-east-1.rds.amazonaws.com
$ export PGPORT=$(echo "${get_secrets_value}" | jq -r .port)
$ export PGDATABASE=iso
$ export PGUSER=$(echo "${get_secrets_value}" | jq -r .username)
$ export PGPASSWORD=$(echo "${get_secrets_value}" | jq -r .password)

$ while true; do
  date +"%H:%M:%S.%3N " | tr -d "\n"
  echo "SELECT COUNT(*) FROM public.subcountry WHERE country='NK';" | psql -t
  sleep 1
done

11:39:29.780     86

11:39:30.834     86

11:39:31.933     87

11:39:32.987     88

11:39:34.051     89

11:39:35.100     90

11:39:36.165     91
.
.
(以下略)
.
.

レプリケーションができていそうです。

この状態で切り替えを行います。

切り替え

切り替え対象の確認とタイムアウトを設定して切り替えをクリックします。

切り替え- blue-green

切り替え中は以下のように全てのAurora DBクラスターとDBインスタンスのステータスが切り替えとなります。

切り替え中

切り替え後の状態確認

2分ほどで切り替えが完了しました。新Blueのエンドポイントが旧Blueのエンドポイントに変わっています。また、旧Blueのエンドポイントには-old1という文字列が付与されています。

ブルーデータベースからグリーンデータベースへの本番トラフィックの切り替えに成功しました

切り替え完了後の設定は以下のとおりです。リソースIDの入れ替わりは発生していません。また、新Blueのパラメーターグループの同期ステータスが再起動を保留中のままになっています。切り替え前からこのステータスだったので、切り替え時に再起動は行われないようです。

切り替え完了設定

発生したイベントは以下のとおりです。

切り替え完了後のイベント

DB 識別子 ロール 時間 システムノート
bgd-cavqrityavom8edj ブルー/グリーンデプロイ February 05, 2024, 18:50 (UTC+09:00) Blue/green deployment tasks completed. You can make more modifications to the green environment databases or switch over the deployment.
db-cluster新しいブルー プライマリ February 05, 2024, 20:41 (UTC+09:00) Switchover from DB cluster db-cluster to db-cluster-green-cvrzdo started.
db-cluster新しいブルー プライマリ February 05, 2024, 20:41 (UTC+09:00) Sequence sync for switchover of DB cluster db-cluster to db-cluster-green-cvrzdo has initiated. Switchover when using sequences may lead to extended downtime.
db-cluster新しいブルー プライマリ February 05, 2024, 20:41 (UTC+09:00) Sequence sync for switchover of DB cluster db-cluster to db-cluster-green-cvrzdo has completed.
bgd-cavqrityavom8edj ブルー/グリーンデプロイ February 05, 2024, 20:41 (UTC+09:00) Switchover started on blue/green deployment blue-green.
db-cluster-old1古いブルー プライマリ February 05, 2024, 20:42 (UTC+09:00) Switchover from DB cluster db-cluster to db-cluster-green-cvrzdo completed. Renamed db-cluster to db-cluster-old1 and db-cluster-green-cvrzdo to db-cluster.
db-cluster新しいブルー プライマリ February 05, 2024, 20:42 (UTC+09:00) Switchover from DB cluster db-cluster to db-cluster-green-cvrzdo completed. Renamed db-cluster to db-cluster-old1 and db-cluster-green-cvrzdo to db-cluster.
bgd-cavqrityavom8edj ブルー/グリーンデプロイ February 05, 2024, 20:42 (UTC+09:00) Switchover completed on blue/green deployment blue-green.

切り替え中に実行していたレコード追加とレコード件数表示のログを確認します。

Blue

.
.
(中略)
.
.
11:41:29.582 INSERT 0 1
11:41:30.629 INSERT 0 1
11:41:31.712 INSERT 0 1
11:41:32.762 INSERT 0 1
11:41:33.823 INSERT 0 1
11:41:34.884 ERROR:  cannot execute INSERT in a read-only transaction
11:42:41.028 INSERT 0 1
11:42:42.089 INSERT 0 1
11:42:43.165 INSERT 0 1
11:42:44.216 INSERT 0 1
11:42:45.274 INSERT 0 1
.
.
(以下略)
.
.

Green

.
.
(中略)
.
.
11:41:30.335    198

11:41:31.398    199

11:41:32.476    200

11:41:33.530    201

11:41:34.593    202

11:41:35.646    202

11:41:36.706    202
.
.
(中略)
.
.
11:42:38.110    202

11:42:39.160    202

11:42:40.209    202

11:42:41.280    203

11:42:42.362    204

11:42:43.413    205

11:42:44.461    206

11:42:45.511    207

11:42:46.563    208

11:42:47.622 psql: error: could not translate host name "db-cluster-green-cvrzdo.cluster-cicjym7lykmq.us-east-1.rds.amazonaws.com" to address: Name or service not known
11:42:48.638 psql: error: could not translate host name "db-cluster-green-cvrzdo.cluster-cicjym7lykmq.us-east-1.rds.amazonaws.com" to address: Name or service not known
11:42:49.652 psql: error: could not translate host name "db-cluster-green-cvrzdo.cluster-cicjym7lykmq.us-east-1.rds.amazonaws.com" to address: Name or service not known
11:42:50.670 psql: error: could not translate host name "db-cluster-green-cvrzdo.cluster-cicjym7lykmq.us-east-1.rds.amazonaws.com" to address: Name or service not known
.
.
(以下略)
.
.

1分ほど書き込みが停止し、その後再開しています。1分というのはAWS公式ドキュメントのとおりですね。

準備ができたら、環境を切り替えてグリーン環境を新しい本稼働環境にプロモートできます。切り替えには通常 1 分もかからず、データが失われることはなく、アプリケーションを変更する必要もありません。

Aurora 用 Amazon RDS ブルー/グリーンデプロイの概要 - Amazon Aurora

切り替え時に内部的には以下のような処理がされています。

  1. ガードレールチェックを実行して、ブルー環境とグリーン環境を切り替える準備ができているかどうかを確認します。
  2. 両方の環境で DB クラスターでの新しい書き込みオペレーションを停止します。
  3. 両方の環境で DB インスタンスへの接続を切断し、新しい接続を許可しません。
  4. グリーン環境がブルー環境と同期するように、レプリケーションがグリーン環境で追いつくのを待ちます。
  5. 両方の環境の DB クラスターと DB インスタンスの名前を変更します。
    RDS は、グリーン環境の DB クラスターと DB インスタンスが、ブルー環境の対応する DB クラスターと DB インスタンスに一致するように名前を変更します。例えば、ブルー環境の DB インスタンスの名前が mydb であるとします。また、グリーン環境の対応する DB インスタンスの名前が mydb-green-abc123 であると仮定します。切り替え時、グリーン環境の DB インスタンスの名前は mydb に変更されます。
    RDS は、現在の名前に -oldn を追加して、ブルー環境の DB クラスターと DB インスタンスの名前を変更します。ここで、n は数字です。例えば、ブルー環境の DB インスタンスの名前が mydb であるとします。切り替え後、DB インスタンス名は mydb-old1 になります。
    また、RDS はグリーン環境のエンドポイントの名前を、ブルー環境の対応するエンドポイントと一致するように変更するため、アプリケーションを変更する必要はありません。
  6. 両方の環境でデータベースへの接続を許可します。
  7. 新しい本稼働環境の DB クラスターへの書き込みオペレーションを許可します。
    切り替え後、以前の本稼働 DB クラスターは、ライターインスタンスが再起動されるまで読み取りオペレーションのみを許可します。

ブルー/グリーンデプロイの切り替え - 切り替えアクション 新Blueに接続してマテビューのリフレッシュができることを確認します。

3のタイミングでGreenでも接続は拒否されるようですが、特にその間接続できないという事象は遭遇しませんでした。

また、Greenのエンドポイントは新エンドポイントに切り替わり、新Blueで書き込みできるようになっても数秒間は接続できています。切り替え後も接続できていたのはTTLの影響かと予想します。

Aurora DNS ゾーンは 5 秒という短い TTL を使用します。しかし、多くのシステムでは、さまざまな設定でクライアントキャッシュを実装しているため、TTL が長くなる可能性があります。

Amazon Aurora ライターエンドポイントに接続する際、接続がリーダーインスタンスにリダイレクトされる理由のトラブルシューティング | AWS re:Post

新Blueに対して接続して、マテビューのリフレッシュを行います。

iso=> SELECT COUNT(*) FROM public.subcountry WHERE country='NK';
 count
-------
   254
(1 row)

iso=> SELECT * FROM nk_view;
 country |        subcountry_name        |          subdivision          | subcountry_level
---------+-------------------------------+-------------------------------+------------------
 NK      | 2024-02-05 05:40:47.217177+00 | 2024-02-05 05:40:47.217177+00 |
 NK      | 2024-02-05 05:40:52.079011+00 | 2024-02-05 05:40:52.079011+00 |
 NK      | 2024-02-05 06:28:00.036196+00 | 2024-02-05 06:28:00.036196+00 |
 NK      | 2024-02-05 08:04:24.233622+00 | 2024-02-05 08:04:24.233622+00 |
 NK      | 2024-02-05 08:04:27.876364+00 | 2024-02-05 08:04:27.876364+00 |
(5 rows)

iso=> REFRESH MATERIALIZED VIEW nk_view;
REFRESH MATERIALIZED VIEW

iso=> SELECT count(*) FROM nk_view;
 count
-------
   254
(1 row)

問題なくリフレッシュできました。

Blue/Green Deploymentsによる切り戻しが行えるか

Blue/Green Deploymentsによる切り戻しが行えるか確認します。

Blue/Green Deploymentsを選択した状態でアクションをクリックしても、切り替えはグレーアウトしていました。

再切替はできない

新Blueで論理レプリケーションのステータスを確認します。

iso=> SELECT * FROM pg_publication;
 oid | pubname | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate | pubviaroot
-----+---------+----------+--------------+-----------+-----------+-----------+-------------+------------
(0 rows)

iso=> SELECT * FROM pg_replication_slots;
 slot_name | plugin | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn | wal_status | safe_wal_size | two_phase
-----------+--------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+---------------------+------------+---------------+-----------
(0 rows)

iso=> SELECT * FROM pg_publication_tables;
 pubname | schemaname | tablename | attnames | rowfilter
---------+------------+-----------+----------+-----------
(0 rows)

切り替えたタイミングで論理レプリケーションは切れているようですね。

切り替え後に、切り戻しを行う場合は以下のような対応が必要になります。

  1. 旧Blueの再起動を行う
  2. 新BlueのAurora DBクラスター名を変更する
  3. 旧BlueのAurora DBクラスター名を元に戻す

それなりのダウンタイムは発生しそうです。

なお、1で再起動を行うのは切り替え後の旧BlueはReadOnlyになっているためです。

RDS は、現在のリソース名に -oldn を付加することによって、ブルー環境の DB クラスターと DB インスタンスの名前を変更します。ここで、n は数字です。DB クラスターは、ライターインスタンスが再起動されるまで読み取り専用です。

ブルー/グリーンデプロイの切り替え - 切り替え後

新Blueに対してAWS CDKによる更新ができるか

新Blueに対してAWS CDKによる更新ができるか確認します。

DBクラスターとDBインスタンスのメンテナンスウィンドウを変更します。

$ npx cdk diff
Stack AuroraStack
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Could not create a change set, will base the diff on template differences (run again with -v to see the reason)
Resources
[-] AWS::RDS::DBClusterParameterGroup Aurora/DbClusterParameterGroup14 AuroraDbClusterParameterGroup145F7E6F5D destroy
[-] AWS::RDS::DBParameterGroup Aurora/DbParameterGroup14 AuroraDbParameterGroup146D0E289A destroy
[~] AWS::RDS::DBCluster Aurora/Default Aurora2CBAB212
 ├─ [~] DBClusterParameterGroupName
 │   └─ [~] .Ref:
 │       ├─ [-] AuroraDbClusterParameterGroup145F7E6F5D
 │       └─ [+] AuroraDbClusterParameterGroup150C75B72F
 └─ [~] PreferredMaintenanceWindow
     ├─ [-] Sun:17:00-Sun:17:30
     └─ [+] Mon:17:00-Mon:17:30
[~] AWS::RDS::DBInstance Aurora/Default/Writer AuroraWriter14FF9353 may be replaced
 ├─ [~] DBParameterGroupName (may cause replacement)
 │   └─ [~] .Ref:
 │       ├─ [-] AuroraDbParameterGroup146D0E289A
 │       └─ [+] AuroraDbParameterGroup158895769D
 └─ [~] PreferredMaintenanceWindow (may cause replacement)
     ├─ [-] Sun:17:30-Sun:18:00
     └─ [+] Mon:17:30-Mon:18:00


✨  Number of stacks with differences: 1

npx cdk deploy中に旧Blueにアタッチしているパラメーターグループが削除できないと表示されました。

$ npx cdk deploy

✨  Synthesis time: 7.64s

AuroraStack: deploying... [1/1]
AuroraStack: creating CloudFormation changeset...
21:16:50 | DELETE_FAILED        | AWS::RDS::DBParameterGroup                  | AuroraDbParameterGroup146D0E289A
One or more database instances are still members of this parameter group aurorastack-auroradbparametergroup146d0e
289a-9rn8loibaljt, so the group cannot be deleted (Service: Rds, Status Code: 400, Request ID: 58a6ed9b-f7bd-45d9
-8adb-e5af9dd586e5)

21:16:50 | DELETE_FAILED        | AWS::RDS::DBClusterParameterGroup           | AuroraDbClusterParameterGroup145F
7E6F5D
One or more database instances are still members of this parameter group aurorastack-auroradbclusterparametergrou
p145f7e6f5d-qe4nrfvnokxj, so the group cannot be deleted (Service: Rds, Status Code: 400, Request ID: 9cd4f56c-5f
89-4a3a-87df-55ca002a88f8)

旧Blueは不要なので削除します。

Blue/Green Deploymentsを削除します。

Blue:Greenの削除中

削除しても、連動して旧Blueが削除されるということは発生しませんでした。

Blue:Green削除後

手動で旧Blueを削除します。

古いBlue削除中

旧Blueの削除が完了してしばらく待つと、npx cdk deployが完了していました。

$ npx cdk deploy

✨  Synthesis time: 7.64s

AuroraStack: deploying... [1/1]
AuroraStack: creating CloudFormation changeset...
.
.
(中略)
.
.
 ✅  AuroraStack

✨  Deployment time: 924.42s

Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/AuroraStack/e6e64230-c3de-11ee-84dd-12d3827feafb

✨  Total time: 932.06s

スタックのイベントは以下のとおりです。

AuroraStackイベント

DBクラスターとDBインスタンスのメンテナンスウィンドウを確認すると、新Blueの値が変わっていました。

$ aws rds describe-db-clusters \
    --query 'DBClusters[].{DBClusterIdentifier : DBClusterIdentifier, PreferredMaintenanceWindow : PreferredMaintenanceWindow}'
[
    {
        "DBClusterIdentifier": "db-cluster",
        "PreferredMaintenanceWindow": "mon:17:00-mon:17:30"
    },
    {
        "DBClusterIdentifier": "db-cluster-old1",
        "PreferredMaintenanceWindow": "sun:17:00-sun:17:30"
    }
]

$ aws rds describe-db-instances \
    --query 'DBInstances[].{DBInstanceIdentifier : DBInstanceIdentifier, PreferredMaintenanceWindow : PreferredMaintenanceWindow}'
[
    {
        "DBInstanceIdentifier": "db-instance-writer",
        "PreferredMaintenanceWindow": "mon:17:30-mon:18:00"
    },
    {
        "DBInstanceIdentifier": "db-instance-writer-old1",
        "PreferredMaintenanceWindow": "sun:17:30-sun:18:00"
    }
]

バックアップの保持期間でも試します。7日から9日に変更します。

$ npx cdk diff
Stack AuroraStack
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Resources
[~] AWS::RDS::DBCluster Aurora/Default Aurora2CBAB212
 └─ [~] BackupRetentionPeriod
     ├─ [-] 7
     └─ [+] 9


✨  Number of stacks with differences: 1

$ npx cdk deploy

✨  Synthesis time: 6.58s

AuroraStack:  start: Building 2d6c48c5f8ad4552b8eb63a9aa7ebc52b62d829b31da938ed94501bebf191e01:<AWSアカウントID>-us-east-1
AuroraStack:  success: Built 2d6c48c5f8ad4552b8eb63a9aa7ebc52b62d829b31da938ed94501bebf191e01:<AWSアカウントID>-us-east-1
AuroraStack:  start: Publishing 2d6c48c5f8ad4552b8eb63a9aa7ebc52b62d829b31da938ed94501bebf191e01:<AWSアカウントID>-us-east-1
AuroraStack:  success: Published 2d6c48c5f8ad4552b8eb63a9aa7ebc52b62d829b31da938ed94501bebf191e01:<AWSアカウントID>-us-east-1
AuroraStack: deploying... [1/1]
AuroraStack: creating CloudFormation changeset...

 ✅  AuroraStack

✨  Deployment time: 43.49s

Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/AuroraStack/e6e64230-c3de-11ee-84dd-12d3827feafb

✨  Total time: 50.06s

npx cdk deploy完了後に確認すると、確かにバックアップの保持期間が9日になっていました。

バックアップの保持期間が変更されたことを確認

Aurora DBクラスターやDBインスタンスにおけるCloudFormationの物理IDはDBクラスター識別子、DBインスタンス識別子です。Blue/Green Deploymentsで切り替えをしてもDBクラスター識別子とDBインスタンス識別子は変わらない = 物理IDは変わらないため、AWS CDKでも操作できたのだと考えます。

ただし、AWS公式ドキュメントにはBlue/Green Deploymentsの制約事項としてCloudFormationはサポートされていないと記載がありました。

  • ブルー/グリーンデプロイは、以下の機能ではサポートされていません。
    • Amazon RDS Proxy
    • クロスリージョンリードレプリカ
    • Aurora Serverless v1 DB クラスター
    • Aurora Global Database の一部である DB クラスター。
    • Babelfish for Aurora PostgreSQL
    • AWS CloudFormation

ブルー/グリーンデプロイの制限事項

切り替え後にAurora DBクラスターの操作ができることから、これはCloudFormationの機能でBlue/Green DeploymentsでAurora DBクラスターを切り替えることができないことを指しているのではないかと推測します。

PostgreSQL固有の制約事項があるので注意しよう

Amazon RDS Blue/Green Deployments で Amazon Aurora PostgreSQL のメジャーバージョンアップをしてみました。

インプレースアップグレードよりも短いダウンタイムで、手動で論理レプリケーションを組むより簡単にメジャーバージョンアップを行うことができました。

RDS Blue/Green Deploymentsにはいくつか制約事項があります。確保できるダウンタイムが短く、制約事項をクリアできるのであればRDS Blue/Green Deploymentsを検討しましょう。なお、PostgreSQL固有の制約事項もあるので注意しましょう。

一般的な制約事項

  • Aurora MySQL バージョン 2.08 と 2.09 はアップグレードのソースバージョンまたはターゲットバージョンとしてはサポートされていない
  • Aurora MySQL の場合、ソース DB クラスターには tmp という名前のデータベースを含めることはできない
    • この名前のデータベースはGreen環境にコピーされない
  • Aurora PostgreSQL ではBlue DB クラスターで rds.logically_replicate_unlogged_tablesパラメータが 1 に設定されていない限り、ログに記録されていないテーブルはGreen環境にレプリケートされない
  • 切り替え中、Blue環境とGreen環境では Amazon Redshift とのzero-ETLはできない
  • Blue/Green Deploymentsを作成するときには、Green環境でイベントスケジューラー (event_schedulerパラメーター) を無効にする必要がある
  • Blue/Green Deploymentsは、以下の機能ではサポートされていない
    • Amazon RDS Proxy
    • クロスリージョンリードレプリカ
    • Aurora Serverless v1 DB クラスター
    • Aurora Global Database 内の DB クラスター
    • Babelfish for Aurora PostgreSQL
    • AWS CloudFormation

変更の制約事項

  • 暗号化されていない DB クラスターを暗号化された DB クラスターに変更することはできない
  • 暗号化された DB クラスターを暗号化されていない DB クラスターに変更することはできない
  • Blue環境の DB クラスターをGreen環境の DB クラスターよりも上位のエンジンバージョンに変更することはできまない
  • Blue環境とGreen環境のリソースは同じ AWS アカウント にある必要がある
  • スイッチオーバー時、Blue DB クラスターを外部レプリケーションのソースまたはターゲットにすることはできない
  • Blueの環境に Aurora Auto Scaling ポリシーが含まれている場合、これらのポリシーはGreenの環境にコピーされない
    • Greenの環境にはポリシーを手動で再追加する必要がある

PostgreSQLの拡張機能についての制約事項

  • Blue/Green Deploymentsを作成するときにはBlue環境でpg_partman拡張機能を無効にする必要がある
    • 有効にしていると CREATE TABLE などの DDL オペレーションを実行され、Blue環境からGreen環境への論理レプリケーションが中断されてしまう可能性がある
  • Blue/Green Deploymentsを作成した後は、すべてのGreenデータベースでpg_cron拡張機能を無効のままにしておく必要がある
  • Blue環境で同じクエリプランが取り込まれた場合にプライマリキーの競合が発生しないように、apg_plan_mgmt拡張機能のapg_plan_mgmt.capture_plan_baselinesパラメータをすべてのGreenデータベースで off に設定する必要がある
  • Aurora Replicas で実行計画をキャプチャする場合は、apg_plan_mgmt.create_replica_plan_capture関数を呼び出すときにBlue DB クラスターエンドポイントを指定する必要がある。
  • Blue DB クラスターが外部データラッパー (FDW) 拡張機能の外部サーバーとして設定されている場合はIP アドレスの代わりにクラスターエンドポイント名を使用する必要がある
  • Blue/Green Deploymentsを作成するときには、Blue環境でpglogicalおよびpg_active拡張機能を無効にする必要がある
    • Blueデータベースは外部インスタンスの論理レプリケーションのサブスクライバーにはなれない

PostgreSQL 論理レプリケーションの制約事項

  • CREATE TABLE や CREATE SCHEMA などのデータ定義言語 (DDL) ステートメントは、Blue環境からGreen環境にはレプリケートされない
  • シーケンスオブジェクトに対する NEXTVAL オペレーションは、Blue環境とGreen環境では同期されない
  • Blue環境での大きなオブジェクトの作成や変更は、Green環境にはレプリケートされない
  • マテリアライズドビューはGreen環境では自動的に更新されない
  • UPDATE および DELETE オペレーションは、プライマリキーのないテーブルでは許可されない

論理レプリケーションを手組みする場合の操作方法も理解しておくと、理解が進みそうです。

Aurora PostgreSQLのメジャーバージョンアップを行う際は以下AWS公式ドキュメントも併せてチェックしておきましょう。アップグレード時のお作法がまとまっています。

トラブルシューティングをする際には以下re:Postが役立ちそうです。

また、Aurora MySQLへのメジャーアップグレードパスの紹介は以下記事がまとまっています。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!