Database

Pgpool-II + Watchdog 클러스터 구성 (1/2) | 기본 설정

빅디 2025. 3. 22. 00:23
728x90

신규 서비스 아키텍처 구성 중 데이터베이스의 고가용성(High Availability)과 장애 대응 체계의 필요성이 대두되었습니다. 이러한 문제점을 해결하기 위해 pgpool-II(이하  pgpool)와 watchdog을 도입해 PostgreSQL 클러스터의 로드밸런싱, 장애 감지, 자동 장애 조치(Automatic Failover)를 구현했습니다.

 

pgpool은 PostgreSQL의 앞단에 위치한 미들웨어입니다. 여러 DB 인스턴스를 대상으로 커넥션 풀링, 로드밸런싱, 장애감지 및 페일오버를 수행합니다. pgpool을 단독으로 운영 시 단일 장애점(single point of failure, SPOF)이 될 수 있습니다.

 

이를 방지하기 위해 다중 pgpool을 구성해야 하는데, watchdog이 다중 pgpool 인스턴스 간의 협업과 장애감지를 가능하게 합니다.

Pgpool + Watchdog Example

요구사항

인스턴스 개수 3개
시스템 OS Rocky Linux 9
가상화 환경 KVM/QEMU
vCore 2 core
Ram 3 GB
pgpool version 4.5.5
postgresql 17
# virsh 생성 명령어
$ sudo virt-install \ --name pg1 \ --ram 3072 \ --vcpus 2 \ --disk path=/var/lib/libvirt/images/pg1.qcow2,size=20,format=qcow2 \ --os-variant rocky9 \ --network network=default \ --location Rocky-9.5-x86_64-minimal.iso \ --graphics none \ --console pty,target_type=serial \ --extra-args 'console=ttyS0,115200n8 serial'

인스턴스 IP & Virtual IP

Hostname IP
pg1 (Primary) 192.168.122.129
pg2 192.168.122.43
pg3 192.168.122.7
VIP(Leader pgpool ip) 192.168.122.199

구현

서버별 패키지 매니저 업데이트 & PostgreSQL 설치

각 주석에 실행 대상 서버를 표기해 두었습니다.

# [all servers]
sudo dnf update
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -qy module disable postgresql
sudo dnf install -y postgresql17-server
BASH

libmemcached 관련 이슈가 생길 시 명령어

# [all servers]
sudo dnf --enablerepo=crb install libmemcached-awesome libmemcached-awesome-tools libmemcached-awesome-devel -y
sudo dnf install memcached -y
sudo systemctl enable memcached
sudo systemctl start memcached
sudo dnf install -y https://www.pgpool.net/yum/rpms/4.5/redhat/rhel-9-x86_64/pgpool-II-release-4.5-1.noarch.rpm
sudo dnf install -y pgpool-II-pg17-*
BASH

아카이빙 디렉토리 생성

# [all servers]
su - postgres
mkdir /var/lib/pgsql/archivedir
BASH

pg init

# only [primary server]
/usr/pgsql-17/bin/initdb -D $PGDATA
BASH

postgresql config 수정

# postgresql.conf 편집
vi $PGDATA/postgresql.conf

# 아래 내용으로 수정
...
listen_addresses = '*'
archive_mode = on
archive_command = 'cp "%p" "/var/lib/pgsql/archivedir/%f"'
max_wal_senders = 10
max_replication_slots = 10
wal_level = replica
hot_standby = on
wal_log_hints = on
...

# 저장 후 pg 인스턴스 실행
/usr/pgsql-17/bin/pg_ctl start -D $PGDATA
BASH

pg user 생성

# primary server
psql -U postgres -p 5432

# 아래는 psql 명령어
# postgres=#
SET password_encryption = 'scram-sha-256';
CREATE ROLE pgpool WITH LOGIN;
CREATE ROLE repl WITH REPLICATION LOGIN;
\password pgpool
\password repl
\password postgres
GRANT pg_monitor TO pgpool;
BASH

pg_hba config 수정

