背景
穷鬼玩PolarDB RAC一写多读集群系列已经写了几篇:
- 《在Docker容器中用loop设备模拟共享存储》
- 《如何搭建PolarDB容灾(standby)节点》
- 《共享存储在线扩容》
- 《计算节点 Switchover》
- 《在线备份》
- 《在线归档》
- 《实时归档》
- 《时间点恢复(PITR)》
- 《读写分离》
本篇文章介绍一下如果PolarDB的主机(计算节点)全挂了, 只剩共享存储, 应该如何修复集群? 实验环境依赖 《在Docker容器中用loop设备模拟共享存储》 , 如果没有环境, 请自行参考以上文章搭建环境.
视频回放
捣毁主机
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" for help. 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" for help. 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一写多读集群系列 | 实时归档》