EDB Postgres Distributed를 활용한 최소 다운타임 Postgres 메이저 버전 업그레이드
Phil Eaton
2025년 2월 28일
이 글에서는 커뮤니티 버전 Postgres 16을 실행하는 3노드 EDB Postgres Distributed(PGD) 클러스터를 설정한 후, Postgres 16에서 Postgres 17로 클러스터 전체를 인플레이스 업그레이드하는 방법을 설명합니다. 개별 노드를 메이저 버전 업그레이드를 위해 다운시키더라도 클러스터 전체는 계속해서 읽기 및 쓰기가 가능함을 시연할 것입니다.
각 환경마다 추가적인 확장, 권한 등의 차이점이 있을 수 있으나, 이 글에서는 단순하고 이상적인 경로에 초점을 맞추겠습니다.
이 실습은 새로운 Debian 12 시스템에서 진행됩니다.
Postgres 16 설치하기
먼저, postgresql.org에서 제공하는 패키지 저장소 설정 지침을 따릅니다.
# Import the repository signing key:
sudo apt install curl ca-certificates
sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
# Create the repository configuration file:
sudo sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
# Update the package lists:
sudo apt update
이제 Postgres 16을 설치할 수 있습니다.
sudo apt install -y postgresql-16 postgresql-client-16
BDR 확장 설치하기
PGD는 bdr이라는 Postgres 확장으로 작동합니다.
먼저, EDB Repos 2.0 페이지에서 EDB 리포지토리 토큰을 가져옵니다.
EDB 계정이 없다면, 무료로 가입하여 EDB 소프트웨어 저장소의 체험판 구독을 받을 수 있습니다.
토큰을 받은 후, 환경 변수를 설정합니다:
export EDB_SUBSCRIPTION_TOKEN=<your-repo-token>
그런 다음, 패키지 저장소를 설정하는 도우미 스크립트를 실행합니다:
curl -1sLf "https://downloads.enterprisedb.com/$EDB_SUBSCRIPTION_TOKEN/postgres_distributed/setup.deb.sh" | sudo -E bash
이제 Postgres 16용 PGD 관련 패키지를 설치합니다:
sudo apt install -y edb-bdr5-pg16 edb-bdr-utilities edb-pgd5-cli
Postgres 16 인스턴스 생성하기
먼저, postgres 사용자로 전환합니다.
sudo su postgres
cd ~
그리고 initdb
를 사용하여 세 개의 Postgres 인스턴스를 생성합니다.
rm -rf /var/lib/postgresql/*
/usr/lib/postgresql/16/bin/initdb /var/lib/postgresql/db1.16
/usr/lib/postgresql/16/bin/initdb /var/lib/postgresql/db2.16
/usr/lib/postgresql/16/bin/initdb /var/lib/postgresql/db3.16
💡 참고:
만약 설정을 잘못했거나, 초기 상태로 되돌리고 싶다면, 각 Postgres 인스턴스를 pg_ctl
을 사용하여 중지한 후 위 명령어를 다시 실행하면 됩니다.
이제 각 인스턴스의 postgresql.conf 파일에 PGD 관련 설정을 추가합니다.
echo "
port=6001
shared_preload_libraries = 'bdr'
wal_level = 'logical'
track_commit_timestamp = on
# These depend on the size of the cluster.
max_worker_processes = 12
max_wal_senders = 6
max_replication_slots = 6" | tee -a \
/var/lib/postgresql/db1.16/postgresql.conf
echo "
port=6002
shared_preload_libraries = 'bdr'
wal_level = 'logical'
track_commit_timestamp = on
# These depend on the size of the cluster.
max_worker_processes = 12
max_wal_senders = 6
max_replication_slots = 6" | tee -a \
/var/lib/postgresql/db2.16/postgresql.conf
echo "
port=6003
shared_preload_libraries = 'bdr'
wal_level = 'logical'
track_commit_timestamp = on
# These depend on the size of the cluster.
max_worker_processes = 12
max_wal_senders = 6
max_replication_slots = 6" | tee -a \
/var/lib/postgresql/db3.16/postgresql.conf
이제 세 개의 인스턴스를 시작합니다.
/usr/lib/postgresql/16/bin/pg_ctl -D /var/lib/postgresql/db1.16 -l /tmp/logfile-db1 start
/usr/lib/postgresql/16/bin/pg_ctl -D /var/lib/postgresql/db2.16 -l /tmp/logfile-db2 start
/usr/lib/postgresql/16/bin/pg_ctl -D /var/lib/postgresql/db3.16 -l /tmp/logfile-db3 start
다음 단계에서는 이 인스턴스들을 PGD 클러스터에 연결할 것입니다.
세 개의 Postgres 데이터베이스를 PGD 클러스터로 변환하기
각 인스턴스에서 데이터베이스를 생성해야 합니다.
/usr/lib/postgresql/16/bin/psql -p 6001 postgres -c "CREATE DATABASE pgdtest;"
/usr/lib/postgresql/16/bin/psql -p 6002 postgres -c "CREATE DATABASE pgdtest;"
/usr/lib/postgresql/16/bin/psql -p 6003 postgres -c "CREATE DATABASE pgdtest;"
이제 각 인스턴스의 pgdtest 데이터베이스에 BDR 확장을 설치합니다.
/usr/lib/postgresql/16/bin/psql -p 6001 pgdtest -c 'CREATE EXTENSION bdr CASCADE;'
/usr/lib/postgresql/16/bin/psql -p 6002 pgdtest -c 'CREATE EXTENSION bdr CASCADE;'
/usr/lib/postgresql/16/bin/psql -p 6003 pgdtest -c 'CREATE EXTENSION bdr CASCADE;'
그다음, 각 인스턴스를 자신만의 PGD 노드로 초기화합니다.
/usr/lib/postgresql/16/bin/psql -p 6001 pgdtest -c "SELECT bdr.create_node(node_name := 'db1', local_dsn := 'port=6001 dbname=pgdtest host=localhost user=postgres');"
/usr/lib/postgresql/16/bin/psql -p 6002 pgdtest -c "SELECT bdr.create_node(node_name := 'db2', local_dsn := 'port=6002 dbname=pgdtest host=localhost user=postgres');"
/usr/lib/postgresql/16/bin/psql -p 6003 pgdtest -c "SELECT bdr.create_node(node_name := 'db3', local_dsn := 'port=6003 dbname=pgdtest host=localhost user=postgres');"
그다음, 첫 번째 인스턴스에서 PGD 그룹을 생성합니다.
/usr/lib/postgresql/16/bin/psql -p 6001 pgdtest -c "SELECT bdr.create_node_group(node_group_name := 'pgdtest-group');"
나머지 두 개의 인스턴스를 해당 그룹에 추가합니다.
/usr/lib/postgresql/16/bin/psql -p 6002 pgdtest -c "SELECT bdr.join_node_group(join_target_dsn := 'port=6001 dbname=pgdtest host=localhost user=postgres');"
/usr/lib/postgresql/16/bin/psql -p 6003 pgdtest -c "SELECT bdr.join_node_group(join_target_dsn := 'port=6001 dbname=pgdtest host=localhost user=postgres');"
✅ 설정 완료! 이제 클러스터를 테스트해보면 됩니다.
PGD 클러스터에서 데이터 초기화
하나의 노드에서(어느 노드든 상관없음) 테이블을 생성하고 데이터를 삽입합니다.
/usr/lib/postgresql/16/bin/psql -p 6002 pgdtest -c "CREATE TABLE x (a INT PRIMARY KEY);"
/usr/lib/postgresql/16/bin/psql -p 6002 pgdtest -c "INSERT INTO x VALUES (1), (34), (-2), (5);"
그런 다음, 모든 노드에서 해당 테이블을 조회합니다.
/usr/lib/postgresql/16/bin/psql -p 6001 pgdtest -c "SELECT * FROM x;"
/usr/lib/postgresql/16/bin/psql -p 6002 pgdtest -c "SELECT * FROM x;"
/usr/lib/postgresql/16/bin/psql -p 6003 pgdtest -c "SELECT * FROM x;"
모든 노드는 동일한 결과를 반환해야 합니다.
a
----
1
34
-2
5
(4 rows)
첫 번째 인스턴스 업그레이드
첫 번째 인스턴스를 중지합니다.
/usr/lib/postgresql/16/bin/pg_ctl -D /var/lib/postgresql/db1.16 -l /tmp/logfile-db1 stop
이제 두 번째 인스턴스에서 데이터를 삽입할 수 있으며, 클러스터는 정상적으로 동작합니다.
/usr/lib/postgresql/16/bin/psql -p 6002 pgdtest -c "INSERT INTO x VALUES (99), (33);"
세 번째 인스턴스에서 데이터를 조회합니다.
/usr/lib/postgresql/16/bin/psql -p 6003 pgdtest -c "SELECT * FROM x;"
a
----
1
34
-2
5
99
33
(6 rows)
이제 일반적인 pg_upgrade
절차와 마찬가지로 새로운 PostgreSQL 버전을 설치합니다.
sudo apt install -y postgresql-17 postgresql-client-17
PostgreSQL 17을 실행하는 새로운 인스턴스를 생성합니다.
/usr/lib/postgresql/17/bin/initdb /var/lib/postgresql/db1.17
이전 인스턴스의 postgresql.conf
파일을 복사합니다.
cp /var/lib/postgresql/db1.16/postgresql.conf /var/lib/postgresql/db1.17/
일반적인 경우라면 pg_upgrade
를 실행하면 되지만, PGD 클러스터에서는 추가적인 처리가 필요하므로 pgd node upgrade
를 사용합니다.
pgd node db1 upgrade \
--old-datadir /var/lib/postgresql/db1.16 \
--new-datadir /var/lib/postgresql/db1.17 \
--old-bindir /usr/lib/postgresql/16/bin \
--new-bindir /usr/lib/postgresql/17/bin \
--username postgres \
--database pgdtest \
--old-port 6001
이제 PostgreSQL 17을 실행하는 db1
을 시작합니다.
/usr/lib/postgresql/17/bin/pg_ctl -D /var/lib/postgresql/db1.17 -l /tmp/logfile-db1 start
서버가 다시 실행되면 오프라인 상태였을 때 추가된 데이터를 자동으로 동기화합니다.
/usr/lib/postgresql/17/bin/psql -p 6001 pgdtest -c "SELECT * FROM x;"
a
----
1
34
-2
5
99
33
(6 rows)
즉, 현재 클러스터에서 하나의 인스턴스(db1
)는 PostgreSQL 17을 실행하고 있으며, 나머지 노드는 여전히 PostgreSQL 16을 사용하고 있습니다.
/usr/lib/postgresql/17/bin/psql -p 6001 pgdtest -c "SELECT * FROM bdr.group_versions_details;"
node_id | node_name | postgres_version | bdr_version
------------+-----------+--------------------------------+-------------
4292081331 | db1 | 17.4 (Debian 17.4-1.pgdg120+2) | 5.6.1
4215100525 | db2 | 16.8 (Debian 16.8-1.pgdg120+1) | 5.6.1
2822738592 | db3 | 16.8 (Debian 16.8-1.pgdg120+1) | 5.6.1
(3 rows)
이제 두 번째 인스턴스(db2
)도 PostgreSQL 17로 업그레이드해 보겠습니다.
두 번째 인스턴스 업그레이드
두 번째 인스턴스를 중지합니다.
/usr/lib/postgresql/16/bin/pg_ctl -D /var/lib/postgresql/db2.16 -l /tmp/logfile-db2 stop
새로운 PostgreSQL 17 인스턴스를 생성합니다.
/usr/lib/postgresql/17/bin/initdb /var/lib/postgresql/db2.17
이전 인스턴스의 postgresql.conf
파일을 복사합니다.
cp /var/lib/postgresql/db2.16/postgresql.conf /var/lib/postgresql/db2.17/
pgd node upgrade
를 실행합니다.
pgd node db2 upgrade \
--old-datadir /var/lib/postgresql/db2.16 \
--new-datadir /var/lib/postgresql/db2.17 \
--old-bindir /usr/lib/postgresql/16/bin \
--new-bindir /usr/lib/postgresql/17/bin \
--username postgres \
--database pgdtest \
--old-port 6002
PostgreSQL 17을 실행하는 db2
를 시작합니다.
/usr/lib/postgresql/17/bin/pg_ctl -D /var/lib/postgresql/db2.17 -l /tmp/logfile-db2 start
/usr/lib/postgresql/17/bin/psql -p 6002 pgdtest -c "SELECT * FROM bdr.group_versions_details;"
node_id | node_name | postgres_version | bdr_version
------------+-----------+--------------------------------+-------------
4292081331 | db1 | 17.4 (Debian 17.4-1.pgdg120+2) | 5.6.1
4215100525 | db2 | 17.4 (Debian 17.4-1.pgdg120+2) | 5.6.1
2822738592 | db3 | 16.8 (Debian 16.8-1.pgdg120+1) | 5.6.1
(3 rows)
두 개의 인스턴스가 업그레이드 완료! 이제 마지막 하나만 남았습니다.
세 번째 인스턴스 업그레이드
세 번째 인스턴스를 중지합니다.
/usr/lib/postgresql/16/bin/pg_ctl -D /var/lib/postgresql/db3.16 -l /tmp/logfile-db3 stop
새로운 PostgreSQL 17 인스턴스를 생성합니다.
/usr/lib/postgresql/17/bin/initdb /var/lib/postgresql/db3.17
이전 인스턴스의 postgresql.conf
파일을 복사합니다.
cp /var/lib/postgresql/db3.16/postgresql.conf /var/lib/postgresql/db3.17/
pgd node upgrade
를 실행합니다.
pgd node db3 upgrade \
--old-datadir /var/lib/postgresql/db3.16 \
--new-datadir /var/lib/postgresql/db3.17 \
--old-bindir /usr/lib/postgresql/16/bin \
--new-bindir /usr/lib/postgresql/17/bin \
--username postgres \
--database pgdtest \
--old-port 6003
PostgreSQL 17을 실행하는 db3
을 시작합니다.
/usr/lib/postgresql/17/bin/pg_ctl -D /var/lib/postgresql/db3.17 -l /tmp/logfile-db3 start
/usr/lib/postgresql/17/bin/psql -p 6002 pgdtest -c "SELECT * FROM bdr.group_versions_details;"
node_id | node_name | postgres_version | bdr_version
------------+-----------+--------------------------------+-------------
4292081331 | db1 | 17.4 (Debian 17.4-1.pgdg120+2) | 5.6.1
4215100525 | db2 | 17.4 (Debian 17.4-1.pgdg120+2) | 5.6.1
2822738592 | db3 | 17.4 (Debian 17.4-1.pgdg120+2) | 5.6.1
(3 rows)
기본 테스트 (Basics tests)
이제 전체 클러스터가 업그레이드되었으므로 복제가 정상적으로 작동하는지 확인해 봅시다!
각 인스턴스에서 고유한 값을 삽입합니다.
/usr/lib/postgresql/17/bin/psql -p 6001 pgdtest -c "INSERT INTO x VALUES (6001);"
/usr/lib/postgresql/17/bin/psql -p 6002 pgdtest -c "INSERT INTO x VALUES (6002);"
/usr/lib/postgresql/17/bin/psql -p 6003 pgdtest -c "INSERT INTO x VALUES (6003);"
이제 모든 인스턴스에서 데이터를 조회합니다.
/usr/lib/postgresql/17/bin/psql -p 6001 pgdtest -c "SELECT * FROM x;"
/usr/lib/postgresql/17/bin/psql -p 6002 pgdtest -c "SELECT * FROM x;"
/usr/lib/postgresql/17/bin/psql -p 6003 pgdtest -c "SELECT * FROM x;"
모든 인스턴스에서 동일한 결과가 나와야 합니다.
a
------
1
34
-2
5
99
33
6001
6002
6003
(9 rows)
잘 작동하네요!
마무리
우리는 3개 노드로 구성된 PGD 클러스터를 PostgreSQL 16에서 17로 롤링 업그레이드했습니다.
각 노드를 하나씩 업그레이드하는 동안에도 클러스터는 지속적으로 읽기 및 쓰기 요청을 처리할 수 있었습니다.
전체적인 다운타임을 최소화할 수 있었으며,
PGD 프록시를 적절히 사용하고 연결을 신중하게 정리하면 거의 무중단(zero-downtime) 업그레이드도 가능합니다.
반면, 물리적 복제(physical replication) 기반의 클러스터에서는 동일한 업그레이드를 수행하려면
두 개의 전체 클러스터를 설정하고, 그 사이에 논리적 복제(logical replication)를 구성해야 합니다.
이는 훨씬 더 많은 노력과 비용이 드는 작업입니다!
PGD 클러스터를 활용하면 보다 효율적이고 유연한 업그레이드 전략을 구현할 수 있습니다
본문: Minimal downtime Postgres major version upgrades with EDB Postgres Distributed