본문 바로가기

Database/PostgreSQL

PostgreSQL HA 구성 -1. Streaming Replication

반응형

데이터베이스를 실제 서비스에서 운용하게 될 때 가장 중요한 요소가 고가용성(Hign Ability)이다. 이번 Post에서는 HA구성시 중요한 요소중에 하나인 DB Replication을 Postgresql에서 구현한 사례를 설명하려고 한다.

HA (Database High Ability)

DB HA구성, 또는 DB 이중화라고 불리는 이 작업은 복수의 DB Server를 준비한 상태에서 Active-Active 또는 Active-Standby 로 동작시키면서 이중 일부의 Server에 문제가 발생하더라도 전체 서비스가 멈추는 사고를 방지하기 위해서 이루어진다. 여기에 포함되는 개념이 아래 2가지 이다.

  • Data는 전체 DB Cluster에 반영되야 한다는 것이다. DB Server가 몇 개이건 모두 공통된 최신 Data를 가져야 한다는 것이고 이 것은 DB Replication 을 통해 이루어진다.
  • DB에 전달되는 각종 Request들이 DB Cluster 내에 존재하는 여러 DB Server에 골고루 전달되서 실행되게 하는 분산 환경이 구성되어야 하고 이 것을 Load Balance라고 한다.
  • Active-Standby로 구성된 DB Cluster에서 Active로 있던 Primary DB Server에 문제가 생겼을 때 Standby Server들 중 1대가 Primary DB Server의 역할을 해야한다. 이것을 Failover 라 한다.

이외에도 DB에 전달되는 각종 Request들을 DB Server 1대가 처리하게 되면 처리 속도가 늦어지거나, 심한 경우에는 DB에 장애가 발생할 수도 있기 때문에 이런 Request들을 DB Cluster 전체가 나누어서 처리하게 됨으로써 전체적인 DB 성능 향상과 안정화를 가져올 수 있다.

Replication in PostgreSQL

  PostgreSQL에서는 여러가지의 Replication 방법을 제안하고 있다.(PostgreSQL Replication 방법) 이 포스트에서 적용하려는 Streming Replication은 WAL(Write Ahead Log)를 전달해서 Replication을 Transaction Log Shipping 방법 중에 하나이다. WAL을 짧게 요약해서 설명하면 Database 변경 사항만을 저장한 Log를 말하며, Transanction Log Shping을 이용한 Replication은 양쪽 DB의 원본이 동일하게 출발하였다면 Primary DB Server에서 발생하는 변경사항을 기록한 WAL 파일들을 다른 DB Server에 순서에 맞춰 적용시키면 동일한 DB가 된다는 개념을 바탕으로 이루어 졌다.

   Postgresql 9.0부터 소개된 Streaming Replication은 이러한 WAL Log를 거의 실시간성으로 전달함으로써(물론 DB 서버 사이에는 Network에 문제가 없어야 한다.) 별다른 지연없이 모든 DB가 동일한 값을 저장할 수 있게 하는 것이다. 아래 내용이 두 PostgreSQL DB Server를 Streming Replication으로 Clustering하는 방법을 정리한 내용이다.


Streming Replication 환경 설정

Streming Replication을 위해 사용될 HW 정보는 다음과 같다.

구분

 HW Name

 IP

역할







 HW1

 ha-test-1

 16.8.35.227

 Primary Database (Active)







 HW2

 ha-test-2

 16.8.35.228

 Backup Database (Stand-by)








그리고 아래 이어지는 설정 순서에 따라 Streaming Replication을 설정했다. Postgrsql Wiki 자료(*)를 참조했는데 이 자료가 9.0을 기준으로 만들어진 내용이라 이후 버전에서 추가된 내용은 조금 누락되어 있다. (Replication Slot에 관련된 내용)

Step 1. Replication(복제)을 위한 user 생성

Replication을 담당할 사용자 계정을 추가한다. 사용자 명은  'replication'으로 하고 Password는 'password'로 했다. 사용자 계정 추가는 'postgres' user로 변경한 후에 실행하는 것을 권장한다. 이전과 같이 psql 실행 후 Command를 통해 user를 추가하였다.

