간마늘작업소

[PGSQL] PostgreSQL 이중화 구성 – Repmgr 이용 본문

Linux/31.PostgreSQL

[PGSQL] PostgreSQL 이중화 구성 – Repmgr 이용

간마늘 2022. 12. 21. 13:15

0.개요

  1. PostgreSQL 이중화(HA) 구성 방안 중 Repmgr을 이용한 구성 진행.
  2. PostgreSQL의 이중화 솔루션 중 가장 보편적인 방법은 PGPool-II을 이용하는 것이 있음.
    • 다만, 공식 문서 미비 등으로 사용하는데 상당히 불친절함.
  3. 따라서, PostgreSQL 기반 엔터프라이즈 DBMS 개발사인 EDB에서 제작한 Repmgr을 이용.

 

1.기본 정보

  • OS 방화벽(Firewalld, iptables)과 SELinux는 비활성화 상태로 설정.
  • DB Node 정보
    1. 192.168.0.16 - DB Node 1, Hostname : ha001
    2. 192.168.0.17 - DB Node 2, Hostname : ha002
    3. 192.168.0.15 - HA VIP
  • PostgreSQL는 PostgreSQL 14로 진행하며 이미 설치되어 있는 것으로 가정.
    1. 설치
    2. 복제 구성
  • 하단 명령어 중 적색 글씨는 DB Node 1에만 적용.
  • 하단 명령어 중 청색 글씨는 DB Node 2에만 적용.

 

2.사전 환경 구성

2.1.Hosts 파일 수정

2.1.1.Hosts 파일 수정

vi /etc/hosts
192.168.0.16 ha001
192.168.0.17 ha002
  • DB Node 정보 추가 후 저장.

2.2.SSH Key 생성

su - postgres

cd ~/.ssh 
mkdir -p ~/.ssh
cd ~/.ssh
ssh-keygen -t rsa
  • 서버의 DB 운영 계정에 대한 SSH Key 생성.
    • 기본 값 : postgres
cat id_rsa.pub >> authorized_keys
  • SSH Key 변환.
cat authorized_keys
  • DB Node 1DB Node 2에서 실행한 값이 다름.
  • 이 두 값을 복사해서 하나로 합친다음 다시 넣어줘야함.
vi authorized_keys
ssh-rsa (cat authorized_keys 명령어로 확인한 엄청 긴 KEY 값) postgres@ha001
ssh-rsa (cat authorized_keys 명령어로 확인한 엄청 긴 KEY 값) postgres@ha002
  • 양 Node에 동일하게 저장.
ssh ha001
ssh ha002
  • 계정 비밀번호 없이 로그인이 되는지 확인.
  • 확인 후 root 계정으로 전환.

2.3.Sudoers 권한 부여

vi /etc/sudoers
(전략)
postgres ALL=NOPASSWD: /sbin/ip, \
                     /usr/bin/systemctl start repmgr-14.service, \
                     /usr/bin/systemctl stop repmgr-14.service
  • sudoers 파일 최하단에 내용 추가.
    • 서버의 DB 운영 계정이 IP 설정을 root 권한 없이 수정할 수 있도록 하는 설정.
      • Failover 시 VIP 할당을 위해서 설정해야함.
    • 서버의 DB 운영 계정이 Repmgr을 root 권한 없이 기동/종료할 수 있도록 하는 설정.

2.4.Repmgr 이용을 위한 PostgreSQL 설정

su - postgres
pg_ctl restart -mf
psql
  • PostgreSQL 14 기동.
  • PostgreSQL 14 Shell 접속.
\du
  • 현재 생성되어 있는 계정 및 Role 정보 확인.
    • PostgreSQL 12 이상에서 해당 포스트대로 따라했다면 replication 계정이 존재함.
    • 해당 계정을 그대로 이용함.
CREATE ROLE replication WITH replication PASSWORD 'password' LOGIN;
  • 계정이 존재하지 않거나 다른 계정을 원할 경우 위 SQL 문으로 생성.
    • 계정 : replication, PW : password (PW는 선택 사항.)
ALTER USER replication WITH superuser;
CREATE database replication OWNER replication;
  • Repmgr 계정에 대한 DB 슈퍼유저 권한 부여.
  • Repmgr에서 사용할 파라미터 DB 생성 및 Repmgr 계정을 사용자로 지정.
