pg_auto_failover 高可用 PostgreSQL 集群部署文档
目录结构
/server/temp/data/
├── monitor/
│ └── pgdata/ # 监控节点数据
├── node1/
│ └── pgdata/ # 主节点数据
├── node2/
│ └── pgdata/ # 备节点数据
└── backup/ # 备份目录
docker-compose.yml
services:
monitor:
image: citusdata/pg_auto_failover:latest
user: "101:102"
command: >-
pg_autoctl create monitor
--ssl-self-signed --auth trust --run
environment:
PGDATA: /server/temp/data/monitor/pgdata
ports:
- "5400:5432"
volumes:
- /server/temp/data/monitor:/server/temp/data/monitor
networks: [pgnet]
node1:
image: citusdata/pg_auto_failover:latest
user: "101:102"
depends_on:
- monitor
command: >-
pg_autoctl create postgres
--name node1 --hostname node1
--pgdata /server/temp/data/node1/pgdata
--monitor 'postgresql://autoctl_node@monitor/pg_auto_failover?sslmode=require'
--ssl-self-signed --auth trust --pg-hba-lan --run
environment:
PGDATA: /server/temp/data/node1/pgdata
ports:
- "5432:5432"
volumes:
- /server/temp/data/node1:/server/temp/data/node1
- /server/temp/data/backup:/server/temp/data/backup
networks: [pgnet]
node2:
image: citusdata/pg_auto_failover:latest
user: "101:102"
depends_on:
- monitor
command: >-
pg_autoctl create postgres
--name node2 --hostname node2
--pgdata /server/temp/data/node2/pgdata
--monitor 'postgresql://autoctl_node@monitor/pg_auto_failover?sslmode=require'
--ssl-self-signed --auth trust --pg-hba-lan --run
environment:
PGDATA: /server/temp/data/node2/pgdata
ports:
- "5433:5432"
volumes:
- /server/temp/data/node2:/server/temp/data/node2
- /server/temp/data/backup:/server/temp/data/backup
networks: [pgnet]
networks:
pgnet:
首次部署
# 创建目录
sudo mkdir -p /server/temp/data/{monitor,node1,node2,backup}
sudo chown -R 101:102 /server/temp/data
sudo chmod 755 /server/temp/data
sudo chmod 700 /server/temp/data/*
# 分步启动
docker compose up -d monitor && sleep 15
docker compose up -d node1 && sleep 30
docker compose up -d node2 && sleep 45
# 查看集群状态
docker compose exec node1 pg_autoctl show state
创建应用用户
# 在 node1(主节点)上创建
docker compose exec node1 psql -U postgres \
-c "CREATE USER dev WITH SUPERUSER PASSWORD 'boawayAdmin';"
docker compose exec node1 psql -U postgres \
-c "CREATE DATABASE dev OWNER dev;"
# 在 node1(主节点)上添加认证规则
docker compose exec node1 bash -c "
sed -i '/host all dev/d' /server/temp/data/node1/pgdata/pg_hba.conf
echo 'host all dev 0.0.0.0/0 md5' >> /server/temp/data/node1/pgdata/pg_hba.conf
pg_ctl reload -D /server/temp/data/node1/pgdata
"
# 在 node2(备节点)上添加认证规则
docker compose exec node2 bash -c "
sed -i '/host all dev/d' /server/temp/data/node2/pgdata/pg_hba.conf
echo 'host all dev 0.0.0.0/0 md5' >> /server/temp/data/node2/pgdata/pg_hba.conf
pg_ctl reload -D /server/temp/data/node2/pgdata
"
端口映射
| 服务 | 宿主机端口 | 容器端口 |
|---|---|---|
| monitor | 5400 | 5432 |
| node1 | 5432 | 5432 |
| node2 | 5433 | 5432 |
常用操作
# 查看集群状态
docker compose exec node1 pg_autoctl show state
# 查看日志
docker compose logs node1 --tail 20
docker compose logs node2 --tail 20
# 重启
docker compose restart
# 完全重建
docker compose down -v
sudo rm -rf /server/temp/data/*
# 重新执行首次部署步骤
注意事项
- 所有节点的 PGDATA 均使用子目录
pgdata,避免 pg_autoctl 删除挂载点时权限不足 - 首次启动需分步进行,等待 monitor 和 node1 就绪后再启动 node2
- 容器内 postgres 用户 UID/GID 为 101:102,宿主机目录权限需匹配
- 重建时需先清理 monitor 上残留的节点注册记录
统一访问入口
通过haproxy提供统一访问入口,需要检测pg的主从身份。
haproxy + psql
首先给haproxy:latest重建(原版haproxy镜像中没有psql工具,需要通过这个工具查询数据库当前是不是primary),
将psql工具打入镜像中,便于后续执行检测。
创建dockerfile(通过国内源安装避免速度过慢)
FROM haproxy:latest
USER root
# 换国内源 + 缓存挂载 + 安装清理,合并为一层
RUN sed -i 's@deb.debian.org@mirrors.tuna.tsinghua.edu.cn@g' /etc/apt/sources.list.d/debian.sources && \
sed -i 's@security.debian.org@mirrors.tuna.tsinghua.edu.cn/debian-security@g' /etc/apt/sources.list.d/debian.sources && \
apt-get update && \
apt-get install -y --no-install-recommends postgresql-client && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
USER haproxy
构建镜像
docker build -t haproxy-psql:latest .
构建成功后可以创建测试容器来获取主备的状态,注意ip、端口、数据库指定为postgres,
通过SELECT pg_is_in_recovery()获取
docker run --rm haproxy-psql:latest bash -c '
export PGPASSWORD="boawayAdmin"
result=$(psql -h "192.168.2.23" -p 5432 -U dev -d postgres -tAc "SELECT pg_is_in_recovery()")
echo "Result: $result"
'
haproxy配置
方案一:单入口 + 主库校验
适用场景
- 只需要一个对外入口
- 后端一主一备,故障切换后自动跟随主库
- 不需要读写分离,所有流量走同一个端口
架构说明
- 对外只暴露一个端口(5000)
- HAProxy 通过 external-check 脚本检测后端节点是否为主库
- 只有主库参与负载,备库不接收流量
- 故障切换后,HAProxy 自动将流量切到新主库
检查脚本
路径:/server/temp/data/haproxy/check_primary.sh
#!/bin/bash
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export PGPASSWORD="boawayAdmin"
HOST="$3"
PORT="$4"
STATUS=$(psql -tAX -h "$HOST" -p "$PORT" -U dev -d postgres \
-c "SELECT pg_is_in_recovery();" 2>/dev/null)
if [ "$STATUS" = "f" ]; then
exit 0
else
exit 1
fi
HAProxy 配置
路径:/server/temp/data/haproxy/haproxy.cfg
global
maxconn 2000
external-check
insecure-fork-wanted
defaults
mode tcp
timeout connect 5s
timeout client 60s
timeout server 60s
frontend main_frontend
bind *:5000
default_backend main_backend
backend main_backend
option external-check
external-check command /usr/local/bin/check_primary.sh
default-server inter 3s fall 2 rise 2 on-marked-down shutdown-sessions
server node1 192.168.2.23:5432 check
server node2 192.168.2.23:5433 check
Docker Compose 配置
haproxy:
image: haproxy-psql:latest
container_name: haproxy
depends_on:
- node1
- node2
networks:
- pgnet
ports:
- "5000:5000"
volumes:
- ./data/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
- ./data/haproxy/check_primary.sh:/usr/local/bin/check_primary.sh:ro
command: ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg"]
restart: unless-stopped
行为说明
| 场景 | 5000 端口 |
|---|---|
| 一主一备 | 只连主库 |
| 主库挂,备库升主 | 自动切到新主库 |
| 原主恢复成备库 | 继续连新主库,原主不接收流量 |
方案二:双入口 + 读写分离
适用场景
- 需要区分读写流量
- 写操作走独立端口,确保只到主库
- 读操作走独立端口,主备均可读取
架构说明
- 5000 写入口:只路由主库
- 5001 读入口:主备轮询,所有节点均可读
- 写入口使用 external-check 脚本检测主库
- 读入口使用 pgsql-check 做基础连通性检测
检查脚本
路径:/server/temp/data/haproxy/check_role.sh
#!/bin/bash
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export PGPASSWORD="boawayAdmin"
EXPECTED="$1"
HOST="$3"
PORT="$4"
STATUS=$(psql -tAX -h "$HOST" -p "$PORT" -U dev -d postgres \
-c "SELECT pg_is_in_recovery();" 2>/dev/null)
if [ "$EXPECTED" = "primary" ]; then
[ "$STATUS" = "f" ] && exit 0 || exit 1
else
[ "$STATUS" = "t" ] && exit 0 || exit 1
fi
HAProxy 配置
路径:/server/temp/data/haproxy/haproxy.cfg
global
maxconn 2000
external-check
insecure-fork-wanted
defaults
mode tcp
timeout connect 5s
timeout client 60s
timeout server 60s
frontend write_frontend
bind *:5000
default_backend write_backend
backend write_backend
option external-check
external-check command /usr/local/bin/check_role.sh primary
default-server inter 3s fall 2 rise 2 on-marked-down shutdown-sessions
server node1 192.168.2.23:5432 check
server node2 192.168.2.23:5433 check
frontend read_frontend
bind *:5001
default_backend read_backend
backend read_backend
balance roundrobin
option pgsql-check user dev
default-server inter 3s fall 3 rise 2
server node1 192.168.2.23:5432 check
server node2 192.168.2.23:5433 check
Docker Compose 配置
haproxy:
image: haproxy-psql:latest
container_name: haproxy
depends_on:
- node1
- node2
networks:
- pgnet
ports:
- "5000:5000"
- "5001:5001"
volumes:
- ./data/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
- ./data/haproxy/check_role.sh:/usr/local/bin/check_role.sh:ro
command: ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg"]
restart: unless-stopped
行为说明
| 场景 | 5000 写端口 | 5001 读端口 |
|---|---|---|
| 一主一备 | 只连主库 | 主备轮询,所有节点可读 |
| 主库挂,备库升主 | 自动切到新主库 | 单节点正常读 |
| 原主恢复成备库 | 继续连新主库 | 主备轮询恢复 |
通用操作
启动服务
cd /server/temp
docker rm -f haproxy
docker compose up -d haproxy
查看日志
docker logs haproxy
留待后查,同时方便他人
联系我:ivesbao@163.com
联系我:ivesbao@163.com

浙公网安备 33010602011771号