데이터를 디스크에 암호화해서 저장하는 클러스터를 백업 도구가 제대로 다룰 수 있을까. 결론부터 말하면, pg_tde로 암호화한 PostgreSQL 클러스터를 pgBackRest로 백업하고 복구하는 과정은 거의 대부분 투명하게 작동한다. 다만 암호화된 데이터를 백업 도구가 해석할 수 없다는 본질적 한계 때문에, 몇 가지 검증·압축 옵션은 꺼야 한다.

Data Egret의 Stefan Fercot(스테판 페르코)가 pgBackRest and pg_tde 글에서 이 조합을 직접 검증했다. 이 블로그에서 다뤄 온 백업 시리즈의 곁가지로, 백업 도구와 암호화 extension의 궁합을 운영 관점에서 정리해 본다.

pg_tde가 암호화하는 것

pg_tde는 Percona가 개발 중인 투명 데이터 암호화(Transparent Data Encryption) extension이다. 이름 그대로 애플리케이션은 암호화 여부를 신경 쓰지 않고, 디스크에 저장되는 데이터(data at rest)만 암호화한다. 암호화 대상은 크게 두 가지다.

  • 테이블 데이터(heap): tde_heap 접근 방식(access method)으로 만든 테이블. 기존 테이블은 ALTER TABLE ... SET ACCESS METHOD tde_heap으로 전환한다. heap 테이블뿐 아니라 거기 딸린 index, TOAST, sequence까지 암호화된다.
  • WAL: pg_tde.wal_encrypt GUC를 켜면 WAL 세그먼트도 암호화된다. 이 설정은 서버 전역이라 재시작이 필요하고, 켠 시점 이후의 WAL write부터 암호화가 적용된다.

다만 모든 게 암호화되지는 않는다. 시스템 카탈로그와 통계 데이터 같은 메타데이터는 아직 암호화 대상이 아니다. 이름 그대로 “데이터” 암호화에 초점이 맞춰져 있다.

키 구조는 2계층이다. 실제 데이터를 암호화하는 internal key는 $PGDATA/pg_tde 아래에 로컬로 저장되고, 이 internal key를 다시 암호화하는 principal key는 외부 KMS에 둔다. principal key는 데이터베이스당 하나다. 검증에서는 KMS로 OpenBao를 Docker로 띄워 썼고, root 자격증명이 아니라 secret/ 경로에만 읽기/쓰기 권한을 준 최소 권한 토큰을 발급해 사용했다.

pgBackRest가 암호화된 클러스터를 다루는 흐름

pgBackRest 입장에서 보면, pg_tde가 이미 암호화해 놓은 데이터 파일과 WAL을 그대로 받아 저장소로 옮긴다. 백업 도구는 그 안을 들여다보지 않는다. 암호화는 PostgreSQL 쪽에서 끝나 있고, pgBackRest는 암호화된 바이트 덩어리를 운반하는 역할이다.

flowchart TD
    A[PostgreSQL
tde_heap 테이블] -->|암호화된 page| B[데이터 파일] A -->|WAL 암호화| C[WAL 세그먼트] K[외부 KMS
principal key] -.보호.-> A B --> D[pgBackRest
backup] C -->|archive-push| D D --> E[백업 저장소
S3 호환] E --> F[pgBackRest
restore] F --> G[복구된 PGDATA
여전히 암호화]

핵심은 복구 이후다. pgBackRest가 복구해 놓은 PGDATA는 여전히 암호화된 상태다. 이걸 PostgreSQL이 다시 읽으려면 internal key를 풀 principal key가 필요하고, principal key는 외부 KMS에 있다. 즉 백업 파일만 들고 있어서는 복구한 클러스터를 기동할 수 없고, KMS 접근이 함께 살아 있어야 한다.

검증 결과와 주의점

Fercot는 pgbench로 부하를 주면서 full 백업과 incremental 백업을 뜨고, pgbench_tellers에서 1,000건을 삭제한 뒤 named restore point로 시점 복구(PITR)를 실행해 삭제된 1,000건이 되살아나는 것까지 확인했다. WAL 세그먼트와 데이터 파일은 백업 저장소 안에서도 암호화된 채 유지됐고, 복구 과정에서 별도의 복호화 래퍼 없이 named restore point에 깔끔하게 도달했다.

