PostgreSQL码农集散地

进阶课 10 灾难恢复

PolarDB 进阶课程10 计算节点全毁灾难恢复

穷鬼玩PolarDB RAC一写多读集群系列已经写了几篇:

  • 《在Docker容器中用loop设备模拟共享存储》
  • 《如何搭建PolarDB容灾(standby)节点》
  • 《共享存储在线扩容》
  • 《计算节点 Switchover》
  • 《在线备份》
  • 《在线归档》
  • 《实时归档》
  • 《时间点恢复(PITR)》
  • 《读写分离》

本篇文章介绍一下如果PolarDB的主机(计算节点)全挂了, 只剩共享存储, 应该如何修复集群?  实验环境依赖 《在Docker容器中用loop设备模拟共享存储》 , 如果没有环境, 请自行参考以上文章搭建环境.

视频回放

b站视频链接

Youtube视频链接

捣毁主机

1、破坏pb1 PolarDB RW节点

pg_ctl stop -m fast -D ~/primary  

rm -rf ~/primary  

2、破坏pb2 PolarDB RO节点

pg_ctl stop -m fast -D ~/replica1  

rm -rf ~/replica1  

 现在只剩下共享存储, PolarDB 还能抢救吗? 

 3、索性破坏彻底一点, 把pb1, pb2容器都删掉, 彻底没了就更好玩了.  

docker stop pb1  
docker stop pb2  

docker rm pb1  
docker rm pb2  

把macOS上的Docker Desktop也重启一下, 连loop块设备都没了.

现在只剩下模拟成共享盘的这个文件, 接下来一步一步恢复.

$ cd ~/data_volumn/  
$ ll VirtualDisk.img   
-rw-r--r--  1 digoal  staff    20G Dec 24 11:29 VirtualDisk.img  

DEMO

和原来的计算节点一样, 使用 polardb_pg_devel:ubuntu22.04 镜像, 自带pfs, 无需再安装pfs.

1、启动容器pb1

cd ~/data_volumn    
PWD=`pwd`    

docker run -d -it -v $PWD:/data -P --shm-size=1g --cap-add=SYS_PTRACE --cap-add SYS_ADMIN --privileged=true --name pb1 registry.cn-hangzhou.aliyuncs.com/polardb_pg/polardb_pg_devel:ubuntu22.04 bash    

记录容器pb1 IP地址

进入容器pb1    
docker exec -ti pb1 bash    

ip addr show    
172.17.0.2  

2、启动容器pb2

cd ~/data_volumn    
PWD=`pwd`    

docker run -d -it -v $PWD:/data -P --shm-size=1g --cap-add=SYS_PTRACE --cap-add SYS_ADMIN --privileged=true --name pb2 registry.cn-hangzhou.aliyuncs.com/polardb_pg/polardb_pg_devel:ubuntu22.04 bash    

记录容器pb2 IP地址

进入容器pb2    
docker exec -ti pb2 bash    

ip addr show    
172.17.0.3    

3、在pb1中使用共享的VirtualDisk.img文件创建loop设备, 需要找到一个空闲的loop设备, 从loop0开始找, 逐渐增加数字. 直到创建loopN成功.

使用 losetup -f 找第一个未使用的loop设备.  
$ losetup -f  
/dev/loop1  

创建loop设备  
$ sudo losetup --direct-io=on /dev/loop1 /data/VirtualDisk.img    

$ ll /dev/loop1    
brw-rw-rw- 1 root root 7, 1 Dec 16 13:52 /dev/loop1    

将loop设备软链到nvme1n1方便使用, 如果你的容器内已经有这个名字, 可以换一个例如nvme2n1.

# 注意: 容器重启后, 这个软链就不存在了, 需重新创建该软链     

sudo ln -s /dev/loop1 /dev/nvme1n1    

4、pb2直接使用pb1创建的loop1即可.  (由于我这个环境任何一个容器创建了loop block设备其他容器都能看到. 在实际的生产环境中, 创建loop设备的过程对应的是SAN给主机分配LUN的过程.)

