EC2에서 직접 굴리는 PostgreSQL의 백업 스크립트를 손보다가, 그동안 pg_basebackup으로 매번 전체본을 통째로 떠오던 게 눈에 걸렸다. DB가 수백 GB로 커지면 이 전체 백업 한 번이 스토리지와 시간을 크게 잡아먹는다. 그래서 예전에는 pgBackRest 같은 외부 도구를 붙여 증분을 뜨곤 했는데, PostgreSQL 17부터는 이 기능이 코어 안으로 들어왔다. 이번 글은 그 네이티브 증분 백업을 실제로 돌려본 기록이다.
정리부터 하자면, PostgreSQL 17의 증분 백업은 서버에서 summarize_wal을 켜 두고, pg_basebackup --incremental=이전_backup_manifest로 이전 백업 이후 바뀐 블록만 떠서 용량과 시간을 줄이는 방식이다. 증분본은 그 자체로는 복구할 수 없고, pg_combinebackup으로 전체 백업과 합쳐 복구용 전체본을 만들어야 데이터 디렉터리로 쓸 수 있다. 아래에서 개념, 서버 준비, 전체와 증분 백업, 합성, 검증 순서로 살펴본다.
1. 증분 백업이 무엇이고 PostgreSQL 17에서 왜 생겼나
증분 백업은 직전 백업 이후에 바뀐 데이터 블록만 저장하는 백업이다. 전체 백업이 모든 파일을 그대로 복사하는 것과 달리, 변경분만 담으므로 백업 크기가 작고 완료도 빠르다. 대신 복구할 때 전체본과 증분본을 다시 합쳐야 한다.
여기서 핵심 장치가 WAL 요약(WAL summary)이다. WAL은 PostgreSQL이 모든 변경을 먼저 기록하는 트랜잭션 로그인데, PostgreSQL 17은 이 WAL을 훑어 "어느 릴레이션의 어느 블록이 바뀌었는지"를 요약 파일로 만들어 pg_wal/summaries 디렉터리에 쌓아 둔다. 이 일을 하는 백그라운드 프로세스가 WAL summarizer다. 증분 백업을 요청하면 서버는 이 요약을 참고해 바뀐 블록만 골라 보낸다.
공식 문서는 이 점을 분명히 한다. 필요한 요약 파일이 없으면 증분 백업 시도는 실패한다. "요약 파일은 직전 백업의 시작 LSN부터 이번 백업의 시작 LSN까지의 모든 LSN을 덮어야 한다"는 게 요구 조건이다. LSN은 WAL 상의 위치를 가리키는 로그 시퀀스 번호다.
2. 서버 준비: summarize_wal 켜기
WAL summarizer는 기본으로 꺼져 있다. summarize_wal의 기본값이 off라서, 이걸 켜지 않으면 요약 파일이 쌓이지 않고 증분 백업도 안 된다. 다행히 이 파라미터는 sighup 컨텍스트라 재시작 없이 설정 리로드만으로 적용된다.
sarc=# ALTER SYSTEM SET summarize_wal = on; ALTER SYSTEM sarc=# SELECT pg_reload_conf(); pg_reload_conf ---------------- t (1 row)
적용됐는지 확인한다.
sarc=# SHOW summarize_wal; summarize_wal --------------- on (1 row)
WAL summarizer 프로세스가 떴는지도 확인해 두면 깔끔하다.
$ ps -ef | grep 'walsummarizer' | grep -v grep postgres 4123 4110 0 10:02 ? 00:00:00 postgres: walsummarizer
주의할 조건이 하나 있다. wal_level이 minimal이면 summarize_wal을 on으로 둬도 summarizer가 요약 파일을 만들지 않는다. 증분 백업을 쓸 서버라면 wal_level은 replica 이상이어야 한다.
요약 파일 보관 기간도 같이 챙긴다. wal_summary_keep_time의 기본값은 10일이다. 이 값은 전체 백업과 그에 딸린 증분 백업 사이에 벌어질 수 있는 시간보다 넉넉하게 잡아야 한다. 요약이 먼저 지워지면 증분 백업이 실패하기 때문이다. 주 단위로 전체를 뜨고 매일 증분을 뜬다면 10일도 빠듯할 수 있으니 여유 있게 늘려 둔다.
sarc=# ALTER SYSTEM SET wal_summary_keep_time = '30d'; ALTER SYSTEM sarc=# SELECT pg_reload_conf();
3. 기준이 될 전체 백업 한 번 뜨기
증분은 항상 어떤 기준 백업을 참조한다. 그 기준의 시작점이 전체 백업이다. 평소대로 pg_basebackup으로 전체본을 뜬다. 버전 번호는 저장소 상태에 따라 다를 수 있다.
$ pg_basebackup --version pg_basebackup (PostgreSQL) 17.5
$ pg_basebackup -h 127.0.0.1 -U repl -D /backup/full_0901 -c fast -P 512000/512000 kB (100%), 1/1 tablespace
백업 디렉터리 안에 backup_manifest가 생겼는지 본다. 이 매니페스트가 다음 증분 백업의 기준 파일이 된다.
$ ls /backup/full_0901/backup_manifest /backup/full_0901/backup_manifest
매니페스트는 WAL 파일을 뺀 백업 내 모든 파일의 크기, 마지막 수정 시각, 체크섬을 담는다. --no-manifest로 생성을 끌 수도 있지만, 그러면 이 백업을 기준으로 삼는 증분을 못 뜬다. 증분 체인의 출발점이 될 전체 백업에서는 매니페스트를 끄지 마라.
4. 증분 백업 뜨기
이제 변경분만 뜬다. -i(또는 --incremental)에 직전 백업의 backup_manifest 경로를 넘긴다. 이 매니페스트는 서버로 업로드되고, 서버는 그 이후 바뀐 블록만 담은 증분 백업을 돌려준다.
$ pg_basebackup -h 127.0.0.1 -U repl \
-D /backup/incr_0902 \
--incremental=/backup/full_0901/backup_manifest \
-c fast -P
41216/41216 kB (100%), 1/1 tablespace
전체본이 500MB대였는데 증분본은 수십 MB 수준으로 떨어지는 걸 볼 수 있다. 실제 절감폭은 그날 얼마나 많은 블록이 바뀌었느냐에 달려 있어 환경에 따라 다르지만, 변경이 적은 날일수록 증분본은 작아진다.
여기서 한 번 걸리기 쉽다. summarizer가 아직 최근 WAL을 요약하지 못했거나 필요한 요약이 이미 지워졌으면 다음처럼 실패한다.
pg_basebackup: error: could not initiate base backup: ERROR: WAL summaries are required on timeline 1 from ... but no summaries for that timeline and LSN range exist
서버는 없는 요약 파일이 나타날 때까지 기다려 주기도 한다. summarizer가 뒤처진 경우라면 잠시 뒤 다시 시도하면 된다. 하지만 wal_summary_keep_time이 지나 요약이 삭제됐다면 그 기준으로는 더 이상 증분을 못 뜬다. 이때는 전체 백업부터 다시 떠야 한다.
다음 증분을 또 뜰 때는 기준을 방금 뜬 증분본의 매니페스트로 바꾼다. 이렇게 하면 전체 하나에 증분이 사슬처럼 이어지는 체인이 된다.
$ pg_basebackup -h 127.0.0.1 -U repl \
-D /backup/incr_0903 \
--incremental=/backup/incr_0902/backup_manifest \
-c fast -P
5. pg_combinebackup으로 복구용 전체본 합성하기
증분본은 그대로는 복구에 쓸 수 없다. 문서에도 못을 박아 뒀다. 증분 백업은 직접 복원할 수 없으며 pg_combinebackup으로 앞선 백업들과 합쳐야 한다. 이 도구는 전체본과 그 뒤로 이어진 증분본들을 받아 하나의 합성 전체본(synthetic full backup)을 만들어 낸다.
인자로 넘기는 백업 디렉터리 순서가 중요하다. 오래된 것부터 최신 순으로, 즉 전체본을 맨 앞에, 복원하려는 마지막 증분본을 맨 뒤에 둔다. 출력 위치는 -o로 지정하며 이 옵션은 필수다.
$ pg_combinebackup \
/backup/full_0901 \
/backup/incr_0902 \
/backup/incr_0903 \
-o /restore/pgdata_0903
실행 전에 어떤 작업이 벌어질지 미리 보고 싶으면 -n(dry-run)을 붙인다. 파일을 만들지 않고 예정된 동작만 출력한다.
$ pg_combinebackup -n \
/backup/full_0901 /backup/incr_0902 /backup/incr_0903 \
-o /restore/pgdata_0903
결과로 나온 /restore/pgdata_0903은 완전한 데이터 디렉터리다. 여기에 알맞은 postgresql.conf와 복구 설정을 얹고 인스턴스를 띄우면 그 시점의 DB가 올라온다. 이 합성 전체본을 다시 다음 pg_combinebackup의 입력으로 넣을 수도 있다.
체인 무결성은 반드시 지켜야 한다. 중간 증분본을 하나라도 빼면 복원은 실패한다. pg_combinebackup은 넘긴 백업들이 올바른 체인을 이루는지는 확인하지만, 각 백업 파일이 손상 없이 온전한지까지 검사하지는 않는다.
6. 백업 무결성은 pg_verifybackup으로 따로 검사
합성이 체인의 논리적 정합성만 본다면, 파일 자체가 멀쩡한지는 pg_verifybackup이 매니페스트의 체크섬과 대조해 확인한다. 백업을 떠 둔 직후, 그리고 복원 직전에 한 번씩 돌려 두면 마음이 놓인다.
$ pg_verifybackup /backup/full_0901 backup successfully verified
증분본도 같은 방식으로 검사할 수 있다. 각 백업 디렉터리를 개별로 검증하면 된다.
7. 실무에서 걸리는 지점들
RDS에서는 이 방법을 못 쓴다. 이 절차는 EC2 등에서 직접 운영하는 PostgreSQL을 대상으로 한다. Amazon RDS나 Aurora는 관리형이라 서버 파일시스템과 슈퍼유저 접근을 열어 주지 않으므로 pg_basebackup으로 base backup을 뜨는 방식이 아니라 자체 스냅샷과 PITR을 쓴다. 네이티브 증분 백업은 서버를 스스로 굴릴 때의 선택지다.
체크섬 상태를 바꿨다면 새 전체본을 떠라. pg_combinebackup은 페이지 체크섬을 다시 계산하지 않는다. 체크섬을 켜거나 끄는 변경 뒤에 옛 체크섬 상태의 백업을 섞어 합성하면 결과물에 잘못된 체크섬이 남을 수 있다. 이럴 때는 증분을 이어 붙이지 말고 전체 백업을 새로 뜨는 게 맞다.
요약 보관 기간과 백업 주기를 맞춰라. 4번에서 본 실패는 대부분 wal_summary_keep_time이 백업 간격보다 짧아서 생긴다. 전체를 뜨는 주기, 증분을 뜨는 주기, 요약 보관 기간을 한 축에 놓고 요약이 항상 살아 있도록 잡는다.
테이블스페이스가 있으면 -T로 재배치한다. 원본과 다른 경로에 복원할 때 pg_combinebackup -T 옛경로=새경로로 테이블스페이스 위치를 옮긴다.
정리
PostgreSQL 17의 증분 백업은 외부 도구 없이 코어만으로 백업 시간과 용량을 줄인다. 순서는 간단하다. 서버에서 summarize_wal을 켜고 wal_summary_keep_time을 백업 주기에 맞춰 잡은 뒤, pg_basebackup으로 전체본을 한 번 뜨고, 이후에는 직전 백업의 backup_manifest를 --incremental에 넘겨 변경분만 뜬다. 복원할 때는 pg_combinebackup으로 전체본과 증분본들을 오래된 순서대로 합쳐 전체본을 만들고, pg_verifybackup으로 무결성을 확인한다. RDS가 아닌 EC2 자체 운영 환경이라면 지금 백업 스크립트에 바로 얹어 볼 만하다.