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
posted @ 2026-10-30 15:47  Hey,Coder!  阅读(11)  评论(0)    收藏  举报