본문으로 건너뛰기

데이터베이스 마이그레이션 가이드

FlowKat APM의 데이터베이스 구성 및 마이그레이션 절차를 정의한다. 버전 업그레이드, 서버 이전, 데이터 정리 시나리오를 모두 다룬다.


1. 개요

DB 구성

FlowKat은 목적에 따라 두 종류의 데이터베이스를 사용한다.

DB역할위치비고
PostgreSQL 16TX-Mining 트랜잭션 데이터, XLog 정규화 데이터Docker 컨테이너 (flowkat-postgres)pg_partman, pgvector, pg_trgm 확장 포함
H2 (Embedded)Dashboard 서버 설정, 사용자/역할/대시보드 구성파일 시스템 (volumes/dashboard-server/META_H2/)Spring Boot 내장, MySQL 호환 모드

마이그레이션이 필요한 시점

  • 버전 업그레이드: 스키마 변경 DDL이 포함된 릴리즈로 업그레이드할 때
  • 서버 이전: 물리 서버 또는 클라우드 환경을 변경할 때
  • 대용량 데이터 정리: 파티션 보존 기간 초과 데이터를 삭제할 때
  • 디스크 용량 확보: VACUUM/REINDEX로 공간을 회수할 때

2. PostgreSQL 백업/복원

2.1 전체 백업 (pg_dump)

# 전체 덤프 (SQL 형식)
docker exec flowkat-postgres pg_dump \
-U postgres \
-d postgres \
--schema=FLOWKAT \
> backup_$(date +%Y%m%d).sql

# custom 형식 (병렬 복원 지원, 권장)
docker exec flowkat-postgres pg_dump \
-U postgres \
-d postgres \
--schema=FLOWKAT \
-Fc \
-f /tmp/flowkat_$(date +%Y%m%d).dump

# 컨테이너 밖으로 복사
docker cp flowkat-postgres:/tmp/flowkat_$(date +%Y%m%d).dump ./backups/

환경변수 확인: POSTGRES_USERdeploy/docker-compose-mining.yml 기준 postgres, POSTGRES_PASSWORD.env에서 POSTGRES_PASSWORD 값을 사용한다.

2.2 pg_dump 주요 옵션

옵션설명
-Fccustom 형식. pg_restore로 병렬 복원 가능
-Fd디렉터리 형식. -j 옵션으로 병렬 덤프
-j NN개 병렬 작업 (custom/directory 형식에서만 사용)
--schema=FLOWKATFLOWKAT 스키마만 덤프
--table=FLOWKAT.XLOG_NORM특정 테이블만 덤프
--no-acl권한 정보 제외
--no-owner소유자 정보 제외 (다른 사용자로 복원 시 유용)
# 병렬 덤프 (대용량 데이터베이스)
docker exec flowkat-postgres pg_dump \
-U postgres \
-d postgres \
--schema=FLOWKAT \
-Fd \
-j 4 \
-f /tmp/flowkat_parallel_$(date +%Y%m%d)

docker cp flowkat-postgres:/tmp/flowkat_parallel_$(date +%Y%m%d) ./backups/

2.3 특정 테이블 별도 백업

XLOG_NORM은 파티션 테이블이므로 파티션별로 덤프한다.

# 파티션 목록 조회
docker exec flowkat-postgres psql \
-U postgres \
-d postgres \
-c "SELECT tablename FROM pg_tables WHERE schemaname='flowkat' AND tablename LIKE 'xlog_norm_%' ORDER BY tablename;"

# 특정 파티션 덤프
docker exec flowkat-postgres pg_dump \
-U postgres \
-d postgres \
-t 'FLOWKAT.XLOG_NORM_2026_03_*' \
-Fc \
-f /tmp/xlog_norm_202603.dump

2.4 복원 (pg_restore)

# SQL 형식 복원
docker exec -i flowkat-postgres psql \
-U postgres \
-d postgres \
< backup_20260326.sql

# custom 형식 복원
docker cp ./backups/flowkat_20260326.dump flowkat-postgres:/tmp/
docker exec flowkat-postgres pg_restore \
-U postgres \
-d postgres \
--schema=FLOWKAT \
-Fc \
/tmp/flowkat_20260326.dump

# 병렬 복원 (-j 옵션은 custom/directory 형식에서만 동작)
docker exec flowkat-postgres pg_restore \
-U postgres \
-d postgres \
--schema=FLOWKAT \
-j 4 \
/tmp/flowkat_parallel_20260326

