硬核实践:使用 Docker 部署生产级 PostgreSQL

硬核实践:使用 Docker 部署生产级 PostgreSQL

本文从最基础的单命令启动,到生产级 Compose 编排,逐层拆解参数原理、数据持久化、账号管理、远程访问等核心知识点,新手可照抄落地,老手可查漏补缺。

0. 前言与前置准备

Docker 部署 PostgreSQL 是开发、测试、生产环境最常用的方案之一,核心优势在于:

  • 环境一致性:彻底解决「我本地能跑,服务器上不行」的问题
  • 秒级部署:一条命令启动实例,版本切换无成本
  • 隔离性强:多版本、多实例互不干扰
  • 运维简单:数据持久化、备份、迁移标准化

前置条件

  1. 已安装 Docker(建议 20.10+)
  2. 已安装 Docker Compose v2(建议 2.20+)
  3. 服务器 / 本地已开放对应端口(默认 5432)

本文所有命令均基于 PostgreSQL 16 最新 LTS 版本演示,兼容 12/13/14/15 全系列。


一、方案一:docker run 单命令部署

适合快速测试、临时实例、单节点快速启动场景。

1.1 最简启动命令(仅用于测试,不推荐生产)

docker run -d --name postgres-test -p 5432:5432 -e POSTGRES_PASSWORD=123456 postgres:16

⚠️ 警告:此命令无数据持久化,容器删除数据全部丢失,仅适合临时测试。

1.2 生产级完整启动命令(推荐)

docker run -d \ --name postgres-prod \ --restart=unless-stopped \ -p 5432:5432 \ -e POSTGRES_USER=admin \ -e POSTGRES_PASSWORD=Admin@2024 \ -e POSTGRES_DB=my_database \ -e TZ=Asia/Shanghai \ -e PGDATA=/var/lib/postgresql/data/pgdata \ -v /opt/postgres/data:/var/lib/postgresql/data \ -v /opt/postgres/conf/pg_hba.conf:/etc/postgresql/pg_hba.conf \ -v /opt/postgres/conf/postgresql.conf:/etc/postgresql/postgresql.conf \ --memory=2G \ --cpus=2 \ postgres:16 \ -c 'config_file=/etc/postgresql/postgresql.conf' \ -c 'hba_file=/etc/postgresql/pg_hba.conf'

1.3 全参数硬核拆解

参数作用说明生产建议
-d后台守护进程运行容器必加
--name postgres-prod给容器指定唯一名称,方便管理建议按「业务 - 版本 - 环境」命名
--restart=unless-stopped容器重启策略生产必选,Docker 重启 / 宿主机重启后自动拉起
-p 5432:5432端口映射,格式「宿主机端口:容器内部端口」生产可修改宿主机端口降低攻击面,如-p 15432:5432
-e POSTGRES_USER=admin指定初始超级用户名,默认postgres生产建议自定义,不使用默认名
-e POSTGRES_PASSWORD=Admin@2024初始超级用户密码,必填参数生产必须使用强密码,包含大小写 + 数字 + 特殊字符
-e POSTGRES_DB=my_database初始自动创建的数据库名,默认和用户名一致业务库可提前创建,省去后续手动建库步骤
-e TZ=Asia/Shanghai设置容器时区为东八区必加,否则默认 UTC 时间,和国内差 8 小时
-e PGDATA=/var/lib/postgresql/data/pgdata指定 PostgreSQL 数据文件存储目录挂载宿主机目录时建议加,避免直接挂载到 data 根目录引发权限问题
-v /opt/postgres/data:/var/lib/postgresql/data数据持久化核心,格式「宿主机目录:容器目录」生产必配,将数据文件映射到宿主机,容器删除数据不丢失
-v 配置文件挂载将 pg_hba.conf、postgresql.conf 映射到宿主机生产建议,方便修改配置,不用进入容器
--memory=2G限制容器最大使用内存生产建议限制,防止数据库占满宿主机资源
--cpus=2限制容器最大使用 CPU 核数按需配置,资源隔离
postgres:16镜像名称 + 版本标签生产必须指定具体版本,禁止使用 latest,避免版本漂移
-c 'config_file=xxx'启动时指定自定义配置文件路径挂载外部配置文件时必加,让 PG 加载我们的配置

