PostgreSQL高可用平台运维项目

大致流程(删除)

总体规划:

阶段 内容 环境 目标
阶段1 主从复制 虚拟机 + 云服务器 熟悉 replication 实现
阶段2 自动备份 + PITR 同上 熟悉归档与恢复流程
阶段3 监控系统 同上 掌握性能监控
阶段4 Docker 容器化 本地 Ubuntu 打包项目可一键复现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
项目:PostgreSQL 运维 & 高可用平台  
├── 模块 A:备份与恢复系统
│ ├── 全量/增量备份脚本
│ ├── 恢复演练流程文档
│ └── 容灾脚本或模拟故障切换
├── 模块 B:主从复制+故障切换架构
│ ├── 主库、从库部署(如两个节点)
│ ├── 使用 Patroni/etcd/HAProxy(或简化版本)实现自动切换
│ ├── 测试主库宕机+切换流程文档
│ └── 延迟监控、同步机制说明
├── 模块 C:监控+性能优化+脚本自动化
├── 收集慢查询(如 pg_stat_statements)、执行 EXPLAIN 分析
├── 编写巡检脚本(检测锁等待、表膨胀、索引冗余等)
├── 部署监控平台(如 Prometheus + Grafana)展示指标
└── 自动告警(邮件/微信/钉钉)或报告生成脚本
└── 文档/演示
├── 项目简介/架构图
├── 安装部署说明
└── 简历版项目总结

第一步:环境准备

在本地或云/虚拟机中准备至少两台 Linux(可用 VirtualBox、Docker + 多容器模拟也行)

安装 PostgreSQL 的某个版本(比如 15),在一台作为主库、一台作为从库;

确保网络通、时间同步,做好基础配置(如 shared_buffers、wal_level=replica 等)

建立 Git 仓库,将代码/脚本/文档都放进去。

第二步:模块 A — 备份与恢复

编写 Shell 或 Python 脚本,实现每天全量备份(使用 pg_dump 或 pg_basebackup)

实现增量或 WAL 归档(如 archive_mode=on、archive_command)

编写脚本清理旧备份(比如保留最近 7 天/30 天)

模拟恢复流程:主库故障后,从备份恢复到新节点,记录恢复时间、挑战、成功/失败情况

在文档中写明 RTO/RPO(你自己设定目标,比如恢复 < 1 小时)和测试结果。

第三步:模块 B — 高可用/主从复制+故障切换

配置主从复制:在主库设置 wal_level=replica、max_wal_senders、hot_standby=on,在从库使用 pg_basebackup 做初始同步。 参考官方文档:物理流复制流程。
PostgreSQL
+2
pgedge.com
+2

选择一种高可用工具如 Patroni + etcd + HAProxy 实现自动故障切换。参考资料中说明:Patroni 可以监控主从状态并自动 promotion。
docs.percona.com
+1

编写故障模拟脚本或手工操作:比如强制断开主库、观察从库提升为主库、客户端连接是否切换、业务中断时间是多少。

在文档里说明架构图、网络拓扑、切换流程、测试结果(例如切换时间、数据丢失情况)。

可以进一步加分:在从库做只读负载均衡、用 HAProxy 做读写分离。

第四步:模块 C — 监控+性能优化+自动化巡检

安装监控工具:例如 Prometheus + Grafana,使用一个 exporter(如 postgres_exporter)收集 PostgreSQL 指标(连接数、缓存命中率、慢查询数、复制延迟等)

编写巡检脚本:定期运行 SQL 查询如 pg_stat_activity, pg_stat_user_indexes, pg_stat_statements,检测如锁等待、未使用索引、表膨胀、死锁情况。

编写脚本生成报告(如 HTML、Markdown),或发送告警(邮件或钉钉)当监控指标超阈值。

进行性能优化实验:在一个模拟业务场景中,先记录慢查询→使用 EXPLAIN ANALYZE 优化 SQL/添加索引/调整参数→记录优化后的性能提升。

在项目文档里展示“优化前 vs 优化后”数据,比如某查询从 5 秒降到 1 秒、缓存命中率提升 X%。

第五步:整合与总结

在 Git 仓库里把三个模块整合起来:统一 README,写项目背景、目标、技术栈、实现步骤、效果/结果。

在 README 或项目文档中添加“项目模块图”“架构图”“流程图”让整体看起来更系统。

写一段项目总结:你的角色、你做了什么、你解决了什么、结果是多少(用具体数字)。

在简历中把这个大项目列为 “个人项目” 或 “实战项目”,说明包含了备份恢复、高可用、监控运维、性能优化等多个维度。

