Postgresql의 read replica를 설정하는 방법을 정리합니다.

큐돈은 서버 외부에서 pull 방식으로 1시간마다 백업을 합니다. 하지만 데이터베이스 덤프의 크기가 기가바이트 단위가 됨에 따라 하루에 수백 기가바이트 정도의 트래픽이 발생하는 문제가 생겼습니다. 그래서 외부에 postgres의 실시간 복제본을 만들고 여기에서 자체적으로 DB 덤프를 통해 백업을 하는 시스템으로 변경했습니다. 참고로, 실시간 복제본 자체는 백업으로 세지 않습니다. 만약 원본 서버에서 버그나 실수 등으로 데이터가 삭제된다면 복제본에서도 곧바로 삭제되기 때문입니다.

실시간 복제본은 read만 가능한 replica로 구성되며, 백업으로만 사용할 것이기 때문에 docker 안에 가두어 놓았습니다.

원본 노드 설정

read-replica가 원본 노드에 붙을 수 있도록 설정을 해야 합니다.

replicator 유저 생성

CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'my_replicator_password';

replication 슬롯 생성

SELECT * FROM pg_create_physical_replication_slot('replication_slot_slave1');

다음 명령을 통해 검증할 수 있습니다.

SELECT * FROM pg_replication_slots;

레플리카 활성화

레플리카가 접속할 수 있도록 원본 노드의 postgresql.conf 파일에 다음과 같이 추가합니다.

wal_level = replica
hot_standby = on
max_wal_senders = 10
max_replication_slots = 10
hot_standby_feedback = on

초기 백업 생성

모든 데이터를 스트리밍을 통해 복사하는 것은 꽤 느립니다. 초기 데이터는 원본 노드에서 생성하고 각자 원하는 방식을 통해 전송한 후에 복원하면, 그 시점 이후의 데이터들만 스트리밍으로 불러들이기 때문에 빠른 동기화가 가능합니다.

다음 명령을 원본 노드에서 실행합니다

pg_basebackup -D /tmp/postgresslave -S replication_slot_slave1 -X stream -P -U replicator -Fp -R

이후 생성된 폴더를 각자 원하는 방식으로 복제 노드쪽으로 이동합니다.

복제 노드 설정

Docker compose

다음과 같은 도커 컴포즈 파일을 생성했습니다. XXX라고 적힌 부분은 수정해야 합니다.

version: '3'

services:
  database:
    image: postgres:14
    # container_name: db
    restart: always
    volumes:
      - ./data-slave:/var/lib/postgresql/data
      - ./postgres.conf:/var/lib/postgresql/data/postgresql.conf
      - ./pg_hba.conf:/var/lib/postgresql/data/pg_hba.conf

    ports:
      - "127.0.0.1:15432:5432"
      - "172.17.0.1:15432:5432"
    environment:
      - 'POSTGRES_PASSWORD=XXX'
      - 'POSTGRES_DB=XXX'

postgresql.auto.conf 수정

원본 노드에서 생성한 초기 데이터에 들어있는 설정은 복제 노드에서 그냥 사용하면 원본 노드로 접속하는 방법이 없기 때문에 그냥은 사용하지 못합니다.

postgresql.auto.conf 파일을 찾아 primary_conninfo를 다음과 같이 수정합니다.

primary_conninfo = 'user=XXX password=XXX channel_binding=prefer host=XXX port=5432 sslmode=prefer sslcompression=0 sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres target_session_attrs=any'

restore_command 설정도 같은 파일에 만들어 줍니다

restore_command = 'cp /var/lib/postgresql/data/pg_wal/%f "%p"'

대략 다음과 같은 파일이 완성됩니다.

# Do not edit this file manually!
# It will be overwritten by the ALTER SYSTEM command.
max_connections = '200'
shared_buffers = '256MB'
effective_cache_size = '768MB'
maintenance_work_mem = '64MB'
checkpoint_completion_target = '0.9'
wal_buffers = '7864kB'
default_statistics_target = '100'
random_page_cost = '1.1'
effective_io_concurrency = '200'
work_mem = '655kB'
min_wal_size = '1GB'
max_wal_size = '4GB'
primary_conninfo = 'user=XXX password=XXX channel_binding=prefer host=XXX port=5432 sslmode=prefer sslcompression=0 sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres target_session_attrs=any'
primary_slot_name = 'rep_shion'

restore_command = 'cp /var/lib/postgresql/data/pg_wal/%f "%p"'

실행, 검증

docker compose up -d 명령을 이용해 컨테이너를 띄운 후 원본 노드에서 다음 명령을 사용해 검증 가능합니다.

SELECT * FROM pg_replication_slots;

wal_statusreserved로 뜨면 정상입니다.

이제 복제 노드에서 pg_dump를 통해 DB 백업을 할 수 있습니다. 물론 이 방법을 사용해 read-replica를 구축해 메인 노드의 부하를 덜어줄 수도 있습니다.

다음엔 pg_bouncer를 설정하는 방법을 정리해 보는 것도 좋을 것 같습니다.


참고 포스트: https://medium.com/swlh/postgresql-replication-with-docker-c6a904becf77