Pgpool-II实现高可用+读写分离+负载均衡(五)---- follow_primary.sh分析

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: Pgpool-II故障转移脚本failover.sh成功执行后,会调用follow_primary.sh脚本将失效主节点转换成新主节点的从节点,本文分析整个过程。

Pgpool检测到PostgreSQL主节点失效,会从standby节点中选举产生一个新的主节点,然后ssh到新主节点,执行failover.sh脚本将新主节点从standby提升为master,成功后会进入后续处理,调用follow_primary.sh脚本将失效主节点设置成standby节点,并设置好从新主节点同步数据,从而成为新主节点的standby节点。主要流程是先尝试使用pg_rewind,如果pg_rewind失败,则使用pg_basebackup重新来过。

#!/bin/bashset-o xtrace
# Special values:# 1)  %d = standby节点ID# 2)  %h = 主机名# 3)  %p = 端口号# 4)  %D = PG_DATA目录# 5)  %m = 新主节点ID# 6)  %H = 新主节点主机名# 7)  %M = old main node id(未使用,和$8一样的含义)# 8)  %P = 旧主节点ID# 9)  %r = 新主节点端口号# 10) %R = 新主节点数据目录(PG_DATA)# 11) %N = 旧主节点主机名# 12) %S = 旧主节点端口号# 13) %% = '%' characterNODE_ID="$1"NODE_HOST="$2"NODE_PORT="$3"NODE_PGDATA="$4"NEW_PRIMARY_NODE_ID="$5"NEW_PRIMARY_NODE_HOST="$6"OLD_MAIN_NODE_ID="$7"OLD_PRIMARY_NODE_ID="$8"NEW_PRIMARY_NODE_PORT="$9"NEW_PRIMARY_NODE_PGDATA="${10}"# 以下参数根据自己的环境设置# pg安装目录PGHOME=/usr/local/pgsql16
# archive log目录ARCHIVEDIR=/data01/pgsql16/archive
# 复制用户名REPLUSER=replica
# PCP用户名PCP_USER=pgpool
# Pgpool bin目录PGPOOL_PATH=/home/postgres/pgpool/bin
PCP_PORT=9898# 复制槽名REPL_SLOT_NAME=${NODE_HOST//[-.]/_}# pg服务启动用户POSTGRESQL_STARTUP_USER=postgres
SSH_KEY_FILE=id_rsa
SSH_OPTIONS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/${SSH_KEY_FILE}"echo follow_primary.sh: start: Standby node${NODE_ID}# 检查standby节点连接状态${PGHOME}/bin/pg_isready -h${NODE_HOST}-p${NODE_PORT} > /dev/null 2>&1
# 检查失败退出if [ $?-ne0 ]; thenecho follow_primary.sh: node_id=${NODE_ID} is not running. skipping follow primary command
exit0fi# 测试免密sshssh-T${SSH_OPTIONS}${POSTGRESQL_STARTUP_USER}@${NEW_PRIMARY_NODE_HOST}ls /tmp > /dev/null
# 测试失败退出if [ $?-ne0 ]; thenecho follow_main.sh: passwordless SSH to ${POSTGRESQL_STARTUP_USER}@${NEW_PRIMARY_NODE_HOST} failed. Please setup passwordless SSH.
exit1fi# 获取PostgreSQL主版本号PGVERSION=`${PGHOME}/bin/initdb -V | awk '{print $3}' | sed 's/\..*//' | sed 's/\([0-9]*\)[a-zA-Z].*/\1/'`# pg版本>=12创建myrecovery.conf,否则创建recovery.confif [ $PGVERSION-ge12 ]; thenRECOVERYCONF=${NODE_PGDATA}/myrecovery.conf
elseRECOVERYCONF=${NODE_PGDATA}/recovery.conf
fi# standby节点同新主节点同步数据echo follow_primary.sh: pg_rewind fornode${NODE_ID}# 运行pg_rewind前先在新主节点上执行checkpoint更新控制文件${PGHOME}/bin/psql -h${NEW_PRIMARY_NODE_HOST}-p${NEW_PRIMARY_NODE_PORT} postgres -c"checkpoint;"# 创建复制槽 "${REPL_SLOT_NAME}"${PGHOME}/bin/psql -h${NEW_PRIMARY_NODE_HOST}-p${NEW_PRIMARY_NODE_PORT} postgres \
-c"SELECT pg_create_physical_replication_slot('${REPL_SLOT_NAME}');"  >/dev/null 2>&1
if [ $?-ne0 ]; thenecho follow_primary.sh: create replication slot \"${REPL_SLOT_NAME}\" failed. You may need to create replication slot manually.
fi# ssh到从节点ssh-T${SSH_OPTIONS}${POSTGRESQL_STARTUP_USER}@${NODE_HOST}"    set -o errexit    # 停止PostgreSQL${PGHOME}/bin/pg_ctl -w -m f -D ${NODE_PGDATA} stop    # 执行pg_rewind${PGHOME}/bin/pg_rewind -D ${NODE_PGDATA} --source-server=\"user=${POSTGRESQL_STARTUP_USER} host=${NEW_PRIMARY_NODE_HOST} port=${NEW_PRIMARY_NODE_PORT} dbname=postgres\"    [ -d \"${NODE_PGDATA}\" ] && rm -rf ${NODE_PGDATA}/pg_replslot/*    # 生成流复制配置文件    cat > ${RECOVERYCONF} << EOTprimary_conninfo = 'host=${NEW_PRIMARY_NODE_HOST} port=${NEW_PRIMARY_NODE_PORT} user=${REPLUSER} application_name=${NODE_HOST} passfile=''/home/postgres/.pgpass'''recovery_target_timeline = 'latest'restore_command = 'scp ${SSH_OPTIONS}${NEW_PRIMARY_NODE_HOST}:${ARCHIVEDIR}/%f %p'primary_slot_name = '${REPL_SLOT_NAME}'EOT    # 修改postgresql.conf文件加入include_if_exists指令以便pg启动时加载recovery配置    # include_if_exists = '/data01/pgsql16/db/myrecovery.conf'    if [ ${PGVERSION} -ge 12 ]; then        sed -i -e \"\\\$ainclude_if_exists = '$(echo ${RECOVERYCONF} | sed -e 's/\//\\\//g')'\" \               -e \"/^include_if_exists = '$(echo ${RECOVERYCONF} | sed -e 's/\//\\\//g')'/d\" ${NODE_PGDATA}/postgresql.conf        # 生成standby节点标志文件                       touch ${NODE_PGDATA}/standby.signal    else        echo \"standby_mode = 'on'\" >> ${RECOVERYCONF}    fi    # 启动PostgreSQL${PGHOME}/bin/pg_ctl -l /dev/null -w -D ${NODE_PGDATA} start"# 如果pg_rewind失败,尝试pg_basebackupif [ $?-ne0 ]; thenecho follow_primary.sh: end: pg_rewind failed. Try pg_basebackup.
ssh-T${SSH_OPTIONS}${POSTGRESQL_STARTUP_USER}@${NODE_HOST}"        set -o errexit        # 删除原来的PG_DATA目录        [ -d \"${NODE_PGDATA}\" ] && rm -rf ${NODE_PGDATA}        # 删除原来的archive log目录        [ -d \"${ARCHIVEDIR}\" ] && rm -rf ${ARCHIVEDIR}/*${PGHOME}/bin/pg_basebackup -h ${NEW_PRIMARY_NODE_HOST} -U $REPLUSER -p ${NEW_PRIMARY_NODE_PORT} -D ${NODE_PGDATA} -X stream        # 生成配置文件        cat > ${RECOVERYCONF} << EOTprimary_conninfo = 'host=${NEW_PRIMARY_NODE_HOST} port=${NEW_PRIMARY_NODE_PORT} user=${REPLUSER} application_name=${NODE_HOST} passfile=''/home/postgres/.pgpass'''recovery_target_timeline = 'latest'restore_command = 'scp ${SSH_OPTIONS}${NEW_PRIMARY_NODE_HOST}:${ARCHIVEDIR}/%f %p'primary_slot_name = '${REPL_SLOT_NAME}'EOT        # 修改postgresql.conf文件加入include_if_exists指令以便pg启动时加载recovery配置        # include_if_exists = '/data01/pgsql16/db/myrecovery.conf'        if [ ${PGVERSION} -ge 12 ]; then            sed -i -e \"\\\$ainclude_if_exists = '$(echo ${RECOVERYCONF} | sed -e 's/\//\\\//g')'\" \                   -e \"/^include_if_exists = '$(echo ${RECOVERYCONF} | sed -e 's/\//\\\//g')'/d\" ${NODE_PGDATA}/postgresql.conf            touch ${NODE_PGDATA}/standby.signal        else            echo \"standby_mode = 'on'\" >> ${RECOVERYCONF}        fi        sed -i \            -e \"s/#*port = .*/port = ${NODE_PORT}/\" \            -e \"s@#*archive_command = .*@archive_command = 'cp \\\"%p\\\" \\\"${ARCHIVEDIR}/%f\\\"'@\" \${NODE_PGDATA}/postgresql.conf    "# 检查pg_basebackup是否成功if [ $?-ne0 ]; then# 失败处理# 删除失效的复制槽${PGHOME}/bin/psql -h${NEW_PRIMARY_NODE_HOST}-p${NEW_PRIMARY_NODE_PORT} postgres \
-c"SELECT pg_drop_replication_slot('${REPL_SLOT_NAME}');"  >/dev/null 2>&1
if [ $?-ne0 ]; thenecho ERROR: follow_primary.sh: drop replication slot \"${REPL_SLOT_NAME}\" failed. You may need to drop replication slot manually.
fi# 提示失败并退出echo follow_primary.sh: end: pg_basebackup failed
exit1fi# 启动新的standby节点 - ${NODE_HOST}ssh-T${SSH_OPTIONS}${POSTGRESQL_STARTUP_USER}@${NODE_HOST}$PGHOME/bin/pg_ctl -l /dev/null -w-D${NODE_PGDATA}startfi# 如果启动成功,调用pcp_attach_node将新的standby节点加入到Pgpool集群if [ $?-eq0 ]; then# 调用pcp_attach_node将新的standby节点加入到Pgpool集群${PGPOOL_PATH}/pcp_attach_node -w-h localhost -U$PCP_USER-p${PCP_PORT}-n${NODE_ID}if [ $?-ne0 ]; thenecho ERROR: follow_primary.sh: end: pcp_attach_node failed
exit1fielse# 启动失败处理# 删除复制槽 "${REPL_SLOT_NAME}"${PGHOME}/bin/psql -h${NEW_PRIMARY_NODE_HOST}-p${NEW_PRIMARY_NODE_PORT} postgres \
-c"SELECT pg_drop_replication_slot('${REPL_SLOT_NAME}');"  >/dev/null 2>&1
if [ $?-ne0 ]; thenecho ERROR: follow_primary.sh: drop replication slot \"${REPL_SLOT_NAME}\" failed. You may need to drop replication slot manually.
fi# 报错并退出echo ERROR: follow_primary.sh: end: follow primary command failed
exit1fiecho follow_primary.sh: end: follow primary command is completed successfully
exit0