192.168.0.0/16 서브넷에 속한 클라이언트에 대해 pgpool, postgres, repl 사용자로의 접근을 각각 허용한다.

# TYPE  DATABASE        USER            ADDRESS                 METHOD
# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             pgpool          192.168.0.0/16          scram-sha-256
host    all             postgres        192.168.0.0/16          scram-sha-256
host    replication     repl            192.168.0.0/16          scram-sha-256
BASH

SSH 접속 허용

쉘 스크립트 명령어 실행을 위해 각 인스턴스는 서로 SSH 접속이 가능해야 합니다.

따라서 다음 명령어들은 모든 인스턴스에서 실행합니다.

# ssh key 생성
su - postgres
mkdir ~/.ssh
chmod 700 ~/.ssh
cd ~/.ssh
ssh-keygen -t rsa -f id_rsa_pgpool

# ssh 연결 시 pubkey 방식으로 변경
su root
vi /etc/ssh/sshd_config

# 아래 내용으로 수정
...
PubkeyAuthentication yes
...

# 저장 후 아래 명령어 실행
systemctl restart sshd
BASH
# 방화벽 허용
sudo firewall-cmd --list-all
sudo firewall-cmd --add-service=ssh --permanent
sudo firewall-cmd --reload
BASH
# RHEL 계열 리눅스 설정
# for rocky linux 9 (SELinux)
su - postgres
restorecon -Rv ~/.ssh
BASH
su - postgres
ssh-copy-id postgres@<your-pg1-ip> # pg1
ssh-copy-id postgres@<your-pg2-ip> # pg2
ssh-copy-id postgres@<your-pg3-ip> # pg3

# 각 서버별 서로 ssh 접속이 비밀번호 없이 가능해야함.
ssh postgres@<your-pg1-ip> -i ~/.ssh/id_rsa_pgpool # pg1
ssh postgres@<your-pg2-ip> -i ~/.ssh/id_rsa_pgpool # pg2
ssh postgres@<your-pg3-ip> -i ~/.ssh/id_rsa_pgpool # pg3
BASH
# 1 -> 1, 1 -> 2, 1 -> 3
# 2 -> 1, 2 -> 2, 2 -> 3
# 3 -> 1, 3 -> 2, 3 -> 3
# 모든 노드가 서로 접속 가능해야한다.

# 아래는 2번 노드에서 1번 노드로 접속한 경우
[postgres@pg2 ~]$ ssh postgres@192.168.122.129 -i ~/.ssh/id_rsa_pgpool
Last login: Tue Jan 28 21:26:30 2025 from 192.168.122.43
[postgres@pg1 ~]$
BASH

pgpass 설정

pgpool이 postgresql 인스턴스에 접근하기 위해 사전에 인증 정보를 설정해야 합니다.

각 인스턴스 별로 설정해주어서 pgpool이 접근 가능하도록 합니다.

[all servers]
# 유저 변경
su - postgres
vi /var/lib/pgsql/.pgpass

# 아래 내용 입력 후 저장
192.168.122.129:5432:replication:repl:<your-repl-username>
192.168.122.43:5432:replication:repl:<your-repl-username>
192.168.122.7:5432:replication:repl:<your-repl-username>
192.168.122.129:5432:postgres:postgres:<your-repl-username>
192.168.122.43:5432:postgres:postgres:<your-repl-username>
192.168.122.7:5432:postgres:postgres:<your-repl-username>

# 저장 후 실행
chmod 600 /var/lib/pgsql/.pgpass
BASH

pgpool 사용 포트를 개방합니다.

[all servers]
su root

firewall-cmd --permanent --zone=public --add-service=postgresql
firewall-cmd --permanent --zone=public --add-port=9999/tcp --add-port=9898/tcp --add-port=9000/tcp  --add-port=9694/udp
firewall-cmd --reload
BASH

pgpool node id 생성

# pg1 서버
echo 0 >> /etc/pgpool-II/pgpool_node_id
cat /etc/pgpool-II/pgpool_node_id
0