...
dwarf=# \c postgres postgres
접속정보: 데이터베이스="postgres", 사용자="postgres".
...
postgres=# CREATE ROLE replication WITH REPLICATION PASSWORD 'password' LOGIN;
CREATE ROLE
...
postgres=# \du
                                   롤 목록
   롤 이름   |                       속성                       | 소속 그룹: 
-------------+--------------------------------------------------+------------
 dwarf        | 슈퍼유저, 롤 만들기, DB 만들기                   | {}
 postgres    | 슈퍼유저, 롤 만들기, DB 만들기, 복제, Bypass RLS | {}
 replication | 복제                                             | {}
...

Step 2. postgresql.conf 수정 (Master Server)

여기서 제일 중요한 설정 값은 listen_address 인데 이 내용은 'PostgreSQL 설정 변경 및 실행 (RHEL7 환경)'에서 이미 설정을 했다. (접속에 별다른 제한을 두지 않게 설정함) 다음으로 설정할 부분은 'WRITE AHEAD LOG' part의 wal_level parameter인데, hot_standby나 logical을 사용해야 replication이 가능한  WAL 이 만들어지는데 logical을 사용하면 Standby Server가 많아질 때 성능이 느려질 수 있다고 한다. 필자는 hot_standby를 사용했다.

...
#------------------------------------------------------------------------------
# WRITE AHEAD LOG
#------------------------------------------------------------------------------

# - Settings -
wal_level = hot_standby 
...

다음은 'REPLICATION' block을 수정해야 한다. 제일 먼저 max_wal_senders 값을 조정해야 하는데 다음 3가지 사항을 고려해야 한다.

  • Standby Server의 수 보다 커야 한다. 특히, 연결이 갑작스럽게 끊긴 후 재접속 되는 경우 기존 연결이 타임아웃될 때까지 기존의 Connection Thread가 남아있기 때문에 Standby Server 수보다 약간 더 크게 설정하는 것을 권장한다.
  • WAL 전송을 위한 connection도 max_connections에 포함되기 때문에 max_connections > max_wal_senders 여야 한다.
  • 이 값이 0이면 Replication이 Disable 된다는 의미이다.

필자는 Standby server가 1대인 상태에서 이 값을 2로 설정하였다.


그리고 이제부터는 조금 어려운 부분이다.(필자도 100% 이해 했는 지 확신이 안든다.) 참고하고 있는 Postgresql wiki는 Postgresql 9.0을 기반으로 작성되었고 여기에서는 WAL 보관(정확한 Naming을 모르겠다.)과 WAL Archiving을 같이 사용했고, 9.4부터 Replication Slot이라는 등장해서 기존 방법보다 좀 더 쉽게 Replication 설정을 할 수 있게 하였다. 두 방식의 특징은 다음과 같다. (**)


 

 WAL 보관 (WAL Achiving 포함)

 Replication Slot

 설명

Standby Server로 전달할 WAL을 pg_xlog에 wal_keep_segments으로 설정한 만큼 보관한다.Physical Slot을 만들어서 Standby Server의 Replication State에 따라 필요한 만큼 WAL을 보관한다.

 장점

 -

설정이 쉽다.

 단점

Standby Server가 Primary Server와의 연결이 오래 끊겼을 때, Primary Server가 보관하고 있던 WAL segment를 삭제하고, Standby Server가 재접속 이 WAL segment를 사용하려 했을 때 Replication에 오류가 생기면서 중단된다. 이 경우 Standby Server를 다시 처음부터 설정하지 않으면 안된다. 이를 막기 위해서는 wal_keep_segments값을 충분히 설정해야 하는데, '충분히'를 만족시키는 명시적인 조건이 없다.

Standby Server가 Primary Server와의 연결이 오래 끊겼을 때, WAL을 보관하는 pg_xlog가 계속 커지게 되고 이로 인해 전체 시스템이 문제를 야기할 수도 있다. (경우에 따라 전체 System의 이상 상태의 Trigger로 활용할 수 있다.)



