Postgres를 위한 간단한 클러스터링 및 복제 솔루션

작성자: Phil Eaton
2025년 9월 10일

우리는 이제 **EDB Postgres Distributed (PGD)**용 새로운 CLI를 제공하고 있습니다. 이 CLI를 사용하면 PGD 노드 클러스터를 아주 쉽게 만들 수 있습니다. PGD 노드는 PGD 메타데이터를 가진 Postgres 인스턴스로, 서로 연결되어 DDL(스키마)과 DML(데이터)을 논리적 복제로 동기화합니다.

PGD의 철학은 클라우드 네이티브적입니다. 어떤 노드에 문제가 생겨도 그냥 삭제한 후 다시 생성하면 놓친 데이터를 모두 자동으로 재동기화합니다.

이번 글에서는 AWS에서 세 개의 PGD 노드 클러스터를 만드는 간단하지만 수동적인 방법을 보여드리겠습니다. 이는 완전한 프로덕션 환경 설정은 아니지만 시작하는 데 도움이 될 것입니다. 이후에는 복제, 노드 삭제 및 재생성을 테스트해보겠습니다.


3개의 EC2 인스턴스 생성

먼저 Ubuntu 24.04 t3.micro EC2 인스턴스 세 개를 생성하고 각 인스턴스에 8GB 디스크를 할당합니다. 동일한 보안 그룹에 넣어주세요. 인스턴스가 생성되면 보안 그룹을 수정하여, 이 보안 그룹 내부에서 오는 모든 TCP 인바운드 연결을 허용합니다.


각 노드에서의 설정

1) 구독 토큰 설정

무료 EDB 계정을 로그인 또는 등록하고, 구독 토큰을 가져옵니다. 토큰을 환경 변수로 설정합니다.

export EDB_SUBSCRIPTION_TOKEN=whatever-it-is

2) 저장소 추가

PGD와 EDB Postgres Extended(EDB의 Postgres 배포판)용 저장소를 설정합니다.

curl -1sLf "https://downloads.enterprisedb.com/$EDB_SUBSCRIPTION_TOKEN/postgres_distributed/setup.deb.sh" | sudo -E bash
curl -1sLf "https://downloads.enterprisedb.com/$EDB_SUBSCRIPTION_TOKEN/enterprise/setup.deb.sh" | sudo -E bash

3) 패키지 설치

sudo apt-get update -y
sudo apt-get install -y edb-pgd6-expanded-pgextended17

postgres 사용자로 전환

sudo su postgres
cd ~

각 노드를 프라이빗 IP 주소로 식별합니다. 예를 들어:

export NODE0="172.31.38.10"
export NODE1="172.31.35.26"
export NODE2="172.31.43.94"

추가로 아래 변수를 지정합니다.

export PGPASSWORD="some password" # 비밀번호는 반드시 변경
export PATH=$PATH:/usr/lib/edb-pge/17/bin/

$NODE0에서의 설정

pgd node node0 setup \
 --dsn "host=$NODE0 dbname=pgddb user=postgres" \
 --listen-addr "$NODE0,localhost" \
 --initial-node-count 3 \
 --pgdata $HOME/pgddb \
 --log-file $HOME/postgres.logfile \
 --group-name "pgd"

클러스터 레이아웃 확인:

$ pgd --dsn "host=$NODE0 dbname=pgddb user=postgres" nodes list
Node Name | Group Name | Node Kind | Join State | Node Status
-----------+------------+-----------+------------+-------------
node0     | pgd        | data      | ACTIVE     | Up

헬스 체크 확인:

$ pgd --dsn 'host=localhost dbname=pgddb user=postgres' cluster show
# Summary
Group Name | Parent Group | Group Type | Node Name | Node Kind
------------+--------------+------------+-----------+-----------
pgd        |              | global     | node0     | data

# Health
Check             | Status | Details
-------------------+--------+-------------------------------------------------
Connections       | Ok     | All BDR nodes are accessible
Raft              | Ok     | Raft Consensus is working correctly
Replication Slots | Ok     | All PGD replication slots are working correctly
Clock Drift       | Ok     | Clock drift is within permissible limit
Versions          | Ok     | All nodes are running the same PGD version

# Clock Drift
Reference Node | Node Name | Clock Drift
----------------+-----------+-------------

출력 결과에서 연결 상태, 합의(Raft), 복제 슬롯, 클럭 드리프트, 버전 등이 정상임을 확인할 수 있습니다.


$NODE1에서의 설정

두 번째 PGD 노드를 설정합니다. ($NODE1에 postgres 사용자로 로그인되어 있는지 확인하세요.)
pgd node "node1" setup \
 --dsn "host=$NODE1 dbname=pgddb user=postgres" \
 --listen-addr "$NODE1,localhost" \
 --pgdata $HOME/pgddb \
 --log-file $HOME/postgres.logfile \
 --cluster-dsn "host=$NODE0 dbname=pgddb user=postgres" \
 --cluster-name "pgd"

이제 $NODE0 또는 $NODE1에서 모든 노드를 확인할 수 있습니다.

$ pgd --dsn "host=$NODE0 dbname=pgddb user=postgres" nodes list
Node Name | Group Name | Node Kind | Join State | Node Status
-----------+------------+-----------+------------+-------------
node0     | pgd        | data      | ACTIVE     | Up
node1     | pgd        | data      | ACTIVE     | Up

