docker 환경에서 DB migration을 자동으로 해줄 수는 없을까?
이번글에서는 docker에서 mysql DB를 Flyway를 사용해서 마이그레이션 하는 방법에 대해 작성했습니다.
DB를 마이그레이션 한다는게 뭘까요?
DB 마이그레이션(DB Migration)은 데이터베이스의 스키마, 데이터 또는 시스템을 변경하는 과정을 의미합니다.
DB 마이그레이션을 왜 해줘야 할까요?
DB 마이그레이션을 해주는 이유는 데이터베이스를 최신 상태로 유지하고, 일관된 DB 환경에서 안정적으로 운영을 위해서 입니다.
쉽게 생각해서, JPA에서 제공해주는 ddl-auto 역할을 한다고 볼 수 있습니다.
만약, DB 마이그레이션을 해주지 않으면 어떻게 될까요?
현재 애플리케이션과 DB 구조가 불일치하거나 팀원 간의 개발 환경이 달라지는 문제가 발생할 수 있습니다.
예를 들어, 애플리케이션에서 새로운 기능이 추가되어, 회원 테이블에 이메일 컬럼을 추가해야 합니다.
만약, 회원 테이블이 마이그레이션되지 않은 경우, 애플리케이션에서 user.getEmail()과 같은 메서드를 호출하게 되면 회원 테이블에 이메일 컬럼이 없기 때문에 오류가 발생하게 됩니다.
ddl-auto 편하던데, JPA가 알아서 DB에 마이그레이션 해주잖아요 굳이 따로 해주는 이유가 있나요?
ddl-auto는 개발 환경에서는 편리하게 사용할 수 있지만, 서버 운영 환경에서는 잘못했다간 데이터가 손실되거나 버전 관리에 문제가 생기거나 예측 불가능한 변경 등으로 생기는 문제들이 발생할 수 있습니다.
공식 문제에서도 인메모리 DB에서 운영용 실제 데이터베이스로 전환하는 경우, 테이블과 데이터가 자동으로 생성된다고 가정하면 안된다고 하면서, 운영 환경에서는 ddl-auto를 명확하게 설정하거나, Flyway 같은 DB 마이그레이션 도구를 사용해야 한다고 합니다.
하나의 예시로 아래와 같은 상황을 보도록 하겠습니다.
(DB 마이그레이션은 JPA에서 제공하는 ddl-auto : update 옵션으로 하고 있습니다.)
(1) A개발자가 users 테이블에 email 컬럼을 추가했습니다. (ddl-auto 이므로 자동 업데이트)
(2) B개발자가 users 테이블에 email 컬럼을 삭제했습니다. (ddl-auto 이므로 자동 업데이트)
(3) A개발자가 개발을 테스트하는 도중, users 테이블에서 email 컬럼 오류가 발생합니다.
(4) A개발자가 DB를 보니 email 컬럼이 존재하지 않았습니다.
ddl-auto 방식은 변경 이력이 남지 않아서 개발자 간 충돌이 발생할 가능성이 높아집니다.
Flyway는 뭘까요?
데이터베이스 마이그레이션을 관리하는 도구입니다.
공식문서에서도 Flyway는 데이터베이스 변경 사항을 버전 관리하고, 데이터베이스 배포를 안전하고 쉽게 자동화할 수 있도록 도와준다고 명시되어 있습니다.
docker 환경에서 Flyway는 어떻게 적용할 수 있을까요?
Docker에서 Flyway 적용하기
docker-compose를 사용하고 있기 때문에, flyway를 실행하는 별도의 컨테이너를 생성하는 방식으로 구현했습니다.
폴더 구조는 아래와 같습니다.
docker-compose.yml에 flyway를 아래와 같이 작성합니다.
flyway:
image: flyway/flyway:9.0.0
container_name: flyway_migrations
depends_on:
mysql:
condition: service_healthy
volumes:
- ./flyway/flyway.conf:/flyway/conf/flyway.config
- ./flyway/db-migration:/flyway/sql
networks:
- my-network
command: -configFiles=/flyway/conf/flyway.config -locations=filesystem:/flyway/sql -connectRetries=60 migrate
한줄 한줄 어떤 의미를 갖는지 알아보겠습니다.
flyway:
- docker-compose에서 Flyway 컨테이너를 정의하는 서비스 이름을 의미합니다.
- 이 이름으로 내부적으로 다른 서비스에서 참조할 때 사용됩니다.(depends_on)
image: flyway/flyway:9.0.0
- Flyway의 공식 Docker 이미지를 사용합니다.
container_name: flyway_migrations
- Docker 컨테이너 이름을 flyway_migrations으로 지정함을 의미합니다.
- 컨테이너 관리시 flyway_migrations로 접근이 가능합니다.
depends_on:
mysql:
condition: service_healthy
- mysql 컨테이너가 정상적으로 헬스 체크가 완료된 후 실행되도록 설정합니다.
- DB가 먼저 실행되고, 마이그레이션이 일어나야 하므로 작성해주었습니다.
volumes:
- ./flyway/flyway.conf:/flyway/conf/flyway.config
- ./flyway/db-migration:/flyway/sql
- Flyway에서 사용하는 설정 파일(flyway.config)와 SQL 마이그레이션 파일을 컨테이너 내부에 마운트합니다.
networks:
- my-network
- Flyway의 MySQL이 같은 네트워크인 my-network에서 통신할 수 있도록 설정합니다.
- mysql이 사용하고 있는 네트워크를 일치시키지 않을 경우엔 mysql 서비스에 접속할 수 없습니다.
command: -configFiles=/flyway/conf/flyway.config -locations=filesystem:/flyway/sql -connectRetries=60 migrate
- 컨테이너가 실행될 때 Flyway가 실행할 명령어를 지정합니다.
명령어의 의미는 차례대로 "cofig파일의 위치를 지정하고, SQL 마이그레이션 파일이 있는 디렉터리를 지정하고, MySQL에 연결할 때 최대 60번을 재시도하고, Flyway 실행 시 마이그레이션을 실행"하라는 의미입니다.
다음으로 flyway conifg 파일을 보겠습니다.
작성한 flyway config파일은 아래와 같습니다.
flyway.url=jdbc:mysql://mysql:3306/test
flyway.user=root
flyway.password=password
flyway.locations=filesystem:/flyway/sql
flyway.baselineOnMigrate=true
flyway.connectRetries=60
flyway.url=jdbc:mysql://mysql:3306/test
flyway.user=root
flyway.password=password
- flyway에서 mysqlDB에 접속할 때 필요한 url, user, password를 명시 해주었습니다.
flyway.locations=filesystem:/flyway/sql
- flyway가 실행할 마이그레이션 SQL 파일이 위치한 디렉토리를 설정해주었습니다.
flyway.baselineOnMigrate=true
- 새로운 데이터베이스가 아닌 이미 생성된 데이터베이스에서도 마이그레이션을 적용할 수 있도록 설정하는 옵션인 baselineOnMigrate를 true로 주었습니다.
flyway.connectRetries=60
- 마지막으로 Flyway가 MySql 연결에 실패하는 경우 최대 60번까지 재시도 할 수 있도록 옵션을 설정해주었습니다.
flyway.baselineOnMigrate 옵션이 뭘까요?
공식 문서에선 아래와 같이 명시되어 있습니다.
쉽게 말해, 기존 DB에 Flyway를 처음 적용하는 경우에, 기존 테이블을 유지하면서 마이그레이션을 진행할 수 있도록 해줍니다.
기존 DB를 기준점으로 잡고, 이후의 마이그레이션만 실행해줍니다.
DB 마이그레이션시, 실행할 sql 쿼리문을 알아보겠습니다.
CREATE DATABASE IF NOT EXISTS test;
CREATE TABLE IF NOT EXISTS stock (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
quantity BIGINT NOT NULL
) engine=InnoDB;
INSERT INTO stock (quantity) VALUES (10000);
쿼리문을 보면, stock(재고)라는 테이블을 만들고, 그 테이블에 quantity가 10000인 stock 데이터를 넣어줍니다.
Flyway 컨테이너가 실행되면, 컨테이너 내부로 마운트한 쿼리문을 자동으로 실행해줍니다.
실행이 잘 될까요? 확인해보겠습니다.
실행을 위해 ./deploy.sh를 입력합니다.
매번, spring을 build하고 compose down & compose up을 하는 과정이 귀찮아서 셀 스크립트 내부에 설정해 줌으로 ./deploy.sh 명령어 하나만으로 앞선 과정을 한번에 실행되도록 구현했습니다.
#!/bin/bash
echo "Gradle build 👀"
./gradlew clean build || { echo "Gradle build fail"; exit1;}
echo "Gradle build ✅"
echo "Docker renew 👀"
cd ./infratest && docker-compose down -v && docker-compose up --build -d
echo "Docker renew ✅"
빌드, 컴포즈 업, 컴포즈 다운이 순차적으로 오류없이 진행됬습니다.
Docker Desktop을 확인해보니, 어랏? flyway 컨테이너를 제외한 모든 컨테이너가 잘 작동합니다.
사실 flyway 컨테이너는 DB 마이그레이션이 성공하는 경우 자동으로 종료됩니다.
즉, 일반적인 서비스처럼 계속 실행되는 컨테이너가 아닌, 한 번 실행 후 종료되는 일회성 컨테이너입니다.
Flyway로 선언해준 sql 쿼리문이 잘 적용이 됬을까요?
먼저 docker exec 명령어로 mysql 해당 컨테이너에 접속합니다.
testDB를 사용해서 stock 테이블을 조회합니다.
MySQL에서 데이터를 조회한 결과, quantity 값이 10,000인 데이터가 정상적으로 저장된 것을 확인할 수 있었습니다.
'Docker' 카테고리의 다른 글
Docker와 Nginx를 활용한 로드 밸런싱 구축 (0) | 2025.02.26 |
---|---|
Docker로 배포해보기 (1) | 2024.08.28 |