Files
project-infra/docs/standards/infra/db-and-migration.md
T

13 KiB

db / migration 기준

목적

이 문서는 Kubernetes 상의 PostgreSQL과 Flyway를 기준으로

  • 데이터베이스를 어떻게 나눌지
  • 어떤 operator / 도구로 운영할지 (CNPG, Zalando, Crunchy, self-managed StatefulSet)
  • migration을 어디서 어떻게 실행할지
  • migration과 배포(Helm / Argo CD)의 순서를 어떻게 보장할지
  • validate / migrate / rollback / backup / PITR을 어떤 순서로 볼지
  • zero-downtime을 위한 expand-migrate-contract를 어떻게 적용할지 를 먼저 고정한다.

이 문서의 목표는 다음과 같다.

  • app rollout과 schema 변경을 분리한다
  • Keycloak DB와 auth-server DB 경계를 먼저 고정한다
  • Flyway를 앱 시작 로직에 숨기지 않는다
  • PostgreSQL backup/restore 전략과 migration 전략을 함께 본다
  • 1000+ 서비스 규모에서 일관된 migration Job 표준을 만든다

공식 의미

  • pg_dump는 logical export다. 정기 production 전체 백업 기본값으로는 보통 적합하지 않다.
  • pg_basebackup은 실행 중인 PostgreSQL cluster의 base backup을 만들며 PITR/standby 시작점으로 쓴다.
  • PostgreSQL PITR은 base backup + WAL archiving 결합이다.
  • 운영 표준 물리 백업 도구: pgBackRest, WAL-G. 또는 operator-native (CNPG Barman Cloud, Crunchy PGO).
  • PostgreSQL의 대부분 DDL은 트랜잭션 내에서 실행 가능하지만, CREATE INDEX CONCURRENTLY, REINDEX CONCURRENTLY, ALTER TYPE ... ADD VALUE, VACUUM은 트랜잭션 밖에서만 실행된다.
  • CloudNativePG operator는 CNCF Sandbox 프로젝트로 K8s-native Postgres 운영 표준 후보다.
  • Flyway validate는 적용된 migration과 로컬 migration 사이의 이름/타입/checksum 차이, 로컬에 없는 적용 버전, 아직 적용되지 않은 로컬 버전을 검증한다.
  • Flyway validateOnMigrate 기본값은 true, cleanDisabled 기본값은 true (Flyway 9+).
  • Flyway migrate는 schema history table을 자동 생성하고 최신 migration까지 적용한다.
  • Flyway Community(OSS)는 undo(U__) migration을 지원하지 않는다. Undo는 Teams/Enterprise 전용이다.
  • Flyway는 migration 실행 시 schema history table에 advisory lock을 걸어 동시 실행을 방지한다.

RPO / RTO

모든 DB는 다음을 runbook에 먼저 적는다.

  • RPO / RTO / Retention
  • 복구 목표 (cluster restore / PITR / standby seed)
  • 운영 tier (gold / silver / bronze)

이 값들이 없으면 backup 도구 선택이 되지 않는다. backup-restore.md 참조.

기본 규칙

1. DB 경계는 애플리케이션 경계보다 먼저 고정

다음을 명시적으로 정한다.

  • Keycloak DB와 auth-server DB를 물리적 cluster로 분리할지, 같은 cluster 내 logical DB / schema로 분리할지
  • test-server가 DB를 가지는지
  • migration 소유권이 누구에게 있는지 (보통 서비스 팀)

기본:

  • 인증 critical data (Keycloak)와 앱 data (auth-server)는 cluster 분리 권장
  • 같은 cluster를 쓰더라도 database / role / schema ownership을 섞지 않음
  • 한 migration tool/job이 여러 서비스 schema를 동시에 소유하지 않음

2. K8s 위 Postgres 운영 기본은 operator

1000+ 서비스 규모에서 self-managed StatefulSet은 운영 부담이 너무 크다. Operator를 기본 후보로 둔다.

우선순위 (2026 기준):

  1. CloudNativePG (CNPG) — CNCF Sandbox, K8s-native, Barman Cloud 내장, Cluster / Backup / ScheduledBackup CRD
  2. Crunchy PGO — 상용 지원, pgBackRest 내장
  3. Zalando postgres-operator — Spilo 기반, 레거시 환경

operator를 쓰면 자동으로 얻는 것:

  • primary/standby 구성 + failover
  • rolling minor upgrade
  • WAL archiving + continuous backup
  • pg_basebackup, PITR, replica re-clone
  • PodMonitor 연동

3. migration은 앱 startup에 숨기지 않는다

Flyway migration은 독립 실행 단계다.

기본:

  • validate → (필요시 info) → migrate → app rollout

기본 금지:

  • app container 시작 시 자동 migration (Spring Boot spring.flyway.enabled=true + @SpringBootApplication 부팅 시 migrate)
  • readiness/liveness와 migration 실패를 섞는 구조
  • "서버가 뜨면 알아서 schema를 맞춘다" 방식

