스키마 마이그레이션 CI/CD(Flyway/Liquibase)
증상
목표: 버전이 지정된 마이그레이션 파일로 데이터베이스 스키마 변경을 관리하고, Flyway 또는 Liquibase로 CI/CD 안에서 자동이지만 통제된 방식으로 적용한다.
1. 버전이 지정된 마이그레이션 파일을 만든다(Vxxx__name.sql)
모든 스키마 변경을 증가하는 버전 번호가 붙은 이름의 파일에 작성한다. Flyway 규칙은 V<version>__<description>.sql이다(예: V1__initial_schemas.sql, V2__orders_table.sql). 이 파일들은 Git에 보관되며 버전 순서로 적용된다. 적용된 마이그레이션은 추적 테이블(Flyway의 flyway_schema_history)에 기록되므로 각 마이그레이션은 정확히 한 번 실행된다.
PostgreSQL
-- V2__orders_table.sql
CREATE TABLE orders (
id bigserial PRIMARY KEY,
amount numeric(12,2) NOT NULL,
created_at timestamptz DEFAULT now()
);2. checksum으로 불변성을 유지한다
Flyway/Liquibase는 적용된 모든 마이그레이션의 checksum을 저장한다. 이미 적용된 파일을 나중에 수정하면 checksum이 일치하지 않아 도구가 실패한다. 규칙: 적용된 마이그레이션은 절대 수정하지 않으며 수정은 새 버전 파일로 한다. 이를 통해 모든 환경(dev/staging/prod)이 같은 이력을 공유함을 보장한다.
PostgreSQL
# repair on checksum mismatch (use with care):
flyway repair
# the right way: add the change in a new Vx file3. 기존 데이터베이스를 baseline한다
이력이 없는 이미 데이터가 있는 데이터베이스에 마이그레이션 도구를 연결할 때 baseline을 사용한다. 현재 상태를 '시작 버전'으로 간주하며 기존 객체를 다시 만들려 하지 않는다. baselineVersion 이후의 마이그레이션이 적용된다. 처음부터 구축하는 환경에서는 baseline이 필요 없으며 V1부터 마이그레이션이 실행된다.
PostgreSQL
flyway baseline -baselineVersion=1 -baselineDescription="existing schema"4. CI 파이프라인에서 자동으로 migrate한다
CI 단계에서 마이그레이션 도구를 실행해 테스트 및 staging 데이터베이스에 변경을 자동 적용한다. migrate 명령은 새로운(적용되지 않은) 파일만 실행하며 멱등적이다. 마이그레이션을 먼저 임시/ephemeral 데이터베이스에 적용해 검증하면 깨진 SQL이 prod에 도달하기 전에 잡을 수 있다. 스키마는 애플리케이션 코드 배포 전에 마이그레이션해야 한다(하위 호환 변경으로).
PostgreSQL
# CI step (Flyway)
flyway -url=jdbc:postgresql://staging/appdb \
-user=ci -password=$PW migrateMySQL
flyway -url=jdbc:mysql://staging/appdb \
-user=ci -password=$PW migrate5. prod에 수동 승인 게이트를 추가한다
테스트/staging은 자동이지만 prod migrate는 수동 승인을 요구하도록 한다. CI/CD에 'approval gate'(수동 승인) 단계를 추가한다. 승인 전에 'flyway info' 또는 'liquibase status'로 실행될 마이그레이션을 보여 준다. 문제가 생기면 되돌릴 수 있도록 prod에 적용하기 전에 항상 최신 백업을 확보한다.
PostgreSQL
# what will run before approval?
flyway info
# liquibase equivalent:
liquibase status --verbose6. 가역성과 롤백 전략을 정의한다
Flyway Community에는 자동 undo가 없으므로 forward-only 마이그레이션 원칙을 채택한다: 실수를 되돌리는 대신 이를 수정하는 새 마이그레이션을 작성한다. Liquibase changeset은 rollback 블록을 정의할 수 있으며 'liquibase rollback'으로 되돌릴 수 있다. 가장 견고한 전략: 변경을 작고 하위 호환되게 유지하고, 위험한 작업(컬럼 삭제) 전에 백업을 확보하며, 문서화된 롤백 계획을 준비한다.
PostgreSQL
-- Liquibase rollback example (inside a changeset)
--changeset dev:2
CREATE TABLE orders (...);
--rollback DROP TABLE orders;