PostgreSQL Replication – Log Shipping
PostgreSQL 리플리케이션은 장애를 대비해 가용성을 높이는 최소한의 방법입니다. 현재 버전의 PostgreSQL 다양한 리플리케이션을 지원하는데, 이 문서는 가장 오래되고 기초적인 리플리케이션인 Log Shipping Replication 에 대해서 다룹니다.
이 리플리케이션은 Warm Standby 이라고 합니다. 이에 대한 설명은 PostgreSQL 문서에 다음과 같이 잘 나와 있습니다.
운영 서버에서 만드는 트랜잭션 로그 조각을 정기적으로 대기 서버로 옮기고, 그것을 적용시켜, 운영 서버가 장애로 멈추게 되면, 대기 서버를 운영해서, 가용성을 향상할 수 있다. 이 기능을 warm standby, 또는 log shipping 기능이라고 한다.
이 복제 방식은 먼저, 운영 서버와 대기 서버가 모두 실행 중이어야한다. 하지만, 두 서버 쌍방간의 연결 상태는 다른 복제 방식보다 나빠도 괜찮다. 운영 서버는 아카이브 모드로 운영 되어야하며, 운영 중에 생기는 다 쓴 WAL 세그먼트 파일(스위칭된 트랜잭션 로그 파일)을 차례대로 대기 서버로 보내고, 대기 서버는 복구 모드 전용(복구가 끝나도 다음 복구 파일이 있으면 계속 복구 작업을 하는 상태)으로 실행된다. 이 방식을 이용하면, 데이터베이스 테이블들을 수정해야할 필요가 없다. 또한 다른 복제 방식에 비해 관리 작업 비용도 적으며, 복제 작업이 운영 서버 쪽으로 끼치는 영향도 다른 복제 방식보다 적다.
이 방식의 구현 방법은 간단하다. 운영 서버에서 다 쓴 WAL 파일을 다른 서버로 운송(shipping) 하는 것 뿐이다. PostgreSQL에서는 그 로그 옮기는 작업은 한 번에 하나의 로그 파일을 옮길 수 있도록 구현되어 있다. WAL 파일(16MB)을 옮기는 작업은 데이터베이스 서버 밖에서 관리자가 정의한 방식으로 진행 되기 때문에, 같은 사이트 내로 옮겨도 되고, 전혀 다른 시스템 쪽으로 보내도 되고, 여러 시스템으로 한꺼번에 보내도 된다. 이 부분은 전적으로 관리자에게 맡긴다. 단지 고려해야할 사항은 파일이 전송될 때의 전송량 때문에 운영 서버에 영향을 줄 수도 있다. 이 부분이 염려되면, 전송 속도를 제한 할 수 있는 방법도 고려해야할 것이다. 아니면, 레코드 기반 로그 전달 방식 (스트리밍 복제)을 고려할 수 도 있다. (25.2.5절 참조)
로그 전달 방식은 비동기식임을 기억해야 한다. 다시 말하면, 전송하는 WAL 내용은 이미 커밋된 자료이기 때문에, 그 자료가 대기 서버로 미쳐 전달 되기전에 운영 서버가 멈춰버리면, 그 자료는 손실 된다. 자료 손실량을 줄이는 방법으로 archive_timeout 환경설정값을 몇 초 정도로 짧게 지정해 자주 사용하는 WAL 파일을 바꾸고, 그것을 전송하면, 되겠지만, 그 파일의 크기가 16MB이기 때문에, 잦은 WAL 파일 전송 작업으로 네트워크 사용량이 증가할 것이다. 스트리밍 복제 방식(25.2.5절 참조)을 이용하면, 이 손실 되는 자료량을 최소화 할 수 있다.
(물론 위에서 언급한 한계점이 있기는 하지만,) 대기 서버로 넘어 오는 WAL 파일이 제때에 잘 넘어 온다면, 운영 서버가 중지 되어 대기 서버가 그 역할을 맡기까지 서비스가 중지되는 시각은 극히 짧다. 이렇게 가용성을 향상 시킨다. 베이스 백업 자료를 준비하고, 지금까지 보관해둔 WAL 파일을 가지고, 서버를 복구 하는 방법은 이 방식보다 꽤 많은 서비스 중지 시간이 필요할 것이다. 서버가 복구 되는 기술적인 방식은 동일하지만, 가용성 입장에서는 차이가 난다. warm standby 방식으로 구현되면, 대기 서버 쪽에서는 어떤 쿼리도 사용할 수 없다. 대기 서버 쪽에서 읽기 전용 쿼리를 사용하려면, hot standby 방식으로 구축해야한다. 이 부분은 25.5절에서 자세히 설명한다.
시작하기에 앞서
WAL 파일
대부분의 데이터베이스 시스템은 데이터베이스에 변화와 관련된 모든 작업에 대해서 파일로 기록을 해 둡니다. 정확하게는 데이터를 조작하기에 앞서 어떤 SQL 를 명령했는지에 대해서 로깅을 한 후에 실제 데이터를 조작 합니다. 이는 갑자기 장애가 발생했을시에 이 로그 파일을 이용해 데이터를 복구할 수 있게 됩니다. 이를 WAL (Write Ahead Log) 파일이라고 합니다.
PostgreSQL 도 같은 방법으로 동작합니다. PostgreSQL 에서 WAL 파일은 pg_xlog 디렉토리에 생성되며 특징은 16MB 크기단위로 파일이 쪼개져 저장됩니다.
1 2 3 4 |
-rw-------. 1 postgres postgres 16M 2월 20 16:49 00000001000000000000002F -rw-------. 1 postgres postgres 16M 2월 20 15:15 000000010000000000000040 -rw-------. 1 postgres postgres 16M 2월 20 15:14 00000001000000000000003F -rw-------. 1 postgres postgres 16M 2월 20 15:14 00000001000000000000003E |
CheckPoint
모든 데이터베이스 시스템은 메모리 기반으로 동작합니다. 메모리에 자주 쓰이는 데이터를 캐쉬해기도 하고 트랜잭션시에도 메모리에 저장됩니다. 특정 시점이 되면 PostgreSQL 은 메모리에 있는 것을 디스크에 쓰기를 함으로써 실제 데이터가 영구적으로 저장이 됩니다.
그런데, 이러한 작업을 수동으로 해야할 때가 있습니다. 대표적으로 PITR(Point In Time Recovery) 백업을 하기 위해서는 모든 메모리에 내용을 강제로 디스크에 쓰여져야 합니다. 이때 PostgreSQL 가 하는 작업이 바로 CheckPoint 입니다.
다시말해, CheckPoint 는 메모리에 있는 내용을 디스크에 강제로 쓰기하는 작업을 말합니다.
리플리케이션 설정하기
설정하는 순서는 다음과 같습니다.
- Master 서버가 standby 서버에 postgres 계정으로 ssh 패스워드 인증없이 바로 접속할 수 있도록 설정을 합니다.
- Master 서버의 postgresql.conf 파일을 수정하고 서버를 재시작 합니다.
- Master 서버에서 PITR 백업을 합니다.
- 백업본을 standby 서버로 전송합니다.
- standby 서버에서 전송받은 백업 압축파일을 해제 합니다.
- standby recovery.conf 파일을 작성하고 pg_xlog 디렉토리를 생성하고 소유권을 postgres 로 바꿉니다.
- standby 서버를 기동 합니다.
SSH no password 접속 설정.
이는 RSA 키를 교환함으로써 가능 합니다. PostgreSQL 은 postgres 계정으로 동작하기 때문에 postgres 계정에서 Standby postgres 계정으로 RSA 키 접속을 설정해 줍니다.
먼저, Master 서버의 postgres 계정에서 다음과 같이 키를 작성해 줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
-bash-4.2$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/opt/pgsql-9.5.0/.ssh/id_rsa): [그냥 엔터] Created directory '/opt/pgsql-9.5.0/.ssh'. Enter passphrase (empty for no passphrase): [그냥 엔터] Enter same passphrase again: [그냥 엔터] Your identification has been saved in /opt/pgsql-9.5.0/.ssh/id_rsa. Your public key has been saved in /opt/pgsql-9.5.0/.ssh/id_rsa.pub. The key fingerprint is: 61:e4:45:21:ed:fe:56:16:22:57:4b:ff:7f:84:28:00 postgres@localhost.localdomain The key's randomart image is: +--[ RSA 2048]----+ | oo+. | | Eo o. o | | .+. o o | | ...o o o .| | So o o o.| | o . + o| | o o ..| | o o| | . .| +-----------------+ |
이렇게하면 postgres 계정에 “.ssh/id_rsa, .ssh/id_rsa.pub” 두개의 파일이 생성이 됩니다. id_rsa 는 private key 이고 id_rsa.pub 는 public key 입니다. 다음과 같이 publickey 를 Standby 의 postgres 계정을 복사해 줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 |
ssh-copy-id -i ./id_rsa.pub postgres@192.168.96.26 The authenticity of host '192.168.96.26 (192.168.96.26)' can't be established. RSA key fingerprint is ba:6a:82:18:0c:88:2b:e8:6b:cb:cb:ef:53:77:ba:4a. Are you sure you want to continue connecting (yes/no)? yes /bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys postgres@192.168.96.26's password: [로그인 패스워드] Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'postgres@192.168.96.26'" and check to make sure that only the key(s) you wanted were added. |
이제 그냥 “ssh ‘postgres@192.168.96.26′” 을 입력하면 패스워드 입력 없이 접속이 가능해 집니다.
Master 서버에 postgresql.conf 파일 설정
다음과 같이 postgresql.conf 파일을 설정해 줍니다.
1 2 3 4 5 6 |
wal_level = archive # minimal, archive, or hot_standby archive_mode = on # allows archiving to be done # (change requires restart) archive_command = 'scp -i /opt/pgsql-9.5.0/.ssh/id_rsa %p postgres@192.168.96.26:/opt/pgsql-9.5.0/archive/%f' # command to use to archive a logfile segment archive_timeout = 30 # force a logfile segment switch after this # number of seconds; 0 disables |
archive_command 에 설정한대로 Standby 서버에 archive 디렉토리를 만들어주고 postgres 소유권으로 바꿔 줍니다.
1 2 |
-bash-4.1$ mkdir /opt/pgsql-9.5.0/archive -bash-4.1$ chown postgres: archive/ |
서버를 재시작 해줍니다.
Standby 를 위한 데이터 백업하기
리플리케이션의 시작은 슬레이브 데이터베이스시스템을 위해 데이터베이스를 옮겨주는 것입니다. PostgreSQL 뿐만아니라 MySQL 도 그렇지만, 데이터베이스의 파일들을 압축해서 옮겨줘야 하는데, 이를 위해서는
- Master 서버의 작업 중단
- 모든 메모리에 있는 내용을 디스크에 쓰기(CheckPoint)
뒤 두가지가 필요합니다. PostgreSQL 은 이 두가지를 명령어를 통해서 한번에 되도록 지원하는데 두가지 방법이 있습니다. 이 문서에서는 PITR 백업 방법을 설명합니다.
먼저, PostgreSQL 서버에 다음과 같이 해줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@localhost ~]# su - postgres Last login: Sun Feb 21 23:00:28 KST 2016 on pts/0 -bash-4.2$ /opt/pgsql-9.5.0/bin/psql -U postgres -c "select pg_start_backup('/opt/pgsql-9.5.0/20160221.tar')" pg_start_backup ----------------- 0/30000028 (1개 행) -bash-4.2$ tar -cv --exclude=/opt/pgsql-9.5.0/data/pg_xlog -f /opt/pgsql-9.5.0/20160221.tar /opt/pgsql-9.5.0/data /opt/pgsql-9.5.0/data/backup_label -bash-4.2$ /opt/pgsql-9.5.0/bin/psql -U postgres -c "select pg_stop_backup(), current_timestamp" NOTICE: pg_stop_backup complete, all required WAL segments have been archived pg_stop_backup | now ----------------+------------------------------ 0/31000050 | 2016-02-21 23:05:05.56062+09 (1개 행) |
이제 백업 완료된 파일을 Slave 서버로 옮겨 줍니다.
백업본을 Standby 서버로 옮겨주기
1 2 3 |
-bash-4.2$ scp -i /opt/pgsql-9.5.0/.ssh/id_rsa 20160221.tar postgres@192.168.96.26:./ 20160221.tar 100% 679MB 32.3MB/s 00:21 -bash-4.2$ |
다음으로 Slave 서버에서 전송되어진 압축파일을 다음과 같이 압축해제해 줍니다.
Standby 서버에서 전송받은 백업 압축파일을 해제
1 2 3 4 |
[root@localhost .ssh]# su - postgres -bash-4.1$ tar xvf 20160221.tar -C / -bash-4.1$ cd data -bash-4.1$ mkdir pg_xlog |
이제 recovery.conf 을 작성해 줍니다.
recovery.conf 파일 작성
다음과 같이 recovery.conf 파일을 작성하기 위해서 다음과 같이 data 디렉토리로 이동해 줍니다.
1 2 3 |
[root@localhost .ssh]# su - postgres -bash-4.1$ cd data -bash-4.1$ vim recovery.conf |
recovery.conf 는 다음과 같이 작성해 줍니다.
1 2 3 4 |
restore_command = 'cp /opt/pgsql-9.5.0/archive/%f %p' archive_cleanup_command = '/opt/pgsql-9.5.0/bin/pg_archivecleanup /opt/pgsql-9.5.0/archive/ %r' standby_mode = 'on' trigger_file = '/tmp/postgresql.trigger.5432' |
이렇게 파일을 작성하고 역시나 소유권을 postgres 로 변경해줍니다. 그리고 서버를 시작해줍니다.
체크하기
이 렇게 해놓으면 Master 서버에서는 archive 해야할 로그파일을 Standby 서버로 전송하고 Standby 서버는 이것을 replay 하게 됩니다. 그러니까 Standby 서버는 replay 로만 동작하게 됩니다. 이는 상태를 보면 알 수 있습니다. Standby 서버의 프로세스 상태는 다음과 같습니다.
1 2 3 4 |
postgres 2380 0.0 2.4 442480 24504 ? S 23:20 0:00 /opt/pgsql-9.5.0/bin/postgres -D /opt/pgsql-9.5.0/data postgres 2381 0.0 0.8 442516 8192 ? Ss 23:20 0:00 postgres: startup process waiting 000000010000000000000038 postgres 2383 0.0 0.9 442480 9920 ? Ss 23:20 0:00 postgres: checkpointer process postgres 2385 0.0 0.9 442480 9628 ? Ss 23:20 0:00 postgres: writer process |
38번째 파일을 기다리고 있다고 나옵니다. . ‘/opt/pgsql-9.5.0/archive’ 에는 Master 에서 전송한 파일을 받고 받자마자 바로 replay를 합니다. 그리고 3개가 넘어가면 pg_archivecleanup 프로그램이 recovery.conf 에 명시된대로 삭제됩니다. 이는 ‘/opt/pgsql-9.5.0/archive’ 디렉토리를 모니터링 해보면 알 수 있습니다. 없던 파일이 나타나고 사라지는것을 확인할 수 있습니다.
1 2 3 4 5 6 7 8 |
-bash-4.1$ tail -f logfile cp: cannot stat `/opt/pgsql-9.5.0/archive/000000010000000000000038': 그런 파일이나 디렉터리가 없습니다 cp: cannot stat `/opt/pgsql-9.5.0/archive/000000010000000000000038': 그런 파일이나 디렉터리가 없습니다 cp: cannot stat `/opt/pgsql-9.5.0/archive/000000010000000000000038': 그런 파일이나 디렉터리가 없습니다 cp: cannot stat `/opt/pgsql-9.5.0/archive/000000010000000000000038': 그런 파일이나 디렉터리가 없습니다 cp: cannot stat `/opt/pgsql-9.5.0/archive/000000010000000000000038': 그런 파일이나 디렉터리가 없습니다 cp: cannot stat `/opt/pgsql-9.5.0/archive/000000010000000000000038': 그런 파일이나 디렉터리가 없습니다 cp: cannot stat `/opt/pgsql-9.5.0/archive/000000010000000000000038': 그런 파일이나 디렉터리가 없습니다 |
로그파일을 보면 현재 상태를 확인할 수 있습니다.
이 Replication 의 특징는 Standby 를 활용할 수 없습니다. Select 전용으로라도 활용할 수 없습니다. 왜냐하면 replay로 동작중이기 때문에 psql 를 이용해서 접속을 하면 다음과 같은 에러메시지를 만나게 됩니다.
1 2 |
-bash-4.1$ /opt/pgsql-9.5.0/bin/psql -U postgres psql: FATAL: the database system is starting up |
만일 Master 서버에 여러 Standby 서버가 필요하다면 Master 서버에 postgresql.conf 파일에 ‘archive_command’ 에 스크립트를 넣어줍니다.
1 2 3 4 5 6 |
archive_command = 'myarchivescript %p %f' myarchivescript 파일 내용. scp $1 $STANDBYNODE1:$PGARCHIVE/$2 scp $1 $STANDBYNODE2:$PGARCHIVE/$2 scp $1 $STANDBYNODE3:$PGARCHIVE/$2 |
단점
이 Log Shipping Replication 은 자세한 Replication 상태를 모니터링 할 수가 없습니다. 파일 전송은 PostgreSQL 이 담당하는게 아닌 리눅스 시스템이 담당합니다. 거기다 Standby 서버에는 접속조차 할 수 없어서 Standby 서버에서 제대로 Recovering 이 되고 있는지를 알 수가 없습니다.
동작 방법이 16MB WAL 파일 작성이 완료되어지면 전송이 되어지는데, 그래서 WAL 파일이 자주 생성된다면 파일을 전송하는데 시스템 자원 소모가 커질 우려도 있습니다.