第六步:展示成果

在 GitHub 上开源(即便是私用也可公开,体现你真做了)

可以做一个 README 或博客文章,截图监控面板、切换日志、备份脚本、优化前后对比。

在简历里加上项目链接(GitHub)+ 项目说明 + 技术栈 + 亮点成果。

1
2
3
4
5
6
7
8
9
项目名称:PostgreSQL 企业级运维平台(个人项目)  
项目概述:搭建基于 PostgreSQL 的运维自动化及高可用平台,覆盖备份恢复、高可用主从架构、监控与性能优化。
关键职责:
- 设计并实现每日全量/增量备份与恢复演练流程,实现恢复时间 ≤ 1 小时;
- 搭建主库-从库复制 + Patroni 自动故障切换方案,模拟主库故障切换,切换时间 ≤ 30秒;
- 部署 Prometheus+Grafana 监控系统,编写脚本检测锁等待、表膨胀、慢查询,并发送自动告警;
- 通过慢查询分析与索引调整,将关键查询响应时间从 5 秒降至 1 秒,缓存命中率提升 20%。
技术栈:PostgreSQL、Shell、Python、Patroni、etcd、HAProxy、Prometheus、Grafana、Linux
项目链接:[GitHub 地址]

环境配置

分别在腾讯云服务器和阿里云服务器上使用源码安装PostgreSQL,安装流程见在Linux上安装PostgresSQL

在主库部署 dvdrental 示例数据库

我的linux虚拟机上的PostgreSQL已经安装过了,因此只需导入数据库就行了。

1. 下载示例数据库文件
官方提供 .tar 备份文件(dvdrental.tar):

1
2
3
cd /usr/local/src
wget https://www.postgresqltutorial.com/wp-content/uploads/2019/05/dvdrental.zip
unzip dvdrental.zip

得到文件:dvdrental.tar
2. 创建数据库并恢复数据

1
2
createdb -U wqstage dvdrental
pg_restore -U wqstage -d dvdrental --no-owner --role=wqstage /usr/local/src/dvdrental.tar
  • --no-owner 忽略备份文件中记录的所有权信息。
  • --role=wqstage 表示以当前用户恢复对象。

    这里出了一点小问题:因为我之前在导入拓展的时候误执行了apt命令安装,导致多了一个apt版本的postgreSQL实例,因此会报错pg_restore: error: connection to server on socket “/var/run/postgresql/.s.PGSQL.5433” failed: FATAL: role “wqstage” does not exist 。解决办法是先关闭apt版本的实例,然后执行sudo systemctl disable postgresql禁止其自启动。

  1. 最后验证
    1
    SELECT COUNT(*) FROM film;
    结果返回1000行,导入成功。

主从同步(复制)手动

主库配置

1. 修改 postgresql.conf (打开远程连接、启用 WAL/归档等)
文件位置示例:/usr/local/pgsql/data/postgresql.conf
首先备份配置文件:

1
sudo cp /usr/local/pgsql/data/postgresql.conf /usr/local/pgsql/data/postgresql.conf.bak

执行vim postgresql.conf 修改以下参数:

1
2
3
4
5
6
7
8
9
10
# 允许外部主机连接数据库
listen_addresses = '*' # 解释: 允许来自任意 IP 的客户端连接(生产中按需限制)

# WAL / 复制相关(必须)
wal_level = replica # 解释: 生成足够的 WAL 信息用于物理复制(必需)
max_wal_senders = 10 # 解释: 同时允许的 WAL 发送进程数量(从库数量 + 余量)
wal_keep_size = 64 # 解释: 保留多少 WAL 用于避免过早删除(单位 MB)
archive_mode = on # 解释: 启用 WAL 归档(做 PITR/归档备份时需要)
archive_command = 'cp %p /usr/local/src/archive/%f'
# 解释: 将 WAL 文件复制到归档目录(

因为修改了archive_command,所以还要手动创建一个/usr/local/src/archive目录并修改权限,因为PostgreSQL不会自动创建。

1
2
3
sudo mkdir /usr/local/src/archive
sudo chown wqstage:wqstage /usr/local/src/archive
sudo chmod 700 /usr/local/src/archive

2. 创建 replication专用账号

1
2
3
# 习惯上我们用 postgres 或 template1 来执行管理操作,因为这些数据库通用且易于管理。
# 主从复制专用用户(因为是源码安装的,所以指定端口为5432)
sudo -u postgres psql -h localhost -p 5432 -c "CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'replica_pass';"

3. 编辑 pg_hba.conf
允许指定用户从指定 IP 地址以复制 (replication) 权限连接。