복원 전 기존 데이터를 제거하려면 --clean 옵션을 추가한다.


3. H2 데이터베이스 관리

3.1 H2 파일 위치

Dashboard 서버의 H2 데이터베이스 파일은 Docker volume에 마운트된다.

volumes/dashboard-server/META_H2/
├── H2DB_PROD.mv.db # 프로덕션 설정 DB
├── H2DB_LOCAL.mv.db # 로컬/개발 환경 DB
└── H2DB_COOKIE.mv.db # 세션/쿠키 관련 DB

flowkat.nextjs/dashboard 모듈의 application.yml에서 JDBC URL을 확인한다.

# 프로덕션 프로파일 예시
spring:
datasource:
jdbc-url: jdbc:h2:file:./META_H2/H2DB_PROD;SCHEMA=PUBLIC;MODE=MySQL;TRACE_LEVEL_FILE=1;

3.2 H2 백업

H2 파일은 단순 파일 복사로 백업한다. Dashboard 서버가 실행 중일 때는 파일이 잠길 수 있으므로 서비스를 중단하고 복사한다.

# Dashboard 서버 중단
docker stop flowkat-dashboard-server

# H2 파일 백업
cp -r volumes/dashboard-server/META_H2 \
backups/META_H2_$(date +%Y%m%d_%H%M%S)

# Dashboard 서버 재시작
docker start flowkat-dashboard-server

flowkat-cli.sh의 서비스별 백업 기능(create_service_backup)도 동일하게 META_H2를 포함한다.

3.3 H2 복원

docker stop flowkat-dashboard-server

# 기존 파일 제거 후 복원
rm -rf volumes/dashboard-server/META_H2
cp -r backups/META_H2_20260326_120000 \
volumes/dashboard-server/META_H2

docker start flowkat-dashboard-server

3.4 H2 초기 데이터 (DDL/DML)

Dashboard 서버 기동 시 hbm2ddl.auto: none 설정이므로 스키마 자동 생성이 비활성화되어 있다. 초기 데이터는 flowkat.nextjs/dashboard/src/main/resources/data/ 아래 SQL 파일로 관리된다.

flowkat.nextjs/dashboard/src/main/resources/data/
├── DEFAULT_MEMBER.sql
├── DEFAULT_ROLE_LIST.sql
├── DEFAULT_ROLE_MENU.sql
├── DEFAULT_ROLE_FUNC_AUTH.sql
├── DEFAULT_DASH_BOARD.sql
├── DEFAULT_WORKGROUP.sql
├── DEFAULT_WORKGROUP_ROLE.sql
├── DEFAULT_MEMBER_WORKGROUP.sql
└── DEFAULT_TRACKING_AUTO_RULE.sql

신규 설치 시 위 파일을 순서대로 적용한다.


4. 버전 업그레이드 시 마이그레이션

4.1 스키마 변경 원칙

FlowKat은 Flyway/Liquibase를 사용하지 않는다. 스키마 변경은 수동 DDL 적용 방식이다.

  • init-db.sqlIF NOT EXISTS 패턴으로 멱등성을 보장한다.
  • 신규 컬럼/인덱스는 ALTER TABLE ... ADD COLUMN IF NOT EXISTS 패턴을 사용한다.
  • TX-Mining 스키마 파일 위치: deploy.release/volumes/mining-server/config/init-db.sql

4.2 PostgreSQL 스키마 변경 적용 절차

# 1. 백업 선행 (필수)
docker exec flowkat-postgres pg_dump \
-U postgres -d postgres --schema=FLOWKAT -Fc \
-f /tmp/pre_upgrade_$(date +%Y%m%d).dump
docker cp flowkat-postgres:/tmp/pre_upgrade_$(date +%Y%m%d).dump ./backups/

# 2. 변경 스크립트 컨테이너에 복사
docker cp ./migrations/v5_0_28_schema.sql \
flowkat-postgres:/tmp/

# 3. DDL 적용
docker exec flowkat-postgres psql \
-U postgres \
-d postgres \
-f /tmp/v5_0_28_schema.sql

# 4. 적용 결과 확인
docker exec flowkat-postgres psql \
-U postgres \
-d postgres \
-c "\d FLOWKAT.XLOG_NORM"

4.3 H2 스키마 변경 (Dashboard 설정 DB)

Dashboard 서버의 JPA 설정은 hbm2ddl.auto: none이므로, 컬럼 추가/변경은 수동으로 진행한다.