필자는 설정이 쉬운 Replication Slot을 이용했으며 이를 위해 max_replication_slots을 2로 하였다. (실제 사용은 1개인데 향후 대비 차원에서 2로 했다.) 'REPLICATION' block 수정 사항을 요약하면 다음과 같다.

...
#------------------------------------------------------------------------------
# REPLICATION
#------------------------------------------------------------------------------

# - Sending Server(s) -

# Set these on the master and on any standby that will send replication data.

max_wal_senders = 2             # max number of walsender processes
max_replication_slots = 2       # max number of replication slots
...

Step 3. Replication Slot 생성 (Master Server)

위에서 언급한 Replication Slot을 생성해야 하는데, 이를 위해서 Step 2에서 변경된 postgresql.conf가 반영된 상태에서 PostgreSQL이 재시작 되야한다. 재시작이 안된 경우에는 아래와 같이 오류가 발생한다. Slot의 생성과 결과 확인도 아래를 참고하면 된다. Slot 이름에 별다른 제약은 없고 필자는 'repl_slot_01'로 설정하였다.

...
postgres=# SELECT * FROM pg_create_physical_replication_slot('repl_slot_01');
오류:  복제 슬롯은 max_replication_slots > 0 상태에서 사용될 수 있습니다.
postgres=# \q
...
[brown@ha-test-1 /]# sudo systemctl restart postgresql-9.6.service 
...
[brown@ha-test-1 /]$ psql -h localhost -d dwarf
psql (9.6.10)
도움말을 보려면 "help"를 입력하십시오.

dwarf=# \c postgres postgres
접속정보: 데이터베이스="postgres", 사용자="postgres".
postgres=# SELECT * FROM pg_create_physical_replication_slot('repl_slot_01');
  slot_name   | xlog_position 
--------------+---------------
 repl_slot_01 | 
(1개 행)
...

Step 4. pg_hba.conf 수정 (Master Server)

Step1에서 만든 'replication' user가 복제할 수 있게 권한을 부여한다. 아래 내용을 pg_hba.conf 파일에 추가한 후 Postgresql을 재시작한다. (16.8.35.x 대역에서 오는 접속은 md5로 인증해서 허가하는 설정이다.)

...
# replication privilege.
host    replication     replication      16.8.35.0/24            md5  
...

Step 5. Standby Server 설정

HW2에 동일한 방법으로 PostgreSQL 설치한 후에 /var/lib/pgsql/9.6/data에 Primary Server의 현재 상태를 그대로 backup 한다. base backup을 하는 데 Wiki에서는 2가지 방법을 알려주고 있는데 필자는 pg_basebackup을 이용했다. 아래를 참고하면 된다. (만약 Standby Server가 Active 상태면 PostgreSQL Service를 중지해야한다.)

...
[brown@ha-test-2 /]$ su postgres
...
[postgres@ha-test-2 /]$ rm -r -f /var/lib/pgsql/9.6/data
...
[postgres@ha-test-2 /]$ pg_basebackup -h 16.8.35.227 -D /var/lib/pgsql/9.6/data -U replication -P -v -X stream
Password: 
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
transaction log start point: 0/2000028 on timeline 1
pg_basebackup: starting background WAL receiver
30431/30431 kB (100%), 1/1 tablespace                                         
transaction log end point: 0/2000130
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: base backup completed
...
[postgres@ha-test-2 /]$ cd /var/lib/pgsql/9.6/data
Directory 접근 및 pg_basebackup 실행 권한 문제 때문에 사용자를  postgres로 변경 후에, 혹시 기존에 남아있는 data directory가 있으면 삭제하고 그 후에 16.8.35.227에 있는 Primary DB를 복제했다. /var/lib/pgsql/9.6/data 에 가면 Primary와 마찬가지로 postgres.conf가 있고, 이 파일의 내용을 Standby Server에 맞게 수정해야 한다. 'REPLICATION' block의 'Standby Servers' 부분에서 아래 두 설정만 'on'으로 변경한다. 'hot_standby'는 StandBy Server를 Read Only로 동작하게 설정하고,  'hot_standby_feedback'은 Replication 동작 시에 Query Conflict를 막는 목적이다.
...
#------------------------------------------------------------------------------
# REPLICATION
#------------------------------------------------------------------------------
...
# - Standby Servers -
hot_standby = on                       
hot_standby_feedback = on    
...

