1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ECS × Aurora 構成で RDS Proxy を使い倒す:接続管理・フェイルオーバー・IAM認証の実践設計

1
Last updated at Posted at 2026-05-31

はじめに

ECS(Fargate)でコンテナを動かしていると、スケールアウト時に RDS の接続数が枯渇して障害になった、という経験はないでしょうか。

特に 大規模な業務システムでは、月末・年末など特定タイミングに処理が集中し、通常の数倍のタスクが同時起動します。このとき Aurora の max_connections を超えてアプリケーションがエラーを返すケースは珍しくありません。

本記事では、この問題の根本原因と、Amazon RDS Proxy による解決アプローチを、設定値の意味・フェイルオーバー挙動・IAM 認証の実装まで踏み込んで解説します。


1. なぜ ECS × Aurora で接続問題が起きるのか

1.1 Aurora の max_connections はメモリに比例する

Aurora MySQL・PostgreSQL の max_connections はインスタンスタイプのメモリに基づいて自動設定されます。

インスタンス メモリ max_connections(概算)
db.t3.medium 4 GiB ~420
db.r6g.large 16 GiB ~1,700
db.r6g.xlarge 32 GiB ~3,400
db.r6g.4xlarge 128 GiB ~13,500

ECS タスクが100個起動し、各タスクがコネクションプールで10本保持すると、それだけで1,000接続を消費します。月末処理でタスクが200個にスケールした瞬間、db.r6g.large の上限 1,700 をあっさり超えます。

1.2 コンテナの接続管理は難しい

VM と違い、ECS タスクは頻繁に起動・停止します。各起動ごとにコネクションプールを初期化し、停止時に解放します。しかし以下の問題が起きます:

  • 接続確立コスト:SSL ハンドシェイクを含む DB 接続確立は数十〜百ミリ秒かかる
  • TIME_WAIT 状態の残留:タスク停止後も TCP 接続が一定時間残り、DB 側のリソースを消費する
  • フェイルオーバー時の再接続:Aurora がフェイルオーバーした際、全タスクが一斉に再接続を試みる(Thundering Herd)

2. RDS Proxy の仕組み

2.1 アーキテクチャの全体像

ECS タスク群(100〜200タスク)
    │ 各タスクが localhost 感覚で接続
    ▼
RDS Proxy(マルチAZ冗長・VPC内)
    │
    ├── Connection Multiplexing(接続多重化)
    │     複数クライアント接続 → 少数の DB 接続に集約
    │
    ├── Connection Pinning(トランザクション中はピン留め)
    │
    └─▶ Aurora クラスター
           ├── Writer エンドポイント(書き込み)
           └── Reader エンドポイント(読み取り)

RDS Proxy はクライアント(ECS タスク)との接続と、Aurora との接続を分離して管理します。200タスクから接続が来ても、Aurora に対して実際に張る接続数は設定した上限内に抑えられます。

2.2 Connection Multiplexing(多重化)の詳細

RDS Proxy の核心機能です。複数のクライアント接続を少数の DB 接続にマッピングします。

クライアント接続(ECS タスク)
  conn_1 ──┐
  conn_2 ──┤         DB接続(Aurora)
  conn_3 ──┼──Proxy──▶ db_conn_A
  conn_4 ──┤           db_conn_B
  conn_5 ──┘           db_conn_C

多重化が有効になる条件(トランザクション外)
クライアントがトランザクション外でクエリを実行している間、Proxy は同じ DB 接続を別のクライアントのクエリにも再利用できます。