# pg2 서버
echo 1 >> /etc/pgpool-II/pgpool_node_id
cat /etc/pgpool-II/pgpool_node_id
1

# pg3 서버
echo 2 >> /etc/pgpool-II/pgpool_node_id
cat /etc/pgpool-II/pgpool_node_id
2
BASH

pcp connection authentication 설정

echo 'pgpool:'`pg_md5 <your-password>` >> /etc/pgpool-II/pcp.conf
cat /etc/pgpool-II/pcp.conf
# USERID:MD5PASSWD
pgpool:6522e450d3581238b5d3c2b4373f058e
BASH

pgpool config 수정

# Primary Server
vi /etc/pgpool-II/pgpool.conf


# 아래 내용으로 수정

# 각 인스턴스(노드) 정보 입력
...
listen_addresses = '*'
pcp_listen_addresses = '*'

sr_check_user = 'pgpool'
sr_check_password = ''

health_check_period = 5
health_check_timeout = 30
health_check_user = 'pgpool'
health_check_password = ''
health_check_max_retries = 3

# - Backend Connection Settings -
backend_hostname0 = 'server1'
backend_port0 = 5432
backend_weight0 = 1
backend_data_directory0 = '/var/lib/pgsql/17/data'
backend_flag0 = 'ALLOW_TO_FAILOVER'
backend_application_name0 = 'server1'

backend_hostname1 = 'server2'
backend_port1 = 5432
backend_weight1 = 1
backend_data_directory1 = '/var/lib/pgsql/17/data'
backend_flag1 = 'ALLOW_TO_FAILOVER'
backend_application_name1 = 'server2'

backend_hostname2 = 'server3'
backend_port2 = 5432
backend_weight2 = 1
backend_data_directory2 = '/var/lib/pgsql/17/data'
backend_flag2 = 'ALLOW_TO_FAILOVER'
backend_application_name2 = 'server3'

# 예시
backend_hostname0 = '192.168.122.129'
backend_port0 = 5432
backend_weight0 = 1
backend_data_directory0 = '/var/lib/pgsql/17/data'
backend_flag0 = 'ALLOW_TO_FAILOVER'
backend_application_name0 = 'pg1'

backend_hostname1 = '192.168.122.43'
backend_port1 = 5432
backend_weight1 = 1
backend_data_directory1 = '/var/lib/pgsql/17/data'
backend_flag1 = 'ALLOW_TO_FAILOVER'
backend_application_name1 = 'pg2'

backend_hostname2 = '192.168.122.7'
backend_port2 = 5432
backend_weight2 = 1
backend_data_directory2 = '/var/lib/pgsql/17/data'
backend_flag2 = 'ALLOW_TO_FAILOVER'
...

# failover 스크립트 설정 (기본 템플릿 사용 예정)
...
vi /etc/pgpool-II/pgpool.conf

failover_command = '/etc/pgpool-II/failover.sh %d %h %p %D %m %H %M %P %r %R %N %S'
follow_primary_command = '/etc/pgpool-II/follow_primary.sh %d %h %p %D %m %H %M %P %r %R'
...

# 수정 후 저장
BASH
# 기본 제공 스크립트 예제로 failover 스크립트 생성
cp -p /etc/pgpool-II/sample_scripts/failover.sh.sample /etc/pgpool-II/failover.sh
cp -p /etc/pgpool-II/sample_scripts/follow_primary.sh.sample /etc/pgpool-II/follow_primary.sh
chown postgres:postgres /etc/pgpool-II/{failover.sh,follow_primary.sh}

# 각 스크립트 내부의 환경변수를 수정해야합니다.
[all servers]# vi /etc/pgpool-II/failover.sh
...
PGHOME=/usr/pgsql-17
...

[all servers]# vi /etc/pgpool-II/follow_primary.sh
...
PGHOME=/usr/pgsql-17
...
BASH
# pcp pass 등록
su - postgres
echo 'localhost:9898:pgpool:<your-pgpool-password>' > ~/.pcppass
chmod 600 ~/.pcppass
BASH