4. Flyway 실행 기본값은 Kubernetes Job

운영 환경에서 Flyway는 다음 중 하나로만 실행한다.

  • Kubernetes Job (권장)
  • CI/CD 명시 단계
  • 운영자 명시 실행 절차

장기 실행 Deployment에 넣지 않는다. Flyway 예시는 docs/examples/infra/flyway.md 참조.

5. migration Job은 배포 흐름 안에서 app보다 먼저 실행

migration을 app보다 선행시키는 것은 manifest 메타데이터로 선언한다.

패턴 A — Helm hook:

metadata:
  annotations:
    "helm.sh/hook": "pre-upgrade,pre-install"
    "helm.sh/hook-weight": "-10"
    "helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"

패턴 B — Argo CD sync wave + hook:

metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "-1"
    argocd.argoproj.io/hook: Sync
    argocd.argoproj.io/hook-delete-policy: BeforeHookCreation

기본:

  • migration은 sync wave가 app보다 작은 값 (먼저 실행)
  • app Deployment는 wave 0 또는 그 이상
  • Helm과 Argo CD를 혼용하는 경우 한 쪽으로 통일 (둘 다 hook을 걸면 순서가 꼬인다)

6. migration Job 안전 설정

모든 migration Job은 다음을 명시한다.

  • parallelism: 1 — 병렬 실행 금지 (Flyway advisory lock이 막아주지만, Job 수준에서도 명시)
  • completions: 1
  • backoffLimit: 0 또는 작은 값 (1~2) — 실패 시 무한 재시도 금지
  • activeDeadlineSeconds — 타임아웃 (예: 1800)
  • ttlSecondsAfterFinished — 완료 후 자동 정리 (예: 86400)
  • restartPolicy: Never
  • 이미지는 digest pinning (flyway/flyway@sha256:...)
  • resources.requests/limits 명시
  • securityContext restricted PSA 준수

7. validate를 먼저, migrate를 나중에

운영 절차 기본 순서:

  1. flyway info (pending migration 확인)
  2. flyway validate
  3. flyway migrate
  4. flyway info (결과 확인)
  5. app rollout

validateOnMigrate=true 기본값이 있더라도, 운영 runbook에서는 validate 단계를 분리 Job 또는 initContainer로 분리한다. docs/examples/infra/flyway.md 참조.

8. migration source는 Git이 source of truth

중요한 것은 아래다.

  • versioned migration script (V__)
  • repeatable migration script (R__)
  • migration ordering
  • schema history table 상태

기본 금지:

  • 운영 서버에서 migration 파일 수동 수정
  • 적용된 migration 파일을 사후 편집 (checksum mismatch)
  • Flyway schema history table을 사람이 직접 UPDATE/DELETE

9. Flyway undo(U__)는 쓰지 않는다

Flyway Community(OSS)는 U__ 파일을 지원하지 않는다. Teams/Enterprise에서만 undo 명령이 동작한다.

기본:

  • undo migration 파일을 만들지 않음
  • rollback은 forward-only migration + PITR로 수행
  • 운영 기본은 "다음 migration으로 앞으로 수정"

10. DB backup 전략과 migration 전략을 같이 본다

schema 변경이 production에 들어간다면, 같은 변경 계획 안에 아래가 같이 있어야 한다.

  • rollback 가능 여부
  • 변경 직전 backup 시점 (예: on-demand CNPG Backup 실행)
  • restore 단위 (전체 cluster / logical DB)
  • PITR 필요 여부 + targetTime 후보
  • migration 실패 시 중단 지점 (어느 V__에서 멈췄는지)

11. PostgreSQL 운영 기본 백업은 continuous physical backup

운영 기본 복구 목표가 cluster-level restore / PITR / standby seed 중 하나면 continuous WAL archiving + base backup이 기본이다.

도구 선택:

  • K8s + CNPG → Barman Cloud (내장)
  • K8s + Crunchy → pgBackRest (내장)
  • 자체 운영 → pgBackRest 또는 WAL-G

pg_dump는 다음 용도로 제한:

  • 선택적 logical export
  • 로컬/테스트 seed
  • 일부 schema/table 보존
  • migration 검증용 비교 데이터

12. PITR 필요 여부를 초기에 결정

다음 질문에 "예"면 PITR을 우선 검토한다.

  • 잘못된 migration/DDL을 특정 시점 직전으로 되돌려야 하는가
  • 운영 데이터 손실 허용 시간이 짧은가 (RPO < 1h)
  • 인증 관련 데이터 정합성이 중요한가

13. schema ownership은 서비스별로 분리

기본:

  • auth-server schema는 auth-server 팀이 소유
  • keycloak schema는 keycloak이 소유
  • 공용 schema 남발 금지
  • "편해서" 하나의 migration 프로젝트로 통합 관리 금지