Connection Pinning(ピン留め)が発生する条件
以下のケースでは1クライアントが1DB接続を専有します(多重化が無効):

  • トランザクション中(BEGINCOMMIT/ROLLBACK
  • セッション変数の設定(SET session_var = value
  • プリペアドステートメントの実行
  • LOCK TABLES の使用
  • GET_LOCK() などの DB ロック関数

設計上の重要ポイント:ピン留めが多いほど Proxy の効果は薄れます。アプリケーションがトランザクションを短く保ち、セッション変数の使用を最小化することで多重化効率が上がります。


3. RDS Proxy の設定パラメータを理解する

3.1 接続プール設定

ConnectionPoolPercent(接続プール使用率の上限)
  Aurora の max_connections のうち、Proxy が使用できる割合(%)
  デフォルト: 100%
  推奨: 読み書き用 Proxy は 70〜80%、読み取り専用は残りを割り当て

IdleClientConnectionTimeout(アイドルクライアント接続のタイムアウト)
  クライアント側が何も送らずにいると切断される時間(秒)
  デフォルト: 1800秒(30分)
  推奨: アプリのコネクションプールの idle timeout より短く設定

設定例(Terraform)

resource "aws_db_proxy" "main" {
  name                   = "company-rds-proxy"
  debug_logging          = false
  engine_family          = "MYSQL"
  idle_client_connection_timeout = 900  # 15分
  require_tls            = true
  role_arn               = aws_iam_role.rds_proxy.arn
  vpc_security_group_ids = [aws_security_group.rds_proxy.id]
  vpc_subnet_ids         = var.private_subnet_ids

  auth {
    auth_scheme = "SECRETS"
    iam_auth    = "REQUIRED"  # IAM 認証を必須化
    secret_arn  = aws_secretsmanager_secret.db_credentials.arn
  }
}

resource "aws_db_proxy_default_target_group" "main" {
  db_proxy_name = aws_db_proxy.main.name

  connection_pool_config {
    connection_borrow_timeout    = 120  # 接続待機の最大秒数
    max_connections_percent      = 75   # max_connections の 75% を使用
    max_idle_connections_percent = 50   # アイドル接続の最大割合
    session_pinning_filters      = ["EXCLUDE_VARIABLE_SETS"]  # SET 変数によるピン留めを除外
  }
}

session_pinning_filtersEXCLUDE_VARIABLE_SETS を設定することで、SET コマンドによる不要なピン留めを抑制できます。多くのフレームワーク(Rails の ActiveRecord など)がセッション変数を設定するため、この設定は重要です。

3.2 Writer と Reader で Proxy を分ける

読み書き分離をしている構成では、Writer 用・Reader 用に別々の Proxy を作成します。

アプリケーション
    │
    ├── 書き込み処理 ──▶ Proxy(Writer)──▶ Aurora Writer
    │
    └── 読み取り処理 ──▶ Proxy(Reader)──▶ Aurora Reader×N
# Reader 用 Proxy
resource "aws_db_proxy" "reader" {
  name          = "company-rds-proxy-reader"
  engine_family = "MYSQL"
  # ... 同様の設定

  connection_pool_config {
    max_connections_percent = 100  # Reader は全接続を使用可
  }
}

resource "aws_db_proxy_target" "reader" {
  db_proxy_name         = aws_db_proxy.reader.name
  target_group_name     = "default"
  db_cluster_identifier = aws_rds_cluster.main.id
  # Proxy がクラスターの Reader エンドポイントに自動で接続を振り分ける
}

4. IAM 認証:パスワードをアプリから完全に排除する

4.1 IAM 認証の仕組み

RDS Proxy の IAM 認証では、DB パスワードの代わりに IAM トークン(有効期限15分の一時認証情報) を使用します。

ECS タスク
    │
    1. 自身の IAM ロールで IAM トークンを生成
    │  (aws rds generate-db-auth-token)
    │
    2. トークンをパスワードとして Proxy に接続(TLS必須)
    ▼
RDS Proxy
    │
    3. IAM にトークンを検証させる
    │
    4. 検証成功 → Secrets Manager から実際の DB パスワードを取得
    │
    5. Aurora に接続
    ▼
Aurora

アプリケーションが DB パスワードを保持しない設計になるため、パスワードの漏洩リスクを根本的に排除できます。

4.2 ECS タスクロールの設定

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "rds-db:connect",
      "Resource": "arn:aws:rds-db:ap-northeast-1:123456789012:dbuser:prx-XXXXXXXXXXXXXXXX/app_user"
    }
  ]
}

rds-db:connect の Resource は arn:aws:rds-db:{region}:{account}:dbuser:{proxy_resource_id}/{db_username} の形式です。

4.3 アプリケーション側の実装(Ruby on Rails の例)

# config/database.yml
production:
  adapter: mysql2
  host: <%= ENV['RDS_PROXY_ENDPOINT'] %>
  port: 3306
  database: company_production
  username: app_user
  password: <%= RdsIamAuthToken.generate %>
  ssl_mode: verify_full
  sslca: /etc/ssl/certs/rds-ca-bundle.pem

# lib/rds_iam_auth_token.rb
require 'aws-sdk-rds'

module RdsIamAuthToken
  def self.generate
    Aws::RDS::AuthTokenGenerator.new(
      region: ENV['AWS_REGION'],
      credentials: Aws::ECSCredentials.new
    ).auth_token(
      region:     ENV['AWS_REGION'],
      endpoint:   ENV['RDS_PROXY_ENDPOINT'],
      port:       3306,
      user_name:  'app_user'
    )
  end
end

注意:IAM トークンの有効期限は15分です。コネクションプールが接続を長時間保持する場合、接続確立時にトークンを再生成する必要があります。接続確立のコールバック(after_connect)でトークンを毎回生成するよう実装してください。


5. フェイルオーバー時の挙動と設計

5.1 RDS Proxy なしのフェイルオーバー

障害発生
    │
    ▼
Aurora フェイルオーバー(30〜60秒)
    │
    ▼
全 ECS タスク(200個)が同時に再接続を試みる
    │
    ▼ Thundering Herd 問題
