aws-aurora について
aws-auroraは、Amazon Auroraを利用する開発者やアーキテクト向けの実装リファレンススキルです。Serverless v2、コネクションプーリング、RDS Proxy、Data APIの選択肢に加え、移行、監視、セキュリティを扱います。
主な機能
- 01Auroraの構成と接続方式の比較
- 02RDS ProxyとData APIの設定
- 03Serverless v2の容量設定の解説
- 04Prismaによるデータベース移行例
- 05監視とIAM認証の解説
活用シーン
- 01Lambdaワークロードのデータベース接続を設計
- 02負荷に応じたサーバーレス容量の選択
- 03Prismaを使ったデータベース移行の管理
- 04Auroraのメトリクス監視と認証の設定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ alinaqi/ maggy --skill aws-auroraインストール後、Claude Code で/aws-auroraを入力して読み込まれたか確認します。
詳細
AWS Aurora スキル
Amazon Aurora は、サーバーレスのスケーリング、高可用性、エンタープライズ機能を備えた、MySQL/PostgreSQL 互換のリレーショナルデータベースです。
ソース: Aurora ドキュメント | Serverless v2 | RDS Proxy
基本原則
サーバーレスには RDS Proxy、シンプルさには Data API を使用し、常にコネクションプーリングを行ってください。
Aurora は ACID 準拠のワークロードに優れています。サーバーレスアーキテクチャ(Lambda)では、コネクション管理に必ず RDS Proxy または Data API を使用してください。Lambda 関数から直接接続を確立してはいけません。
Aurora のオプション
| オプション | 最適な用途 |
|---|---|
| Aurora Serverless v2 | 変動するワークロード、自動スケーリング(0.5-128 ACUs) |
| Aurora Provisioned | 予測可能なワークロード、最大限のパフォーマンス |
| Aurora Global | マルチリージョン、災害復旧 |
| Data API | VPC を使わないサーバーレス構成、シンプルな HTTP アクセス |
| RDS Proxy | Lambda のコネクションプーリング、高い同時実行性 |
接続方式
方式 1:RDS Proxy(Lambda に推奨)
Lambda → RDS Proxy → Aurora
(pool)
- コネクションプーリングと再利用
- 自動フェイルオーバー処理
- IAM 認証をサポート
- 既存の SQL クライアントで利用可能
方式 2:Data API(サーバーレスで最もシンプル)
Lambda → Data API (HTTP) → Aurora
- VPC 不要
- コネクション管理不要
- クエリごとのレイテンシーが高い
- Aurora Serverless のみで利用可能
方式 3:直接接続(Lambda には不適)
App Server → Aurora
(persistent connection)
- 長時間実行されるサーバー(ECS、EC2)専用
- コネクションプールを自分で管理
- サーバーレスには不適
RDS Proxy の設定
プロキシの作成(AWS Console/CDK)
// CDK example
import * as rds from 'aws-cdk-lib/aws-rds';
const proxy = new rds.DatabaseProxy(this, 'Proxy', {
proxyTarget: rds.ProxyTarget.fromCluster(cluster),
secrets: [cluster.secret!],
vpc,
securityGroups: [proxySecurityGroup],
requireTLS: true,
idleClientTimeout: cdk.Duration.minutes(30),
maxConnectionsPercent: 90,
maxIdleConnectionsPercent: 10,
borrowTimeout: cdk.Duration.seconds(30)
});
プロキシ経由での接続(TypeScript/Node.js)
// lib/db.ts
import { Pool } from 'pg';
import { Signer } from '@aws-sdk/rds-signer';
const signer = new Signer({
hostname: process.env.RDS_PROXY_ENDPOINT!,
port: 5432,
username: process.env.DB_USER!,
region: process.env.AWS_REGION!
});
// IAM authentication
async function getPool(): Promise<Pool> {
const token = await signer.getAuthToken();
return new Pool({
host: process.env.RDS_PROXY_ENDPOINT,
port: 5432,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: token,
ssl: { rejectUnauthorized: true },
max: 1, // Single connection for Lambda
idleTimeoutMillis: 120000,
connectionTimeoutMillis: 10000
});
}
// Usage in Lambda
let pool: Pool | null = null;
export async function handler(event: any) {
if (!pool) {
pool = await getPool();
}
const result = await pool.query('SELECT * FROM users WHERE id = $1', [event.userId]);
return result.rows[0];
}
プロキシ設定のベストプラクティス
# Key settings for Lambda workloads
MaxConnectionsPercent: 90 # Use most of DB connections
MaxIdleConnectionsPercent: 10 # Keep some idle for bursts
ConnectionBorrowTimeout: 30s # Wait for available connection
IdleClientTimeout: 30min # Close idle proxy connections
# Monitor these CloudWatch metrics:
# - DatabaseConnectionsCurrentlyBorrowed
# - DatabaseConnectionsCurrentlySessionPinned
# - QueryDatabaseResponseLatency
Data API(HTTP ベース)
Data API の有効化
# Must be Aurora Serverless
aws rds modify-db-cluster \
--db-cluster-identifier my-cluster \
--enable-http-endpoint
Data API Client v2 を使用した TypeScript
npm install data-api-client
// lib/db.ts
import DataAPIClient from 'data-api-client';
const db = DataAPIClient({
secretArn: process.env.DB_SECRET_ARN!,
resourceArn: process.env.DB_CLUSTER_ARN!,
database: process.env.DB_NAME!,
region: process.env.AWS_REGION!
});
// Simple query
const users = await db.query('SELECT * FROM users WHERE active = :active', {
active: true
});
// Insert with returning
const result = await db.query(
'INSERT INTO users (email, name) VALUES (:email, :name) RETURNING *',
{ email: '[email protected]', name: 'Test User' }
);
// Transaction
const transaction = await db.transaction();
try {
await transaction.query('UPDATE accounts SET balance = balance - :amount WHERE id = :from', {
amount: 100, from: 1
});
await transaction.query('UPDATE accounts SET balance = balance + :amount WHERE id = :to', {
amount: 100, to: 2
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
boto3 を使用した Python
# requirements.txt
boto3>=1.34.0
# db.py
import boto3
import os
rds_data = boto3.client('rds-data')
CLUSTER_ARN = os.environ['DB_CLUSTER_ARN']
SECRET_ARN = os.environ['DB_SECRET_ARN']
DATABASE = os.environ['DB_NAME']
def execute_sql(sql: str, parameters: list = None):
"""Execute SQL via Data API."""
params = {
'resourceArn': CLUSTER_ARN,
'secretArn': SECRET_ARN,
'database': DATABASE,
'sql': sql
}
if parameters:
params['parameters'] = parameters
return rds_data.execute_statement(**params)
def get_user(user_id: int):
result = execute_sql(
'SELECT * FROM users WHERE id = :id',
[{'name': 'id', 'value': {'longValue': user_id}}]
)
return result.get('records', [])
def create_user(email: str, name: str):
result = execute_sql(
'INSERT INTO users (email, name) VALUES (:email, :name) RETURNING *',
[
{'name': 'email', 'value': {'stringValue': email}},
{'name': 'name', 'value': {'stringValue': name}}
]
)
return result.get('generatedFields')
# Transaction
def transfer_funds(from_id: int, to_id: int, amount: float):
transaction = rds_data.begin_transaction(
resourceArn=CLUSTER_ARN,
secretArn=SECRET_ARN,
database=DATABASE
)
transaction_id = transaction['transactionId']
try:
execute_sql(
'UPDATE accounts SET balance = balance - :amount WHERE id = :id',
[
{'name': 'amount', 'value': {'doubleValue': amount}},
{'name': 'id', 'value': {'longValue': from_id}}
]
)
execute_sql(
'UPDATE accounts SET balance = balance + :amount WHERE id = :id',
[
{'name': 'amount', 'value': {'doubleValue': amount}},
{'name': 'id', 'value': {'longValue': to_id}}
]
)
rds_data.commit_transaction(
resourceArn=CLUSTER_ARN,
secretArn=SECRET_ARN,
transactionId=transaction_id
)
except Exception as e:
rds_data.rollback_transaction(
resourceArn=CLUSTER_ARN,
secretArn=SECRET_ARN,
transactionId=transaction_id
)
raise e
Aurora での Prisma
設定(RDS Proxy 経由の VPC 接続)
npm install prisma @prisma/client
npx prisma init
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
}
環境
# Use RDS Proxy endpoint
DATABASE_URL="postgresql://user:[email protected]:5432/mydb?schema=public&connection_limit=1"
Prisma を使用する Lambda ハンドラー
// handlers/users.ts
import { PrismaClient } from '@prisma/client';
// Reuse client across invocations
let prisma: PrismaClient | null = null;
function getPrisma(): PrismaClient {
if (!prisma) {
prisma = new PrismaClient({
datasources: {
db: { url: process.env.DATABASE_URL }
}
});
}
return prisma;
}
export async function handler(event: any) {
const db = getPrisma();
const users = await db.user.findMany({
include: { posts: true },
take: 10
});
return {
statusCode: 200,
body: JSON.stringify(users)
};
}
Aurora Serverless v2
キャパシティの設定
// CDK
const cluster = new rds.DatabaseCluster(this, 'Cluster', {
engine: rds.DatabaseClusterEngine.auroraPostgres({
version: rds.AuroraPostgresEngineVersion.VER_15_4
}),
serverlessV2MinCapacity: 0.5, // Minimum ACUs
serverlessV2MaxCapacity: 16, // Maximum ACUs
writer: rds.ClusterInstance.serverlessV2('writer'),
readers: [
rds.ClusterInstance.serverlessV2('reader', { scaleWithWriter: true })
],
vpc,
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }
});
キャパシティのガイドライン
| ワークロード | 最小 ACUs | 最大 ACUs |
|---|---|---|
| 開発/テスト | 0.5 | 2 |
| 小規模な本番環境 | 2 | 8 |
| 中規模な本番環境 | 4 | 32 |
| 大規模な本番環境 | 8 | 128 |
ゼロへのスケール後の起動への対処
// Data API Client v2 handles this automatically
// For direct connections, implement retry logic:
import { Pool } from 'pg';
async function queryWithRetry(
pool: Pool,
sql: string,
params: any[],
maxRetries = 3
): Promise<any> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await pool.query(sql, params);
} catch (error: any) {
// Aurora Serverless waking up
if (error.code === 'ETIMEDOUT' || error.message?.includes('Communications link failure')) {
if (attempt === maxRetries) throw error;
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
continue;
}
throw error;
}
}
}
マイグレーション
Prisma Migrate の使用
# Development (creates migration)
npx prisma migrate dev --name add_users_table
# Production (apply migrations)
npx prisma migrate deploy
# Generate client
npx prisma generate
CI/CD マイグレーションスクリプト
# .github/workflows/deploy.yml
- name: Run migrations
run: |
# Connect via bastion or use a migration Lambda
npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
マイグレーション用 Lambda
// lambdas/migrate.ts
import { execSync } from 'child_process';
export async function handler() {
try {
execSync('npx prisma migrate deploy', {
env: {
...process.env,
DATABASE_URL: process.env.DATABASE_URL
},
stdio: 'inherit'
});
return { statusCode: 200, body: 'Migrations applied' };
} catch (error) {
console.error('Migration failed:', error);
throw error;
}
}
コネクションプーリング(Lambda 以外)
PgBouncer サイドカー(ECS/EKS)
# docker-compose.yml
services:
app:
build: .
environment:
DATABASE_URL: postgresql://user:pass@pgbouncer:6432/mydb
pgbouncer:
image: edoburu/pgbouncer
environment:
DATABASE_URL: postgresql://user:pass@aurora-endpoint:5432/mydb
POOL_MODE: transaction
MAX_CLIENT_CONN: 1000
DEFAULT_POOL_SIZE: 20
アプリケーションレベルのプーリング
// For long-running servers (not Lambda)
import { Pool } from 'pg';
const pool = new Pool({
host: process.env.DB_HOST,
port: 5432,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
max: 20, // Max connections
idleTimeoutMillis: 30000, // Close idle after 30s
connectionTimeoutMillis: 10000
});
// Use pool for all queries
export async function query(sql: string, params?: any[]) {
const client = await pool.connect();
try {
return await client.query(sql, params);
} finally {
client.release();
}
}
モニタリング
主要な CloudWatch メトリクス
# Aurora
- CPUUtilization
- DatabaseConnections
- FreeableMemory
- ServerlessDatabaseCapacity (ACUs)
- AuroraReplicaLag
# RDS Proxy
- DatabaseConnectionsCurrentlyBorrowed
- DatabaseConnectionsCurrentlySessionPinned
- QueryDatabaseResponseLatency
- ClientConnectionsReceived
Performance Insights
# Enable via console or CLI
aws rds modify-db-cluster \
--db-cluster-identifier my-cluster \
--enable-performance-insights \
--performance-insights-retention-period 7
セキュリティ
IAM データベース認証
import { Signer } from '@aws-sdk/rds-signer';
const signer = new Signer({
hostname: process.env.DB_HOST!,
port: 5432,
username: 'iam_user',
region: 'us-east-1'
});
const token = await signer.getAuthToken();
// Use token as password (valid for 15 minutes)
const pool = new Pool({
host: process.env.DB_HOST,
user: 'iam_user',
password: token,
ssl: true
});
Secrets Manager のローテーション
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManagerClient({ region: 'us-east-1' });
async function getDbCredentials() {
const response = await client.send(
new GetSecretValueCommand({ SecretId: process.env.DB_SECRET_ARN })
);
return JSON.parse(response.SecretString!);
}
CLI クイックリファレンス
# Cluster operations
aws rds describe-db-clusters
aws rds create-db-cluster --engine aurora-postgresql --db-cluster-identifier my-cluster
aws rds delete-db-cluster --db-cluster-identifier my-cluster --skip-final-snapshot
# Serverless v2
aws rds modify-db-cluster \
--db-cluster-identifier my-cluster \
--serverless-v2-scaling-configuration MinCapacity=0.5,MaxCapacity=16
# Data API
aws rds-data execute-statement \
--resource-arn $CLUSTER_ARN \
--secret-arn $SECRET_ARN \
--database mydb \
--sql "SELECT * FROM users"
# Proxy
aws rds describe-db-proxies
aws rds create-db-proxy --db-proxy-name my-proxy --engine-family POSTGRESQL ...
# Snapshots
aws rds create-db-cluster-snapshot --db-cluster-identifier my-cluster --db-cluster-snapshot-identifier backup-1
aws rds restore-db-cluster-from-snapshot --db-cluster-identifier restored --snapshot-identifier backup-1
アンチパターン
- Lambda から Aurora への直接接続 - 必ず RDS Proxy または Data API を使用してください
- コネクション数の上限を設定しない - Lambda では
max: 1を設定し、サーバーではプーリングを使用してください - コールドスタートを無視する - Serverless v2 のスケーリングには時間がかかります。本番環境では最小 ACUs を維持してください
- 読み取りレプリカを使用しない - 負荷の高いワークロードでは、読み取りをレプリカにオフロードしてください
- IAM 認証を使用しない - 可能な場合は、固定パスワードより IAM を使用してください
- リトライロジックがない - スケーリングやフェイルオーバーによる一時的なエラーに対処してください
- キャパシティの過剰なプロビジョニング - 変動するワークロードには Serverless v2 を使用してください
- Secrets Manager を使用しない - 認証情報をハードコードしてはいけません
コメント
Markdown 対応