基于ZFS(Zettabyte File System)的快照功能,您可以设置一个ECS实例作为备节点,通过Cron定期创建快照(秒级快照),就可以实现数据的秒级闪回,从而达到实时容灾的目的。

背景信息

ZFS是一款动态文件管理系统,与其他文件系统的管理方式不同,ZFS采用存储池的概念来管理物理存储空间,把所有设备集中到一个存储池中来进行管理。存储池描述了存储的物理特征(设备的布局、数据的冗余等),使得文件系统不再局限于单独的物理设备,而且允许物理设备把自带的文件系统共享到存储池中。

ZFS使用写时拷贝事务模型技术,写新数据时,包含旧数据的块被保留着,由于ZFS在读写操作时已经存储了所有构建快照的数据,所以快照的创建非常快,而且由于任何文件的修改都是在文件系统和它的快照之间共享的,所以ZFS的快照也是空间优化的。

基于ZFS的这些特性,本文介绍如何设置一个ECS实例作为备节点,通过Cron定期创建快照(秒级快照)。恢复时您就可以找到最近的快照,实现快速恢复数据、实时容灾。

准备工作

创建快照

  1. 连接ECS实例
  2. 安装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  
  3. 修改文件rc.local。
    vi /etc/rc.local  
    /sbin/modprobe zfs  
    
    chmod +x /etc/rc.local  
  4. 测试ZFS是否正常,然后重启实例。
    # modprobe zfs   
    
    # zpool list  
    no pools available  
    
    # reboot  
  5. 安装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*  
  6. 在云盘上进行如下配置。
    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属性。
  7. 创建数据文件目录。
    zfs create -o mountpoint=/zpdata01 -o recordsize=8K -o atime=off -o primarycache=metadata -o logbias=throughput -o secondarycache=none zp1/zpdata01
  8. 创建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  
  9. 创建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 
  10. 同步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实例,放通方法请参见设置白名单
    同步数据
  11. 注释/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  
  12. 修改配置文件。
    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'  
  13. 修改文件夹权限。
    chmod 700 /zpdata02/pg12_1921_wal  
    chmod 700 /zpdata01/pg12_1921_data  
  14. 启动数据库。
    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.
  15. 设置数据库自动启动。
    vi /etc/rc.local  
    
    su - postgres -c "/usr/pgsql-12/bin/pg_ctl start -D /zpdata01/pg12_1921_data"  
  16. 配置数据文件目录自动快照(归档目录不需要快照)。
    1. 创建脚本,配置执行权限。
      vi /etc/snap.sh  
      
      STIME=`date +%F%T`  
      /usr/sbin/zfs snapshot zp1/zpdata01@$STIME   
      
      chmod 500 /etc/snap.sh  
    2. 测试快照是否正常。
      /etc/snap.sh  
      
      # zfs list -t snapshot  
      NAME                              USED  AVAIL     REFER  MOUNTPOINT  
      zp1/zpdata01@2020-03-2117:06:47   144K      -      770M  -  
    3. 自动启动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  
    4. 配置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 {} \;  
  17. 校验备份集可用性
  18. 检测主从延迟。
    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实例已经实现定期快照备份,可以参见下文进行秒级闪回,实现实时容灾。

秒级闪回

  1. 基于快照克隆一个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  
  2. 配置数据恢复相关参数。
    说明 如果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'   
  3. 删除克隆文件系统中的socket、pid等文件。
    rm -f /test_recovery/pg12_1921_data/.s.*  
    rm /test_recovery/pg12_1921_data/postmaster.pid  
  4. 进行恢复操作。
    su - postgres
    /usr/pgsql-12/bin/pg_ctl start -D /test_recovery/pg12_1921_data 
  5. 查看恢复库中的数据。
    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