주제: 백업/복구, RTO/RPO, 멀티AZ/리전, 카오스 테스트, 비용관리
- RTO/RPO를 “서비스·데이터 유형”별로 숫자로 못 박기(예: 결제 RTO 15m/RPO 1m)
- 계층별 DR 패턴: 앱(무상태)·DB(PITR+리전 복제)·캐시(버림)·오브젝트(S3 버저닝)·서치(재색인)·큐(DLQ 보전)
- 아키텍처: 멀티AZ 기본, 필요 시 크로스리전 Warm-standby(가성비) → Active/Active(고가용)
- 복구 신뢰성: 자동 백업 + 정기 복구 리허설(Runbook/증빙)
- 페일오버: DNS·게이트웨이·DB 리더 전환 단계별 스위치와 되돌리기
- 카오스 실험: “노드/POD 사망, DB 장애, 리전 격절” 시나리오를 분기별로
- 비용: 중요도 낮은 워크로드는 Pilot-light, 스토리지는 라이프사이클로 냉장/아카이브
1) 용어·목표 고정
- RTO(복구시간): 장애 → 정상 복구까지 허용 시간
- RPO(복구시점): 데이터 손실 허용 시점(최신에서 얼마나 뒤로)
권장 매트릭스(예시)
- 사용자 인증: RTO 15m / RPO 5m
- 주문/결제: RTO 15m / RPO 1m
- 로그/분석: RTO 2h / RPO 1h
- 마케팅 페이지: RTO 30m / RPO 10m
위 표를 기준으로 각 컴포넌트(DB/캐시/서치/오브젝트/큐)별 DR 등급을 매핑하세요.
2) 계층별 DR 설계
2.1 애플리케이션(무상태 우선)
- Next.js/BFF/FastAPI: 컨테이너 이미지만 있으면 어디서든 가동 → 다중 리전 배포 파일(Helm values 복수 세트)
- Feature Flag / Kill Switch: 무거운 기능을 즉시 끄고 핵심 경로만 살리기
- 구성 데이터는 GitOps(Argo)로 소스오브트루스 유지
2.2 데이터베이스(Postgres)
- 멀티AZ + 자동 백업(PITR)
- 크로스리전 읽기 복제(Warm-standby) 또는 논리적 복제
- 스키마 변경은 Expand→Migrate→Contract(9편) — DR 중단 리스크 감소
2.3 캐시/세션(Redis)
- 세션은 짧은 TTL + 재인증 가능 설계
- Redis는 클러스터/멀티AZ. 리전 DR은 필요 시만(대부분 캐시는 버려도 됨)
2.4 오브젝트 스토리지(S3 등)
- 버저닝 + MFA Delete + 라이프사이클
- **크로스리전 복제(CRR)**로 백업 겸 DR
2.5 서치(Elastic/OpenSearch)
- 색인은 재생성 가능을 전제로 DB→서치 CDC/리플레이 파이프라인 유지
- DR 시 재색인 스크립트/잡을 즉시 돌릴 수 있게 준비
2.6 큐/스트림(SQS/Kafka)
- DLQ로 실패 이벤트 보관
- Kafka는 다중 AZ 브로커 + 스냅샷/미러(MirrorMaker/크로스리전) 검토
3) 페일오버 전략(단계별)
- 내부 장애(Pod/노드/AZ): HPA/재시작/PDB/TopologySpread로 흡수
- DB 장애: 프라이머리 → 리더 전환, 앱 커넥션 재시도/Idempotency로 이중처리 방지
- 리전 장애:
- DNS 레벨(Route 53 Health Check Failover)
- CDN 오리진 그룹(주/보조 원본)
- 데이터 스위치: RDS 크로스리전 리더 승격, 시크릿/엔드포인트 교체
4) 백업·복구 표준(“쓰기보다 복구 검증”)
- DB: 자동 스냅샷 + WAL 아카이브(PITR). 월 1회 타임라인 복구 리허설
- 오브젝트: 버전 복원 스크립트, 삭제 보호(MFA Delete)
- 서치: 스냅샷 or 재색인 잡 검증
- 인프라: Terraform state 백업, ArgoCD App of Apps로 재구성 가능한 템플릿
5) 실행(복붙) 스니펫
5.1 Route 53 페일오버(요약)
# Terraform 예시
resource "aws_route53_health_check" "api" {
type = "HTTPS"
resource_path = "/healthz"
fqdn = "api-primary.example.com"
failure_threshold = 3
request_interval = 30
}
resource "aws_route53_record" "api_failover_primary" {
zone_id = var.zone_id
name = "api.example.com"
type = "A"
set_identifier = "primary"
failover_routing_policy { type = "PRIMARY" }
alias {
name = aws_lb.primary.dns_name
zone_id = aws_lb.primary.zone_id
evaluate_target_health = true
}
health_check_id = aws_route53_health_check.api.id
}
resource "aws_route53_record" "api_failover_secondary" {
zone_id = var.zone_id
name = "api.example.com"
type = "A"
set_identifier = "secondary"
failover_routing_policy { type = "SECONDARY" }
alias {
name = aws_lb.secondary.dns_name
zone_id = aws_lb.secondary.zone_id
evaluate_target_health = true
}
}
5.2 S3 버저닝/라이프사이클
resource "aws_s3_bucket_versioning" "main" {
bucket = aws_s3_bucket.main.id
versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_lifecycle_configuration" "main" {
bucket = aws_s3_bucket.main.id
rule {
id = "logs"
filter { prefix = "logs/" }
status = "Enabled"
transition { days = 30 storage_class = "STANDARD_IA" }
transition { days = 90 storage_class = "GLACIER" }
expiration { days = 365 }
}
}
5.3 Postgres WAL 아카이브(컨테이너 + wal-g)
# env 예시
WALG_S3_PREFIX=s3://ex-pg/wal
AWS_REGION=ap-northeast-2
WALE_S3_ENDPOINT=s3.amazonaws.com
PGHOST=localhost
# 복구 리허설(개념)
export WALG_S3_PREFIX=s3://ex-pg/basebackups_005
wal-g backup-fetch /var/lib/postgresql/data LATEST
wal-g wal-fetch LATEST /var/lib/postgresql/data/pg_wal
# postgresql.conf: restore_command='wal-g wal-fetch %f %p'
5.4 K8s: 백업 CronJob(논리 덤프)
apiVersion: batch/v1
kind: CronJob
metadata: { name: pg-dump }
spec:
schedule: "0 3 * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: dump
image: postgres:16
envFrom: [{ secretRef: { name: api-secrets } }]
args: ["bash","-lc","pg_dump -Fc -d $DB_URL > /backup/$(date +%F).dump"]
volumeMounts: [{ name: backup, mountPath: /backup }]
volumes: [{ name: backup, persistentVolumeClaim: { claimName: backup-pvc } }]
5.5 CloudFront 오리진 페일오버(요지)
- Origin Group: Primary: api-primary, Secondary: api-secondary
- HealthCheck:
/healthz5xx/타임아웃 시 Secondary로
5.6 카오스 테스트(Chaos Mesh 예)
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata: { name: kill-api-pod }
spec:
action: pod-kill
mode: one
selector: { namespaces: ["prod"], labelSelectors: { app: "api" } }
duration: "60s"
scheduler: { cron: "@monthly" }
6) 운영 시나리오(런북 템플릿)
A. DB 프라이머리 장애
- 알람 확인(에러율↑/DB 연결 실패)
- 트래픽 리드온리 모드 또는 큐잉(쓰기 차단)
- 리더 승격 → 앱 시크릿의 DB 엔드포인트 교체 → 롤아웃
- 일관성 확인(replication lag, 최근 트랜잭션 검증)
- 정상화 후 원복 계획 수립(역복제/재동기)
B. 리전 장애
- DNS/오리진 페일오버 트리거
- RDS 보조 리전 리더 승격
- Redis/서치 대체(세션 재인증, 재색인 잡 시작)
- SLO/버짓 모니터링, 비핵심 기능 Kill Switch
- 사후 보고(영향 범위/복구시간/재발 방지)
7) 테스트·증빙(필수)
- 월 1회: DB 복구(지정 시점), 서치 재색인, S3 버전 복원
- 분기 1회: 카오스(노드/Pod Kill, 네트워크 단절) + Runbook 타임드릴
- 반기 1회: 리전 DR 데이(파일럿) — Route53/오리진 페일오버 실전
- 결과는 **증빙 저장소(evidence/dr/)**에 리포트/스크린샷 첨부
8) 비용 최적화
- Warm-standby: 컴퓨트는 최소, DB는 저사양 리더/리플리카로 유지
- 데이터 계층별 차등: 핵심(DB/S3 메타)만 크로스리전, 서치는 DR시 재색인
- 스토리지 라이프사이클: 장기보관 로그/스냅샷은 아카이브 클래스로 자동 이전
- 테스트 주기 합리화: 과도한 카오스는 비용↑ → 핵심 시나리오 위주
9) 48시간 액션 플랜
- RTO/RPO 표를 서비스/데이터 유형별로 확정
- RDS 자동백업+PITR 확인, WAL 아카이브 설정
- S3 버저닝+라이프사이클 적용, 삭제보호(MFA Delete) 점검
- Route 53 Failover 레코드(헬스체크) 초안 생성
- 카오스 퀵테스트: Pod Kill + 런북 타임드릴(15분 내 종료 목표)
10) 2주 액션 플랜
- 크로스리전 리더(DB) 준비 또는 논리 복제 경로 확보
- 재색인 잡(서치)과 데이터 리플레이(Outbox/CDC) 스크립트 작성
- DR 데이 체크리스트/역할 분담 문서화(연락망 포함)
- 앱 Kill Switch/Degrade 모드 플래그 도입(프론트 배너/공지 자동)
- 증빙 저장소(evidence/dr) 신설: 리허설 결과 주기 기록
마무리
신뢰성·DR의 핵심은 **“수치로 목표를 고정하고(=RTO/RPO), 복구를 주기적으로 연습한다”**입니다.
멀티AZ는 기본, Warm-standby로 가성비 있는 리전 DR을 깔고, 복구 리허설과 카오스 실험으로 자신감을 쌓으세요.
다음은 13편. 비용·운영 최적화 — FinOps, 캐파 계획, 성능/비용 트레이드오프로 갈게요.
답글 남기기