$NODE2에서의 설정

세 번째 PGD 노드를 설정합니다. ($NODE2에 postgres 사용자로 로그인되어 있는지 확인하세요.)

pgd node "node2" setup \
 --dsn "host=$NODE2 dbname=pgddb user=postgres" \
 --listen-addr "$NODE2,localhost" \
 --pgdata $HOME/pgddb \
 --log-file $HOME/postgres.logfile \
 --cluster-dsn "host=$NODE0 dbname=pgddb user=postgres" \
 --cluster-name "pgd"

이제 클러스터 내 어느 노드에서든 pgd nodes list 명령을 실행하면 세 개의 노드가 모두 보입니다.

$ pgd --dsn "host=$NODE0 dbname=pgddb user=postgres" nodes list
Node Name | Group Name | Node Kind | Join State | Node Status
-----------+------------+-----------+------------+-------------
node0     | pgd        | data      | ACTIVE     | Up
node1     | pgd        | data      | ACTIVE     | Up
node2     | pgd        | data      | ACTIVE     | Up

DDL과 DML 복제

클러스터 구성이 끝났으니 이제 PGD를 본격적으로 사용해봅시다. $NODE0에 orders 테이블을 만들고 백만 개의 행을 삽입해보겠습니다.

DSN을 사용하기 때문에 어디에서 실행하든 실제로는 $NODE0에서 동작하게 됩니다.

$ psql "host=$NODE0 dbname=pgddb user=postgres" -c "
BEGIN;
DROP TABLE IF EXISTS orders;
CREATE TABLE orders (
  customer_id INT,
  order_id BIGSERIAL,
  amount_pennies BIGINT,
  PRIMARY KEY (customer_id, order_id)
);
INSERT INTO orders (customer_id, amount_pennies)
SELECT
  random() * 9 + 1,
  random() * 100000 + 1
FROM
  generate_series(1, 1_000_000);
COMMIT;"

출력:

$ psql "host=$NODE0 dbname=pgddb user=postgres" -c "SELECT count(1) FROM orders"
 count
---------
1000000
(1 row)

$ psql "host=$NODE1 dbname=pgddb user=postgres" -c "SELECT count(1) FROM orders"
 count
---------
1000000
(1 row)

$ psql "host=$NODE2 dbname=pgddb user=postgres" -c "SELECT count(1) FROM orders"
 count
---------
1000000
(1 row)

테이블이 모든 노드에 생성되었고 데이터도 복제되었습니다!


$NODE2 제거 및 재생성

가령 $NODE2에 어떤 문제가 생겼다고 해봅시다. 다시 프로비저닝해야 한다면 어떻게 할까요? 문제 없습니다! 먼저 클러스터에 이 노드를 제거하라고 알립니다.

$ pgd --dsn 'host=$NODE0 dbname=pgddb user=postgres' node node2 part
Starting a part node operation for node: node2
This may take some time, please wait...
NOTICE:  node node2 has been removed from the BDR group

이제 $NODE2에서 Postgres를 중지하고 데이터 디렉토리를 삭제한 뒤, 위에서 사용했던 pgd node setup 명령을 다시 실행합니다.

$ pg_ctl -D $HOME/pgddb -l $HOME/postgres.logfile stop
waiting for server to shut down.... done
server stopped
$ rm -rf $HOME/postgres.logfile $HOME/pgddb

먼저 클러스터 상태를 조회하여 이 노드가 제거된 것을 확인합니다.

$ pgd --dsn "host=$NODE0 dbname=pgddb user=postgres" nodes list
Node Name | Group Name | Node Kind | Join State | Node Status
-----------+------------+-----------+------------+-------------
node0     | pgd        | data      | ACTIVE     | Up
node1     | pgd        | data      | ACTIVE     | Up

이제 $NODE2를 다시 빌드합니다.

$ pgd node "node2" setup \
 --dsn "host=$NODE2 dbname=pgddb user=postgres" \
 --listen-addr "$NODE2,localhost" \
 --pgdata $HOME/pgddb \
 --log-file $HOME/postgres.logfile \
 --cluster-dsn "host=$NODE0 dbname=pgddb user=postgres" \
 --cluster-name "pgd"

다시 노드 목록을 확인합니다.

$ pgd --dsn "host=$NODE0 dbname=pgddb user=postgres" nodes list
Node Name | Group Name | Node Kind | Join State | Node Status
-----------+------------+-----------+------------+-------------
node0     | pgd        | data      | ACTIVE     | Up
node1     | pgd        | data      | ACTIVE     | Up
node2     | pgd        | data      | ACTIVE     | Up

복귀 완료! 재생성하기 전 클러스터에 있던 테이블과 데이터도 모두 그대로 복구되었습니다.

$ psql "host=$NODE2 dbname=pgddb user=postgres" -c "SELECT count(1) FROM orders"
 count
---------
1000000
(1 row)

롤링 업그레이드 활용

참고로, 이 재생성 과정을 클러스터 롤링 업그레이드에도 사용할 수 있습니다.

  1. 클러스터에서 노드 하나를 제거
  2. 더 최신 버전의 Postgres와 PGD를 가진 새 노드를 프로비저닝
  3. 새 노드를 클러스터에 조인

이 과정을 모든 노드에 반복하면 클러스터 전체가 최신 버전의 Postgres와 PGD로 업그레이드됩니다.

메일: salesinquiry@enterprisedb.com