1
2
3
4
vim /usr/local/pgsql/data/pg_hba.conf

# 允许 standby 用 replicator 用户做 replication
host replication replicator <STANDBY_IP>/32 md5

4. 重启 PostgreSQL 使配置生效

1
pg_ctl restart

内网组建

首先我的一台虚拟机和云服务器不在一个局域网,我选择使用Tailscale进行内网穿透。

Tailscale是一个基于 WireGuard 的 点对点网状 VPN (mesh VPN),每台节点通过加密隧道直接互联,允许分布在不同网络、不同 NAT/防火墙后的设备互联。

  1. 在每台服务器上(主库 + 从库 + 仲裁/见证机)下载Tailscale执行下面的命令,然后登录同一个账号。

    1
    2
    curl -fsSL https://tailscale.com/install.sh | sh
    sudo tailscale up

    完成后每台机器会加入同一个 Tailnet,并分配一个 Tailscale 私有 IP 地址(如 100.x.y.z)。

  2. 查看PostgreSQL监听的端口:

    1
    sudo ss -ltnp | grep postgres
  3. 测试机器之间能相互连通(至少主库能被 Standby 连通,仲裁节点也需要与主/从连通):

    1
    2
    # 测试能连到主库的 5432 端口
    nc -zv <PRIMARY_IP> 5432

使用Tailscale后发现阿里云的Workbench一键连接会断开连接,试了半天也不行,转用ssh连接解决

从库配置

把 主库 PostgreSQL安装的 整个数据目录(包括系统库、所有用户库、配置、WAL 日志等) 拷贝到从库目标目录。它拉取的是 PostgreSQL 集群所有数据库和相关文件的物理拷贝。这样从库就可以基于这个拷贝启动,与主库同步进行复制。

1. 停止从库服务(确保数据目录没被占用)

1
sudo -u postgres pg_ctl -D /usr/local/pgsql/data stop

2. 创建一个负责管理的Linux用户postgres

1
2
3
4
5
6
sudo adduser postgres

## 新加的用户没有sudo命令权限
su - # 切换为 root
usermod -aG sudo postgres # 将 postgres 用户加入 sudoers 或 sudo 组
su - postgres #切换回来

3. 清空旧数据目录并重建

