基于ZFS(Zettabyte File System)的快照功能,您可以设置一个ECS实例作为备节点,通过Cron定期创建快照(秒级快照),就可以实现数据的秒级闪回,从而达到实时容灾的目的。
背景信息
ZFS是一款动态文件管理系统,与其他文件系统的管理方式不同,ZFS采用存储池的概念来管理物理存储空间,把所有设备集中到一个存储池中来进行管理。存储池描述了存储的物理特征(设备的布局、数据的冗余等),使得文件系统不再局限于单独的物理设备,而且允许物理设备把自带的文件系统共享到存储池中。
ZFS使用写时拷贝事务模型技术,写新数据时,包含旧数据的块被保留着,由于ZFS在读写操作时已经存储了所有构建快照的数据,所以快照的创建非常快,而且由于任何文件的修改都是在文件系统和它的快照之间共享的,所以ZFS的快照也是空间优化的。
基于ZFS的这些特性,本文介绍如何设置一个ECS实例作为备节点,通过Cron定期创建快照(秒级快照)。恢复时您就可以找到最近的快照,实现快速恢复数据、实时容灾。
准备工作
- 创建合适规格的ECS实例。建议可用区、专有网络和交换机与RDS PostgreSQL相同,这样内网访问速度最快。如果跨地域容灾,需要使用云企业网连通实例,详情请参见云企业网。
- 设置白名单,允许ECS实例访问RDS实例。
创建快照
- 连接ECS实例。
- 安装ZFS。
wget http://download.zfsonlinux.org/epel/zfs-release.el7_7.noarch.rpm
rpm -ivh zfs-release.el7_7.noarch.rpm
yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum install -y "kernel-devel-uname-r == $(uname -r)" zfs
- 修改文件rc.local。
vi /etc/rc.local
/sbin/modprobe zfs
chmod +x /etc/rc.local
- 测试ZFS是否正常,然后重启实例。
# modprobe zfs
# zpool list
no pools available
# reboot
- 安装PostgreSQL 12。
yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
yum install postgresql12*
- 在云盘上进行如下配置。
parted -a optimal -s /dev/vdb1 mklabel gpt mkpart primary 1MiB 100%FREE // 对齐分区,/dev/vdb1是新增云盘的路径。
zpool create zp1 -f -o ashift=13 vdc1 //配置ZFS存储池。
zfs set canmount=off zp1 //设置canmount属性。
- 创建数据文件目录。
zfs create -o mountpoint=/zpdata01 -o recordsize=8K -o atime=off -o primarycache=metadata -o logbias=throughput -o secondarycache=none zp1/zpdata01
- 创建WAL归档目录。
zfs create -o mountpoint=/zpdata02 -o recordsize=8K -o atime=off -o primarycache=metadata -o logbias=throughput -o secondarycache=none -o compression=on zp1/zpdata02
zfs set compression=on zp1/zpdata02
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
zp1 1.29M 1.42T 192K /zp1
zp1/zpdata01 192K 1.42T 192K /zpdata01
zp1/zpdata02 192K 1.42T 192K /zpdata02
- 创建RDS实例的备库文件夹。
mkdir /zpdata01/pg12_1921_data
mkdir /zpdata02/pg12_1921_wal
chown -R postgres:postgres /zpdata01/pg12_1921_data
chown -R postgres:postgres /zpdata02/pg12_1921_wal
- 同步RDS实例数据到ECS实例。
su - postgres
export PGPASSWORD=<RDS实例高权限账号的密码>
nohup pg_basebackup -D /zpdata01/pg12_1921_data -F p -R -c fast -X stream -h <RDS实例连接地址> -p <RDS实例连接端口> -U <RDS实例高权限账号名> >./bak.log 2>&1 &
说明 请确保白名单已放通ECS实例,放通方法请参见
设置白名单。