\l+ replication
\du
  • Repmgr 파라미터 DB 정보 확인.
  • 현재 생성되어 있는 계정 및 Role 정보에서 Repmgr 계정에 슈퍼유저와 복제 권한이 부여되어 있는지 확인.
cd /data/pgdata/
vi postgresql.conf
(전략)
shared_preload_libraries='repmgr'
  • postgresql.conf 파일 하단에 내용 추가.
vi pg_hba.conf
(전략)
# IPv4 local connections:
(중략)
host    all             all             192.168.0.16/32         trust
host    all             all             192.168.0.17/32         trust
host    all             all             0.0.0.0/0               md5
(후략)
  • pg_hba.conf 파일 하단에 위치한 '# IPv4 local connections:' 부분에 내용 추가 후 저장.
  • 주의사항
    1. 0.0.0.0/0 설정이 있다면 꼭 그 위의 행에 추가할 것.
    2. 설정을 위에서부터 읽기 때문에 0.0.0.0/0 설정 아래에 추가한다면 비밀번호를 물어봄.
      • 0.0.0.0/0이 md5나 scram-sha-256가 아닌 trust로 되어있다면 상관이 없음.
        • md5, scram-sha-256 : 비밀번호 암호화 방식. 로그인시 비밀번호 확인함.
        • trust : 로그인시 비밀번호 확인하지 않음.
      • 다만, 이렇게 되면 모든 외부 환경에서 비밀번호 없이 접속할 수 있으므로 0.0.0.0/0은 사실상 trust로 설정하지 않음.
    3. Repmgr은 각 Node 간 비밀번호 없이 접속할 수 있도록 요구함.

 

3.Repmgr 설치

  • Repmgr 버전에 따른 지원 PostgreSQL 버전은 다음과 같음.

yum install repmgr_14*
cd /etc/repmgr/14
mkdir old
mv repmgr.conf old/repmgr.conf_first
vi repmgr.conf
node_id=1
node_name='ha001'

conninfo='host=ha001 dbname=replication user=replication connect_timeout=2'
data_directory='/data/pgdata'

service_start_command = '/usr/pgsql-14/repmgr/repmgr-pg_ctl.sh start'
service_stop_command = '/usr/pgsql-14/repmgr/repmgr-pg_ctl.sh stop'
service_restart_command = '/usr/pgsql-14/repmgr/repmgr-pg_ctl.sh restart'
service_reload_command = '/usr/pgsql-14/repmgr/repmgr-pg_ctl.sh reload'

failover=automatic
promote_command='/usr/pgsql-14/bin/repmgr standby promote'
follow_command='/usr/pgsql-14/bin/repmgr standby follow --upstream-node-id=%n'

repmgrd_service_start_command = 'sudo systemctl start repmgr-14.service'
repmgrd_service_stop_command = 'sudo systemctl stop repmgr-14.service'
log_file='/usr/pgsql-14/repmgr/repmgr.log'

reconnect_attempts=3
reconnect_interval=3

#monitoring_history=yes
#monitor_interval_secs=10

event_notification_command='/usr/pgsql-14/repmgr/promote.sh %n %e'
event_notifications='standby_promote'
  • Repmgr 설정
    • node_id : Repmgr에서 사용하는 Node의 고유 ID 값. Node들 끼리 겹치면 안됨.
    • node_name : DB Node 이름. 혼동 방지를 위해 Hostname을 권고.
    • conninfo :
      • host : DB Node의 Hostname.
      • dbname : Repmgr이 사용할 파라미터 DB.
      • user : Repmgr 계정.
    • data_directory : DB 디렉토리 위치.
    • service_*_command : PostgreSQL 실행/중지/재시작/리로드 명령어.
    • log_file : log 파일 생성 위치.
    • event_notification_command : Failover 발생시 실행할 명령어.

 

4.Repmgr 스크립트 작성

su - postgres
mkdir -p /usr/pgsql-14/repmgr
cd /usr/pgsql-14/repmgr
  • Repmgr 동작 중 사용할 스크립트들을 배치할 디렉토리 생성.
vi repmgr-pg_ctl.sh
#!/bin/bash