pg>=12时,生成的的配置文件myrecovery.conf文件内容如下:

primary_conninfo ='host=db02 port=5432 user=replica application_name=db01 passfile=''/home/postgres/.pgpass'''recovery_target_timeline ='latest'restore_command ='scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_rsa zsy-db02:/data01/pgsql14/archive/%f %p'primary_slot_name ='db01'

同时会在postgresql.conf文件末尾加下下面一行,以便pg启动时加载以应用流复制相关设置。

include_if_exists ='/data01/pgsql16/db/myrecovery.conf'


相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
4月前
|
负载均衡 算法 调度
负载均衡原理分析与源码解读
负载均衡原理分析与源码解读
|
4月前
|
消息中间件 负载均衡 Kafka
Kafka 实现负载均衡与故障转移:深入分析 Kafka 的架构特点与实践
【8月更文挑战第24天】Apache Kafka是一款专为实时数据处理和流传输设计的高性能消息系统。其核心设计注重高吞吐量、低延迟与可扩展性,并具备出色的容错能力。Kafka采用分布式日志概念,通过数据分区及副本机制确保数据可靠性和持久性。系统包含Producer(消息生产者)、Consumer(消息消费者)和Broker(消息服务器)三大组件。Kafka利用独特的分区机制实现负载均衡,每个Topic可以被划分为多个分区,每个分区可以被复制到多个Broker上,确保数据的高可用性和可靠性。
78 2
|
5月前
|
负载均衡 NoSQL 应用服务中间件
搭建高可用及负载均衡的Redis
【7月更文挑战第10天】
150 1
|
5月前
|
负载均衡 安全 Cloud Native
云上负载均衡:构建高可用、高性能的网络应用架构
与云原生技术深度融合:随着云原生技术的普及和发展未来的云上负载均衡将更加紧密地与云原生技术相结合。例如与Kubernetes等容器编排平台集成实现自动化的服务发现和路由管理;与Serverless架构结合提供无缝的流量接入和请求处理能力。 安全性能提升:面对日益严峻的网络安全威胁云上负载均衡将更加注重安全性能的提升。通过引入加密传输、访问控制、DDoS防护等安全措施确保网络流量的安全性和隐私性;同时还将建立完善的安全监控和应急响应机制以应对各种安全事件和突发事件。 支持多协议和多场景:未来的云上负载均衡将支持更多种类的网络协议和应用场景以满足不同用户和业务的需求。例如支持HTTP/2、
248 0
|
5月前
|
负载均衡 算法 Java
实现高可用和可扩展的负载均衡系统的Java方法
实现高可用和可扩展的负载均衡系统的Java方法
|
6月前
|
负载均衡 应用服务中间件 开发工具
技术笔记:nginx和keeplive实现负载均衡高可用
技术笔记:nginx和keeplive实现负载均衡高可用
|
7月前
|
负载均衡 关系型数据库 分布式数据库
【PolarDB开源】PolarDB读写分离实践:优化读取性能与负载均衡策略
【5月更文挑战第26天】PolarDB是云原生关系型数据库,通过读写分离优化性能和扩展性。它设置主节点处理写操作,从节点处理读操作,异步复制保证数据一致性。优化读取性能的策略包括增加从节点数量、使用只读实例和智能分配读请求。负载均衡策略涉及基于权重、连接数和地理位置的分配。实践示例中,电商网站通过主从架构、只读实例和负载均衡策略提升商品查询效率。PolarDB的读写分离与负载均衡为企业应对大数据和高并发提供了有效解决方案。
247 0
|
7月前
|
存储 缓存 运维
解密一致性哈希算法:实现高可用和负载均衡的秘诀
解密一致性哈希算法:实现高可用和负载均衡的秘诀
830 0
|
7月前
|
Kubernetes 负载均衡 监控
Kubernetes高可用集群二进制部署(一)主机准备和负载均衡器安装
Kubernetes高可用集群二进制部署(一)主机准备和负载均衡器安装
|
6月前
|
缓存 负载均衡 算法
解读 Nginx:构建高效反向代理和负载均衡的秘密
解读 Nginx:构建高效反向代理和负载均衡的秘密
123 2