14. Flyway history table 전략을 먼저 고정

초기에 결정:

  • flyway.table (기본 flyway_schema_history)
  • flyway.defaultSchema
  • flyway.schemas
  • flyway.createSchemas
  • 필요 시 flyway.initSql

기본:

  • history table을 service별 schema에 배치 (예: auth_server.flyway_schema_history)
  • 여러 서비스의 history table을 하나의 schema에 몰지 않음

15. baseline / repair는 예외 절차

baseline과 repair는 정상 운영 흐름이 아니다.

허용 예:

  • legacy DB를 처음 Flyway 관리로 편입 (baseline)
  • 의도적 migration 수정 후 공식 절차로 checksum 회복 (repair)
  • history corruption 복구 (repair)

기본 금지:

  • CI/CD에서 습관적 baseline/repair
  • validate 오류를 없애기 위해 무분별하게 repair

16. migration은 forward-only를 기본값으로

운영 기본값:

  • 새 migration으로 앞으로 수정
  • rollback용 SQL을 미리 기대하지 않음
  • 실패 시 restore/PITR 또는 다음 migration으로 교정

17. Keycloak DB와 auth-server DB는 따로 본다

둘 다 PostgreSQL을 써도 운영 기준은 별도로 둔다.

  • migration 파이프라인 분리
  • backup/restore 영향도 분리
  • schema/table ownership 분리
  • 버전 업그레이드 절차 분리
  • Keycloak은 자체 migration을 내장하므로 Flyway로 관리하지 않는다

18. test-server는 DB를 기본 전제로 두지 않는다

test-server가 DB 연결이 없으면 migration 대상 아님, DB secret 불필요, rollout 절차도 DB 의존 없이 단순화된다.

19. destructive migration은 expand → migrate → contract

Zero-downtime을 위한 3단계 릴리즈:

  1. Expand — 새 컬럼/테이블 추가 (NULL 허용 또는 default 값 있음). 기존 앱 호환.
  2. Migrate — 앱을 새 스키마 기준으로 배포 + 데이터 backfill.
  3. Contract — 기존 컬럼/테이블/제약 제거. 한 릴리즈 이상 뒤.

각 단계는 별도 릴리즈로 나간다. 같은 릴리즈에서 expand와 contract를 같이 하지 않는다.

인증/권한/토큰 관련 테이블은 특히 보수적으로.

20. DB 변경은 애플리케이션 호환성 윈도우를 고려

migration 문서는 다음을 포함한다.

  • 이전 앱 버전과 호환 여부
  • 새 앱 버전과 호환 여부
  • 중간 배포 구간에서 허용되는 상태 (N-1 ↔ N 동시 운영 가능 여부)
  • 롤백 시 DB가 이미 바뀐 상태일 때의 대응

21. 대용량 / long-running DDL은 트랜잭션 밖에서

Postgres에서 다음은 트랜잭션 밖에서 실행해야 한다.

  • CREATE INDEX CONCURRENTLY
  • REINDEX CONCURRENTLY
  • ALTER TYPE ... ADD VALUE (Postgres 12+에서는 트랜잭션 내에서도 제한적으로 가능)
  • VACUUM

Flyway에서는 해당 migration 파일 상단에 다음을 적는다:

-- flyway:executeInTransaction=false
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);

22. restore 테스트 없는 backup/migration 전략 금지

다음은 반드시 drill이 있어야 한다.

  • PostgreSQL base backup 복구
  • WAL/PITR 절차
  • Flyway 적용 후 실패 시 중단 및 복구 절차
  • Keycloak/auth-server 개별 DB restore 절차

현재 스택 기본 권장안

  • auth-server DB: CNPG Cluster 3 instances, Barman Cloud, RPO 5분, Flyway Job으로 migration
  • keycloak DB: CNPG Cluster 별도, Keycloak 자체 migration (Flyway 밖)
  • test-server: DB 없음
  • migration-flyway: Kubernetes Job (Helm/Argo hook), 앱 Deployment보다 먼저 실행
  • backup: CNPG Barman continuous WAL + daily base backup, pg_dump는 보조

프로젝트 기준 요약

  • app rollout과 migration을 분리 (app startup migration 금지)
  • Postgres on K8s는 CNPG operator를 기본 후보로
  • migration Job은 Helm hook 또는 Argo CD sync wave로 app보다 먼저 실행
  • migration Job은 parallelism: 1, backoffLimit: 0, digest pinning, restricted PSA
  • Flyway undo(U__) 파일 만들지 않음 (OSS 미지원)
  • validate → migrate → app rollout 순서
  • CNPG Barman Cloud (또는 pgBackRest / WAL-G) 물리 백업 + PITR, pg_dump는 보조
  • schema ownership은 서비스별 분리
  • destructive migration은 expand → migrate → contract, 여러 릴리즈에 걸쳐
  • non-transactional DDL은 -- flyway:executeInTransaction=false