在实际生产环境中,PostgreSQL(简称 Pgsql)作为一款强大的开源关系型数据库,单节点部署存在明显的单点故障风险------一旦主库宕机,将直接导致业务中断、数据丢失,严重影响系统可用性。而主从复制技术,正是解决这一痛点的核心方案:通过搭建主库(Master)与从库(Slave)架构,主库负责处理所有写操作(增删改),从库实时同步主库数据并提供读操作支持,既能实现数据备份,又能分担主库读压力,同时在主库故障时可快速切换到从库,保障业务连续性。
Docker 作为轻量级容器化工具,能够快速部署、隔离 Pgsql 实例,大幅降低主从复制的环境搭建复杂度,无需担心不同系统的环境差异,实现"一次配置,多环境复用"。本文将以通俗易懂的语言,结合详细的示例代码,从环境准备、主库配置、从库配置、同步验证、故障排查到进阶拓展,完整讲解 Docker 下 Pgsql 主从复制的配置全过程,适合新手入门和运维人员实操参考。
一、核心概念与前置说明
在开始配置前,先明确几个核心概念,避免后续操作混淆,同时梳理好前置准备工作,确保配置过程顺畅。
1.1 主从复制核心原理
Pgsql 主从复制基于 WAL(Write-Ahead Log,预写日志) 实现,核心流程如下:
-
主库执行所有写操作时,会先将操作记录写入 WAL 日志文件;
-
从库通过专用复制用户连接主库,实时获取主库的 WAL 日志;
-
从库将获取到的 WAL 日志解析并重放,从而同步主库的数据,保持主从数据一致性。
根据 WAL 日志的同步方式,Pgsql 主从复制主要分为两种:
-
异步复制(默认):主库写入 WAL 日志后,无需等待从库确认接收,即可返回写操作成功。优点是性能好,缺点是主库宕机时,可能存在少量未同步的数据丢失,适合对数据一致性要求不极致的场景。
-
同步复制:主库写入 WAL 日志后,必须等待至少一个从库确认接收并写入本地磁盘,才能返回写操作成功。优点是数据一致性高,无数据丢失风险,缺点是主库写性能会受网络延迟影响,适合金融、电商等对数据一致性要求极高的场景。
本文将先实现最常用的异步复制,后续拓展部分会讲解如何切换为同步复制。
1.2 前置准备
确保你的环境满足以下条件,避免配置过程中出现异常:
-
已安装 Docker 和 Docker Compose(推荐使用 Docker Compose 管理容器,简化配置和启动流程);
-
服务器空闲端口:主库默认使用 5432 端口,从库建议使用 5433 端口(可自定义,需确保端口未被占用);
-
网络通畅:主从容器需在同一网络下,确保从库能正常访问主库的 5432 端口;
-
Pgsql 镜像:推荐使用官方镜像(postgres:14,稳定版,兼容性好),避免使用第三方镜像导致配置异常。
验证 Docker 环境(终端执行以下命令):
bash
# 验证 Docker 是否启动
docker --version
# 验证 Docker Compose 是否安装
docker compose --version
若均能正常显示版本信息,说明 Docker 环境就绪。
二、Docker 环境搭建 Pgsql 主从架构(详细步骤)
本次配置采用"单主单从"架构(最基础、最常用的架构),使用 Docker Compose 统一管理主从容器,步骤分为:创建自定义网络、编写 Docker Compose 配置、启动容器、配置主库、配置从库、验证同步,每一步都提供详细示例代码,可直接复制实操。
2.1 第一步:创建自定义 Docker 网络
为了让主从容器能够通过容器名直接通信(避免使用 IP 地址,减少配置复杂度),我们先创建一个自定义的 Docker 网络,主从容器都加入该网络。
bash
# 创建自定义网络(网络名:pg_network,可自定义)
docker network create pg_network
# 查看网络是否创建成功
docker network ls
执行后,若能在网络列表中看到 pg\_network,说明网络创建成功。后续主从容器将通过该网络通信,主库容器名可直接作为"主机名"被从库访问。
2.2 第二步:编写 Docker Compose 配置文件
创建一个工作目录(如pg\-master\-slave),在目录下创建 docker\-compose\.yml 文件,该文件将定义主库(pg-master)和从库(pg-slave)的容器配置,包括镜像、端口映射、数据挂载、环境变量等。
示例代码(可直接复制,注释已详细说明每个配置的作用):
yaml
version: '3.8' # Docker Compose 版本,兼容大多数 Docker 版本
services:
# 主库配置(pg-master)
pg-master:
image: postgres:14 # 使用 Pgsql 14 官方镜像
container_name: pg-master # 容器名,用于从库连接
restart: always # 容器异常退出时自动重启
network_mode: pg_network # 加入自定义网络
ports:
- "5432:5432" # 端口映射:宿主机5432端口 → 容器5432端口(主库默认端口)
environment:
- POSTGRES_USER=postgres # 数据库超级管理员用户名
- POSTGRES_PASSWORD=123456 # 数据库超级管理员密码(生产环境请修改为复杂密码)
- POSTGRES_DB=testdb # 初始化默认数据库(可自定义)
volumes:
# 数据挂载:将宿主机目录 ./master/data 挂载到容器 /var/lib/postgresql/data
# 作用:持久化主库数据,容器删除后数据不丢失
- ./master/data:/var/lib/postgresql/data
# 配置文件挂载:后续将主库配置文件放在 ./master/conf 目录,挂载到容器对应位置
- ./master/conf:/var/lib/postgresql/data/conf
# 启动后执行的命令(确保配置文件目录权限正确)
command: ["sh", "-c", "chmod 700 /var/lib/postgresql/data && exec postgres"]
# 从库配置(pg-slave)
pg-slave:
image: postgres:14 # 与主库使用相同版本的镜像,避免版本兼容问题
container_name: pg-slave # 容器名
restart: always
network_mode: pg_network
ports:
- "5433:5432" # 端口映射:宿主机5433端口 → 容器5432端口(避免与主库端口冲突)
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=123456 # 与主库超级管理员密码一致,方便管理
- POSTGRES_DB=testdb
volumes:
- ./slave/data:/var/lib/postgresql/data
- ./slave/conf:/var/lib/postgresql/data/conf
command: ["sh", "-c", "chmod 700 /var/lib/postgresql/data && exec postgres"]
# 依赖主库:确保主库启动后,再启动从库(避免从库先启动无法连接主库)
depends_on:
- pg-master
配置说明:
-
数据挂载:通过
volumes挂载宿主机目录,确保容器删除后,Pgsql 数据不会丢失,同时方便后续修改配置文件; -
网络配置:
network\_mode: pg\_network表示主从容器都加入自定义网络,可通过容器名(pg-master、pg-slave)直接通信; -
依赖配置:
depends\_on: \[pg\-master\]确保主库先启动,避免从库启动时主库未就绪,导致连接失败; -
权限设置:
chmod 700 /var/lib/postgresql/data确保 Pgsql 数据目录权限正确(Pgsql 要求数据目录权限为 700,否则无法启动)。
2.3 第三步:启动主从容器
在 docker\-compose\.yml 文件所在目录,执行以下命令启动主从容器:
bash
# 启动容器(后台运行)
docker compose up -d
# 查看容器启动状态
docker compose ps
若输出中 pg\-master 和pg\-slave 的状态均为 Up,说明容器启动成功。若启动失败,可通过 docker logs pg\-master 或 docker logs pg\-slave 查看日志,排查错误(常见错误为端口占用、数据目录权限不足)。
2.4 第四步:配置主库(关键步骤)
主库的核心配置的是:开启 WAL 日志、创建复制专用用户、配置访问权限,允许从库连接并获取 WAL 日志。
4.1 进入主库容器
bash
# 进入主库容器(容器名:pg-master)
docker exec -it pg-master bash
进入容器后,默认处于 / 目录,接下来需要修改 Pgsql 的核心配置文件 postgresql\.conf 和pg\_hba\.conf(这两个文件位于容器内 /var/lib/postgresql/data 目录下)。
4.2 修改主库 postgresql.conf 配置
该文件是 Pgsql 的核心配置文件,需开启 WAL 日志相关配置,支持主从复制。
bash
# 进入 Pgsql 数据目录(配置文件所在目录)
cd /var/lib/postgresql/data
# 使用 vi 编辑 postgresql.conf(若容器内无 vi,可先执行 apt update && apt install -y vim)
vi postgresql.conf
找到以下配置项,修改为对应值(若配置项被注释,先取消注释;若不存在,直接添加到文件末尾):
ini
# 1. 监听所有IP地址(允许从库远程连接)
listen_addresses = '*'
# 2. 开启 WAL 日志归档(主从复制必需)
archive_mode = on
# 3. WAL 日志归档命令(将 WAL 日志复制到归档目录,这里简化配置,实际可自定义路径)
archive_command = 'cp %p /var/lib/postgresql/data/pg_archive/%f'
# 4. WAL 日志级别(设置为 replica,支持主从复制)
wal_level = replica
# 5. 最大 WAL 发送进程数(控制从库最大连接数,建议设置为从库数量+1,这里设为5)
max_wal_senders = 5
# 6. WAL 日志保留数量(单位:个,每个 WAL 日志默认16MB,保留64个即1024MB,避免从库同步时日志被删除)
wal_keep_segments = 64
# 7. WAL 发送超时时间(从库连接超时时间,设为60秒)
wal_sender_timeout = 60s
# 8. 主库最大连接数(建议从库最大连接数大于主库,避免从库连接数不足)
max_connections = 100
配置说明:
-
archive\_mode = on:开启 WAL 日志归档,确保主库的 WAL 日志不会被自动清理,以便从库同步; -
wal\_level = replica:设置 WAL 日志级别为 replica,支持主从复制所需的日志信息; -
max\_wal\_senders:控制从库最大连接数,若有多个从库,需适当增大该值。
修改完成后,保存并退出 vi(按 Esc,输入 :wq 回车)。
4.3 创建 WAL 归档目录
由于上面配置了 archive\_command,需要创建对应的归档目录,并设置权限:
bash
# 在主库数据目录下创建归档目录
mkdir -p /var/lib/postgresql/data/pg_archive
# 设置目录权限(与数据目录一致,为700)
chmod 700 /var/lib/postgresql/data/pg_archive
4.4 修改主库 pg_hba.conf 配置
该文件是 Pgsql 的访问控制配置文件,用于控制哪些 IP、哪些用户可以访问数据库,需添加从库的访问权限,允许复制用户连接。
bash
# 继续在数据目录下,编辑 pg_hba.conf
vi pg_hba.conf
在文件末尾添加以下内容(允许从库通过复制用户连接主库):
ini
# 允许从库(同一自定义网络下的所有IP)使用复制用户连接主库
# 格式:host 数据库类型 用户名 允许访问的IP 认证方式
host replication replica 172.18.0.0/16 trust
# 可选:允许宿主机访问主库(方便本地测试)
host all all 0.0.0.0/0 md5
配置说明:
-
replication:表示数据库类型为复制(主从复制专用); -
replica:后续将创建的复制专用用户名(可自定义); -
172\.18\.0\.0/16:自定义 Docker 网络的网段(可通过docker network inspect pg\_network查看网段,替换为实际网段); -
trust:认证方式为信任(同一网络下,无需密码即可连接,适合测试环境;生产环境建议改为md5,需输入密码)。
修改完成后,保存并退出 vi。
4.5 创建复制专用用户
进入 Pgsql 客户端,创建一个具备 replication(复制)权限的专用用户,用于从库连接主库同步数据(避免使用超级管理员用户,提升安全性)。
bash
# 进入 Pgsql 客户端(使用超级管理员用户 postgres)
psql -U postgres
# 创建复制专用用户(用户名:replica,密码:replica123,可自定义)
CREATE ROLE replica LOGIN REPLICATION ENCRYPTED PASSWORD 'replica123';
# 验证用户是否创建成功(查看所有用户)
\du
# 退出 Pgsql 客户端
\q
执行 \\du 后,若能看到 replica 用户,且 Attributes 包含 Replication,说明用户创建成功。
4.6 重启主库容器,使配置生效
主库配置修改完成后,需重启容器,让配置生效:
bash
# 退出主库容器
exit
# 重启主库容器
docker restart pg-master
# 查看主库容器日志,确认重启成功
docker logs pg-master
若日志中出现 database system is ready to accept connections,说明主库重启成功,主库配置完成。
2.5 第五步:配置从库(关键步骤)
从库的核心配置是:初始化主库数据(通过基础备份同步主库现有数据)、配置从库连接主库的信息,开启热备模式,实现实时同步。
5.1 进入从库容器
bash
# 进入从库容器(容器名:pg-slave)
docker exec -it pg-slave bash
5.2 清空从库默认数据(关键)
从库需要同步主库的完整数据,因此需先清空从库默认生成的数据目录(避免数据冲突):
bash
# 进入从库数据目录
cd /var/lib/postgresql/data
# 清空数据目录(注意:执行前确认是从库容器,避免误删主库数据)
rm -rf *
5.3 从主库同步基础数据(使用 pg_basebackup)
pg\_basebackup 是 Pgsql 官方提供的基础备份工具,可快速将主库的完整数据备份到从库,为后续同步奠定基础。
bash
# 执行基础备份(同步主库数据到从库)
pg_basebackup -h pg-master -U replica -D /var/lib/postgresql/data -X stream -P
命令参数说明:
-
\-h pg\-master:主库的主机名(即主库容器名,因主从在同一网络,可直接使用容器名); -
\-U replica:复制专用用户名(主库创建的 replica 用户); -
\-D /var/lib/postgresql/data:备份数据的存储目录(从库数据目录); -
\-X stream:流式备份,同步主库的 WAL 日志,确保备份数据的一致性; -
\-P:显示备份进度(方便查看备份是否正常进行)。
执行命令后,会提示输入复制用户(replica)的密码(即 replica123),输入后开始备份,等待备份完成(备份速度取决于主库数据量,小型测试库几秒即可完成)。
备份完成后,从库数据目录将包含与主库完全一致的数据。
5.4 配置从库 recovery.conf 文件(PostgreSQL 12+ 版本)
PostgreSQL 12 及以上版本,recovery\.conf 文件已被整合到 postgresql\.conf 中,无需单独创建,只需在从库的 postgresql\.conf 中添加从库相关配置,或创建 standby\.signal 文件(标记从库身份)。
我们采用更简洁的方式,创建 standby\.signal 文件,并修改 postgresql\.conf 配置:
bash
# 1. 在从库数据目录下,创建 standby.signal 文件(标记该节点为从库)
touch /var/lib/postgresql/data/standby.signal
# 2. 编辑从库 postgresql.conf 文件
vi /var/lib/postgresql/data/postgresql.conf
找到以下配置项,修改为对应值(重点配置从库相关参数):
ini
# 1. 监听所有IP地址
listen_addresses = '*'
# 2. 开启热备模式(从库可提供只读服务,同时同步主库数据)
hot_standby = on
# 3. 从库最大连接数(建议大于主库,分担读压力)
max_connections = 200
# 4. 流复制最大延迟时间(超过该时间,从库将停止接收 WAL 日志,默认30秒)
max_standby_streaming_delay = 30s
# 5. 从库向主库报告状态的间隔时间(默认10秒)
wal_receiver_status_interval = 10s
# 6. 从库反馈(若从库有长时间运行的查询,通知主库不要清理相关 WAL 日志)
hot_standby_feedback = on
# 7. 从库连接主库的配置(核心)
primary_conninfo = 'host=pg-master port=5432 user=replica password=replica123'
# 8. 同步到最新的时间线(确保从库同步主库的所有数据)
recovery_target_timeline = 'latest'
关键配置说明:
-
hot\_standby = on:开启热备模式,从库在同步主库数据的同时,可提供只读查询服务,分担主库读压力; -
primary\_conninfo:从库连接主库的核心配置,包含主库主机名、端口、复制用户、密码,与主库配置一致; -
recovery\_target\_timeline = \&\#39;latest\&\#39;:确保从库同步主库的最新数据,即使主库发生故障切换,也能同步到最新时间线。
修改完成后,保存并退出 vi。
5.5 重启从库容器,使配置生效
bash
# 退出从库容器
exit
# 重启从库容器
docker restart pg-slave
# 查看从库容器日志,确认重启成功
docker logs pg-slave
若日志中出现entering standby mode(进入 standby 模式)和 started streaming WAL from primary(开始从主库流式同步 WAL 日志),说明从库配置成功,已开始同步主库数据。
2.6 第六步:验证主从复制是否成功(核心验证)
配置完成后,需通过实际操作验证主从复制是否正常,确保主库的写操作能实时同步到从库。
6.1 验证主库复制状态
进入主库容器,查看从库的连接状态,确认从库已成功连接主库:
bash
# 进入主库容器
docker exec -it pg-master bash
# 进入 Pgsql 客户端
psql -U postgres
# 查看主库复制状态(核心查询语句)
SELECT * FROM pg_stat_replication;
若查询结果中包含一条记录,且 state 字段为 streaming(表示正在流式复制)、usename 为 replica、client\_addr 为从库的 IP 地址,说明从库已成功连接主库,正在同步数据。
6.2 验证数据同步(写主库,读从库)
通过"主库写入数据,从库读取数据"的方式,验证同步是否正常:
- 主库写入数据:
sql
# 主库 Pgsql 客户端中,执行以下 SQL(创建表、插入数据)
CREATE TABLE test_repl (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
# 插入一条测试数据
INSERT INTO test_repl (name) VALUES ('Docker Pgsql 主从复制测试');
# 查看主库数据
SELECT * FROM test_repl;
- 从库读取数据:
sql
# 打开新的终端,进入从库容器
docker exec -it pg-slave bash
# 进入从库 Pgsql 客户端
psql -U postgres
# 查看从库数据(确认是否同步主库数据)
SELECT * FROM test_repl;`
若从库能查询到主库插入的测试数据,说明主从复制正常生效。
6.3 验证从库只读模式
从库开启热备模式(hot\_standby = on)后,仅支持只读操作,无法执行写操作(如插入、修改、删除),验证如下:
bash
# 从库 Pgsql 客户端中,尝试插入数据
INSERT INTO test_repl (name) VALUES ('从库写入测试');
# 预期结果:报错,提示从库处于只读模式
# 错误信息示例:ERROR: cannot execute INSERT in a read-only transaction
报错说明从库只读模式生效,符合主从复制的设计(写操作仅在主库执行,从库负责读和备份)。
三、进阶拓展:优化与故障处理
完成基础主从复制配置后,结合实际生产场景,补充一些实用的拓展技巧,包括同步模式切换、故障转移、性能优化和常见问题排查,让主从架构更稳定、更实用。
3.1 切换为同步复制(提升数据一致性)
默认配置为异步复制,若需要更高的数据一致性(如金融场景),可修改主库配置,切换为同步复制:
bash
# 1. 进入主库容器,编辑 postgresql.conf
docker exec -it pg-master bash
vi /var/lib/postgresql/data/postgresql.conf
# 2. 添加/修改以下配置
# 同步复制模式(on 表示同步,off 表示异步)
synchronous_commit = on
# 指定同步的从库名称(从库容器名,可多个,用逗号分隔)
synchronous_standby_names = 'pg-slave'
# 3. 保存退出,重启主库容器
exit
docker restart pg-master
切换后,主库执行写操作时,会等待从库确认接收 WAL 日志并写入本地磁盘,才会返回成功,确保主从数据完全一致。但需注意:若从库宕机,主库的写操作会被阻塞,因此同步复制建议搭配"一主多从"架构。
3.2 主库故障转移(从库提升为主库)
当主库宕机(如容器崩溃、服务器故障)时,需将从库提升为主库,保障业务正常运行,步骤如下:
bash
# 1. 进入从库容器,停止从库的 standby 模式(提升为主库)
docker exec -it pg-slave bash
psql -U postgres
# 执行提升命令(停止恢复模式,成为主库)
SELECT pg_promote();
# 验证:查询从库是否已成为主库(返回 false 表示不是从库,即为主库)
SELECT pg_is_in_recovery();
提升成功后,从库将成为新的主库,可接收写操作。此时需修改业务系统的数据库连接地址,将原来的主库地址(5432端口)改为新主库地址(5433端口),待原主库修复后,可将其配置为新主库的从库,恢复主从架构。
3.3 性能优化建议(生产环境必备)
-
优化 WAL 日志配置 :将
wal\_buffers设为 16MB(默认较小),减少 WAL 日志写入磁盘的频率,提升主库写性能; -
限制从库读压力 :从库开启热备后,可通过
max\_connections限制只读连接数,避免从库因读压力过大影响同步效率; -
数据挂载优化:生产环境建议使用 SSD 磁盘挂载数据目录,提升磁盘 I/O 速度,减少同步延迟;
-
定期清理 WAL 日志:主库的 WAL 日志归档目录会不断增大,需定期清理过期日志(如保留最近7天的日志),避免磁盘占满。
3.4 常见问题排查(避坑指南)
问题1:从库无法连接主库,日志提示"connection refused"
原因:主从容器未在同一网络、主库端口未开放、主库 pg\_hba\.conf 配置错误。
解决:
-
确认主从容器都加入
pg\_network网络(docker network inspect pg\_network); -
验证主库端口是否开放(
docker exec \-it pg\-master netstat \-tuln \| grep 5432); -
检查主库
pg\_hba\.conf中,复制用户的访问权限是否正确(网段是否匹配)。
问题2:从库同步失败,日志提示"could not receive data from WAL stream"
原因:主库 WAL 日志被清理(wal\_keep\_segments 设置过小)、主从版本不一致、复制用户权限不足。
解决:
-
增大主库
wal\_keep\_segments的值(如设为 128),重启主库; -
确保主从使用相同版本的 Pgsql 镜像(本文均使用 14 版本);
-
验证复制用户(replica)是否具备
replication权限(\\du查看)。
问题3:主库写操作正常,从库数据同步延迟过大
原因:网络延迟过高、从库性能不足、主库写压力过大。
解决:
-
确保主从服务器在同一局域网,减少网络延迟;
-
提升从库服务器配置(CPU、内存),优化从库
postgresql\.conf配置; -
分担主库写压力,避免批量写入操作集中执行。
四、总结:Docker + Pgsql 主从复制的核心价值与实践建议
Docker 下配置 Pgsql 主从复制,核心优势在于"快速部署、环境隔离、配置灵活",无需复杂的系统环境配置,通过 Docker Compose 即可快速搭建主从架构,大幅降低运维成本。主从复制不仅能解决 Pgsql 单点故障问题,还能实现数据备份和读写分离,提升系统可用性和并发处理能力,是生产环境中 Pgsql 部署的必备方案。
结合本文的实操步骤,总结以下实践建议:
-
测试环境可使用本文的基础配置(异步复制、trust 认证),生产环境需修改为复杂密码、md5 认证、同步复制(或一主多从),提升安全性和数据一致性;
-
定期备份主库数据(除了主从同步,建议每周执行一次全量备份),避免极端情况下的数据丢失;
-
定期监控主从复制状态(通过
pg\_stat\_replication和容器日志),及时发现并解决同步异常; -
若业务量较大,可搭建"一主多从"架构,多个从库分担读压力,进一步提升系统并发能力。
本文从基础配置到进阶拓展,详细讲解了 Docker 下 Pgsql 主从复制的全过程,所有示例代码均可直接实操,适合新手和运维人员参考。实际生产环境中,可根据业务需求灵活调整配置,让 Pgsql 主从架构更好地服务于业务,保障数据安全和系统稳定。