REPMGR_USER=postgres
PGUSER_HOME=`cat /etc/passwd | grep ${REPMGR_USER} | cut -d: -f6`
. ${PGUSER_HOME}/*_env.sh

UNAME=`id -u -n`

while true
do
        case "$1" in
                start)
                        pg_ctl start
                        exit 0
                        ;;
                restart)
                        pg_ctl restart -mf
                        exit 0
                        ;;
                stop)
                        pg_ctl stop -mf
                        exit 0
                        ;;
                reload)
                        pg_ctl reload
                        exit 0
                        ;;
                *)
                        echo "잘못된 명령입니다."
                        exit 0
                        ;;
        esac
done
  • repmgr-pg_ctl.sh : root 계정으로 PostgreSQL을 실행할 때 사용하는 스크립트.
    • repmgr.conf에 지정되어 있음.
vi promote.sh
#!/bin/bash

UP_NODEID=$1
EVENT_TYPE=$2

if [ ${EVENT_TYPE} == 'standby_promote' ]; then
    declare -A servers
    if [ ${UP_NODEID} == 1 ] ; then
        UP_NODENUM=1
        DOWN_NODENUM=2
    else
        UP_NODENUM=2
        DOWN_NODENUM=1
    fi
    SERVERS[2]=ha002
    SERVERS[1]=ha001
    sudo /sbin/ip addr add 192.168.0.15/32 dev eth0:1
    SSH_CONNETION=`ssh -o ConnectTimeout=5 postgres@${SERVERS[${DOWN_NODENUM}]} echo ok 2>&1`
    while [[ "$SSH_CONNETION" != "ok" ]]
    do
        sleep 2
        echo "Failed node ${SERVERS[${DOWN_NODENUM}]} is not reachable" >> /usr/pgsql-14/repmgr/repmgr.log
        unset SSH_CONNETION
        SSH_CONNETION=`ssh -o ConnectTimeout=5 postgres@${SERVERS[${DOWN_NODENUM}]} echo ok 2>&1`
    done
    bash /usr/pgsql-14/repmgr/session_kill.sh &
    sleep 1
    ssh postgres@${SERVERS[${DOWN_NODENUM}]} '/usr/pgsql-14/repmgr/failover-restore.sh'
fi
  • promote.sh : Failover 발생시 Repmgr 프로세스가 자동으로 실행할 스크립트.
    • 기존 Standby Node에서 실행됨.
      1. Repmgr이 Standby Node를 신규 Primary Node로 변경했으므로 VIP를 추가함.
      2. 장애 발생 Node를 SSH 접속 시도를 하며 2초 간격으로 상태를 체크.
      3. 장애 발생 Node의 상태가 확인되면  SSH로 접속하여 신규 Standby Node로 전환하는 스크립트 실행.
    • '${UP_NODEID} == 1'에서 1은 DB Node 1의 node_id를 의미.
      • node_id 값을 1, 2가 아닌 다른 숫자로 했다면 여기에 맞춰서 UP_NODENUM 값과 DOWN_NODENUM 값을 변경해줘야함.
      • DB Node 2에서 스크립트를 작성한다 하더라도 기준은 DB Node 1의 node_id에 맞춰서 작성.
        • 해당 부분은 DB Node 1DB Node 2의 내용이 같아야함.
vi failover-restore.sh
#!/bin/bash

UNAME=`id -u -n`
REPMGR_USER=postgres
PGDATA=/data/pgdata

PGUSER_HOME=`cat /etc/passwd | grep ${REPMGR_USER} | cut -d: -f6`
. ${PGUSER_HOME}/*_env.sh

if [ e$UNAME != "e$REPMGR_USER" ]
then
    echo "Use ${REPMGR_USER} account to start Script..."
    exit;
fi

sleep 1
sudo /sbin/ip addr del 192.168.0.15/32 dev eth0:1

pg_ctl stop -mf

sleep 2
rm -rf ${PGDATA}
ls -l ${PGDATA}/..

sleep 2
repmgr -h @@상대 노드@@ -U replication -d replication standby clone

sleep 2
pg_ctl start

sleep 1
repmgr standby register -F
sudo systemctl stop repmgr-14.service

sleep 1
sudo systemctl start repmgr-14.service
  • failover-restore.sh : Failover 발생시 장애 발생 Node를 Standby Node로 전환해주는 스크립트.
    • 신규 Primary Node가 SSH로 장애 발생 Node로 접속해 실행.
      1. 등록되어 있던 VIP 해제.
      2. 기존 PostgreSQL DB 디렉토리 제거
      3. Repmgr을 이용하여 Primary Node에서 새롭게 DB 디렉토리 다운로드.
        • @@상대 노드@@에다 자기 자신이 아닌 상대 Node의 IP 혹은 Hostname 등록.
vi session_kill.sh
#!/bin/bash

while true
do
    PGSQL_STATUS=`/usr/pgsql-14/bin/repmgr cluster show | grep @@상대 노드@@ | awk -F'|' '{print $3}' | awk -F' ' '{print $1}'`
    if [ "${PGSQL_STATUS}" == "" ]; then
        unset PGSQL_STATUS
        sleep 5
    else
        SSH_PID=`ps -ef | grep -v grep | grep ssh | grep failover | awk '{print $2}'`
        if [ "${SSH_PID}" != "" ]; then
            SHUTDOWN_PID=`ps -ef | grep -v grep | grep repmgr | grep 'standby promote' | awk '{print $2}'`			
            sleep 30
            kill -9 ${SSH_PID} ${SHUTDOWN_PID}
            exit 0
        else
            unset SSH_PID
            sleep 5
        fi
    fi
done
  • session_kill.sh : 필자가 구성하는 과정에서 promote.sh 실행 후 장애 발생 Node로 SSH 접속을 했고 장애 발생 Node가 정상적으로 신규 Standby Node가 되었음에도 불구하고 SSH 접속 프로세스가 정리되지 않아서 이를 해결하기 위해 작성한 스크립트.
    • 장애 발생 Node가 정상적으로 신규 Standby Node가 된 것을 백그라운드에서 5초 간 체크.
    • 신규 Standby Node를 확인하면 30초 이후 SSH 접속 프로세스와 Repmer의 Failover 프로세스를 정리.
    • @@상대 노드@@에다 자기 자신이 아닌 상대 Node의 IP 혹은 Hostname 등록.
chmod +x *.sh
  • 작성한 4개의 스크립트에 대한 실행 권한 부여.

 

5.Repmgr 실행

  • Primary Node : DB Node 1
  • Standby Node : DB Node 2

5.1.Primary Node

su - postgres
pg_ctl restart -mf
ps -ef | grep -v grep | grep -v ps | grep -v su | grep -v bash | grep postgres
  • PostgreSQL 재시작.
  • PostgreSQL 프로세스 확인.
repmgr primary register
  • Repmgr Cluster에 Primary Node로 등록.
repmgr cluster show
sudo systemctl start repmgr-14.service
  • Repmgr Cluster 확인.
  • Repmgr 프로세스 기동.
sudo /sbin/ip addr add 192.168.0.15/32 dev eth0:1
ip addr
  • VIP 등록.
  • VIP 등록 확인.

5.2.Standby Node

su - postgres
cd /data
rm -rf pgdata
  • 기존 생성되어 있던 DB 디렉토리 삭제.
repmgr -h ha001 -U replication -d replication standby clone
  • Repmgr을 이용하여 Primary Node에서 새롭게 DB 디렉토리 다운로드.
pg_ctl start
ps -ef | grep -v grep | grep -v ps | grep -v su | grep -v bash | grep postgres
  • PostgreSQL 시작.
  • PostgreSQL 프로세스 확인.
repmgr standby register -F
  • Repmgr Cluster에 Standby Node로 등록.
repmgr cluster show
sudo systemctl start repmgr-14.service
  • Repmgr Cluster 확인.
  • Repmgr 프로세스 기동.

 

6.Failover Test 시나리오

6.1.DB 서비스 종료

pg_ctl stop -mf
repmgr cluster show
  • DB Node 1의 PostgreSQL 서비스를 종료.
  • 이후 repmgr cluster show로 Repmgr Cluster 상태 확인.
    • DB Node 2가 Primary Node가 되고 DB Node 1이 Standby Node가 되면 정상.
  • VIP 전환 확인.

6.2.서버 재부팅

  • PostgreSQL 서비스를 종료하지 않고 Primary Node의 서버를 재부팅.
  • 이후 Standby Node에서 repmgr cluster show로 Repmgr Cluster 상태 확인.
    • 기존 Standby Node가 Primary Node가 되고 장애 발생 Node가 재부팅된 이후에 Standby Node가 되면 정상.
  • VIP 전환 확인.

6.3.Primary - Standby 전환

su - postgres
repmgr standby switchover
  • Standby Node에서 명령어 실행.
    • 기존 Standby Node가 Primary Node가 되고 Primary Node가 Standby Node가 되면 정상.
  • VIP 전환 확인.

 

7.참고 문서

Comments