Aurora が接続の波を受け、max_connections を一時的に超過
または 再接続の競合によりレイテンシが急増

5.2 RDS Proxy ありのフェイルオーバー

障害発生
    │
    ▼
Aurora フェイルオーバー(30〜60秒)
    │
    ▼
RDS Proxy が新しい Writer に自動的に再接続
(ECS タスクは Proxy への接続を維持し続ける)
    │
    ▼
アプリケーション側の接続断: 最小限
(Proxy が再接続を完了するまでの数秒のみ)

RDS Proxy を使うことで、Aurora のフェイルオーバー時にアプリケーションが受ける影響を大幅に低減できます。ただし、Proxy 自体もフェイルオーバー完了まで一時的に接続エラーを返すため、アプリケーション側でのリトライ実装は引き続き必要です。

5.3 接続タイムアウトの推奨設定

アプリ側コネクションプール設定:
  connect_timeout:         10秒  # Proxy への接続タイムアウト
  checkout_timeout:        5秒   # プールからの接続取得タイムアウト
  dead_connection_check:   有効  # 死んだ接続を検出して除去
  retry_count:             3     # リトライ回数
  retry_interval:          1秒   # リトライ間隔(指数バックオフ推奨)

Proxy 設定:
  ConnectionBorrowTimeout: 120秒 # DB 接続が全て使用中の場合の待機時間

6. 月末・大量バッチ処理での活用

6.1 バッチ処理専用の Proxy を作る

給与計算など月末に大量の ECS タスクが起動するバッチ処理には、バッチ専用の Proxy を用意し、接続プールの上限を分けることを推奨します。

通常時:
  API サーバー(50タスク)──▶ Proxy-API(max_connections × 60%)──▶ Aurora
  バッチ(0タスク)──▶ Proxy-Batch(max_connections × 30%)──▶ Aurora

月末バッチ処理時:
  API サーバー(50タスク)──▶ Proxy-API(max_connections × 60%)──▶ Aurora
  バッチ(150タスク)──▶ Proxy-Batch(max_connections × 30%)──▶ Aurora

バッチが急増しても API サーバーの接続を圧迫しません。

6.2 バッチ処理での Connection Pinning を最小化する

バッチ処理はトランザクションをバルク処理に使うことが多く、ピン留めが発生しがちです。以下の設計を意識してください:

# NG: 1件ずつトランザクションを張る(1件 = 1接続ピン留め)
records.each do |record|
  ActiveRecord::Base.transaction do
    record.update!(...)
  end
end

# OK: 適切なサイズでバルク処理(接続の再利用効率が上がる)
records.each_slice(100) do |batch|
  ActiveRecord::Base.transaction do
    batch.each { |record| record.update!(...) }
  end
end

7. モニタリング:見るべきメトリクス

RDS Proxy が正常に機能しているか確認するために、以下の CloudWatch メトリクスを監視します。

メトリクス 意味 アラート目安
DatabaseConnectionsCurrentlyBorrowed Proxy が Aurora に張っている接続数 max_connections の 80% を超えたら警告
ClientConnectionsReceived クライアントから受け付けた接続数 急増時に要確認
DatabaseConnectionRequests Aurora への接続要求数 ピン留めの多さの指標
DatabaseConnectionsCurrentlyInTransaction トランザクション中の接続数 高止まりはロック懸念
QueryDatabaseResponseLatency DB レスポンスのレイテンシ P99 が閾値超過でアラート
# Terraform での CloudWatch アラーム設定例
resource "aws_cloudwatch_metric_alarm" "proxy_connection_high" {
  alarm_name          = "rds-proxy-connections-high"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name         = "DatabaseConnectionsCurrentlyBorrowed"
  namespace           = "AWS/RDS"
  period              = 60
  statistic           = "Maximum"
  threshold           = 1200  # max_connections の 70% 程度

  dimensions = {
    ProxyName = aws_db_proxy.main.name
  }

  alarm_actions = [aws_sns_topic.alerts.arn]
}

8. まとめ

RDS Proxy は「接続数を減らすためのプロキシ」として語られがちですが、実際に得られる価値はそれ以上です。

課題 RDS Proxy による解決
ECS スケールアウト時の接続枯渇 接続多重化で DB への接続数を制御
フェイルオーバー時の Thundering Herd Proxy が再接続を集約・吸収
DB パスワードの管理リスク IAM 認証でパスワードをアプリから排除
月末バッチによる API 影響 Proxy を分けて接続枠を分離
接続確立コストの高さ 接続プール再利用で初期化コストを削減

ECS でアプリケーションを運用していて RDS/Aurora を使っているなら、RDS Proxy の導入は費用対効果の高い投資です。特に スケールアウトが発生する業務システムでは、月末の処理集中や障害時の挙動が大きく改善されます。


参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?