1.4 重启策略详解

--restart有 4 种可选值,区别如下:

  1. no:默认值,容器退出时不自动重启
  2. on-failure[:max-retries]:容器非正常退出(退出码非 0)时自动重启,可设置最大重试次数
  3. always:无论什么原因退出都自动重启,包括手动停止的容器,Docker 重启后也会自动拉起
  4. unless-stopped最推荐生产使用,总是自动重启,但如果容器之前被手动停止了,Docker 重启后不会自动拉起

二、方案二:Docker Compose 编排部署

适合生产环境、多服务协同部署、配置需要版本化管理的场景,是企业级首选方案。

2.1 完整 docker-compose.yml 配置

创建docker-compose.yml文件,内容如下:

version: '3.8' services: postgres: image: postgres:16-alpine container_name: postgres-compose-prod restart: unless-stopped ports: - "5432:5432" environment: POSTGRES_USER: admin # 超级用户名 POSTGRES_PASSWORD: Admin@2024 # 超级用户密码 POSTGRES_DB: my_database # 初始数据库 TZ: Asia/Shanghai # 时区 PGDATA: /var/lib/postgresql/data/pgdata # 数据目录 volumes: # 数据持久化挂载 - ./data:/var/lib/postgresql/data # 自定义配置文件挂载 - ./conf/postgresql.conf:/etc/postgresql/postgresql.conf - ./conf/pg_hba.conf:/etc/postgresql/pg_hba.conf # 初始化脚本挂载(第一次启动自动执行) - ./init:/docker-entrypoint-initdb.d # 资源限制 deploy: resources: limits: cpus: '2' memory: 2G reservations: cpus: '0.5' memory: 512M networks: - pg-network # 启动命令,指定自定义配置文件 command: - -c - config_file=/etc/postgresql/postgresql.conf - -c - hba_file=/etc/postgresql/pg_hba.conf # 自定义网络,方便和其他服务(如pgadmin、业务服务)互通 networks: pg-network: driver: bridge

2.2 配置文件与初始化脚本

① 准备配置文件目录

在 docker-compose.yml 同级目录创建confdatainit三个文件夹:

mkdir -p conf data init
② 自定义 postgresql.conf

创建conf/postgresql.conf,核心配置示例:

# 监听地址,允许所有IP访问(配合pg_hba.conf使用) listen_addresses = '*' port = 5432 max_connections = 200 # 内存配置(根据宿主机内存调整,建议总内存的25%) shared_buffers = 512MB work_mem = 4MB maintenance_work_mem = 128MB # 日志配置 logging_collector = on log_directory = 'log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' log_timezone = 'Asia/Shanghai' # 时区 timezone = 'Asia/Shanghai' datestyle = 'iso, mdy'
③ 客户端认证配置 pg_hba.conf

创建conf/pg_hba.conf,控制哪些 IP 可以连接数据库:

# TYPE DATABASE USER ADDRESS METHOD # 本地unix套接字连接 local all all trust # IPv4 本地回环 host all all 127.0.0.1/32 trust # 允许所有IP用密码连接(生产建议限制IP段,如 192.168.1.0/24) host all all 0.0.0.0/0 md5 # IPv6 host all all ::1/128 trust

⚠️ 生产环境禁止直接写0.0.0.0/0,应限制为具体的业务服务器 IP 段,降低安全风险。

④ 初始化脚本(可选)

init目录下放入.sql.sh文件,仅在容器第一次启动、数据目录为空时自动执行,适合初始化表结构、创建普通用户、导入基础数据。

示例init/01-init-user.sql