Step 6. recovery.conf 만들기 (Standby Server)

Standby Server에서는 postgres.conf가 있는 디렉토리에 recovery.conf라는 별개의 파일을 만들어야 한다. Boiler-template Code가 없는 관계로 아래 사항을 따로 입력해 주면 된다. standby_mode는 이 서버가 Standby Server라는 것을 가리키는 것이고, primary_conninfo는 Master Server의 접속정보 이다. (복제 전용 사용자로 접속해야 한다.) Step 2 에 언급한 대로 Replication Slot을 사용하기 때문에 Step 3에서 추가한 Slot Name을 primary_slot_name의 값으로 설정한다.(WAL Achiving 방식을 사용하는 경우 restore_command 값을 설정해야 한다.) 마지막으로 trigger_file은 뒤에 failover를 대비해서 설정한 값으로 Standby Server에서 해당 경로에 파일 존재를 확인했을 때 Streaming Replication을 종료하고, Master DB 역할을 하게 된다.

...
standby_mode='on'
primary_conninfo='host=16.8.35.227 port=5432 user=replication password=password'
primary_slot_name='repl_slot_01'
trigger_file='/var/lib/pgsql/9.6/data/failover_trigger'
...


Step 6까지 작업하면 Streaming Replication을 위한 설정은 끝났다. Standby 서버를 Start 시키면 Streaming Replication을 시작하게 된다.


Streaming Replication 동작 확인

Standby Server의 PostgreSQL Service를 가동 시키면 위에서 정리한 설정 값에 의해 Straming Replication 이 동작하게 된다. 동작 여부를 몇 가지 방법으로 확인할 수 있다. 첫째 아래와 같이 Master Server의 pg_stat_replication view를 확인하는 방법이 있다.

...
postgres=# SELECT * FROM pg_stat_replication;
 pid  | usesysid |   usename   | application_name | client_addr | client_hostname | client_port |         backend_start         | backend_xmin |   state   | sent_location | write_location | flush_location | replay_location | sync_priority | sync_state 
------+----------+-------------+------------------+-------------+-----------------+-------------+-------------------------------+--------------+-----------+---------------+----------------+----------------+-----------------+---------------+------------
 9089 |    16393 | replication | walreceiver      | 16.8.35.228 |                 |       40272 | 2018-09-27 16:19:22.933584+09 |              | streaming | 0/303AD18     | 0/303AD18      | 0/303AD18      | 0/303AD18       |             0 | async
(1개 행)
...

두 번째로 systemctl로 service status를 조회할 때, Master Server에서는 wal sender process가 Standby Server에서는 wal receiver process를 조회할 수 있다. 아래는 Master Server에서 조회한 내용이다. 제일 마지막에 wal sender process를 확인할 수 있다.

...
[root@ha-test-1 /]# systemctl status postgresql-9.6.service 
● postgresql-9.6.service - PostgreSQL 9.6 database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql-9.6.service; enabled; vendor preset: disabled)
   Active: active (running) since 금 2018-09-26 22:49:54 KST; 3 days ago
     Docs: https://www.postgresql.org/docs/9.6/static/
  Process: 1800 ExecStartPre=/usr/pgsql-9.6/bin/postgresql96-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
 Main PID: 1806 (postmaster)
   CGroup: /system.slice/postgresql-9.6.service
           ├─1806 /usr/pgsql-9.6/bin/postmaster -D /var/lib/pgsql/9.6/data/
           ├─1808 postgres: logger process   
           ├─1810 postgres: checkpointer process   
           ├─1811 postgres: writer process   
           ├─1812 postgres: wal writer process   
           ├─1813 postgres: autovacuum launcher process   
           ├─1814 postgres: stats collector process   
           ├─1863 postgres: postgres postgres ::1(59616) idle
           └─9089 postgres: wal sender process replication 16.8.35.228(40272) streaming 0/303AC38
...