1
2
3
4
sudo -u postgres rm -rf /usr/local/pgsql/data/*
sudo -u postgres mkdir -p /usr/local/pgsql/data
sudo chown -R postgres:postgres /usr/local/pgsql/data
sudo chmod 700 /usr/local/pgsql/data

4. 从主库拉取基础备份,实现物理复制初始化

1
sudo -u postgres pg_basebackup -h <PRIMARY_IP> -p 5432 -U replicator -D /usr/local/pgsql/data -Fp -Xs -P -R
  • -D 指定目标数据目录。
  • -Fp 指定格式为 “plain files”(默认格式,不压缩为 tar)。
  • -Xs 指定 WAL 文件通过流方式获取(stream)且包括相关 WAL
  • -P 显示进度。
  • -R 指示自动创建恢复配置文件(比如 standby.signal 或 recovery.conf 根据 PostgreSQL 版本),使得该节点自动作为 standby 启动。

pg_basebackup 会提示输入 replica_pass(即在主库创建的 replicator 的密码): replica_pass
执行成功后 /usr/local/pssql/data 会包含主库的 basebackup 数据,并生成一个文件 standby.signalpostgresql.auto.conf 文件中应该包含一行 primary_conninfo = 'host=<PRIMARY_IP> port=5432 user=replicator password=...'

可能的报错:sudo: pg_basebackup: command not found,原因是sudo 会用一个受限的环境执行命令,没有继承用户的 PATH 。用绝对路径执行命令:sudo -u postgres /usr/local/pgsql/bin/pg_basebackup -h <PRIMARY_IP> -p 5432 -U replicator -D /usr/local/pgsql/data -Fp -Xs -P -R

5. 在拉取完成后,启动从库

1
sudo -u postgres pg_ctl -D /usr/local/pgsql/data start

遇到了一个报错:waiting for server to start....2025-10-28 17:45:57.797 CST [4539] FATAL: could not access file "pg_stat_statements" 这是之前学习时在主库安装了拓展,从库没安装。安装方法:cd /usr/local/src/postgresql-16.8/contrib 执行make 然后sudo make install

验证主从同步

在从库上查看 WAL 接收器状态

1
SELECT * FROM pg_stat_wal_receiver;

正常时会看到 status = streaming 和有相关连接信息
结果返回:

1
2
3
4
5
6
7
8
9
10
11
bash
pid | status | receive_start_lsn | receive_start_tli | written_lsn | flushed_lsn | received_tli | last_msg_send_time | last_msg_receipt_time | latest_end_lsn | latest_end_time | slot
_name | sender_host | sender_port | conninfo

------+-----------+-------------------+-------------------+-------------+-------------+--------------+-------------------------------+-------------------------------+----------------+-------------------------------+-----
------+---------------+-------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------------------------
5583 | streaming | 0/13000000 | 1 | 0/13000148 | 0/13000148 | 1 | 2025-10-28 18:05:01.389383+08 | 2025-10-28 18:05:01.507617+08 | 0/13000148 | 2025-10-28 18:01:27.806105+08 |
| 100.89.48.112 | 5432 | user=replicator password=******** channel_binding=prefer dbname=replication host=100.89.48.112 port=5432 fallback_application_name=walreceiver sslmode=prefer sslcompression=0 sslcert
mode=allow sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=disable krbsrvname=postgres gssdelegation=0 target_session_attrs=any load_balance_hosts=disable
(1 row)

试插入数据,验证主从同步

  1. 在主库创建测试表并插入记录(在主库 psql 执行):
1
2
3
4
5
6
7
8
sudo -u postgres psql -d dvdrental -c "CREATE TABLE test_ha(id serial primary key, note text);"
sudo -u postgres psql -d dvdrental -c "INSERT INTO test_ha(note) VALUES ('from_primary');"

#或者执行,然后在psql执行SQL语句
psql dvdrental

CREATE TABLE test_ha(id serial primary key, note text);
INSERT INTO test_ha(note) VALUES ('from_primary');
  1. 在从库查询(注意:从库是 hot_standby,只能读)
1
sudo -u postgres psql -d dvdrental -c "SELECT * FROM test_ha;"

结果是能看到主库插入的数据。

有关Linux用户和PostgreSQL用户的问题

Linux用户

Linux 系统用户是操作系统层面的用户账号,比如 root、wqstage、postgres 等。它用于控制文件系统访问、进程启动、系统权限、登录操作等。OS 用户有用户名、UID(用户 ID)、所属群组、家目录、shell 等等。
当运行:sudo -u wqstage psql …命令时,只是操作系统的 “切换用户” 活动,而不是切换数据库角色。

值得注意的是:**sudo 命令默认会 重置或清除大部分环境变量,包括 PATH,以保证安全。当运行 sudo -u wqstage which psql 时,该命令在一个新的环境下执行,此时 PATH 可能只包含标准系统路径(如 /usr/bin、/bin)而不包含你源码安装的 /usr/local/pgsql/bin。因此,即便在普通登录 session 中设定了 export PATH=/usr/local/pgsql/bin:$PATH,但使用 sudo 切换用户时,这条 PATH 设定未被继承或已被重置 。结果就是:直接登录 wqstage 使用源码安装的客户端正常;但 sudo 切换执行时却用了系统版本客户端,导致连接可能指向错误的 PostgreSQL 实例或 socket 路径,从而出现 “没有那个文件或目录” 的错误**。

PostgreSQL角色

PostgreSQL 中,数据库用户其实是 “角色(role)” 的一种情况。一个角色如果被赋予 LOGIN 属性,就可以作为“用户”登录数据库。没有 LOGIN 的角色更像“组”或“权限集合”。角色拥有或被授予权限(如创建数据库、复制权限、表的所有者、连接权限等),并控制谁能访问哪个数据库、哪个表、执行何种操作。

数据库角色 不需要与 OS 用户名、UID 对应。容易混淆的是:在安装 PostgreSQL 时,系统自动创建 OS 用户 postgres,数据库也自动创建角色 postgres。由于名称相同,人们容易误以为它们是同一个账号。事实上它们是不同层级的账号,但出于便利它们常被“配对”。

连接默认行为:在 PostgreSQL 中,如果你使用 Linux用户 wqstage 登录系统,然后执行 psql 但未指定 -U 参数,那么 psql 会尝试用角色名 wqstage 登录数据库。因为默认数据库用户名为当前 OS 用户名(例如 Linux 用户 wqstage)。正如文档指出:默认连接数据库名为当前 OS 用户。

手动故障切换主从库

1. 在从库上执行命令,将从库提升(promote)为新的主库

1
sudo -u postgres /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data promote

之后该从库成功 promotion,会成为可写的主库。可以验证其可写性:

1
sudo -u postgres psql -c "CREATE TABLE promoted_test(id serial);"

2. 若要退回到未提升前的状态
当你在某台从库上执行了 pg_ctl promote 让它成为新的主库后,原主库自动变不会成从库,可以手动将其作为从库重新加入集群

使用pg_rewind工具进行回退流程:
(这里原主库指虚拟机实例,新主库指阿里云的实例)
在原主库节点停止 PostgreSQL 服务: pg_ctl stop
在新主库节点执行 pg_rewind

1
2
3
sudo -u postgres /usr/local/pgsql/bin/pg_rewind \
-D /usr/local/pgsql/data \
--source-server="host=<NEW_PRIMARY_IP> port=5432 user=replicator password=replica_pass dbname=postgres"

在原主库的数据目录中 创建 standby.signal 文件:

1
sudo -u postgres touch /usr/local/pgsql/data/standby.signal

编辑 postgresql.auto.confpostgresql.conf,在其中加入或确认:

1
2
primary_conninfo = 'host=<NEW_PRIMARY_IP> port=5432 user=replicator password=replica_pass'
recovery_target_timeline = 'latest'

最后启动从库就可以了。

需要注意,使用pg_rewind工具,该工具需要开启 wal_log_hints = on 才能生效, 是用来在 WAL 文件中添加额外的日志提示信息的。但是如果启用 wal_log_hints = on 这个设置是在某些 WAL 文件已经生成之后才启用的, 这些已经生成的 WAL 文件将没有 wal_log_hints 信息,因此 pg_rewind 可能无法识别和同步它们。就会报错pg_rewind: error: target server needs to use either data checksums or "wal_log_hints = on"

实在没办法可以使用另一种方法,那就是删从库再重新搭建主从,也就是重新执行之前的步骤从库搭建


使用repmgr工具实现主从同步和自动化故障切换

通过repmgr实现自动化故障切换。
repmgr(replication manager)是一套开源工具套件,用于管理 PostgreSQL 服务器集群中的复制和故障转移。它通过提供设置备用服务器、监控复制以及执行故障转移或手动切换操作等管理任务的工具,增强了内置的热备功能。

安装repmgr

特别注意:如果使用源码安装的PostgreSQL,那么也使用源码安装repmgr,否则会导致/usr/local/pgsql/lib 缺失repmgr.so文件,导致一系列缺乏拓展的后果,非常严重!!!
同样,如果使用apt安装postgreSQL,那么也使用apt安装repmgr。

1
2
sudo apt-get update
sudo apt-get install postgresql-16-repmgr # 16:postgresql的版本

修改主库 postgresql.conf 文件

最好不要在默认postgresql.conf 文件中编辑这些设置,而是创建一个单独的文件,例如postgresql.replication.conf,并将其从主配置文件的末尾包含进来,如下所示: include 'postgresql.replication.conf'

1
2
3
4
5
6
7
max_wal_senders = 10
max_replication_slots = 10
wal_level = replica
hot_standby = on
archive_mode = on # repmgr 本身不需要 WAL 文件归档。
archive_command = '/usr/local/src/archive' # 需要自己创建文件夹
wal_log_hints = on #如果打算使用pg_rewind,并且集群未使用数据校验和进行初始化,则需要考虑启用

在主库实例上创建 repmgr 用户和数据库

1
2
CREATE USER repmgr WITH PASSWORD 'repmgr_pass' SUPERUSER;
CREATE DATABASE repmgr OWNER repmgr;

修改主库 pg_hba.conf 文件

1
2
3
4
5
6
7
8
9
10
$ vim pg_hba.conf

# 示例ip
local   replication   repmgr                              trust
host    replication   repmgr      100.119.50.59/32        trust
host    replication   repmgr      0.0.0.0/0               trust

local   repmgr        repmgr                              trust
host    repmgr        repmgr      100.119.50.59/32        trust
host    repmgr        repmgr      0.0.0.0/0               trust

在主库上创建repmgr.conf 配置文件

repmgr.conf不应将其存储在 PostgreSQL 数据目录中,因为在设置或重新初始化 PostgreSQL 服务器时可能会被覆盖。
可以放在/etc/repmgr.conf目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
node_id=1
node_name='node1'
conninfo='host=100.89.48.112 user=repmgr dbname=repmgr password=repmgr_pass connect_timeout=30'
data_directory='/usr/local/pgsql/data'

# failover设置(用于repmgrd)
failover=automatic
promote_command='/usr/bin/repmgr -f /etc/repmgr.conf standby promote --log-to-file' #当 repmgrd 确定当前节点将成为新的主节点时 ,将在故障转移情况下执行 promote_command 中定义的程序或脚本
follow_command='/usr/bin/repmgr -f /etc/repmgr.conf standby follow --log-to-file --upstream-node-id=%n' # %n将被替换 repmgrd与新的主节点的ID, 如果没有提供, repmgr standby follow将尝试自行确定新的主repmgr standby follow节点,
# 但如果在新主节点提升后原主节点重新上线,则存在导致节点继续跟随原主节点的风险 。

# 显示复制进度
pg_basebackup_options = '-P'
# 高级设置
monitoring_history=true # 是否将监控数据写入“monitoring_history”表
monitor_interval_secs=10 # 写入监控数据的间隔
reconnect_attempts=8 # 故障转移之前,尝试重新连接的次数
reconnect_interval=5 # 故障转移之前,尝试重新连接的间隔(以秒为单位)

# 日志设置
log_level=INFO
log_file='/usr/log/repmgr/repmgrd.log' # log 文件需要提前创建
log_status_interval=300

注册主服务器

要使 repmgr 支持复制集群,必须使用 repmgr 注册主节点(repmgr primary register)。这将安装repmgr的扩展和元数据对象,并为主服务器添加元数据记录。

1
2
3
4
5
6
7
repmgr -f /etc/repmgr.conf primary register

#成功会出现以下信息
#INFO: connecting to primary database...
#NOTICE: attempting to install extension "repmgr"
#NOTICE: "repmgr" extension successfully installed
#NOTICE: primary node record (id: 1) registered

注册后查看集群信息命令:

1
repmgr -f /etc/repmgr.conf cluster show

在从库上创建repmgr.conf配置文件

/etc/repmgr.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
node_id=2
node_name='node2'
conninfo='host=100.119.50.59 user=repmgr dbname=repmgr password=repmgr_pass connect_timeout=30'
data_directory='/usr/local/pgsql/data'

# failover设置
failover=automatic
promote_command='/usr/bin/repmgr -f /etc/repmgr.conf standby promote --log-to-file'
follow_command='/usr/bin/repmgr -f /etc/repmgr.conf standby follow --log-to-file --upstream-node-id=%n'

# 显示复制进度
pg_basebackup_options = '-P'

# 高级设置
monitoring_history=true # 是否将监控数据写入“monitoring_history”表
monitor_interval_secs=10 # 写入监控数据的间隔
reconnect_attempts=8 # 故障转移之前,尝试重新连接的次数
reconnect_interval=5 # 故障转移之前,尝试重新连接的间隔(以秒为单位)

# 日志设置
log_level=INFO
log_file='/usr/log/repmgr/repmgrd.log' # log 文件需要提前创建
log_status_interval=300

repmgr standby clone 是基于 pg_basebackup 来实现克隆的,原生 pg_basebackup 支持-P选项以显示复制进度

克隆从库

在从库上,不要创建 PostgreSQL 实例(即不要执行 initdb 或任何包提供的数据库创建脚本),但要确保目标数据目录(以及希望 PostgreSQL 使用的任何其他目录)存在并归其所有 postgres 系统用户。权限必须设置为 0700 (drwx------)
使用--dry-run测试是否具备复制的条件

1
repmgr -h 100.89.48.112 -U repmgr -d repmgr -f /etc/repmgr.conf standby clone --dry-run

没有问题的话直接执行就行:

1
repmgr -h 100.89.48.112 -U repmgr -d repmgr -f /etc/repmgr.conf standby clone

执行过程图如下:

如果报错需要密码,可以配置密码:

1
2
3
4
5
6
7
8
9
# su postgres
$ vim ~/.pgpass

# 添加以下内容到 ~/.pgpass 文件,用户、数据库和密码修改为自己的即可
#hostname:port:database:username:password
100.89.48.112:5432:repmgr:repmgr:repmgr_pass
# 修改 ~/.pgpass 文件权限

chmod 600 ~/.pgpass

注册从库

首先先启动从库pg_ctl start

这里遇到了一个问题,启动时报错FATAL: could not access file "repmgr",其实是因为通过apt安装的repmgr的默认库文件存放目录是/usr/lib/postgresql/16/lib/repmgr.so,而通过源码安装的PostgreSQL 的库文件在:pg_config --pkglibdir 显示的 /usr/local/pgsql/lib,可以直接cp .so文件过去来解决。

验证复制功能是否正常(会显示一条记录):

1
SELECT * FROM pg_stat_replication;

没问题就可以注册从库啦:

1
2
3
4
repmgr -f /etc/repmgr.conf standby register

# 或者提供主服务器的连接参数:
# repmgr -f /etc/repmgr.conf standby register -h 100.119.50.59 -p 5432 -U repmgr -d repmgr

同样可以查看注册信息:repmgr -f /etc/repmgr.conf cluster show

注册见证节点(witness)

发生故障转移的情况下,见证节点提供证据表明是主服务器本身是不可用的,而不是例如不同的物理位置之间的网络分离(防止脑裂问题出现) ,只有在使用 repmgrd 时,见证服务器才有用。
在与集群主服务器位于同一网段的服务器上设置一个普通 PostgreSQL 实例,此实例不应与主服务器位于同一物理主机上,否则,如果主服务器因硬件问题发生故障,见证服务器也会丢失。安装 repmgrrepmgrd,注册该实例为 witness(repmgr witness register)

  1. 启动节点 postgres 服务:pg_ctl start

  2. 添加 repmgr.conf 配置,基本配置与主库相同,保持 node_idnode_nameconninfo 与主库不同即可。

  3. 修改 postgresql.conf 文件,加入repmgr 共享库

    1
    shared_preload_libraries = 'passwordcheck, repmgr'

    连接数据库,执行:

    1
    2
    3
    4
    5
    CREATE USER repmgr WITH PASSWORD 'repmgr_pass' SUPERUSER;
    CREATE DATABASE repmgr OWNER repmgr;

    # 在repmgr数据库执行添加拓展
    CREATE EXTENSION repmgr;
  4. 重启数据库 pg_ctl restart

5. 注册见证节点

1
repmgr -h 100.89.48.112 -f /etc/repmgr.conf witness  register
  1. 启动 repmgrd
    1
    repmgrd -f /etc/repmgr.conf start

启动 repmgrd 守护进程

repmgrd(replication manager daemon)是一个管理和监控守护进程,它可以自动执行故障转移和更新备用节点以跟随新的主节点等操作,是自动化故障切换的核心。它在复制集群中的每个节点上运行。并提供有关每个备用节点状态的监控信息。

  1. 要使用repmgrd ,必须在postgresql.conf进行配置:

    1
    shared_preload_libraries = 'repmgr'
  2. 配置从库的 failover 设置
    已在上面步骤的 repmgr.conf 配置过了

  3. 在主库、从库和见证节点上分别启动 repmgrd 进程

    1
    repmgrd -f /etc/repmgr.conf start
  4. 查看repmgrd是否已经在后台运行

    1
    ps -ef | grep repmgrd
  5. 若需要重启

    1
    2
    kill -9 `cat /tmp/repmgrd.pid`
    repmgrd -f /etc/repmgr.conf --daemonize

故障测试和rejoin

  1. 关闭主库node1

  2. 在从库node2执行 repmgr -f /etc/repmgr.conf cluster show可以看到原主库连接不上了,而一个从库node2被自动提升为primary

  3. 如果node1恢复,可以选择将其作为从库重新加入集群
    首先停止node1的postgreSQL服务pg_ctl stop ,然后使用repmgr的rejoin命令:

    1
    2
    repmgr -f /etc/repmgr.conf node rejoin -h 100.89.48.112 -U repmgr -d repmgr --force-rewind
    # --force-rewind 其实就是在加入前执行pg_rewind来确保时间点一致

    解决了一个问题,在执行rejoin命令时报错ERROR: this node ahead of the rejoin target on the same timeline ,重启主库和从库的postgresql服务就好了。具体原因不确定,推测是因为多次切换主从导致时间点错误,通过重启触发了清理未完事务、生成新的 timelinecheckpoint,从而让本地和目标之间的时间点差异消除

编写自动化 bash 脚本自动将恢复的节点加入

编写bash脚本实现rejoin的自动化,通过crontab -e设置每十分钟执行一次脚本,并未其设置为带锁模式,防止与上一轮重叠执行导致的逻辑混乱。减少了操作量实现故障消除的节点自动重新加入集群。

1
2
# 间隔十分钟执行
*/10 * * * * /usr/bin/flock -n /tmp/auto_rejoin.lock /usr/local/src/auto_rejoin.sh >> /usr/local/src/rejoin.log 2>&1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/bin/bash

