Pglogical을 사용한 PostGres 데이터베이스 복제
Source: Dev.to
Postgres 데이터베이스 복제: pglogical 사용하기
소개
PostgreSQL은 기본적으로 물리적 복제와 스트리밍 복제를 제공하지만, 논리적 복제는 더 세밀한 제어와 다양한 사용 사례를 가능하게 합니다. pglogical은 PostgreSQL용 논리 복제 확장으로, 테이블 수준에서 데이터를 복제하고, 복제 토폴로지를 자유롭게 구성할 수 있게 해줍니다. 이 글에서는 pglogical을 사용해 두 개의 PostgreSQL 인스턴스 간에 복제를 설정하는 과정을 단계별로 살펴보겠습니다.
사전 요구 사항
- PostgreSQL 12 이상 (pglogical은 9.4 이상을 지원하지만 최신 버전을 권장합니다)
- 두 개의 서버(또는 Docker 컨테이너) – Primary와 Subscriber
pglogical확장 설치 권한 (슈퍼유저)
1. pglogical 설치
Ubuntu/Debian
sudo apt-get update
sudo apt-get install postgresql-14-pglogical
CentOS/RHEL
sudo yum install pglogical_14
Tip: PostgreSQL 버전에 맞는
pglogical패키지를 선택하세요. 패키지가 없을 경우, 소스에서 직접 컴파일할 수 있습니다.
2. PostgreSQL 설정 변경
두 서버 모두 postgresql.conf와 pg_hba.conf를 수정해야 합니다.
postgresql.conf
shared_preload_libraries = 'pglogical' # 서버 재시작 필요
wal_level = logical
max_replication_slots = 4
max_wal_senders = 4
pg_hba.conf
# Primary → Subscriber
host replication all <subscriber_ip>/32 md5
# Subscriber → Primary (역방향 복제 시 필요)
host replication all <primary_ip>/32 md5
설정 변경 후 PostgreSQL을 재시작합니다.
sudo systemctl restart postgresql
3. 확장 및 레플리케이션 세트 생성
Primary (출판자)에서
-- 슈퍼유저로 접속
CREATE EXTENSION IF NOT EXISTS pglogical;
-- 레플리케이션 세트 정의 (전체 테이블 복제)
SELECT pglogical.create_node(
node_name := 'primary',
dsn := 'host=primary_ip port=5432 dbname=yourdb user=replicator password=yourpassword'
);
SELECT pglogical.replication_set_add_all_tables('default', ARRAY['public']);
Subscriber (구독자)에서
CREATE EXTENSION IF NOT EXISTS pglogical;
SELECT pglogical.create_node(
node_name := 'subscriber',
dsn := 'host=subscriber_ip port=5432 dbname=yourdb user=replicator password=yourpassword'
);
4. 구독 설정
Subscriber에서 Primary에 대한 구독을 생성합니다.
SELECT pglogical.create_subscription(
subscription_name := 'sub_to_primary',
provider_dsn := 'host=primary_ip port=5432 dbname=yourdb user=replicator password=yourpassword',
replication_sets := ARRAY['default']
);
구독이 생성되면 pglogical이 자동으로 초기 스냅샷을 가져와 테이블을 복제합니다.
5. 복제 확인
Primary
SELECT * FROM pglogical.show_subscription_status();
Subscriber
SELECT * FROM pglogical.show_subscription_status();
두 쿼리 모두 replication_status가 replicating으로 표시되면 복제가 정상적으로 동작하고 있는 것입니다.
6. 데이터 변경 테스트
Primary에서 데이터를 삽입/수정/삭제하고, Subscriber에서 동일하게 반영되는지 확인합니다.
-- Primary
INSERT INTO public.users (id, name) VALUES (1, 'Alice');
-- Subscriber (몇 초 후)
SELECT * FROM public.users;
7. 고급 옵션 (선택 사항)
- 필터링: 특정 컬럼이나 조건에 따라 복제 대상 제한
SELECT pglogical.replication_set_add_table( set_name := 'default', relation := 'public.orders', row_filter := 'order_total > 1000' ); - 다중 구독: 하나의 Primary가 여러 Subscriber에 복제 가능
- 역방향 복제: 필요 시 Subscriber에서 Primary로 데이터 흐름을 설정
결론
pglogical을 사용하면 PostgreSQL 간에 유연하고 확장 가능한 논리 복제를 구현할 수 있습니다. 물리적 복제와 달리 테이블 단위로 복제 대상을 선택하고, 복제 토폴로지를 자유롭게 설계할 수 있기 때문에 마이크로서비스 아키텍처, 데이터 마이그레이션, 실시간 분석 등에 매우 유용합니다. 위 단계들을 따라 설정해 보시고, 필요에 따라 필터링이나 다중 구독 같은 고급 기능을 활용해 보세요.
이 글은 2024년 2월 기준 PostgreSQL 및 pglogical 최신 버전을 기준으로 작성되었습니다.
사전 요구 사항
-
Docker Engine
sudo systemctl start docker sudo systemctl enable docker # optional – start at boot -
Debian용 Docker Desktop (
.deb패키지에서 설치)systemctl --user start docker-desktop
프로젝트 구조
//docker/
│
├── Dockerfile.pglogical
└── postgres/
└── docker-compose.yml
docker-compose.yml
/docker/postgres/ 안에 docker-compose.yml 파일을 생성합니다:
services:
pg1:
build:
context: ..
dockerfile: Dockerfile.pglogical
container_name: pg1
command:
- postgres
- -c
- shared_preload_libraries=pglogical
- -c
- wal_level=logical
- -c
- max_replication_slots=10
- -c
- max_wal_senders=10
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: testdb
POSTGRES_HOST_AUTH_METHOD: trust
ports:
- "5433:5432"
volumes:
- pg1_data:/var/lib/postgresql/data
networks:
- pgnet
pg2:
build:
context: ..
dockerfile: DockerFile.pglogical
container_name: pg2
command:
- postgres
- -c
- shared_preload_libraries=pglogical
- -c
- wal_level=logical
- -c
- max_replication_slots=10
- -c
- max_wal_senders=10
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: testdb
POSTGRES_HOST_AUTH_METHOD: trust
ports:
- "5434:5432"
volumes:
- pg2_data:/var/lib/postgresql/data
networks:
- pgnet
networks:
pgnet:
driver: bridge
volumes:
pg1_data:
pg2_data:
Dockerfile.pglogical
베이스 PostgreSQL 이미지에 pglogical 확장이 포함되어 있지 않으므로, 사용자 정의 Dockerfile을 생성합니다:
FROM postgres:16
RUN apt-get update \
&& apt-get install -y postgresql-18-pglogical \
&& rm -rf /var/lib/apt/lists/*
컨테이너 빌드 및 시작
cd /docker/postgres
docker compose up -d
컨테이너가 실행 중인지 확인합니다:
docker ps
다음과 유사한 출력이 표시됩니다:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c61ef5d393cc postgres-pg1 "docker-entrypoint.s…" 3 days ago Up 4 minutes 0.0.0.0:5433->5432/tcp, [::]:5433->5432/tcp pg1
b055528784d5 postgres-pg2 "docker-entrypoint.s…" 3 days ago Up 4 minutes 0.0.0.0:5434->5432/tcp, [::]:5434->5432/tcp pg2
각 컨테이너에서 pglogical 설치 및 활성화
docker exec -it pg1 psql -U postgres -d testdb
psql 내부에서:
CREATE EXTENSION pglogical;
\dx -- 확장이 설치되었는지 확인
다음과 유사한 항목이 표시됩니다:
List of installed extensions
Name | Version | Schema | Description
----------+---------+------------+--------------------------------
pglogical| 2.4.6 | pglogical | PostgreSQL Logical Replication
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
pg2 컨테이너에 대해서도 동일한 단계를 반복합니다.
데이터베이스를 pglogical 노드로 등록하기
다음 명령을 pg1에서 실행하십시오:
SELECT pglogical.create_node(
node_name := 'pg1',
dsn := 'host=pg1 port=5432 dbname=testdb user=postgres password=postgres'
);
다음 명령을 pg2에서 실행하십시오:
SELECT pglogical.create_node(
node_name := 'pg2',
dsn := 'host=pg2 port=5432 dbname=testdb user=postgres password=postgres'
);
pg1에서 복제 세트 만들기
SELECT pglogical.create_replication_set(
'demo_set',
replicate_insert := true,
replicate_update := true,
replicate_delete := true,
replicate_truncate := true
);
테이블 생성 및 기본 행 삽입 (pg1)
CREATE TABLE demo_events (
id SERIAL PRIMARY KEY,
message TEXT,
created_at TIMESTAMP DEFAULT now()
);
INSERT INTO demo_events (message)
SELECT 'baseline row ' || g
FROM generate_series(1,5) g;
복제 세트에 테이블 추가 (pg1)
SELECT pglogical.replication_set_add_table(
'demo_set',
'demo_events'
);
pg2에 구독 만들기
SELECT pglogical.create_subscription(
subscription_name := 'sub_pg1',
provider_dsn := 'host=pg1 port=5432 dbname=testdb user=postgres password=postgres',
replication_sets := ARRAY['demo_set'],
synchronize_structure := true,
synchronize_data := true
);
몇 초 기다린 후, 데이터가 복제되었는지 확인합니다:
SELECT * FROM demo_events;
pg1에 삽입된 기본 5개 행이 표시되어야 합니다.
쓰기 방향 테스트 (pg1 → pg2)
기본(primary) (pg1)에 새 행을 삽입합니다:
INSERT INTO demo_events (message)
VALUES ('inserted new row');
해당 행은 pg2에 자동으로 나타납니다. pg2에 직접 쓰기를 시도하면 오류가 발생하며, 이는 pg2가 읽기 전용 복제본임을 확인시켜 줍니다.
결론
이제 pglogical 확장을 사용하여 데이터를 복제하는 두 개의 PostgreSQL 컨테이너가 있습니다. 기본(pg1)에서 수행된 변경 사항은 자동으로 복제본(pg2)에 전파되어 물리적 스트리밍 없이 논리적, 거의 실시간 복제를 제공합니다. 동기화된 데이터베이스를 즐기세요! 🎉