- 注释/zpdata01/pg12_1921_data/postgresql.conf的部分配置。
#Fri Mar 13 09:55:03 CST 2020
#ssl_key_file='server.key'
#huge_pages=try
#auto_explain.sample_rate=1
#zhparser.multi_zall=off
#shared_preload_libraries='pg_stat_statements,auth_delay,auto_explain,zhparser,timescaledb,pg_pathman'
#promote_trigger_file='/data/postgresql.trigger'
#ssl=off
#rds_max_log_files=20
#pg_pathman.enable_auto_partition=on
#shared_buffers=32768MB
#zhparser.punctuation_ignore=off
#pg_pathman.override_copy=on
#port=1922
#pg_stat_statements.max=5000
#auth_delay.milliseconds=3s
#auto_explain.log_nested_statements=off
#track_io_timing=on
#zhparser.multi_zmain=off
#auto_explain.log_analyze=off
#archive_mode=on
#ssl_cert_file='server.crt'
#zhparser.multi_short=off
#zhparser.dict_in_memory=off
#auto_explain.log_format=text
#auto_explain.log_min_duration=-1
#rds.rds_max_non_super_conns=12800
#pg_pathman.enable=on
#archive_command='/bin/date'
#auto_explain.log_verbose=off
#log_line_prefix='\1\n\t%p\t%r\t%u\t%d\t%t\t%e\t%T\t%S\t%U\t%E\t\t'
#pg_pathman.enable_runtimemergeappend=on
#zhparser.extra_dicts='dict_extra.xdb'
#auto_explain.log_buffers=off
#pg_stat_statements.track=top
#jit_provider='llvmjit'
#pg_pathman.enable_partitionrouter=off
#pg_stat_statements.track_utility=off
#pg_stat_statements.save=off
#zhparser.dicts_type='EXTRA'
#auto_explain.log_timing=on
#pg_pathman.enable_runtimeappend=on
#zhparser.seg_with_duality=off
#rds.rds_max_super_conns=100
#pg_pathman.enable_partitionfilter=on
#log_destination='stderr,csvlog'
#zhparser.multi_duality=off
#pg_pathman.insert_into_fdw='postgres'
#pg_pathman.enable_bounds_cache=on
#rds.rds_max_non_super_wal_snd=32
#auto_explain.log_triggers=off
#rds_sync_replication_timeout=0
- 修改配置文件。
vi /zpdata01/pg12_1921_data/postgresql.auto.conf
primary_conninfo = 'user=<RDS实例高权限账号名> password=''<RDS实例高权限账号的密码> '' host=''<RDS实例连接地址>'' port=<RDS实例连接端口> application_name=hello_rds_pg12'
port=1922
shared_buffers=32GB
log_destination='csvlog'
archive_mode=always
archive_command='test ! -f /zpdata02/pg12_1921_wal/%f && cp %p /zpdata02/pg12_1921_wal/%f'
- 修改文件夹权限。
chmod 700 /zpdata02/pg12_1921_wal
chmod 700 /zpdata01/pg12_1921_data
- 启动数据库。
su - postgres
/usr/pgsql-12/bin/pg_ctl start -D /zpdata01/pg12_1921_data
说明 如果您的ECS实例配置过低,可能会出现如下报错,建议升级配置。
HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space, or huge pages.
- 设置数据库自动启动。
vi /etc/rc.local
su - postgres -c "/usr/pgsql-12/bin/pg_ctl start -D /zpdata01/pg12_1921_data"
- 配置数据文件目录自动快照(归档目录不需要快照)。
- 创建脚本,配置执行权限。
vi /etc/snap.sh
STIME=`date +%F%T`
/usr/sbin/zfs snapshot zp1/zpdata01@$STIME
chmod 500 /etc/snap.sh
- 测试快照是否正常。
/etc/snap.sh
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zp1/zpdata01@2020-03-2117:06:47 144K - 770M -
- 自动启动Crond。
# systemctl start crond
# systemctl enable crond
# systemctl status crond
● crond.service - Command Scheduler
Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2020-03-21 16:16:08 CST; 53min ago
Main PID: 2526 (crond)
CGroup: /system.slice/crond.service
└─2526 /usr/sbin/crond -n
- 配置Crontab。
# crontab -e
1 1 * * * /etc/snap.sh
# crontab -l -u root
1 1 * * * /etc/snap.sh
说明 您也可以根据磁盘空间和业务需求设置自动清理快照或自动清理归档。手动操作示例如下:
手动清理快照示例
# zfs list -t snapshot
zNAME USED AVAIL REFER MOUNTPOINT
zp1/zpdata01@2020-03-2117:06:47 144K - 770M -
zp1/zpdata01@2020-03-2117:17:01 0B - 786M -
# zfs destroy zp1/zpdata01@2020-03-2117:06:47
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zp1/zpdata01@2020-03-2117:17:01 0B - 786M -
手动清理归档示例
find /zpdata02/pg12_1921_wal/ -type f -mtime +7 -exec rm -f {} \;
- 校验备份集可用性。
- 检测主从延迟。
postgres=> select pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_flush_lsn(),sent_lsn)) as sent_delay,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_flush_lsn(),replay_lsn)) as replay_dealy,*
from pg_stat_replication ;
-[ RECORD 1 ]----+------------------------------
sent_delay | 0 bytes
replay_dealy | 0 bytes
pid | 84098
usesysid | 886185
usename | rep
application_name | hello_rds_pg12
client_addr | 192.168.0.173
client_hostname |
client_port | 60402
backend_start | 2020-03-21 16:59:01.890775+08
backend_xmin |
state | streaming
sent_lsn | 11D/97002068
write_lsn | 11D/97002068
flush_lsn | 11D/97002068
replay_lsn | 11D/97002068
write_lag |
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
reply_time | 2020-03-21 17:01:17.198139+08
至此,您的ECS实例已经实现定期快照备份,可以参见下文进行秒级闪回,实现实时容灾。
秒级闪回
- 基于快照克隆一个ZFS文件系统。
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zp1/zpdata01@2020-03-2117:17:01 312K - 786M -
# zfs clone -o mountpoint=/test_recovery zp1/zpdata01@2020-03-2117:17:01 zp1/zpdata_test
[root@iZbp135pwcjjoxqgfpw9k1Z pg12_1921_wal]# cd /test_recovery
[root@iZbp135pwcjjoxqgfpw9k1Z test_recovery]# ll
total 17
drwx------ 20 postgres postgres 35 Mar 21 16:59 pg12_1921_data
- 配置数据恢复相关参数。
说明 如果ECS实例的内存不足,可以设置较小的shared_buffer。
vi /test_recovery/pg12_1921_data/postgresql.auto.conf
port=1923
shared_buffers=32GB
log_destination='csvlog'
recovery_end_command = 'cp /zpdata02/pg12_1921_wal/%f %p'
recovery_target_time = '2020-03-21 17:28:37.670338+08'
recovery_target_timeline = 'latest'
recovery_target_action = 'pause'
- 删除克隆文件系统中的socket、pid等文件。
rm -f /test_recovery/pg12_1921_data/.s.*
rm /test_recovery/pg12_1921_data/postmaster.pid
- 进行恢复操作。
su - postgres
/usr/pgsql-12/bin/pg_ctl start -D /test_recovery/pg12_1921_data
- 查看恢复库中的数据。
psql -h /test_recovery/pg12_1921_data -p 1923 -U <RDS实例高权限账号> postgres
psql (12.1)
Type "help" for help.
postgres=> \dt
说明 确认数据正常,您可以使用pgdump工具将数据恢复至RDS实例。
清理恢复库
恢复库使用完成后,按如下命令清理克隆的test_recovery文件系统即可。
su - postgres
/usr/pgsql-12/bin/pg_ctl stop -m fast -D /test_recovery/pg12_1921_data
sudo zfs destroy zp1/zpdata_test