들어가기 전에
앞서 PostgreSQL에서의 built-in replication 방식에 대해 학습했다.
[PostgreSQL] Chapter 26. High Availability, Load Balancing, and Replication
※ 이 글은 PostgreSQL의 built-in replication 방식을 학습하기 위해 일부 공식 문서와 관련 개념을 정리한 글입니다. 0. 서론본론에 앞서 중요한 개념인 고가용성과 로드 밸런싱에 대해 알아보자. 고
justsora.tistory.com
위의 내용을 토대로 각 방식에 대해 간단히 정의내려보자면 다음과 같다.
- File-Based Log-Shipping : WAL 기록을 파일로 보내 복제하는 방식
- Streaming Replication : 주 서버에서 WAL 파일이 완성되기 전에 변경 사항들을 실시간으로 스트리밍하는 방식
- Logical Replication : 주 서버에서 데이터의 변경 사항이 생기면 그 변경 사항을 게시(Publication)하고 Standby 서버는 그 게시된 변경 사항을 구독(Subscription)하는 형태로 데이터를 전송하는 방식
그중에서도 Streaming Replication 방식을 사용했을 때 주 서버에서 스트리밍 해야 할 WAL 세그먼트를 전송하기도 전에 새로운 변경 사항으로 덮어씌우는 세그먼트 재활용 문제가 발생하는데 이 문제를 방지하기 위한 방법으로 아래의 내용을 소개했다.
<단기적 데이터 유실 방지>
- wal_keep_size 설정 값을 충분히 크게 설정하여 WAL 세그먼트를 더 유지할 수 있게 한다.
- 복제 슬롯을 구성하여 WAL 세그먼트를 보존할 수 있게 한다.
<장기적 데이터 유실 방지>
archive_mode를 on 상태로 변경하여 WAL 파일이 저장될 때마다 별도의 위치로 복사한다.
이번 글에서는 Docker를 활용하여 Streaming Replication 방식으로 데이터베이스를 구성하는 방법과 그 과정 중에 세그먼트 재활용 문제를 방지하기 위해 어떻게 설정하였는지 다루고자 한다.
0. 준비
구성 환경
OS : Ubunutu-24.04
PostgreSQL : 16.4
Replication을 구성하기 이전에 먼저 아래의 항목들이 선행되어야 한다.
[1] docker 설치
Ubuntu
Jumpstart your client-side server applications with Docker Engine on Ubuntu. This guide details prerequisites and multiple methods to install Docker Engine on Ubuntu.
docs.docker.com
[2] postgresql image pull
docker pull postgres:16.4
[3] 작업 디렉토리 생성 (선택)
mkdir -p home/docker/sr-kim/postgres
1. Docker-compose 파일 생성 및 실행
[1] 파일 내부 속성
Dockerdocs 에서 Compose file reference 항목들을 보면 생각하는 것보다 엄청난 수의 속성이 있는 걸 알 수 있다. 이 글에서는 아래 템플릿을 사용해서 파일을 생성하는데 관련된 간단한 정의들은 템플릿 내부의 주석으로 확인할 수 있다.
# compose 파일 버전
version: ""
services:
# 서비스 명
[서비스 명]:
# 사용할 Docker 이미지
image:
# 컨테이너 실행 시 재시작 여부
restart:
# 컨테이너 명
container_name:
# 접근 포트 설정 (컨테이너 외부:내부)
ports:
- ":"
# 환경 변수 설정
environment:
#PostgreSQL 계정 및 패스워드 설정
POSTGRES_USER: ""
POSTGRES_PASSWORD: ""
# 타임존
TZ: ""
# 볼륨(도커에 의해 관리되는 데이터 저장 공간) 설정
volumes:
- [마운트 대상]:[컨테이너 내부 경로]
다른 속성들은 모두 간단히 정보를 기입할 수 있는데 restart는 상황에 따라 옵션 값들을 선택할 수 있다.
- no : 기본값으로 어떤 상황에서도 컨테이너를 재시작하지 않는다.
- always : 컨테이너가 삭제되지 않는 이상 항상 재시작한다.
- on-failure[:max-retries] : 종료 코드가 에러일 경우 제한 횟수까지 재시작한다.
- unless-stopped : 종료 코드에 상관없이 서비스가 중지되거나 삭제된 경우가 아니라면 재시작한다.
📢 공식 문서에 따르면 version 속성은 하위 호환성과 정보 제공 용도로만 사용된다고 한다. 굳이 명시하지 않아도 최신 스키마를 참고해 설정을 파악하고 실행한다고 하니 아주 낮은 버전을 사용해야 하는 것이 아니라면 생략해도 되겠다.
The top-level version property is defined by the Compose Specification for backward compatibility. It is only informative and you'll receive a warning message that it is obsolete if used.
version
최상위에 사용되는 version 속성은 Compose 사양에서 하위 호환성을 위해 정의되었으며, 정보 제공용으로만 사용됩니다. 사용 시에는 이 요소가 더 이상 필요하지 않다는 경고 메시지를 받을 수 있습니다.
Compose doesn't use version to select an exact schema to validate the Compose file, but prefers the most recent schema when it's implemented.
Compose는 version 속성을 통해 특정 스키마를 선택하여 Compose 파일을 검증하지 않고, 사용 가능한 최신 스키마를 우선적으로 적용합니다.
출처 : https://docs.docker.com/reference/compose-file/version-and-name/#version-top-level-element-obsolete
[2] 파일 설정
만약 작업 디렉토리를 생성했다면 이동 혹은 경로를 명시한 후 docker-compose.yml 파일을 편집하면 된다.
vi docker-compose.yml
아래는 Replication 구성을 위해 작성한 최종 Docker-compose.yml 파일이다. image만 앞서 내려받은 이미지의 버전까지 잘 명시하여 설정해 준 뒤 나머지는 원하는대로 채워나가면 된다.
version: "3.8"
services:
postgres-primary:
image: postgres:16.4
restart: always
container_name: "postgres-primary"
ports:
- "5432:5432"
environment:
POSTGRES_USER: "srkim"
POSTGRES_PASSWORD: "RDEzaI6TR6piJAu"
TZ: "Asia/Seoul"
volumes:
- ./data/primary:/var/lib/postgresql/data
postgres-replica:
image: postgres:16.4
restart: always
container_name: "postgres-replica"
ports:
- "5433:5432"
environment:
POSTGRES_USER: "repluser"
POSTGRES_PASSWORD: "RDEzaI6TR6piJAu"
TZ: "Asia/Seoul"
depends_on:
- postgres-primary
volumes:
- ./data/replica:/var/lib/postgresql/data
[3] 파일 실행
아래 명령어들로 Compose 파일 내부에 정의되어있는 컨테이너를 실행시키고 정지시킬 수 있다. 지금은 docker compose up -d 명령어로 파일 내부의 모든 서비스 컨테이너들을 백그라운드로 실행하자.
docker compose up -d # 모든 컨테이너 백그라운드 실행
docker compose down # 모든 컨테이너 정지 및 삭제
docker compose start # 특정 컨테이너 실행
docker compose stop # 특정 컨테이너 정지
실행 중인 컨테이너 목록을 확인할 수 있는 명령어이다.
docker ps
2. Primary server
[1] 서버 접속
postgres-primary 컨테이너에 접속한다.
docker exec -it postgres-primary /bin/bash # 백그라운드에 실행 중인 컨테이너 접속
➀ docker exec : 현재 실행 중인 컨테이너 접속
➁ -i (interactive) : 표준 입력(stdin) 활성화, 명령어를 입력하고 실시간으로 실행 결과 출력 가능
➂ -t (TTY) : 가상 터미널 할당, 명령어 실행 중 출력이 사람이 읽기 좋은 형태로 제공
➃ /bin/bash : 컨테이너 내부에서 Bash shell을 실행하도록 지정
[2] 데이터베이스 작업
➀ 아래 명령어를 통해 데이터베이스에 접근한다.
psql -U srkim # Compose 파일에서 설정한 사용자 계정으로 접근
➁ Replication 사용자 생성
데이터베이스 복제 작업을 수행할 수 있도록 계정을 생성한다.
# 복제 전용 사용자 생성 -> CREATE USER [사용자명] WITH REPLICATION PASSWORD [비밀번호] LOGIN;
CREATE USER repluser WITH REPLICATION PASSWORD password LOGIN;
- WITH REPLICATION : 생성된 사용자가 복제 권한을 가지도록 설정
- LOGIN : 해당 사용자의 DB 로그인 허용
생성된 계정 목록을 확인할 수 있는 명령어이다.
\du
➂ Replication slot 생성
단기적으로 데이터 유실을 방지하는 방법으로 복제 슬롯을 통해 WAL 세그먼트를 보존하기 위해 바로 아래 쿼리문을 통해 슬롯을 생성할 수 있다.
select * from pg_create_physical_replication_slot('[슬롯 이름]');
복제 슬롯 정보를 확인할 수 있는 쿼리문이다.
select slot_name, slot_type, active from pg_replication_slots;
➃ postgresql.conf 설정
DB 설정이 끝나면 해당 설정 파일이 있는 디렉토리로 이동 혹은 경로를 명시해준 후 파일을 편집한다.
cd /var/lib/postgresql/data # 디렉토리 이동
vi postgresql.conf # 파일 편집
📢 파일의 위치를 찾기 위해 find / -name "postgresql.conf" 명령어를 활용해도 좋다.
# 활성화해야 하는 속성
총 6가지의 속성들에 대한 설명과 옵션이다.
속성 | 설명 | 옵션 |
wal_level | WAL에 기록되는 정보의 양 | - minimal : 충돌이나 종료 시 복구하는 데 필요한 정보를 제외한 모든 로깅 제외 - replica : 트랜잭션 로그를 복구용으로 쓰기 위해 따로 보관, 대기 서버에서 읽기 전용 쿼리에 필요한 정보 추가 - logical : 논리적 디코딩을 지원하는 데 필요한 정보 추가 |
archive_mode | 완료된 WAL 세그먼트가 archive_command 설정에 의해 아카이브 저장소로 전달 (wal_level이 minimal이면 사용 불가) | on | off |
archive_command | 완료된 WAL 파일 세그먼트를 아카이브하기 위해 실행하는 로컬 쉘 명령 | %p (WAL 로그파일 절대경로), %f (보관할 로그 파일 이름) 예약 인자를 사용할 수 있다. |
max_wal_senders | 대기 서버 또는 스트리밍 백업 클라이언트 클라이언트로부터의 동시 연결 최대 수 (기본값 : 10) | |
max_repliation_slots | 서버가 지원할 수 있는 복제 슬롯의 최대 수 (기본값: 10) | |
hot_standby | 복구 중에 사용자가 쿼리를 연결하고 실행할 수 있는지 지정 (기본값: on) | on | off |
아래 속성을 설정 파일 내부에서 검색하여 주석을 해제하고 값을 설정하자. 복제 슬롯을 생성하고 max_replication_slots를 설정해줌으로써 단기적인 유실을, archive_mode를 on으로 설정함으로써 장기적인 유실을 방지할 수 있다.
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /var/lib/postgresql/data/pg_archive/%f && cp %p /var/lib/postgresql/data/pg_archive/%f'
max_wal_senders = 10
max_replication_slots = 10
hot_standby = on
📢 Windows에서 archive_command 값을 설정할 때 위의 경로는 다음과 같이 적을 수 있다.
archive_command = 'cp %p /var/lib/postgresql/data/pg_archive/%f'
➄ pg_hba.conf 설정
복제 계정인 repluser와 접근 허용할 서버 IP를 all로 설정한다. 맨 마지막 줄에 추가하면 된다.
# type database user address method
host replication repluser all trust
➅ 설정 파일 변경 사항 반영
우선 PostgreSQL 서버 설정을 변경하기 위해 root 사용자에서 postgres 사용자로 전환한다.
su postgres
다음으로 pg_ctl 도구를 사용해 설정 파일의 변경 사항을 적용한다.
pg_ctl reload -D /var/lib/postgresql/data
- reload : 서버를 재시작하지 않고 설정 파일의 변경사항 적용
- -D [데이터 폴더 경로] : PostgreSQL의 데이터 폴더 경로 지정
3. Host Server
[1] postgres-replica 컨테이너 중지
Primary Server에서 나온 후 아래의 명령어로 postgres-replica 컨테이너를 중지시킨다
docker compose stop postgres-replica
[2] postgres-replica 컨테이너 - 터미널 접근
아래의 run 명령어로 컨테이너를 일회성으로 새로 생성하고 실행하면서 현재의 터미널 창을 통해 해당 컨테이너 내부에서 작업할 수 있다.
docker compose run postgres-replica bash
📢 터미널로 바로 접근하지 않고 기존처럼 백그라운드로 재실행 후 컨테이너에 접속하면 이후의 작업을 수행할 때 문제가 발생할 수 있다. 실제로 후자의 방법으로는 data 폴더의 데이터를 삭제할 수 없어 해당 방법을 사용했다.
4. Standby server
[1] 기존 데이터 삭제
Primary 서버의 데이터를 복제하기 전에 기존에 있던 데이터와 복제하려는 데이터 간에 생길 수 있는 문제가 발생하는 것을 막기 위해 기존 데이터를 삭제한다. Compose 파일의 볼륨 속성에 지정한 디렉토리 경로의 데이터들을 대상으로 한다.
rm -rf /var/lib/postgresql/data/*
[2] Primary server 의 데이터 복제
필수 옵션을 통해 복제할 Primary 서버의 호스트와 백업을 저장할 디렉토리 경로, 그리고 접속할 사용자 계정 정보를 지정한다. 이후에는 복제 방식에 따라 선택적으로 옵션을 사용할 수 있으며 아래의 명령어에서는 추가적으로 4개의 옵션을 사용했다. (-X, -S, -P, -R)
pg_basebackup -h postgres-primary -D /var/lib/postgresql/data -U repluser -X stream -S repl_slot_0 -P -R
➀ pg_basebackup : PostgreSQL 데이터베이스를 백업하는 명령어
➁ -h [호스트 이름] : 백업할 PostgreSQL 서버의 호스트 이름 또는 IP 주소 지정 (Compose 파일 내부의 서비스 이름)
➂ -D [데이터 디렉토리] : 백업 데이터를 저장할 디렉토리 지정
➃ -U [사용자] : 백업을 수행하는 사용자 지정
➄ -X stream : WAL 파일을 스트리밍 방식으로 백업 (실시간으로 데이터 읽기 가능)
➅ -S : 특정 복제 슬롯을 사용하여 백업 수행
➆ -P : 백업 진행 상황 출력
➇ -R : recovery.conf 파일 생성 (백업된 데이터베이스 복구 시 필요한 설정 정보 포함)
5. 데이터 복제 확인
[1] postgres-replica 컨테이너 실행
터미널 접근에서 빠져나와 Host Server 에서 postgres-replica 컨테이너를 다시 실행해야 한다. 이때는 일회성 컨테이너를 생성하기 전에 중지했던 컨테이너가 있기 때문에 start 명령어를 사용하면 된다.
docker compose start postgres-replica
[2] Primary Server - DB 작업 수행
테스트를 위해 랜덤한 작업을 수행해주면 되는데 간단하게 데이터베이스를 생성했다.
# 백그라운드에 실행 중인 컨테이너 접속
docker exec -it postgres-primary /bin/bash
# 데이터베이스 접근
psql -U srkim
# 데이터베이스 생성
create database replica_test;
# 데이터베이스 목록 확인
\l
exit;
[3] Standby Server - 복제 확인
이제 postgres-replica 컨테이너 내부에서 데이터베이스 목록을 확인해 postgres-primary 컨테이너에서 생성한 값이 잘 복제되었는지 확인하자.
# 백그라운드에 실행 중인 컨테이너 접속
docker exec -it postgres-replica /bin/bash
# 데이터베이스 접근
psql -U postgres
# 데이터베이스 목록 확인
\l
정리
이렇게 PostgreSQL의 built-in replication 방식 중 Streaming Replication 을 선택해 구성해 보았다. 이전 포스팅에서 데이터 유실 문제가 발생한다는 것을 학습했기 때문에 방지하기 위해 사용하는 속성을 더 강조하여 작성했다. 이번 구성 방식을 통해 Primary 서버와 Standby 서버의 데이터를 다른 방식들보다 더 빠르고 안전하며, 일관성있게 유지할 수 있을 것이다.
구성 중에 Primary 서버에 PostgreSQL 설정을 모두 끝마치고 reload 할 때 잘못된 명령어를 사용해 설정이 초기화되기도 하고, postgres-replica 컨테이너에서 데이터 디렉토리의 데이터들을 삭제하지 못해 한참을 헤매기도 했다. 그래서 최대한 같은 실수를 반복하지 않도록 자세히 쓰려고 노력했는데 처음 작업하는 개발자들에게 도움이 되었으면 한다.
참고
'DB' 카테고리의 다른 글
[jOOQ] ad-hoc 방식을 통한 One-to-One, One-to-Many 조회 (0) | 2025.02.02 |
---|---|
[PostgreSQL] Chapter 26. High Availability, Load Balancing, and Replication (0) | 2024.10.14 |
[jOOQ] jOOQ는 처음이라 (0) | 2024.07.12 |
[MyBatis] selectKey로 다중 컬럼 값 가져오기 (0) | 2023.05.30 |
[MyBatis] BindingException 해결 (feat. IntelliJ Gradle 설정) (1) | 2023.05.29 |