将loop设备软链到nvme1n1方便使用, 如果你的容器内已经有这个名字, 可以换一个例如nvme2n1.

# 注意: 容器重启后, 这个软链就不存在了, 需重新创建该软链     

sudo ln -s /dev/loop1 /dev/nvme1n1    

5、在pb1, pb2都启动pfsd, -w为工作进程数量, 数字越大IO性能越好, 这里受制于容器资源限制设置为1.

pb1 , pb2

sudo /usr/local/polarstore/pfsd/bin/start_pfsd.sh -p nvme1n1 -w 1    

现在共享盘都挂好了, pfsd也启动了, 可以看到共享盘中的数据都还在.

$ sudo pfs -C disk ls /nvme1n1/  

  File  1     4194304           Tue Dec 24 09:45:40 2024  .pfs-paxos  
  File  1     1073741824        Tue Dec 24 09:45:41 2024  .pfs-journal  
   Dir  1     1536              Tue Dec 24 09:52:24 2024  shared_data  
total 2105344 (unit: 512Bytes)  

接下来就安装PolarDB 二进制, 创建计算节点, 恢复PolarDB集群.

6、编译polardb 15.

之前编译过, 直接从共享盘拷贝二进制. 如果你想体验一下编译过程请参考: 《穷鬼玩PolarDB RAC一写多读集群系列 | 在Docker容器中用loop设备模拟共享存储》

pb1, pb2. 拷贝PolarDB二进制.

$ cp -r /data/polardb/tmp_polardb_pg_15_base ~/

# 或者使用软链也可以, 软链还有个好处, 编译插件时只需要编译一次即可:   
$ ln -s /data/polardb/tmp_polardb_pg_15_base ~/  


$ which psql    
/home/postgres/tmp_polardb_pg_15_base/bin/psql    

7、pb1 恢复primary节点

实际参考了增加replica节点的步骤, 只是把配置文件修改了一下.

mkdir -m 0700 $HOME/primary    
sudo /home/postgres/tmp_polardb_pg_15_base/bin/polar-initdb.sh $HOME/primary/ /nvme1n1/shared_data/ replica    
sudo chmod -R 700 $HOME/primary    
sudo chown -R postgres:postgres  $HOME/primary     

生成配置文件模板