# H2 Console 접근 (Dashboard 서버 기동 중)
# URL: http://<host>:6300/h2-console
# JDBC URL: jdbc:h2:file:./META_H2/H2DB_PROD
# User: (application.yml에서 확인)

# 또는 컨테이너 내부에서 직접 실행
docker exec -it flowkat-dashboard-server java \
-cp /app/lib/h2*.jar \
org.h2.tools.Shell \
-url "jdbc:h2:./META_H2/H2DB_PROD;MODE=MySQL" \
-user sa

4.4 pg_partman 파티션 관리

XLOG_NORM 테이블은 end_time 컬럼(BIGINT epoch ms)으로 일별 파티션을 자동 생성한다.

-- 파티션 상태 조회
SELECT * FROM public.part_config
WHERE parent_table = 'flowkat.xlog_norm';

-- 파티션 유지 관리 수동 실행 (cron 대체)
SELECT public.run_maintenance('flowkat.xlog_norm');

-- 미래 파티션 사전 생성 수 조정 (기본 7일)
UPDATE public.part_config
SET premake = 14
WHERE parent_table = 'flowkat.xlog_norm';

5. 서버 이전 절차

5.1 전체 이전 순서

1. 소스 서버: 서비스 중단
2. 소스 서버: PostgreSQL 전체 백업
3. 소스 서버: H2 및 설정 파일 백업 (volumes/ 전체)
4. 대상 서버: FlowKat 신규 설치 (docker-compose up)
5. 대상 서버: PostgreSQL 복원
6. 대상 서버: H2 파일 복원
7. 대상 서버: 환경변수(.env) 복원 및 검증
8. 대상 서버: 에이전트 재설정
9. 대상 서버: 서비스 기동 및 동작 확인

5.2 Docker Volume 이전

# 소스 서버: 볼륨 전체 압축
tar -czf flowkat_volumes_$(date +%Y%m%d).tar.gz \
-C /path/to/deploy.release volumes/

# PostgreSQL 데이터는 pg_dump 방식 권장 (바이너리 포맷 호환성)
# volumes/mining-server/postgres-data/ 직접 복사는 PostgreSQL 버전이 동일할 때만 안전

# 대상 서버로 전송
scp flowkat_volumes_$(date +%Y%m%d).tar.gz user@target:/path/to/deploy.release/

# 대상 서버: 압축 해제
cd /path/to/deploy.release
tar -xzf flowkat_volumes_$(date +%Y%m%d).tar.gz

5.3 환경변수 이전

.env 파일에는 비밀번호, 도메인 URL 등 환경별 값이 포함된다. 이전 시 아래 항목을 반드시 검토한다.

# 주요 검토 항목
POSTGRES_PASSWORD # 변경 없이 이전 가능
NEXTAUTH_URL # 새 서버의 접속 URL로 변경 필수
NEXTAUTH_SECRET # 유지 권장 (세션 호환성)
CORS_ORIGINS # 새 서버 URL로 변경 필수
NETWORK_MODE_COLLECT # 서버 환경(bridge/host)에 따라 조정

5.4 에이전트 재설정

서버 이전 후 각 에이전트의 Collect Server 연결 설정을 새 서버 IP로 변경한다. 에이전트 파라미터 설정은 10.agent-java.md를 참조한다.


6. 데이터 정리

6.1 파티션 드롭 (보존 기간 초과 데이터)

init-db.sql에서 retention은 365일로 설정되어 있다. 수동으로 오래된 파티션을 삭제할 때는 아래 절차를 따른다.

-- 현재 파티션 목록 및 행 수 조회
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'flowkat'
AND tablename LIKE 'xlog_norm_%'
ORDER BY tablename;

-- 특정 파티션 삭제 (데이터 영구 삭제)
DROP TABLE IF EXISTS FLOWKAT.XLOG_NORM_p20250101;

-- pg_partman run_maintenance로 retention 초과 파티션 자동 삭제
SELECT public.run_maintenance('flowkat.xlog_norm');

retention_keep_table = false이므로 run_maintenance 실행 시 보존 기간 초과 파티션은 자동 DROP된다.

6.2 VACUUM / REINDEX

파티션 드롭 또는 대량 삭제 후 디스크 공간을 회수한다.

# VACUUM ANALYZE (dead tuple 제거 + 통계 갱신)
docker exec flowkat-postgres psql \
-U postgres \
-d postgres \
-c "VACUUM ANALYZE FLOWKAT.XLOG_NORM;"