마지막으로, Standby Server에서 log를 통해 Streaming Replication 동작 여부를 확인할 수도 있다. Log 파일의 기본 설정은 요일 별로 만들고, 해당 요일에 맞는 Log 내용을 확인하면 아래와 같이 'started straming WAL' 이라는 메시지를 확인할 수 있다

...
[brown@ha-test-2 /]$ tail -f ./var/lib/pgsql/9.6/data/pg_log/postgresql-Thu.log 
< 2018-09-27 16:19:21.401 KST > LOG:  database system was interrupted; last known up at 2018-09-28 23:12:00 KST
< 2018-09-27 16:19:22.482 KST > LOG:  entering standby mode
< 2018-09-27 16:19:22.537 KST > LOG:  redo starts at 0/2000028
< 2018-09-27 16:19:22.570 KST > LOG:  consistent recovery state reached at 0/2000130
< 2018-09-27 16:19:22.571 KST > LOG:  database system is ready to accept read only connections
< 2018-09-27 16:19:22.610 KST > LOG:  started streaming WAL from primary at 0/3000000 on timeline 1
...

필자는 좀 다양한 상황에 쳐해 있어서 여러가지 방법으로 Replication 동작을 확인하게 됐는데, 일반적으로는 하나만 사용해도 확인이 가능하다.


Test

Primary Server에 'dwarf' Table에 Record 하나를 Insert 해서 Streaming Replication 동작이 잘 이루어지는 지 확인해 보았다. 아래와 같이 아무 Record가 없을 때 1개의  Record를 추가한 후에 확인해 보았다.

...
postgres=# \c dwarf brown
접속정보: 데이터베이스="dwarf", 사용자="brown".
dwarf=# select * from public.star ;
 id | name | class | age | radius | lum | magnt 
----+------+-------+-----+--------+-----+-------
(0개 행)

dwarf=# INSERT INTO public.star(id, name, class, age, radius, lum, magnt)  VALUES(1, 'Sun', 'G2V', 4500, 1391, 5, 1);
INSERT 0 1
dwarf=# select * from star;
 id | name | class | age  | radius | lum | magnt 
----+------+-------+------+--------+-----+-------
  1 | Sun  | G2V   | 4500 |   1391 |   5 |     1
(1개 행)
...

INSERT 명령이 실행되고 나서 status를 확인하면 Key값이 '0/303AC38'에서 '0/303B1E0'로 변한 것을 확인할 수 있다.

...
[root@ha-test-1 /]# systemctl status postgresql-9.6.service 
● postgresql-9.6.service - PostgreSQL 9.6 database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql-9.6.service; enabled; vendor preset: disabled)
   Active: active (running) since 금 2018-09-28 22:49:54 KST; 3 days ago
     Docs: https://www.postgresql.org/docs/9.6/static/
  Process: 1800 ExecStartPre=/usr/pgsql-9.6/bin/postgresql96-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
 Main PID: 1806 (postmaster)
   CGroup: /system.slice/postgresql-9.6.service
           ├─1806 /usr/pgsql-9.6/bin/postmaster -D /var/lib/pgsql/9.6/data/
           ├─1808 postgres: logger process   
           ├─1810 postgres: checkpointer process   
           ├─1811 postgres: writer process   
           ├─1812 postgres: wal writer process   
           ├─1813 postgres: autovacuum launcher process   
           ├─1814 postgres: stats collector process   
           ├─9089 postgres: wal sender process replication 16.8.35.228(40272) streaming 0/303B1E0
           └─9716 postgres: brown dwarf ::1(55792) idle
...

 이후 Standby Server에서 psql을 실행하고 'star' Table의 내용을 확인하면 Master Server에서 추가한 Record가 Standby에도 반영되어 있는 것을 확인할 수 있다.

...
[brown@ha-test-2 /]$ psql -h localhost  -U postgres -d dwarf
psql.bin (9.6.3)
Type "help" for help.
...
dwarf=# select * from public.star;
 id | name | class | age  | radius | lum | magnt 
----+------+-------+------+--------+-----+-------
  1 | Sun  | G2V   | 4500 |   1391 |   5 |     1
(1 row)
...



참고