recovery 스크립트 설정

vi /etc/pgpool-II/pgpool.conf

# 아래 내용으로 편집
...
recovery_user = 'postgres'
recovery_password = ''
recovery_1st_stage_command = 'recovery_1st_stage'

cp -p /etc/pgpool-II/sample_scripts/recovery_1st_stage.sample /var/lib/pgsql/17/data/recovery_1st_stage
cp -p /etc/pgpool-II/sample_scripts/pgpool_remote_start.sample /var/lib/pgsql/17/data/pgpool_remote_start
chown postgres:postgres /var/lib/pgsql/17/data/{recovery_1st_stage,pgpool_remote_start}
...

# 수정 후 저장
# 이후 recovery 스크립트 환경변수를 수정해줍니다.
[server1]# vi /var/lib/pgsql/17/data/recovery_1st_stage
...
PGHOME=/usr/pgsql-17
...

[server1]# vi /var/lib/pgsql/17/data/pgpool_remote_start
...
PGHOME=/usr/pgsql-17
...
BASH

pgpool hba config

vi /etc/pgpool-II/pgpool.conf

# 아래 내용으로 수정
...
use_watchdog = on
...

# 수정 후 저장

vi /etc/sudoers
# 아래 내용으로 수정
...
## Allow root to run any commands anywhere
root    ALL=(ALL)       ALL
postgres ALL=NOPASSWD: /sbin/ip
postgres ALL=NOPASSWD: /usr/sbin/arping
...

# 수정 후 저장
BASH

pgpool끼리의 등록정보를 입력해서 watchdog이 알 수 있게 합니다.

인스턴스의 네트워크명을 확인하고 enp0s8 부분을 수정하세요. * 각 인스턴스는 동일한 네트워크에 속해야 합니다.

vi /etc/pgpool-II/pgpool.conf

# 아래 내용으로 수정
...
if_cmd_path = '/sbin'
arping_path = '/usr/sbin'

# ip -a 명령어를 통해 현재 네트워크명 파악 후 'enp0s8' 부분 수정
if_up_cmd = '/usr/bin/sudo /sbin/ip addr add $_IP_$/16 dev enp0s8 label enp0s8:0'
if_down_cmd = '/usr/bin/sudo /sbin/ip addr del $_IP_$/16 dev enp0s8'
arping_cmd = '/usr/bin/sudo /usr/sbin/arping -U $_IP_$ -w 1 -I enp0s8'

hostname0 = 'server1'
wd_port0 = 9000
pgpool_port0 = 9999

hostname1 = 'server2'
wd_port1 = 9000
pgpool_port1 = 9999

hostname2 = 'server3'
wd_port2 = 9000
pgpool_port2 = 9999
...

# 수정 후 저장
BASH

Primary 서버의 pgpool 설정 정보 다른 인스턴스에 덮어쓰기

# 설정된 pgpool.conf pool_hba.conf escalation.sh을 원격으로 덮어쓴다.
# Primary Server
scp -p /etc/pgpool-II/pgpool.conf postgres@192.168.122.7:/etc/pgpool-II/pgpool.conf
scp -p /etc/pgpool-II/pgpool.conf postgres@192.168.122.43:/etc/pgpool-II/pgpool.conf

scp -p /etc/pgpool-II/pgpool.conf postgres@192.168.122.7:/etc/pgpool-II/pool_hba.conf
scp -p /etc/pgpool-II/pgpool.conf postgres@192.168.122.43:/etc/pgpool-II/pool_hba.conf

scp -p /etc/pgpool-II/pgpool.conf postgres@192.168.122.7:/etc/pgpool-II/escalation.sh
scp -p /etc/pgpool-II/pgpool.conf postgres@192.168.122.43:/etc/pgpool-II/escalation.sh
BASH

 

길었던 기본 설정이 마무리 되었습니다.

다음 포스팅에서는 클러스터를 실행해 보고 failover 테스트를 진행하겠습니다.

 

참고

- https://www.pgpool.net/docs/45/en/html/example-cluster.html