pg_rewind
는 PostgreSQL에서 제공하는 유틸리티 중 하나로, 스트리밍 복제 또는 로그 복제를 사용하는 PostgreSQL 서버들 간에 데이터 디렉토리를 동기화하는 데 사용됩니다. 주로 스트리밍 복제를 설정한 후 스트리밍 장애나 서버 간 데이터 불일치 문제를 해결할 때 유용하게 사용됩니다.
보통 PostgreSQL 스트리밍 복제 설정에서는 마스터(Master)와 슬레이브(Slave) 서버가 있습니다. 마스터 서버의 데이터베이스가 변경될 때마다 이 변경 사항이 슬레이브로 전송되어 데이터 일관성을 유지합니다. 그러나 때로는 슬레이브가 마스터와 동기화되지 않거나, 스트리밍 장애로 인해 슬레이브가 마스터보다 뒤처진 상태가 될 수 있습니다.
이런 상황에서 pg_rewind
는 슬레이브 서버의 데이터 디렉토리를 마스터 서버와 일치시키는 도구로 사용됩니다. 주요 기능은 다음과 같습니다:
데이터 디렉토리 동기화: 마스터와 슬레이브 서버의 데이터 디렉토리를 비교하여 슬레이브의 데이터 디렉토리를 마스터와 일치시킵니다.
재설정된 장애 복구: 스트리밍 장애로 인해 데이터 디렉토리가 불일치할 경우, pg_rewind
를 사용하여 슬레이브를 마스터의 상태로 되돌립니다.
복제 불일치 해결: 슬레이브가 마스터와 동기화되지 않은 경우, pg_rewind
를 사용하여 슬레이브를 마스터의 레벨로 동기화할 수 있습니다.
검색해본 결과 rsync
를 사용한다고 합니다. pg_rewind는 데이터 디렉토리를 동기화하기 위해 rsync를 사용합니다. pg_rewind는 PostgreSQL 데이터 디렉토리의 변경 사항을 식별하고, 이를 기반으로 마스터 데이터 디렉토리와 슬레이브 데이터 디렉토리 간의 차이를 확인합니다. 그 후 rsync를 이용하여 마스터 데이터 디렉토리의 변경 사항을 슬레이브 데이터 디렉토리에 적용하여 동기화를 수행합니다.
rsync는 파일과 디렉토리를 동기화하기 위해 널리 사용되는 유틸리티로, 변경된 부분만 효율적으로 전송하여 대상 디렉토리를 원본과 동일하게 유지시키는 기능을 제공합니다. 이를 통해 pg_rewind는 PostgreSQL 데이터 디렉토리의 동기화 작업을 효율적으로 처리할 수 있습니다.
따라서 pg_rewind는 PostgreSQL 스트리밍 복제 환경에서 스트리밍 장애나 데이터 불일치 문제를 해결하는 데 사용될 때, rsync를 통해 데이터 디렉토리를 빠르게 동기화할 수 있는 장점을 가지고 있습니다.
테스트를 할 때 구글 시트에 커맨드를 기록하고 정리합니다. 테스트한 구글 시트 내용을 공유합니다.
과정 | primary | secondary | 비고 |
---|---|---|---|
primary 기본 설정 | listen_addresses = '*' archive_mode = on archive_command = 'test ! -f /archive/%f && cp %p /archive/%f' logging_collector = on log_filename = 'postgresql-%Y-%m-%d.log' wal_level = replica synchronous_commit = local wal_keep_size = 32 synchronous_standby_names = '*' promote_trigger_file = '/home/agensql/data/trigger.signal' hot_standby = on | ||
primary 기본 설정 | host all all 0.0.0.0/0 trust host replication repluser 0.0.0.0/0 trust | ||
primary 기동 | ag_ctl start | ||
repluser 생성 및 권한 부여 | create user repluser with replication password '1234' login; grant all privileges on database agensdb to repluser; | ||
Replication 초기 구성 | pg_basebackup -h 192.168.54.184 -D $PGDATA -U repluser -p 5333 -v -P -R --wal-method=stream | ||
ag_ctl start | |||
recovery 모드 확인 | select pg_is_in_recovery(); | ||
wal_reciever 확인 | select * from pg_stat_replication; | ||
Primary 장애 상황 | ag_ctl stop | postgres=# select * from pg_stat_replication; pid | usesysid | usename | application_name | client_addr | client_hostname | clien t_port | backend_start | backend_xmin | state | sent_lsn | write_lsn | flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state | reply_time --------+----------+----------+------------------+---------------+-----------------+------ -------+-------------------------------+--------------+-----------+-----------+----------- +-----------+------------+-----------+-----------+------------+---------------+----------- -+------------------------------- 382500 | 16384 | repluser | walreceiver | 192.168.54.83 | | 35792 | 2024-06-04 08:44:55.378618+09 | | streaming | 0/3000060 | 0/3000060 | 0/3000060 | 0/3000060 | | | | 1 | sync | 2024-06-04 08:45:35.489058+09 (1 row) postgres=# select pg_is_in_recovery(); pg_is_in_recovery ------------------- t (1 row) | |
Secondary Promote | [방법 2 가지] 1. pg_ctl promote -D $PGDATA 2. vi $PGDATA/postgresql.conf promote_trigger_file = '/home/agens/data/trigger.signal' pg_ctl reload / select pg_reload_conf(); touch /home/agens/data/trigger.signal | [agens@bitnine data]$ pg_ctl promote -D $PGDATA waiting for server to promote.... done server promoted | |
DDL 발생 | create table test(a int); insert into test select * from generate_series(1,1000); | ||
과정 | Single DB | 비고 | |
설정 추가 | vi postgresql.conf wal_log_hints = on | vi postgresql.conf wal_log_hints = on | vi postgresql.conf wal_log_hints = on 후 pg_reload_conf() 해도 반영안됨. 재기동 필요. 그리고 결정적으로 primary는 wal_log_hints off여도 pg_rewind 기능 적용됨 (깨진 standby 만 on으로 설정 후 재기동 한다면) |
재기동 및 설정 등록 | ag_ctl start ag_ctl stop | pg_ctl reload | |
pg_rewind(sync) | pg_rewind --source-server='host=192.168.54.83 port=5333 user=agens dbname=postgres' --target-pgdata=$PGDATA -P | [agens@bitnine data]$ pg_rewind --source-server='host=192.168.54.83 port=5333 user=agens dbname=postgres' --target-pgdata=$PGDATA -P pg_rewind: connected to server pg_rewind: servers diverged at WAL location 0/30000D8 on timeline 1 pg_rewind: rewinding from last common checkpoint at 0/3000060 on timeline 1 pg_rewind: reading source file list pg_rewind: reading target file list pg_rewind: reading WAL in target pg_rewind: need to copy 86 MB (total source directory size is 124 MB) 88135/88135 kB (100%) copied pg_rewind: creating backup label and updating control file pg_rewind: syncing target data directory pg_rewind: Done! | |
rm $PGDATA/postgresql.auto.conf | |||
설정 추가 | vi postgresql.conf primary_conninfo='user=repluser password=1234 host=192.168.54.83 port=5333' | ||
standby.signal 추가 | touch $PGDATA/standby.signal | [agens@bitnine ~]$ pg_rewind --source-server='host=192.168.54.83 port=5333 user=agens dbname=postgres' --target-pgdata=$PGDATA -P pg_rewind: connected to server pg_rewind: servers diverged at WAL location 0/50000A0 on timeline 1 pg_rewind: rewinding from last common checkpoint at 0/5000028 on timeline 1 pg_rewind: reading source file list pg_rewind: reading target file list pg_rewind: reading WAL in target pg_rewind: need to copy 85 MB (total source directory size is 124 MB) 87921/87921 kB (100%) copied pg_rewind: creating backup label and updating control file pg_rewind: syncing target data directory pg_rewind: Done! | |
재기동 | ag_ctl start | ||
과정 | new_secondary | new_primary | 비고 |
recovery 모드 확인 | select pg_is_in_recovery(); | postgres=# select pg_is_in_recovery(); pg_is_in_recovery ------------------- t (1 row) | |
wal_reciever 확인 | select * from pg_stat_replication; | postgres=# select * from pg_stat_replication; |
주변에서 pg_rewind에 대한 관심이 적지 않아 모두가 테스트해볼 수 있도록 팀 내 서버에 Docker Container로 구성해두었습니다. 이 내용은 향후 기록할 Docker 기본 커맨드를 정리할 때도 들어가겠지만, 구체적인 사례를 기록해두는 것도 좋을 것 같아 기록합니다.
Docker 컨테이너 간 네트워크 통신을 위해 Docker network를 생성합니다.
docker network create pg-network
Rocky8.9에 pg15를 설치한 Docker container를 이미지로 만들어 사용합니다. 이미지를 만드는 방법에 대해서는 생략하겠습니다.
docker run --name pg15_1 -it -d --privileged=true --network pg-network pg_15_2 /sbin/init
docker run --name pg15_2 -it -d --privileged=true --network pg-network pg_15_2 /sbin/init
Docker 컨테이너 내부로 접속하여 네트워크를 확인한 결과는 다음과 같습니다.
Container_name | Prviate_IP |
---|---|
pg15_1 | 172.18.0.2 |
pg15_2 | 172.18.0.3 |
Docker 관련한 커맨드는 다음과 같습니다.
pg_rewind 커맨드 사용
# 172.18.0.2 서버에서 실행
$PGHOME/pg_rewind --source-server='host=172.18.0.3 port=5432 user=postgres dbname=postgres' --target-pgdata=$PGDATA -P -R
# 172.18.0.3 서버에서 실행
$PGHOME/pg_rewind --source-server='host=172.18.0.2 port=5432 user=postgres dbname=postgres' --target-pgdata=$PGDATA -P -R
부하 생성을 위한 Python 커맨드는 다음과 같습니다.
nohup python3.8 /var/lib/pgsql/test/threading_test.py &
Python threading을 이용해서 부하를 생성한 이유
Replication이 깨진 상태에서 promote된 새로운 Primary가 많은 트랜잭션이 일어나 max_wal_size를 넘는 wal파일이 쓰여질 경우 wal 파일은 덮어쓰기 되어 단순히 pg_wal파일만을 이용해서 동기화가 불가능할 것입니다.
rsync 방식을 통해 파일을 동기화하는 방식이기 때문에 이러한 상황에서 $PGDATA/base 디스크 파일 역시 동기화 될 것이기 때문에 pg_wal이 덮어쓰기된 것과 관계없이 Replication이 이어질 것입니다. 이를 확인해보고 싶었습니다. 결과는 예상한대로 정상적인 pg_rewind 후 replication이 복구되었습니다.