# VACUUM FULL (전체 공간 회수, 테이블 잠금 발생 — 유지보수 시간대 실행)
docker exec flowkat-postgres psql \
-U postgres \
-d postgres \
-c "VACUUM FULL FLOWKAT.XLOG_NORM;"

# REINDEX (인덱스 재구성, 성능 저하 시)
docker exec flowkat-postgres psql \
-U postgres \
-d postgres \
-c "REINDEX TABLE FLOWKAT.XLOG_NORM;"

VACUUM FULL과 REINDEX는 테이블 잠금이 발생한다. 서비스 트래픽이 없는 시간대에 실행한다.

6.3 디스크 공간 현황 확인

# PostgreSQL 전체 DB 크기
docker exec flowkat-postgres psql \
-U postgres \
-d postgres \
-c "SELECT pg_size_pretty(pg_database_size('postgres')) AS db_size;"

# FLOWKAT 스키마 테이블별 크기
docker exec flowkat-postgres psql \
-U postgres \
-d postgres \
-c "
SELECT
tablename,
pg_size_pretty(pg_total_relation_size('flowkat.'||tablename)) AS total_size,
pg_size_pretty(pg_relation_size('flowkat.'||tablename)) AS table_size
FROM pg_tables
WHERE schemaname = 'flowkat'
ORDER BY pg_total_relation_size('flowkat.'||tablename) DESC
LIMIT 20;
"

# H2 파일 크기
du -sh volumes/dashboard-server/META_H2/

7. 트러블슈팅

7.1 마이그레이션 실패 시 롤백

# 사전 백업에서 복원
docker exec flowkat-postgres psql \
-U postgres \
-d postgres \
-c "DROP SCHEMA IF EXISTS FLOWKAT CASCADE;"

docker cp ./backups/pre_upgrade_20260326.dump \
flowkat-postgres:/tmp/

docker exec flowkat-postgres pg_restore \
-U postgres \
-d postgres \
--schema=FLOWKAT \
/tmp/pre_upgrade_20260326.dump

7.2 시퀀스 불일치 복구

대용량 복원 후 SERIAL 컬럼의 시퀀스 값이 실제 데이터와 맞지 않을 때 발생한다.

-- 시퀀스 현재 값과 실제 최댓값 비교
SELECT
sequence_name,
last_value
FROM information_schema.sequences
WHERE sequence_schema = 'flowkat';

-- DIM_SERVICE 시퀀스 재설정 예시
SELECT setval(
'flowkat.dim_service_service_id_seq',
(SELECT MAX(service_id) FROM FLOWKAT.DIM_SERVICE)
);

-- 전체 테이블 시퀀스 일괄 재설정
DO $$
DECLARE
r RECORD;
BEGIN
FOR r IN
SELECT sequence_schema, sequence_name,
replace(sequence_name, '_id_seq', '') AS tbl,
replace(replace(sequence_name, '_id_seq', ''), 'dim_', 'dim_') || '_id' AS col
FROM information_schema.sequences
WHERE sequence_schema = 'flowkat'
LOOP
BEGIN
EXECUTE format(
'SELECT setval(%L, COALESCE((SELECT MAX(%I) FROM %I.%I), 1))',
r.sequence_schema || '.' || r.sequence_name,
r.col,
r.sequence_schema,
r.tbl
);
EXCEPTION WHEN OTHERS THEN
-- 테이블/컬럼 불일치 무시
END;
END LOOP;
END $$;

7.3 파티션 생성 실패

pg_partman이 새 파티션을 생성하지 못할 때 확인 사항이다.

-- pg_partman 설정 확인
SELECT * FROM public.part_config
WHERE parent_table = 'flowkat.xlog_norm';

-- 파티션 유지 관리 로그 확인
SELECT * FROM public.part_config_sub
WHERE sub_parent = 'flowkat.xlog_norm';

-- 수동으로 특정 날짜 파티션 생성
SELECT public.create_partition_time(
'flowkat.xlog_norm',
ARRAY['2026-04-01'::timestamptz]
);

7.4 H2 DB 잠금 오류 (Database may be already in use)

# Dashboard 서버 프로세스 강제 종료 후 잠금 파일 제거
docker stop flowkat-dashboard-server

# .lock.db 파일 제거
find volumes/dashboard-server/META_H2/ -name "*.lock.db" -delete

docker start flowkat-dashboard-server

관련 문서