-- 创建普通业务用户 CREATE USER app_user WITH PASSWORD 'AppUser@2024'; -- 创建业务库 CREATE DATABASE app_db OWNER app_user; -- 授予权限 GRANT ALL PRIVILEGES ON DATABASE app_db TO app_user;

2.3 Compose 常用运维命令

在 docker-compose.yml 所在目录执行:

# 启动服务(后台运行) docker compose up -d # 查看服务状态 docker compose ps # 查看日志(实时滚动) docker compose logs -f postgres # 停止服务(不删除容器) docker compose stop # 启动已停止的服务 docker compose start # 重启服务 docker compose restart # 停止并删除容器、网络(数据卷不会删) docker compose down # 停止并删除容器、网络、数据卷(⚠️ 数据会丢失,谨慎使用) docker compose down -v

三、核心原理:数据持久化与初始化机制

3.1 为什么必须做数据持久化?

Docker 容器的文件系统是临时的,容器销毁时,内部所有数据都会一起删除。PostgreSQL 的数据默认存在容器内的/var/lib/postgresql/data目录下,必须通过-v卷挂载映射到宿主机,才能实现数据持久化。

3.2 两种挂载方式对比

表格

挂载方式写法示例优点缺点适用场景
绑定挂载(宿主机目录)-v /opt/pg/data:/var/lib/postgresql/data路径直观,方便备份、迁移、手动查看文件需注意权限问题,PG 容器内部用户 UID 是 999生产环境、需要手动管理数据
具名卷(Docker 管理)-v pg_data:/var/lib/postgresql/dataDocker 自动管理权限,无权限问题,性能更好路径不直观,备份需要用 docker 命令测试环境、快速部署

3.3 环境变量初始化机制(巨坑预警)

POSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_DB这三个环境变量,仅在容器第一次启动、数据目录为空时生效!

也就是说:

  • 容器启动过一次后,再修改环境变量里的密码,不会生效
  • 想修改密码,必须进入数据库执行 SQL 语句修改
  • 想让新的环境变量生效,必须清空数据目录,重新初始化

这是 90% 的新手都会踩的坑,务必记住。

3.4 权限问题解决方案

挂载宿主机目录时,经常会遇到「Permission denied」报错,原因是 PostgreSQL 容器内部使用postgres用户运行,UID 为 999,而宿主机的目录权限属于 root 或其他用户。

解决方案二选一:

# 方案1:给宿主机目录开放权限(简单粗暴,测试可用) chmod 777 /opt/postgres/data # 方案2:将目录所有者改为 UID 999(推荐,生产更安全) chown -R 999:999 /opt/postgres/data

四、必备运维操作大全

4.1 进入容器与进入数据库

① 进入容器 bash 终端
docker exec -it postgres-prod bash
② 直接进入 PostgreSQL 命令行(推荐,一步到位)
# 格式:docker exec -it 容器名 psql -U 用户名 -d 数据库名 docker exec -it postgres-prod psql -U admin -d my_database

进入 psql 后,提示符变为my_database=#,表示已进入数据库命令行。

4.2 psql 常用命令

命令作用
\l列出所有数据库
\c 数据库名切换到指定数据库,如\c app_db
\dt列出当前数据库所有表
\d 表名查看表结构,如\d users
\du列出所有用户和角色
\conninfo查看当前连接信息
\timing on开启 SQL 执行耗时显示
\q退出 psql 命令行

4.3 账号与数据库管理

① 修改用户密码
-- 语法:ALTER USER 用户名 WITH PASSWORD '新密码'; ALTER USER admin WITH PASSWORD 'NewPass@2024';
② 创建新用户
-- 创建普通用户 CREATE USER app_user WITH PASSWORD 'AppUser@2024'; -- 创建带超级权限的用户(谨慎使用) CREATE USER super_user WITH SUPERUSER PASSWORD 'Super@2024';
③ 创建数据库并授权
-- 创建数据库,指定所有者 CREATE DATABASE business_db OWNER app_user; -- 授予用户对数据库的所有权限 GRANT ALL PRIVILEGES ON DATABASE business_db TO app_user; -- 授予连接权限 GRANT CONNECT ON DATABASE business_db TO app_user;
④ 删除用户 / 数据库
-- 删除数据库 DROP DATABASE IF EXISTS old_db; -- 删除用户 DROP USER IF EXISTS old_user;

