[アップデート] RDS ブルー/グリーンデプロイの RDS Proxy サポートを検証してみた

[アップデート] RDS ブルー/グリーンデプロイの RDS Proxy サポートを検証してみた

2026.04.10

はじめに

Amazon RDS の ブルー/グリーンデプロイが、アップデートにより RDS Proxy をサポートするようになりました。

https://aws.amazon.com/jp/about-aws/whats-new/2026/04/rds-proxy-blue-green/

どのような挙動になるのか、早速試してみました。

やってみた

検証用に RDS for MySQL を用意しました。
DB エンジンのバージョンは 8.0 系とします。なお、8.0 系の標準サポートは 2026 年の 7 月に終了となります。
この DB を 8.4 系にブルー/グリーンデプロイするというシナリオを想定してみます。

RDS Proxy も作成済みです。

RDS

では、ブルー/グリーンデプロイを作成しましょう。

グリーンのエンジンバージョンが 8.4 系であることを確認します。インスタンスやストレージの設定は今回は変更しません。

RDS

内容を確認し、問題なければ作成します。

RDS

しばらく待っていると、無事に作成されました。

RDS

デプロイする前に、以下のような Lambda を用意して、接続断が発生するかどうかを検証してみることにします。

Lambda の実行環境は以下のとおりです。

  • Python ランタイム: 3.14
  • VPC: RDS Proxy にアクセスできるサブネット・セキュリティグループを設定
  • 環境変数: RDS_PROXY_HOSTDB_USERDB_PASSWORDDB_NAME を設定
import json
import os
import time
from datetime import datetime, timezone

import pymysql

_SSL_CA = "/etc/pki/tls/certs/ca-bundle.crt"

conn: pymysql.Connection | None = None


def _get_connection() -> pymysql.Connection:
    """
    グローバルコネクションを返す。
    初回呼び出し時にコネクションを確立し、以降は再利用する。
    切断されている場合は _probe() 側で conn を None にリセットしてから呼ぶこと。
    """
    global conn
    if conn is None:
        conn = pymysql.connect(
            host=os.environ["RDS_PROXY_HOST"],
            port=int(os.environ.get("DB_PORT", "3306")),
            user=os.environ["DB_USER"],
            password=os.environ["DB_PASSWORD"],
            database=os.environ["DB_NAME"],
            connect_timeout=5,
            read_timeout=5,
            write_timeout=5,
            ssl={"ca": _SSL_CA},
        )
    return conn


def _probe() -> dict:
    """
    DB に SELECT NOW() を 1 回実行し、結果と所要時間を返す。
    エラー時は例外を握りつぶし ok=False のレコードを返す。
    """
    ts = datetime.now(timezone.utc).isoformat()
    t0 = time.perf_counter()
    try:
        global conn
        if conn is not None:
            try:
                conn.ping(reconnect=False)
            except Exception:
                conn = None
        c = _get_connection()
        with c.cursor() as cur:
            cur.execute("SELECT NOW()")
            db_now = str(cur.fetchone()[0])
        elapsed_ms = round((time.perf_counter() - t0) * 1000)
        return {"ts": ts, "ok": True, "db_now": db_now, "elapsed_ms": elapsed_ms}
    except Exception as e:
        elapsed_ms = round((time.perf_counter() - t0) * 1000)
        conn = None
        return {"ts": ts, "ok": False, "error": str(e), "elapsed_ms": elapsed_ms}


def _run_downtime_check(duration_seconds: int, interval_seconds: float) -> dict:
    """
    duration_seconds 秒間、interval_seconds 秒ごとに DB への接続を試みる。
    各接続テスト結果を CloudWatch Logs に出力し、最後にサマリーを返す。
    """
    records: list[dict] = []
    end_at = time.monotonic() + duration_seconds

    print(json.dumps({
        "event": "start",
        "duration_seconds": duration_seconds,
        "interval_seconds": interval_seconds,
    }))

    while time.monotonic() < end_at:
        result = _probe()
        print(json.dumps(result))
        records.append(result)
        time.sleep(interval_seconds)

    errors = [r for r in records if not r["ok"]]
    first_error = errors[0] if errors else None
    last_error = errors[-1] if errors else None

    downtime_ms: int | None = None
    if first_error and last_error and first_error is not last_error:
        t_first = datetime.fromisoformat(first_error["ts"])
        t_last = datetime.fromisoformat(last_error["ts"])
        downtime_ms = round((t_last - t_first).total_seconds() * 1000) + last_error["elapsed_ms"]
    elif first_error:
        downtime_ms = first_error["elapsed_ms"]

    summary = {
        "total_probes": len(records),
        "success": len(records) - len(errors),
        "errors": len(errors),
        "estimated_downtime_ms": downtime_ms,
        "first_error": first_error,
        "last_error": last_error,
    }
    print(json.dumps({"event": "summary", **summary}))
    return summary


def lambda_handler(event: dict, context: object) -> dict:
    """
    duration_seconds 秒間、interval_seconds 秒ごとに DB への接続を試みる。
    Lambda のタイムアウトを duration_seconds + 30 秒以上に設定すること。
    """
    duration = int(event.get("duration_seconds", 120))
    interval = float(event.get("interval_seconds", 1))
    summary = _run_downtime_check(duration, interval)
    return {"statusCode": 200, "body": json.dumps(summary, ensure_ascii=False)}

5 分間 1 秒間隔で接続を試みるようにして Lambda 関数を実行し、RDS 側もブルー環境からグリーン環境に切り替えます。

RDS

切り替え自体は 2 分くらいで完了しました。

RDS

Lambda 関数のログを見てみると、295 回の接続テストのうちエラーは 1 回のみでした。
RDS Proxy がバックエンドの接続先を切り替えるタイミングで Lost connection to MySQL server during query が発生しましたが、推定ダウンタイムは約 1.1 秒で、次の接続テストではすぐに回復しています。

CloudWatch

おわりに

RDS Proxy がサポートされたことで、RDS のブルー/グリーンデプロイがさらに使いやすくなりました。
まずは検証環境などでお試しいただき、その後、本番環境で実施することをおすすめいたします。

なお、本番環境での Lambda から RDS Proxy への接続には、今回のようなパスワード認証ではなく IAM 認証の利用をおすすめします。
IAM 認証を利用することでパスワード管理が不要になり、よりセキュアな構成を実現できます。

この記事をシェアする

関連記事