다만 암호화 특성 때문에 꺼야 하는 설정들이 있다. 정리하면 다음과 같다.

설정이유
archive-header-checkn암호화된 WAL 헤더를 pgBackRest가 해석 못 함
checksum-pagen암호화된 page의 checksum 검증 불가
compress-typenone암호화된 데이터는 무작위에 가까워 압축 이득 거의 없음
repo1-blocknblock 단위 incremental의 효율이 암호화로 무력화

표의 항목들은 모두 같은 원인에서 나온다. pgBackRest는 암호화된 내용을 들여다볼 수 없으므로, 데이터 내부를 읽어야 성립하는 검증과 최적화가 작동하지 않는다. 그래서 page checksum 검증과 WAL 헤더 검증을 끄고, 압축도 끄는 편이 낫다. 압축을 켜 두면 줄어들지도 않을 데이터를 압축하느라 CPU만 쓴다.

한 가지 더, incremental 백업 크기가 예상보다 컸다. block 단위 incremental은 변경된 블록만 골라 담아 용량을 아끼는데, 암호화된 데이터에서는 이 절감 효과가 크게 줄어든다.

WAL 암호화 관련: 과거 Percona 문서는 암호화 환경에서 비동기 아카이빙(archive-async=y)을 권하지 않았다. 그러나 이번 검증에서는 비동기 아카이빙이 문제없이 작동했다. 운영에서 적용하기 전에는 사용하는 pg_tde 버전 기준으로 한 번 더 확인하는 편이 안전하다.

키 관리 관점: 백업과 복구가 투명하게 돈다고 해서 백업만으로 복구가 끝나는 것은 아니다. 복구된 PGDATA는 암호화 상태 그대로이므로, principal key를 보관한 KMS에 접근하지 못하면 클러스터를 기동할 수 없다. 백업 파일과 KMS 접근 권한을 함께 보존하고, KMS 자체의 가용성과 백업도 별도로 챙겨야 한다. 백업 저장소 한쪽만 살아남는 시나리오를 복구 절차에 반드시 포함해 두자.

저장소 암호화 중복: pgBackRest 자체에도 저장소 암호화 옵션(repo1-cipher-type)이 있다. pg_tde가 이미 데이터를 암호화한 뒤 넘겨주므로 저장소 암호화는 선택 사항이다. 다만 시스템 카탈로그처럼 pg_tde가 암호화하지 않는 영역이 백업에 포함되는 점을 고려하면, 저장소 암호화를 한 겹 더 두는 선택도 합리적이다.

검증에는 Percona Server for PostgreSQL이 쓰였다. pg_tde가 특정 패치에 의존하기 때문에 표준 PostgreSQL이 아닌 Percona 배포판이 필요하고, pgBackRest가 버전을 제대로 인식하도록 pg-version-force로 버전을 강제해야 했다.

운영 관점과 백업 시리즈 연결

백업 도구를 고를 때는 도구 자체의 기능만 보기 쉽지만, 실제 운영에서는 암호화·복제·버전 같은 주변 환경과의 궁합이 더 자주 발목을 잡는다. 이번 검증의 의미는 “pg_tde를 켜도 pgBackRest를 그대로 쓸 수 있다"는 점을 확인했다는 데 있다. 암호화 때문에 백업 도구를 새로 고민할 필요는 없고, 검증·압축 옵션 몇 개를 끄는 선에서 정리된다.

대신 복구 절차의 무게중심이 옮겨 간다. 평소처럼 백업 무결성만 점검하는 데서 그치지 말고, principal key를 가진 KMS까지 포함한 복구 리허설을 정기적으로 돌려야 한다. 암호화를 도입하는 순간, 백업 전략은 “데이터 백업"에서 “데이터 백업 + 키 관리"로 확장된다.

pgBackRest를 운영에 들이는 과정 자체가 처음이라면, 이 블로그의 pgBackRest 도입기에서 기본 stanza 구성과 아카이빙 흐름을 먼저 잡고 오는 편이 좋다. 암호화 궁합은 그 위에 얹는 한 겹이다.

참고