4.4 数据备份与恢复

① 全库备份(pg_dump)
# 格式:docker exec 容器名 pg_dump -U 用户名 数据库名 > 备份文件路径 docker exec postgres-prod pg_dump -U admin my_database > /opt/backup/my_database_20240601.sql
② 恢复数据
# 方式1:从sql文件恢复 docker exec -i postgres-prod psql -U admin -d my_database < /opt/backup/my_database_20240601.sql # 方式2:进入容器后恢复 docker exec -it postgres-prod bash psql -U admin -d my_database -f /tmp/backup.sql
③ 备份整个数据卷(适合整实例迁移)
# 停止容器后,打包整个数据目录 docker stop postgres-prod tar -zcvf pg_data_backup.tar.gz /opt/postgres/data

五、进阶:开启远程访问完整步骤

很多人部署完后无法用 Navicat/DBeaver 连接,按以下 4 步检查:

  1. 端口映射正确:确保-p 5432:5432已配置,宿主机防火墙已开放 5432 端口【默认端口,建议修改,防止被攻击】

    # 云服务器需在安全组放行端口,服务器防火墙放行 firewall-cmd --add-port=5432/tcp --permanent firewall-cmd --reload
  2. 配置监听地址postgresql.conflisten_addresses = '*'

  3. 配置客户端认证pg_hba.conf中添加允许连接的 IP 规则

  4. 重启容器生效

    docker restart postgres-prod

🔒 安全建议:生产环境不要直接暴露 5432 端口到公网,建议通过 VPN 或内网连接,或修改默认端口,配合强密码使用。


六、避坑指南:90% 的人都踩过的坑

坑 1:修改环境变量密码不生效

原因:环境变量仅第一次初始化生效。 解决:进入数据库执行ALTER USER语句修改密码。

坑 2:挂载目录后启动报错 Permission denied

原因:宿主机目录权限和容器内 postgres 用户 UID 不匹配。 解决:chown -R 999:999 宿主机数据目录

坑 3:用 latest 标签导致版本异常

原因:latest 标签始终指向最新版本,重启后可能自动升级大版本,引发数据不兼容。 解决:生产必须指定具体版本号,如postgres:16.3

坑 4:时区不对,时间差 8 小时

原因:容器默认 UTC 时区。 解决:添加环境变量TZ=Asia/Shanghai,同时配置文件中设置log_timezonetimezone

坑 5:数据没挂载,容器删除数据丢失

原因:新手忘记加-v参数。 解决:部署第一时间配置数据卷挂载,养成习惯。

坑 6:远程连接报错「password authentication failed」

排查顺序:

  1. 密码是否输入正确
  2. pg_hba.conf 是否配置了对应 IP 的 md5 认证规则
  3. 是否修改过密码但没生效
  4. 连接的用户名和数据库名是否存在

七、总结

  • 快速测试:用docker run单命令启动,简单高效
  • 生产部署:用Docker Compose编排,配置可版本化管理,运维更规范
  • 核心底线:数据持久化必配、强密码必设、端口暴露需谨慎
  • 运维习惯:定期备份数据、不使用 latest 标签、限制远程访问 IP

PostgreSQL + Docker 的组合,兼顾了灵活性和稳定性,掌握本文内容足以应对 99% 的部署场景。下一篇将讲解 PostgreSQL 主从复制的 Docker 部署方案,敬请关注。

原创不易,如果对你有帮助,欢迎点赞、收藏、关注,持续输出硬核运维干货。