AWS RDS (Relational Database Service) là managed database — AWS lo toàn bộ: backup tự động, failover, patching, monitoring. Bài này hướng dẫn setup RDS PostgreSQL và kết nối từ NestJS.
1. RDS vs Self-managed
| Tiêu chí | AWS RDS | Self-managed EC2 |
|---|---|---|
| Setup | Vài click | Phức tạp |
| Backup | Tự động (35 ngày) | Manual |
| Failover | Multi-AZ tự động | Manual |
| Patching | Tự động | Manual |
| Monitoring | CloudWatch tích hợp | Tự setup |
| Chi phí | Cao hơn ~30% | Thấp hơn |
| Control | Ít hơn | Toàn quyền |
Nên dùng RDS khi: Production, cần HA (High Availability), team nhỏ không có DBA.
2. Tạo RDS Instance
# Tạo RDS PostgreSQL với AWS CLI
aws rds create-db-instance \
--db-instance-identifier shopxyz-db \
--db-instance-class db.t3.micro \
--engine postgres \
--engine-version 16.1 \
--master-username dbadmin \
--master-user-password "StrongPassword123!" \
--allocated-storage 20 \
--storage-type gp3 \
--db-name shopxyz \
--vpc-security-group-ids sg-xxxxxxxx \
--db-subnet-group-name my-subnet-group \
--backup-retention-period 7 \
--multi-az \ # Tự động failover sang AZ khác
--no-publicly-accessible # Chỉ access từ VPC
--region ap-southeast-1
3. Security Group — Chỉ cho phép app access
# Cho phép NestJS app trên port 5432
aws ec2 authorize-security-group-ingress \
--group-id sg-rds-xxxxxxxx \
--protocol tcp \
--port 5432 \
--source-group sg-app-xxxxxxxx # Security group của EC2/ECS app
Không bao giờ để RDS publicly accessible (--publicly-accessible).
4. Kết nối từ NestJS
// src/database/database.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
TypeOrmModule.forRootAsync({
useFactory: (config: ConfigService) => ({
type: 'postgres',
host: config.get('DB_HOST'), // RDS endpoint
port: config.get<number>('DB_PORT', 5432),
username: config.get('DB_USER'),
password: config.get('DB_PASSWORD'),
database: config.get('DB_NAME'),
// SSL bắt buộc với RDS
ssl: {
rejectUnauthorized: true,
ca: fs.readFileSync('rds-ca-2019-root.pem').toString(), // RDS CA cert
},
// Connection pool
extra: {
max: 20, // Tối đa 20 connections
min: 2, // Duy trì ít nhất 2 connections
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
},
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
migrations: [__dirname + '/migrations/*{.ts,.js}'],
synchronize: false, // KHÔNG dùng synchronize trong production!
logging: config.get('NODE_ENV') === 'development',
}),
inject: [ConfigService],
}),
5. Download RDS CA Certificate
# Download cert để verify SSL
wget https://truststore.pki.rds.amazonaws.com/ap-southeast-1/ap-southeast-1-bundle.pem \
-O rds-ca-bundle.pem
// Dùng cert bundle
ssl: {
rejectUnauthorized: true,
ca: fs.readFileSync(path.join(__dirname, '../certs/rds-ca-bundle.pem')).toString(),
},
6. Connection String format
# .env.production
DB_HOST=shopxyz-db.abc123xyz.ap-southeast-1.rds.amazonaws.com
DB_PORT=5432
DB_USER=dbadmin
DB_PASSWORD=StrongPassword123!
DB_NAME=shopxyz
DATABASE_URL=postgresql://dbadmin:StrongPassword123!@shopxyz-db.abc123xyz.ap-southeast-1.rds.amazonaws.com:5432/shopxyz?sslmode=require
7. Prisma với RDS
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// Prisma tự handle SSL khi URL có ?sslmode=require
const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL,
},
},
log: process.env.NODE_ENV === 'development' ? ['query'] : [],
});
8. RDS Proxy — Connection Pooling
Khi Lambda hoặc nhiều containers kết nối RDS, cần RDS Proxy để tránh quá tải:
# Tạo RDS Proxy
aws rds create-db-proxy \
--db-proxy-name shopxyz-proxy \
--engine-family POSTGRESQL \
--auth '[{"AuthScheme":"SECRETS","SecretArn":"arn:aws:secretsmanager:..."}]' \
--role-arn arn:aws:iam::xxx:role/rds-proxy-role \
--vpc-subnet-ids subnet-xxx subnet-yyy \
--vpc-security-group-ids sg-xxx
// Thay endpoint trong .env
DB_HOST=shopxyz-proxy.proxy-abc123.ap-southeast-1.rds.amazonaws.com
RDS Proxy quản lý connection pool, kết nối Lambda/ECS nhiều instance mà không overwhelm DB.
9. Backup và Restore
# Tạo manual snapshot
aws rds create-db-snapshot \
--db-instance-identifier shopxyz-db \
--db-snapshot-identifier shopxyz-before-migration
# Restore từ snapshot
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier shopxyz-db-restored \
--db-snapshot-identifier shopxyz-before-migration
# Point-in-time restore (bất kỳ thời điểm nào trong 35 ngày)
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier shopxyz-db \
--target-db-instance-identifier shopxyz-db-pitr \
--restore-time 2026-01-20T10:30:00Z
10. Monitoring với CloudWatch
// Các metric quan trọng cần theo dõi
const metrics = [
'CPUUtilization', // CPU > 80% → tăng instance
'DatabaseConnections', // Gần limit → bật RDS Proxy
'FreeStorageSpace', // < 20% → tăng storage
'ReadLatency', // > 10ms → cần index
'WriteLatency', // > 5ms → check slow queries
'FreeableMemory', // < 256MB → tăng RAM
];
// Tạo CloudWatch Alarm
aws cloudwatch put-metric-alarm \
--alarm-name "RDS-CPU-High" \
--metric-name CPUUtilization \
--namespace AWS/RDS \
--dimensions Name=DBInstanceIdentifier,Value=shopxyz-db \
--statistic Average \
--period 300 \
--threshold 80 \
--comparison-operator GreaterThanThreshold \
--alarm-actions arn:aws:sns:...:db-alerts
11. Kết luận
- Multi-AZ: Bật cho production — tự động failover khi một AZ down
- SSL: Bắt buộc kết nối qua SSL — download RDS CA cert
- Security Group: Chỉ cho phép app security group access, không public
- RDS Proxy: Dùng khi có Lambda hoặc nhiều containers — tránh connection exhaustion
- Automated backup: 7 ngày minimum, point-in-time restore cho mọi sự cố
RDS đơn giản hóa database operations đáng kể — trade-off chi phí cao hơn đổi lấy reliability và thời gian quản trị ít hơn.