# ========== PostgreSQL 自动检测与 rejoin脚本 ==========
# 说明:检测掉线节点 -> 停止服务 -> 清空目录 -> clone -> rejoin
# =====================================================

PATH=/usr/local/pgsql/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

log() {
echo "$(date '+[%Y-%m-%d %H:%M:%S]') - $*"
}

log "脚本开始执行"
# 获取 cluster 状态
REPMGR_CLUSTER_SHOW=$(repmgr -f "$REPMGR_CONF" cluster show | grep -v '^WARNING')
PRIMARY_LINE=$(echo "$REPMGR_CLUSTER_SHOW" | grep -i '! running as primary' || true)
PGDATA="/usr/local/pgsql/data"
CLONE_TS_DIR="/mnt/ssd/pg_tablespace"
REPMGR_CONF="/etc/repmgr.conf"

pg_ctl -D "PGDATA" start

# 检查是否有掉线的节点
CURRENT_PRIMARY_IP=$(echo "$PRIMARY_LINE" | grep -o "host=[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | cut -d'=' -f2 || true)
if [ -z "$CURRENT_PRIMARY_IP" ]; then
log "没有节点掉线,一切正常"
log "脚本退出"
exit 0
else
log "检测到节点掉线,当前 primary_ip = $CURRENT_PRIMARY_IP"
fi
if ping -c 1 "$CURRENT_PRIMARY_IP" &>/dev/null; then
log "本节点能连接到主库,开始执行恢复流程"
# 停止 PostgreSQL 服务
log "停止 PostgreSQL 服务 ..."
if pg_ctl -D "$PGDATA" stop; then
log "成功关闭 PostgreSQL 服务"
else
log "ERROR: 关闭 PostgreSQL 服务失败,退出脚本"
exit 1
fi

#首先尝试直接rejoin
MAX_REJOIN=10
count=1
while [ $count -le $MAX_REJOIN ]; do
log "尝试rejoin 第$count次"
if repmgr -f "$REPMGR_CONF" -h "$CURRENT_PRIMARY_IP" node rejoin -U repmgr -d repmgr --force-rewind; then
log "rejoin成功,退出脚本"
exit 0
else
log "rejoin 失败"
fi
count=$(( count + 1 ))
done
log "rejoin失败,重新clone后再rejoin"
# 清空数据目录 & tablespace 目录
log "清空数据目录 $PGDATA/*"
sudo rm -rf "$PGDATA"/*
log "清空 tablespace 目录 $CLONE_TS_DIR/*"
sudo rm -rf "$CLONE_TS_DIR"/*

# 多次尝试 clone
MAX_ATTEMPTS=15
attempt=1
SUCCESS_CLONE=0
while [ $attempt -le $MAX_ATTEMPTS ]; do
log "尝试 clone 第 $attempt 次"
if repmgr -f "$REPMGR_CONF" -h "$CURRENT_PRIMARY_IP" -U repmgr -d repmgr standby clone; then
log "clone 成功"
SUCCESS_CLONE=1
break
else
log "clone 第 $attempt 次失败,等待10秒后重试"
sleep 10
fi
attempt=$(( attempt + 1 ))
done

if [ $SUCCESS_CLONE -eq 0 ]; then
log "ERROR: 所有 clone 尝试失败,退出脚本"
exit 1
fi
# 注册从库
log "执行 rejoin 操作 ..."
count=1
while [ $count -le $MAX_REJOIN ]; do
log "尝试rejoin 第$count次"
if repmgr -f "$REPMGR_CONF" -h "$CURRENT_PRIMARY_IP" node rejoin -U repmgr -d repmgr --force-rewind; then
log "rejoin成功,退出脚本"
exit 0
else
log "rejoin 失败"
fi
count=$(( count + 1 ))
done


log "脚本执行完毕"
else
log "当前节点 尚未恢复到主库连接,跳过恢复流程"
log "脚本结束"
fi

# 启动 PostgreSQL
log "启动 PostgreSQL 服务 ..."
if pg_ctl -D "$PGDATA" start; then
log "PostgreSQL 启动成功"
else
log "ERROR: PostgreSQL 启动失败,退出脚本"
exit 1
fi

repmgrd 的暂停

如果需要调试或者其他什么原因想要暂停repmgrd守护服务,可以使用命令:

1
2
3
4
5
6
repmgr -f /etc/repmgr.conf daemon pause
# 查看守护是否暂停
repmgr -f /etc/repmgr.conf daemon status

# 恢复
repmgr -f /etc/repmgr.conf daemon unpause

关于 repmgr.conf 文件

  • 若修改了元数据关键参数(如 node_id, node_name, data_directory),需要在每个节点执行

    1
    2
    3
    $ repmgr primary register --force -f path/to/repmgr.conf
    $ repmgr standby register --force -f path/to/repmgr.conf
    $ repmgr witness register --force -f path/to/repmgr.conf -h primary_host
  • 若使用 repmgrd 守护进程并修改了其相关参数 → 重载或重启守护进程:

    1
    sudo systemctl reload repmgrd

  • Copyrights © 2023-2025 Hexo

请我喝杯咖啡吧~

支付宝
微信