305 lines
13 KiB
Markdown
305 lines
13 KiB
Markdown
# Flyway 기준
|
|
|
|
## 목적
|
|
|
|
이 문서는 PostgreSQL 기반 서비스에서 Flyway를
|
|
- 어디서 실행할지 (Kubernetes Job)
|
|
- 어떤 순서로 실행할지 (validate / info / migrate / app rollout)
|
|
- 어떤 배포 흐름과 맞물릴지 (Helm hook / Argo CD sync-wave)
|
|
- config를 어떻게 공급할지 (env var + Secret via Vault Secrets Operator)
|
|
- schema history table을 어떻게 둘지
|
|
- baseline / repair / out-of-order / undo를 어떻게 다룰지
|
|
- non-transactional DDL을 어떻게 처리할지
|
|
를 먼저 고정한다.
|
|
|
|
이 문서의 목표는 다음과 같다.
|
|
|
|
- Flyway를 앱 startup 내부 로직처럼 숨기지 않는다
|
|
- validate / migrate / repair / baseline의 역할을 분리한다
|
|
- schema history table을 운영 감사 추적의 일부로 본다
|
|
- migration Job을 1000+ 서비스 규모에서 재현 가능하게 표준화한다
|
|
- DB 변경을 애플리케이션 rollout과 분리해 운영한다
|
|
|
|
## 공식 의미
|
|
|
|
- Flyway `validate`는 적용된 migration과 로컬 migration 사이의 이름/타입/checksum 차이, 로컬에 없는 적용 버전, 아직 적용되지 않은 로컬 버전을 검증한다.
|
|
- `migrate`는 schema history table이 없으면 자동 생성하고 최신 migration까지 적용한다.
|
|
- schema history table은 migration 실행 내역, checksum, 성공/실패 상태를 기록하는 audit trail이다.
|
|
- `repair`는 schema history table을 수정하는 명령이며, 실패한 migration 엔트리 제거, checksum/description/type 재정렬, missing migration 삭제 표시를 수행한다. user object는 정리하지 않는다.
|
|
- schema history table 기본 이름은 `flyway_schema_history`다.
|
|
- schema history table 위치는 `table`, `defaultSchema`, `schemas`로 제어할 수 있다.
|
|
- `createSchemas=false`일 때 history table이 들어갈 schema가 미리 준비되지 않으면 migrate가 실패할 수 있다.
|
|
- 기존 non-empty schema에 Flyway를 도입할 때 history table이 없으면 `baseline` 또는 `baselineOnMigrate`가 필요할 수 있다.
|
|
- schema history에는 `Pending`, `Success`, `Missing`, `Out of Order`, `Outdated`, `Superseded`, `Deleted` 등 상태가 기록될 수 있다.
|
|
- Flyway는 migration 실행 중 schema history table에 **advisory lock**을 걸어 동시 실행을 직렬화한다. 다중 replica Job 수준의 race를 방지한다.
|
|
- `cleanDisabled`는 Flyway 9 이후 기본 `true`. production에서는 반드시 `true`를 명시한다.
|
|
- Flyway 8.2+ 에서 `-- flyway:executeInTransaction=false` directive로 migration 파일 단위 트랜잭션 비활성화가 가능하다.
|
|
- Flyway Community(OSS)는 **undo(U__) migration을 지원하지 않는다.** Undo는 Teams/Enterprise 상용 기능이다.
|
|
- 환경변수 config 지원: `FLYWAY_URL`, `FLYWAY_USER`, `FLYWAY_PASSWORD`, `FLYWAY_LOCATIONS`, `FLYWAY_SCHEMAS`, `FLYWAY_DEFAULT_SCHEMA`, `FLYWAY_TABLE`, `FLYWAY_BASELINE_ON_MIGRATE`, `FLYWAY_VALIDATE_ON_MIGRATE`, `FLYWAY_CLEAN_DISABLED`, `FLYWAY_OUT_OF_ORDER`, 그 외 `FLYWAY_*`.
|
|
|
|
## 기본 규칙
|
|
|
|
### 1. Flyway는 앱 startup이 아니라 독립 실행 단계
|
|
운영 환경에서 Flyway는 다음 중 하나로만 실행한다.
|
|
|
|
- Kubernetes Job (권장)
|
|
- CI/CD 명시 단계
|
|
- 운영자 명시 실행 절차
|
|
|
|
기본 금지:
|
|
- 애플리케이션 startup 시 자동 migration
|
|
- Spring Boot `spring.flyway.enabled=true`로 앱 부팅 경로에 포함
|
|
- readiness/liveness와 migration 실패를 섞는 구조
|
|
|
|
### 2. 기본 순서는 info → validate → migrate → info → app rollout
|
|
운영 기본 순서:
|
|
|
|
1. `flyway info` (pending 확인)
|
|
2. `flyway validate`
|
|
3. `flyway migrate`
|
|
4. `flyway info` (결과 확인)
|
|
5. 애플리케이션 rollout
|
|
|
|
`validateOnMigrate=true`가 기본값이지만, 운영 절차상 validate를 **분리 initContainer** 또는 **사전 단계**로 둔다.
|
|
|
|
### 3. 배포 흐름 안에서 app보다 먼저 실행 — 두 가지 패턴
|
|
|
|
**패턴 A: Helm hook**
|
|
```yaml
|
|
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**
|
|
```yaml
|
|
annotations:
|
|
argocd.argoproj.io/sync-wave: "-1"
|
|
argocd.argoproj.io/hook: Sync
|
|
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
|
```
|
|
|
|
기본:
|
|
- 두 패턴을 혼용하지 않는다 (Argo CD가 Helm chart를 렌더링할 때 Helm hook을 일반 리소스로 취급해 순서가 꼬임)
|
|
- 배포 도구에 맞춰 한쪽만 사용
|
|
|
|
### 4. migration Job 안전 설정 체크리스트
|
|
Job manifest에 반드시 다음이 있어야 한다.
|
|
|
|
- `parallelism: 1`, `completions: 1`
|
|
- `backoffLimit: 0` 또는 작은 값 (1~2)
|
|
- `activeDeadlineSeconds` (권장 1800 = 30분, 대형 migration은 더 길게)
|
|
- `ttlSecondsAfterFinished` (권장 86400 = 1일)
|
|
- `restartPolicy: Never`
|
|
- 이미지 digest pinning (`flyway/flyway@sha256:...`)
|
|
- `imagePullPolicy: IfNotPresent`
|
|
- `resources.requests/limits`
|
|
- `securityContext`: `runAsNonRoot: true`, `readOnlyRootFilesystem: true`, `capabilities: drop: [ALL]`
|
|
- Pod-level `seccompProfile: RuntimeDefault`
|
|
- `fsGroup` 명시 (필요 시)
|
|
|
|
### 5. config는 환경변수 + Secret
|
|
Flyway CLI는 `FLYWAY_*` 환경변수를 읽는다. Secret은 Vault Secrets Operator(VSO) 또는 External Secrets Operator를 통해 클러스터에 동기화된 `Secret`에서 주입한다.
|
|
|
|
필수 env var:
|
|
- `FLYWAY_URL` — `jdbc:postgresql://host:5432/db`
|
|
- `FLYWAY_USER`
|
|
- `FLYWAY_PASSWORD` — Secret에서 주입
|
|
- `FLYWAY_LOCATIONS` — `filesystem:/flyway/sql`
|
|
|
|
운영 권장 env var:
|
|
- `FLYWAY_SCHEMAS` — 대상 schema
|
|
- `FLYWAY_DEFAULT_SCHEMA` — history table 위치
|
|
- `FLYWAY_TABLE` — 기본 `flyway_schema_history`
|
|
- `FLYWAY_VALIDATE_ON_MIGRATE=true`
|
|
- `FLYWAY_BASELINE_ON_MIGRATE=false` (운영 기본값)
|
|
- `FLYWAY_CLEAN_DISABLED=true` (production 필수)
|
|
- `FLYWAY_OUT_OF_ORDER=false`
|
|
- `FLYWAY_MIXED=false`
|
|
|
|
### 6. `cleanDisabled=true`는 production 필수
|
|
`flyway clean`은 모든 object를 drop 하는 파괴적 명령이다.
|
|
|
|
- production: `FLYWAY_CLEAN_DISABLED=true` 반드시 명시 (Flyway 9+ 기본값이지만 명시적으로 선언)
|
|
- dev/test: 필요 시 `false` 허용, 단 접근 권한 분리
|
|
|
|
### 7. migration SQL은 ConfigMap 또는 이미지 레이어로
|
|
옵션:
|
|
- **ConfigMap**: 서비스 manifest와 함께 Argo CD로 관리. small/medium migration set에 적합. ConfigMap 1MiB 제한 주의.
|
|
- **이미지 레이어**: 서비스 repo에서 migration SQL을 Docker image로 빌드하고 Flyway image와 합쳐 사용. 대규모 migration set에 적합.
|
|
|
|
기본:
|
|
- 두 방식 모두 Git이 source of truth
|
|
- 운영 서버에서 `kubectl edit configmap`으로 migration 편집 금지
|
|
|
|
### 8. schema history table은 운영 감사 추적의 일부
|
|
수동 UPDATE / DELETE 금지. 위치는 명시적으로 결정.
|
|
|
|
기본:
|
|
- service별 schema를 `FLYWAY_DEFAULT_SCHEMA`로 지정 (예: `auth_server`)
|
|
- history table 이름은 기본값 `flyway_schema_history` 유지
|
|
- 여러 서비스의 history table을 하나의 schema에 몰지 않음
|
|
|
|
### 9. `createSchemas=false`면 history schema를 사전 준비
|
|
`createSchemas=false`를 쓰면 history table이 들어갈 schema를 별도 준비해야 한다.
|
|
|
|
기본:
|
|
- `FLYWAY_INIT_SQL`로 `CREATE SCHEMA IF NOT EXISTS` 지시 가능
|
|
- 또는 CNPG `Cluster.bootstrap.initdb.postInitSQL`에서 schema 사전 생성
|
|
- 생성 책임이 누구인지 문서화
|
|
|
|
### 10. baseline은 예외 절차
|
|
허용 예:
|
|
- legacy DB를 처음 Flyway 관리로 편입
|
|
- 기존 non-empty schema를 Flyway에 편입할 때
|
|
|
|
기본 금지:
|
|
- 새 프로젝트인데 baseline부터 쓰기
|
|
- 운영 배포 파이프라인에서 습관적으로 baseline 사용
|
|
|
|
### 11. `baselineOnMigrate`는 기본값 아님
|
|
`baselineOnMigrate=true`는 도입/전환 시 편의를 줄 수 있지만, 운영 기본값으로 두지 않는다.
|
|
|
|
이유:
|
|
- 예상치 못한 기존 schema를 "정상 상태"처럼 받아들일 수 있다
|
|
- 실수 탐지력이 떨어진다
|
|
|
|
`FLYWAY_BASELINE_ON_MIGRATE=false`로 명시.
|
|
|
|
### 12. `repair`는 예외 절차
|
|
허용 예:
|
|
- 의도적으로 migration 파일을 수정했고 checksum 정렬이 필요
|
|
- missing migration을 문서화된 절차로 정리
|
|
- failed repeatable migration 이후 history 정리
|
|
|
|
기본 금지:
|
|
- validate 오류가 나면 원인 분석 없이 바로 repair
|
|
- CI/CD에서 습관적으로 repair 실행
|
|
|
|
### 13. `repair`는 user object를 고쳐주지 않는다
|
|
repair는 schema history table만 정리한다. 실패한 migration이 남긴 DB object 정리, 불완전한 DDL/DML 정리는 별도 절차로 수행해야 한다.
|
|
|
|
### 14. 적용된 migration 파일은 수정 금지
|
|
이유:
|
|
- checksum mismatch
|
|
- 재현 불가
|
|
- 환경 간 drift
|
|
|
|
대응:
|
|
- 새 migration으로 교정
|
|
- 정말 예외적인 수정만 공식 repair 절차와 함께 수행
|
|
|
|
### 15. out-of-order는 기본 금지
|
|
Out-of-order migration은 전체 migration history를 다시 실행할 때 다른 결과를 만들 수 있다.
|
|
|
|
기본:
|
|
- `FLYWAY_OUT_OF_ORDER=false`
|
|
- 뒤늦게 빠진 migration을 넣는 방식을 기본값으로 두지 않음
|
|
- 예외 허용 시 영향 범위 검토 문서 필수
|
|
|
|
### 16. repeatable migration(R__)은 목적 제한
|
|
Repeatable migration은 다음 용도에 제한한다.
|
|
|
|
- view 정의
|
|
- function / procedure
|
|
- trigger 재생성
|
|
- reference / static data refresh
|
|
|
|
기본 금지:
|
|
- 순서가 중요한 핵심 schema change를 repeatable로 남발
|
|
- versioned migration 대신 repeatable로 대체
|
|
|
|
### 17. Undo(U__) migration은 만들지 않는다
|
|
Flyway Community(OSS)는 undo를 지원하지 않는다.
|
|
|
|
- U__ 파일을 repo에 두지 않음 (오해 유발)
|
|
- rollback은 forward-only 새 migration + PITR로 대응
|
|
|
|
### 18. locations는 environment별로 흔들지 않는다
|
|
`migrate`와 `repair`는 같은 `locations` 전제를 가져야 한다.
|
|
|
|
기본:
|
|
- env마다 location 구조가 달라지지 않게 유지
|
|
- 운영과 개발에서 전혀 다른 migration set을 쓰지 않음
|
|
- env별 변수는 `placeholders`(`FLYWAY_PLACEHOLDERS_*`)로 분리
|
|
|
|
### 19. migration은 서비스 소유권 단위로 분리
|
|
기본:
|
|
- auth-server는 auth-server migration set
|
|
- keycloak은 keycloak 고유 migration (사실 Keycloak은 내부 migration을 사용하므로 Flyway 대상이 아님)
|
|
- 공용 migration 프로젝트 금지
|
|
|
|
### 20. migration naming / versioning
|
|
기본:
|
|
- versioned: `V<N>__<snake_case>.sql`, N은 증가하는 정수 또는 점표기(예: `V12__`, `V1.2.3__`)
|
|
- repeatable: `R__<snake_case>.sql`
|
|
- 이름은 변경 의도를 드러나게 작성
|
|
|
|
예:
|
|
- `V42__add_refresh_token_audit_columns.sql`
|
|
- `R__refresh_user_views.sql`
|
|
|
|
### 21. destructive change는 expand → migrate → contract
|
|
`db-and-migration.md` #19 참조. Flyway 입장에서 각 단계는 **별도 릴리즈**의 versioned migration으로 나간다.
|
|
|
|
### 22. non-transactional DDL은 `executeInTransaction=false`
|
|
Postgres에서 트랜잭션 밖 실행이 필요한 DDL:
|
|
|
|
- `CREATE INDEX CONCURRENTLY`
|
|
- `REINDEX CONCURRENTLY`
|
|
- `ALTER TYPE ... ADD VALUE`
|
|
- `VACUUM`
|
|
|
|
migration 파일 상단:
|
|
```sql
|
|
-- flyway:executeInTransaction=false
|
|
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
|
|
```
|
|
|
|
기본:
|
|
- 이런 DDL은 **전용 migration 파일**로 분리 (다른 statement와 섞지 않음)
|
|
- runtime 추정치 주석
|
|
- low-traffic window로 배포 일정 조정
|
|
|
|
### 23. rollback은 Flyway 명령에 기대지 않는다
|
|
운영 기본 rollback:
|
|
|
|
- 새 migration으로 수정
|
|
- PostgreSQL PITR (CNPG bootstrap.recovery)
|
|
- 애플리케이션 버전 rollback + DB 호환 윈도우 유지 (expand-contract의 효과)
|
|
|
|
rollout undo가 DB schema rollback을 대신하지 않는다.
|
|
|
|
### 24. 현재 스택 기준 기본 권장안
|
|
|
|
- **auth-server**
|
|
- Flyway Job (Helm hook 또는 Argo sync-wave)
|
|
- `FLYWAY_DEFAULT_SCHEMA=auth_server`
|
|
- validate → migrate → app rollout
|
|
- digest pinning
|
|
- **keycloak**
|
|
- Keycloak 자체 migration 사용, Flyway 대상 아님
|
|
- **test-server**
|
|
- DB가 없으면 Flyway 대상 아님
|
|
- **운영 절차**
|
|
- repair/baseline은 예외 승인 절차
|
|
- applied migration 수정 금지
|
|
- CLEAN_DISABLED=true 필수
|
|
|
|
## 프로젝트 기준 요약
|
|
|
|
- Flyway는 독립 실행 단계 (Kubernetes Job)
|
|
- info → validate → migrate → info → app rollout
|
|
- 배포 흐름 내 순서는 Helm hook 또는 Argo CD sync-wave 중 하나로 통일
|
|
- Job: `parallelism: 1`, `backoffLimit: 0`, `ttlSecondsAfterFinished`, digest pinning, restricted PSA
|
|
- config는 `FLYWAY_*` env var + Secret (VSO / ESO)
|
|
- `FLYWAY_CLEAN_DISABLED=true` 필수, `FLYWAY_BASELINE_ON_MIGRATE=false`, `FLYWAY_OUT_OF_ORDER=false`
|
|
- schema history table 위치를 `FLYWAY_DEFAULT_SCHEMA`로 명시
|
|
- baseline / repair / out-of-order는 예외 절차
|
|
- applied migration 수정 금지
|
|
- Undo(U__) 파일 만들지 않음 (OSS 미지원)
|
|
- non-transactional DDL은 `-- flyway:executeInTransaction=false`로 파일 단위 분리
|
|
- service별 migration ownership 분리
|
|
- destructive migration은 expand → migrate → contract
|