Pglogical을 사용한 PostGres 데이터베이스 복제

발행: (2026년 2월 25일 오전 05:52 GMT+9)
9 분 소요
원문: Dev.to

Source: Dev.to

Postgres 데이터베이스 복제: pglogical 사용하기

소개

PostgreSQL은 기본적으로 물리적 복제와 스트리밍 복제를 제공하지만, 논리적 복제는 더 세밀한 제어와 다양한 사용 사례를 가능하게 합니다. pglogical은 PostgreSQL용 논리 복제 확장으로, 테이블 수준에서 데이터를 복제하고, 복제 토폴로지를 자유롭게 구성할 수 있게 해줍니다. 이 글에서는 pglogical을 사용해 두 개의 PostgreSQL 인스턴스 간에 복제를 설정하는 과정을 단계별로 살펴보겠습니다.

사전 요구 사항

  • PostgreSQL 12 이상 (pglogical은 9.4 이상을 지원하지만 최신 버전을 권장합니다)
  • 두 개의 서버(또는 Docker 컨테이너) – PrimarySubscriber
  • 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.confpg_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_statusreplicating으로 표시되면 복제가 정상적으로 동작하고 있는 것입니다.

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)에 전파되어 물리적 스트리밍 없이 논리적, 거의 실시간 복제를 제공합니다. 동기화된 데이터베이스를 즐기세요! 🎉

0 조회
Back to Blog

관련 글

더 보기 »