
오늘은 Docker 환경에서 PostgreSQL 물리적 복제(Physical Replication)를 구성하는 방법을 단계별로 작성해볼게요.
이 글을 보고 레플리케이션을 적용하면 다음과 같은 효과를 기대할 수 있어요.
- Master 장애 발생 시 서비스 중단 위험 감소
- Read/Write 분리를 통한 데이터베이스 부하 분산
- PostgreSQL 물리적 복제 방식에 대한 이해 향상
본 가이드는 Docker + PostgreSQL 16을 기준으로 작성되었습니다.
AWS RDS 기반 복제 구성은 별도 문서에서 다룰 예정이니, 기대해주세요!
준비 사항
환경
- Docker Desktop 또는 Docker CLI 사용 가능 환경
- 5432 포트를 바인딩하여 실행 중인 PostgreSQL Master 컨테이너
- PostgreSQL 16 버전 기준
목표
- Master → Slave로 WAL 파일을 전달해 실시간 복제를 구성
- Slave 인스턴스를 읽기 전용(READ ONLY) 으로 운영
전체 구성도

Master는 쓰기 작업과 WAL(Log) 생성·전송을 담당합니다.
Slave는 전달받은 WAL 파일을 기반으로 데이터 파일을 재구성하며, 읽기 전용(Read) 요청을 처리합니다.
Slave DB 컨테이너 생성
Master가 이미 5432 포트를 사용하고 있으므로 Slave는 5433 포트로 실행합니다.
docker run -d \
--name slave_container \
-e POSTGRES_PASSWORD=password \
-p 5433:5432 \
-v postgres_slave_data:/var/lib/postgresql/data \
postgres
각 옵션의 의미는 다음과 같습니다.
- POSTGRES_PASSWORD: 기본
>postgres계정 비밀번호 - -v postgres_slave_data:/var/lib/postgresql/data: Slave 데이터 파일을 저장하기 위한 볼륨
- -p 5433:5432: 호스트 5433 포트를 컨테이너의 5432로 매핑
컨테이너 IP 확인
Master와 Slave가 통신할 수 있도록 각각의 컨테이너 IP를 확인합니다.
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAdress}}{{end}}' container_name_or_id
조회한 master_ip와 slave_ip는 이후 설정에 사용하므로 기록해 둡니다.
Master DB 설정
1. 컨테이너 접속
docker exec -it masterdb_container bash
2. 편집 도구 설치
apt-get update && apt-get install nano
3. postgresql.conf 수정
nano /var/lib/postgresql/data/postgresql.conf
아래 설정을 적용합니다.
listen_addresses = '*' # 모든 연결을 허용합니다
port = 5432 # 포트번호를 작성합니다
max_connections = 100 # 최대 연결 수를 제한합니다 (충분한 값으로 설정)
shared_buffers = 256MB # 컨테이너 메모리의 1/4 정도로 설정합니다
work_mem = 4MB
maintenance_work_mem = 64MB
dynamic_shared_memory_type = posix
wal_level = replica # WAL 레벨을 설정합니다
max_wal_size = 1GB
min_wal_size = 80MB
archive_mode = on # WAL 파일을 아카이빙 합니다
archive_command = 'cp %p /archive/%f' # WAL 파일의 경로
max_wal_senders = 3 # Slave DB의 수 +1로 설정합니다
wal_keep_size = 1024
logging_collector = on
log_directory = pg_log
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_timezone = 'Etc/UTC'
이 설정들은 Master가 생성한 WAL 로그를 복제 서버(Slave)로 안정적으로 전송하기 위해 필요합니다.
아카이브 디렉터리 생성:
mkdir /archive
4. 레플리케이션 전용 유저 생성
PostgreSQL에 접속한 뒤 실행합니다.
CREATE USER replica_user REPLICATION LOGIN CONNECTION LIMIT 2 ENCRYPTED PASSWORD '1234';
5. pg_hba.conf 수정
nano /var/lib/postgresql/data/pg_hba.conf
Slave에서 복제 연결을 허용하기 위해 다음 한 줄을 추가합니다.
host replication replica_user slave_ip/32 trust
6. Master 재시작
docker restart master_container
Master 로그는 아래 경로에서 확인할 수 있습니다.
/var/lib/postgresql/data/pg_log
Slave DB 설정
1. Slave 컨테이너 접속
docker exec -it slave_container bash
Master에서 사용 중인 확장(PostGIS 등)이 있다면 동일하게 설치합니다
apt-get update
apt-get install postgis postgresql-16-postgis-3
2. Slave 데이터 디렉터리 초기화
먼저 Master를 일시 중지합니다.
docker stop master_container

이후 Slave가 사용 중인 볼륨의 데이터 파일을 모두 삭제합니다.
(컨테이너는 반드시 중지된 상태여야 합니다.)
3. Master 데이터 복사
Master → 로컬:
docker cp master_container:/var/lib/postgresql/data ./master_data
로컬 → Slave:
docker cp ./master_data slave_container:/var/lib/postgresql
이 과정은 물리 복제를 위해 Slave의 데이터 디렉터리를 Master와 동일한 상태로 맞추는 필수 단계입니다.
4. Slave 설정 파일 수정
nano /var/lib/postgresql/data/postgresql.conf
아래 항목을 추가 또는 수정합니다.
primary_conninfo = 'host=master_ip port=5432 user=replica_user password=1234 sslmode=prefer'
hot_standby = on
5. Slave 재시작
docker restart slave_container
복제 정상 동작 확인
Master에서 실행
SELECT * FROM pg_stat_replication;
결과가 1행 이상 출력되면 Slave가 Master에 정상적으로 연결된 상태입니다.
Slave에서 확인
SELECT * FROM pg_stat_wal_receiver;
WAL 파일을 지속적으로 수신 중이라면 복제 구성이 정상적으로 동작하고 있습니다.
문제 해결 (Troubleshooting)
Slave가 Master에 연결되지 않을 때
pg_hba.conf에 Slave IP가 정확히 등록되어 있는지 확인primary_conninfo의 host, port, user, password 설정 확인
FATAL: password authentication failed
replica_user비밀번호 불일치 여부 확인- Master 재시작 여부 확인
requested WAL segment ... has already been removed
wal_keep_size값을 증가해 WAL 보관 기간을 연장- Slave가 해당 WAL 파일을 읽기 전에 삭제된 경우 발생
PostgreSQL이 시작되지 않는 경우
로그에서 원인을 확인합니다.
/var/lib/postgresql/data/pg_log
여기까지 Docker 환경에서 PostgreSQL 물리적 복제(Master–Slave)를 구성하는 전체 과정을 살펴봤습니다.
이 구성을 적용하면 Master의 쓰기 부하를 분산할 수 있고, 장애 발생 시 서비스 안정성을 한층 높일 수 있습니다.
다음 문서에서는 Spring Boot 환경에서 Master/Slave를 자동으로 라우팅하는 방법을 이어서 소개해볼게요!
'Computer Science > Data 📊' 카테고리의 다른 글
| [MySQL] 문자열 데이터를 저장하는 방법 (0) | 2025.03.24 |
|---|---|
| 데이터베이스 트랜잭션 완전 가이드 - 개념부터 ACID, MySQL, 격리 수준, 스프링 적용까지 (0) | 2024.05.16 |
| 데이터베이스 vs 데이터 웨어하우스 vs 데이터 레이크 (0) | 2024.03.08 |
안녕하세요, 저는 주니어 개발자 박석희 입니다. 언제든 하단 연락처로 연락주세요 😆