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