initdb -D /tmp/primary    
cp /tmp/primary/*.conf $HOME/primary/  
rm -rf /tmp/primary  

修改配置文件

postgresql.conf

echo"    
port=5432    
polar_hostid=1    # 注意  
polar_enable_shared_storage_mode=on    
polar_disk_name='nvme1n1'    
polar_datadir='/nvme1n1/shared_data/'    
polar_vfs.localfs_mode=off    
shared_preload_libraries='\$libdir/polar_vfs,\$libdir/polar_worker'    
polar_storage_cluster_name='disk'    
logging_collector=on    
log_line_prefix='%p\t%r\t%u\t%m\t'    
log_directory='pg_log'    
listen_addresses='0.0.0.0'    
max_connections=200  
# 下面几个参数解决replica不能promote的问题, 因为RO依赖logindex.  
polar_logindex_mem_size=64MB  
polar_xlog_queue_buffers=64MB  
polar_xlog_page_buffers=64MB  
# 使用pfs时可以关掉 full page write 和 polar_has_partial_write , 否则请打开这两   
full_page_writes = off  
polar_has_partial_write = off

polar_resource_manager.enable_resource_manager=off
"

 >> ~/primary/postgresql.conf     

pg_hba.conf

echo"      
host all all 0.0.0.0/0 md5      
host  replication postgres  172.17.0.0/16 trust      
"
 >> ~/primary/pg_hba.conf      

启动RW节点

pg_ctl start -D $HOME/primary    

PolarDB实例里的数据都还在, slot也还在, 启动后standby立刻就连上来了.

$ psql  
psql (PostgreSQL 15.10 (PolarDB 15.10.2.0 build d4f5477d debug) on aarch64-linux-gnu)  
Type "help"forhelp.  

postgres=# \dt  
        List of relations  
 Schema | Name | Type  |  Owner     
--------+------+-------+----------  
 public | t    | table | postgres  
 public | t1   | table | postgres  
 public | t2   | table | postgres  
 public | tbl  | table | postgres  
(4 rows)  

postgres=# select * from pg_stat_replication;  
 pid | usesysid | usename  | application_name | client_addr | client_hostname | client_port |         backend_start         | backend_xmin |   state   |  sent_lsn  | write_lsn  | flush_lsn  | replay_lsn  
 |    write_lag    |    flush_lag    |   replay_lag    | sync_priority | sync_state |          reply_time             
-----+----------+----------+------------------+-------------+-----------------+-------------+-------------------------------+--------------+-----------+------------+------------+------------+-----------  
-+-----------------+-----------------+-----------------+---------------+------------+-------------------------------  
 794 |       10 | postgres | standby1         | 172.17.0.4  |                 |       57176 | 2024-12-24 11:24:41.435003+08 |              | streaming | 0/40945898 | 0/40945898 | 0/40945898 | 0/40945898  
 | 00:00:00.000545 | 00:00:00.000579 | 00:00:00.000822 |             0 | async      | 2024-12-24 11:24:52.820741+08  
(1 row)  

postgres=# select * from pg_replication_slots ;  
 slot_name | plugin | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn | wal_status | safe_wal_size | two_phase   
-----------+--------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+---------------------+------------+---------------+-----------  
 replica1  |        | physical  |        |          | f         | f      |            |      |              | 0/409455B0  |                     | reserved   |               | f  
 standby1  |        | physical  |        |          | f         | t      |        794 |      |              | 0/40945898  |                     | reserved   |               | f  
(2 rows)  

RW节点启动过后, 写入一些数据

postgres=# \d t1  
                 Table "public.t1"
 Column |  Type   | Collation | Nullable | Default   
--------+---------+-----------+----------+---------  
 id     | integer |           |          |   

postgres=# insert into t1 values (100);  
INSERT 0 1  
postgres=# select * from t1;  
 id    
-----  
   1  
 100  
(2 rows)  

pb3 standby 节点立即就能查到新增的数据.  (由于docker desktop重启过, pb3的loop设备可能要新建, nvme软链也要新建, pfsd要启动, 再启动standby.)

$ sudo losetup --direct-io=on /dev/loop2 /data/VirtualDisk_standby.img   
$ sudo ln -s /dev/loop2 /dev/nvme2n1    
$ sudo /usr/local/polarstore/pfsd/bin/start_pfsd.sh -p nvme2n1 -w 1   
$ pg_ctl start -D ~/standby   

$ psql  
psql (PostgreSQL 15.10 (PolarDB 15.10.2.0 build d4f5477d debug) on aarch64-linux-gnu)  
Type "help"forhelp.  

postgres=# select * from t1;  
 id    
-----  
   1  
 100  
(2 rows)  

到此RW节点就恢复正常了.

8、pb2 恢复replica节点

mkdir -m 0700 $HOME/replica1    
sudo /home/postgres/tmp_polardb_pg_15_base/bin/polar-initdb.sh $HOME/replica1/ /nvme1n1/shared_data/ replica    
sudo chmod -R 700 $HOME/replica1    
sudo chown -R postgres:postgres  $HOME/replica1     

生成配置文件模板

initdb -D /tmp/replica1    
cp /tmp/replica1/*.conf $HOME/replica1/  
rm -rf /tmp/replica1  

修改配置文件

postgresql.conf 与primary略有不同,

echo"    
port=5432    
polar_hostid=2    # 注意  
polar_enable_shared_storage_mode=on    
polar_disk_name='nvme1n1'    
polar_datadir='/nvme1n1/shared_data/'    
polar_vfs.localfs_mode=off    
shared_preload_libraries='\$libdir/polar_vfs,\$libdir/polar_worker'    
polar_storage_cluster_name='disk'    
logging_collector=on    
log_line_prefix='%p\t%r\t%u\t%m\t'    
log_directory='pg_log'    
listen_addresses='0.0.0.0'    
max_connections=200  
# 下面几个参数解决replica不能promote的问题, 因为RO依赖logindex.  
polar_logindex_mem_size=64MB  
polar_xlog_queue_buffers=64MB  
polar_xlog_page_buffers=64MB  
# 使用pfs时可以关掉 full page write 和 polar_has_partial_write , 否则请打开这两   
full_page_writes = off  
polar_has_partial_write = off

polar_resource_manager.enable_resource_manager=off

# replication  # 注意  
primary_slot_name='replica1'    
primary_conninfo='host=172.17.0.2 port=5432 user=postgres dbname=postgres application_name=replica1'    
"

 >> ~/replica1/postgresql.conf     

pg_hba.conf

echo"      
host all all 0.0.0.0/0 md5      
host  replication postgres  172.17.0.0/16 trust      
"
 >> ~/replica1/pg_hba.conf      

 标识节点以 Replica 模式启动 

touch $HOME/replica1/replica.signal    

启动RO节点

pg_ctl start -D $HOME/replica1    

在pb1 RW节点, 检查slot状态是否正常, 修改数据

postgres=# select * from pg_stat_replication;  
 pid  | usesysid | usename  | application_name | client_addr | client_hostname | client_port |         backend_start         | backend_xmin |   state   |  sent_lsn  | write_lsn  | flush_lsn  | replay_ls  
n |    write_lag    |    flush_lag    |   replay_lag    | sync_priority | sync_state |          reply_time             
------+----------+----------+------------------+-------------+-----------------+-------------+-------------------------------+--------------+-----------+------------+------------+------------+----------  
--+-----------------+-----------------+-----------------+---------------+------------+-------------------------------  
 6299 |       10 | postgres | replica1         | 172.17.0.3  |                 |       53504 | 2024-12-24 14:13:34.411357+08 |              | streaming | 0/40945F50 | 0/40945F50 | 0/40945F50 | 0/40945F5  
0 | 00:00:00.000111 | 00:00:00.000111 | 00:00:00.000328 |             0 | async      | 2024-12-24 14:14:01.09425+08  
  215 |       10 | postgres | standby1         | 172.17.0.4  |                 |       53910 | 2024-12-24 14:07:11.652935+08 |              | streaming | 0/40945F50 | 0/40945F50 | 0/40945F50 | 0/40945F5  
0 | 00:00:00.000422 | 00:00:00.000468 | 00:00:00.000696 |             0 | async      | 2024-12-24 14:14:01.094546+08  
(2 rows)  

postgres=# insert into t1 values (200);  
INSERT 0 1  
postgres=# select * from t1;  
 id    
-----  
   1  
 100  
 200  
(3 rows)  

在pb2 检查修改已生效

postgres=# select * from t1;  
 id    
-----  
   1  
 100  
 200  
(3 rows)  

到此RO节点完成修复.

参考

《穷鬼玩PolarDB RAC一写多读集群系列 | 在Docker容器中用loop设备模拟共享存储》

《穷鬼玩PolarDB RAC一写多读集群系列 | 如何搭建PolarDB容灾(standby)节点》

《穷鬼玩PolarDB RAC一写多读集群系列 | 共享存储在线扩容》

《穷鬼玩PolarDB RAC一写多读集群系列 | 计算节点 Switchover》

《穷鬼玩PolarDB RAC一写多读集群系列 | 在线备份》

《穷鬼玩PolarDB RAC一写多读集群系列 | 在线归档》

《穷鬼玩PolarDB RAC一写多读集群系列 | 实时归档》

《穷鬼玩PolarDB RAC一写多读集群系列 | 时间点恢复(PITR)》

《穷鬼玩PolarDB RAC一写多读集群系列 | 读写分离》