PG18:云原生与部署
把 PostgreSQL 跑在容器、Kubernetes 或云托管服务上,在今天已经不是「可选项」而是「默认选项」。云原生环境改变了数据库与底层资源的关系:存储变成网络盘、实例随时可能被调度迁移、配置通过环境变量注入、备份靠快照与对象存储。PG18 的多个新特性恰好落在这个交点上——异步 I/O 子系统直面「网络存储延迟高、并发读多」的现实,initdb 默认开启校验和重塑了镜像初始化的默认安全基线,pg_upgrade 保留统计则直接压缩了容器化集群的大版本升级窗口。
本页聚焦「怎么把 PG18 部署好」:官方 Docker 镜像的细节(尤其是 PGDATA 路径在 18 上的重要变化)、Kubernetes 上的三种主流形态、云托管服务的选型考量,以及配置管理、备份与可观测性在云上的正确姿势。所有 PostgreSQL 侧的参数与行为均以官方 release notes 与文档为准;所有镜像侧的细节(环境变量、路径)均来自 Docker 官方镜像文档。
前置知识
建议先阅读:PG18:核心新特性总览
基础概念
核心术语
| 概念 | 英文 / 记法 | 说明 |
|---|---|---|
| 镜像 | image | 包含 PostgreSQL 二进制与 entrypoint 的容器镜像,官方镜像名为 postgres |
| 数据卷 | volume / persistent storage | 容器外持久化数据目录的机制,PG 的 PGDATA 必须落在其上 |
| 环境变量注入 | env vars | 容器初始化参数的标准传递方式,如 POSTGRES_PASSWORD |
| Operator | kubernetes operator | 用 CRD + 控制循环把「运维知识」编码进 K8s 的组件 |
| 有状态负载 | StatefulSet | K8s 中为数据库类工作负载提供稳定标识与有序部署的控制器 |
| 托管服务 | managed service | 云厂商运维控制面、用户只管数据与连接的形态 |
| 存算分离 | storage-compute separation | 计算节点与存储层解耦的云架构,典型如共享存储或页服务 |
什么才算「云原生地用 PostgreSQL」
跑在容器里只是起点。云原生的完整要求是:数据可持久(数据卷的生命周期独立于容器)、实例可替换(任何节点都能被杀掉重建,由副本与备份兜底)、配置可声明(所有参数进版本管理,而非 SSH 上去改文件)、故障可自愈(failover 由 Operator 或控制面自动完成)、成本可伸缩(存储与算力独立扩缩)。这五条里,PG 本体只负责第一层的「数据正确性」,其余由镜像、Operator、云平台共同补齐。
PG18 里与部署直接相关的变化
| 变化 | 对部署的影响 |
|---|---|
异步 I/O 子系统(io_method 等) | 容器/云盘场景可以显式配置 I/O 执行方式;effective_io_concurrency 默认 16 且不再依赖 fadvise() |
initdb 默认启用数据校验和 | 新初始化的集群默认带 checksums;pg_upgrade 要求新旧集群设置一致 |
pg_upgrade 保留优化器统计、新增 --swap | 大版本升级窗口显著缩短 |
idle_replication_slot_timeout | 云上失联副本/订阅的槽有了自动失效的保险丝 |
监控视图增强(pg_stat_io 字节计数、每后端 I/O/WAL) | 云上可观测性直接受益 |
语法与用法
官方镜像最小用法(来自 Docker 官方文档):POSTGRES_PASSWORD 是唯一必填变量:
docker run --name pg18 \
-e POSTGRES_PASSWORD=mysecretpassword \
-d postgres:18带持久化与初始化的完整用法:
docker run --name pg18 \
-e POSTGRES_PASSWORD=mysecretpassword \
-e POSTGRES_USER=app \
-e POSTGRES_DB=appdb \
-e PGDATA=/var/lib/postgresql/18/docker \
-v pg18-data:/var/lib/postgresql \
-p 5432:5432 \
-d postgres:18PG18 镜像的数据路径变了
官方镜像文档明确:PG18 起 PGDATA 默认值改为版本化路径 /var/lib/postgresql/18/docker,镜像声明的 VOLUME 改为 /var/lib/postgresql。PG17 及以前的习惯做法是把卷挂在 /var/lib/postgresql/data——这个路径在 PG18 镜像上不再是数据目录。挂载与迁移务必对准新路径,详见下文「官方 Docker 镜像」一节。
docker-compose 写法:
services:
pg18:
image: postgres:18
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?required}
POSTGRES_USER: app
POSTGRES_DB: appdb
PGDATA: /var/lib/postgresql/18/docker
volumes:
- pg18-data:/var/lib/postgresql
ports:
- "5432:5432"
volumes:
pg18-data: {}容器内调整 PG18 的 I/O 与复制参数:
# postgresql.conf(或通过 ALTER SYSTEM / 启动参数覆盖)
io_method = worker
io_workers = 3
# 逻辑复制/物理复制场景的槽保险丝(新增于 PG18)
idle_replication_slot_timeout = 2h-- 运行时查看与修改(io_method 只能启动时设置,其余多数可热改)
SHOW io_method;
ALTER SYSTEM SET idle_replication_slot_timeout = '1h';
SELECT pg_reload_conf();详细说明
官方 Docker 镜像:标签、环境变量与 PGDATA 变化
官方镜像由 PostgreSQL Docker 社区(docker-library/postgres)维护,标签形如 postgres:18(另有 postgres:18-alpine 等变体)。初始化由 entrypoint 脚本完成:首次启动(数据目录为空)时执行 initdb,再按顺序执行 /docker-entrypoint-initdb.d/ 下的 .sql、.sql.gz、.sh 初始化脚本。
镜像文档定义的环境变量(与部署最相关的部分):
| 变量 | 必填 | 说明 |
|---|---|---|
POSTGRES_PASSWORD | 是 | 超级用户密码;不允许为空。除非显式把 POSTGRES_HOST_AUTH_METHOD 设为 trust(不建议),否则必须提供 |
POSTGRES_USER | 否 | 初始超级用户名,默认 postgres;指定时会同时创建同名数据库 |
POSTGRES_DB | 否 | 初始数据库名,默认取 POSTGRES_USER 的值 |
PGDATA | 否 | 数据目录路径。PG18 镜像默认值变为 /var/lib/postgresql/18/docker |
POSTGRES_HOST_AUTH_METHOD | 否 | 控制 pg_hba.conf 中 host 连接的认证方法,14+ 默认 scram-sha-256 |
POSTGRES_INITDB_ARGS | 否 | 透传给 initdb 的参数(如 --data-checksums 等) |
POSTGRES_INITDB_WALDIR | 否 | 把事务日志放到数据目录之外的位置 |
PGDATA 的版本化是 PG18 镜像最重要的变更。 官方文档原文明确:PG18 及以上,PGDATA 默认为 /var/lib/postgresql/18/docker(后续大版本会把 18 换成对应主版本号),镜像声明的 VOLUME 相应改为 /var/lib/postgresql。这带来两个直接后果:
- 旧挂载习惯必须改。PG17 及以前要把卷挂到
/var/lib/postgresql/data(挂到/var/lib/postgresql不会持久化数据);PG18 起则应该把卷挂到/var/lib/postgresql,让镜像去管理版本化子目录。 - 大版本升级更顺滑。版本化子目录让「同一卷上并存 18 与 19 两个数据目录」成为自然形态,官方文档指出这正是为了在挂载
/var/lib/postgresql的前提下配合pg_upgrade --link实现快速升级。
从 PG17 及以前镜像迁移到 PG18 镜像时,官方给出的路径是:显式设置 PGDATA,并先把既有数据挪进 PG_MAJOR/docker 子目录,再调整卷结构。
异步 I/O 在云盘与 NVMe 上的意义
云环境的存储普遍是网络盘(EBS、云盘、持久卷),单次 I/O 延迟比本地 NVMe 高且波动大,而 PG 的顺序扫描、位图堆扫描、VACUUM 这类操作恰恰要发起大量独立的页读请求。PG18 的异步 I/O 让后端把这些请求排队并发提交,而不是一个一个同步等——release notes 明确列出受益场景为顺序扫描、位图堆扫描、VACUUM 等。存储越慢、并发读越密集,收益越明显;本地 NVMe 上收益相对小但通常仍为正。
三种 io_method 在云原生语境下的取舍:
| 取值 | 依赖 | 适用 | 备注 |
|---|---|---|---|
worker(默认) | 无 | 绝大多数容器与云环境 | 常驻 io_workers(默认 3)个 I/O 进程,跨平台稳妥 |
io_uring | 构建期启用 liburing;Linux 内核支持 io_uring(该接口自内核 5.1 引入) | 高吞吐 NVMe 场景 | 需确认镜像构建与运行时(seccomp 等)都放行 io_uring |
sync | 无 | 对照基线、排障 | 即 PG17 及以前的同步行为 |
选型的定性顺序是「先默认、再对照、后切换」:保持 worker 跑基准 → 用 sync 重跑同负载建立基线 → 确认差距值得追且依赖满足时再评估 io_uring。多数场景走到第二步就会发现,瓶颈往往不在 I/O 执行方式上。
另一个对容器友好的变化:effective_io_concurrency 与 maintenance_io_concurrency 的默认值提升到 16,并且在没有 fadvise() 支持的系统上也可以设置大于 0——部分精简容器镜像与某些存储栈恰好缺这个系统调用,过去只能被锁死在 0。
Kubernetes 部署概览
在 K8s 上跑 PostgreSQL,主流形态有三类,共同点是:不手写 StatefulSet 的全部细节,而是把拓扑、failover、备份策略声明出来。三者均围绕「期望状态 + 控制循环」运转,差异在于控制循环里沉淀的运维知识范围与治理归属。
CloudNativePG(CNPG):PG 生态里目前最活跃的 K8s Operator 之一,声明式管理集簇(primary + replicas)、内置备份到对象存储、支持滚动的大版本升级与受控的 pg_upgrade 流程。清单层面,用户声明的是一个 Cluster 资源(实例数、存储、备份目标),Operator 负责把期望状态变成现实。对 PG18 的支持情况以 CNPG 官方发布说明为准——Operator 对新大版本的支持通常在内核发布后数周内跟进。
Zalando Postgres Operator:老牌 Operator,同样以 CRD 声明集簇,围绕 Patroni 构建高可用,强调 Spilo 镜像(集成了 Patroni 与常用扩展的「胖镜像」)。
Patroni(自行编排):Patroni 本身不是 Operator,而是用 DCS(如 etcd)做共识的 HA 组件;可以在 K8s 里配合 StatefulSet 自行搭建,也可以在虚拟机环境使用。它的原理、DCS 配置与托管方式在本教程高可用阶段已有专页:
延伸阅读
Patroni 的架构与配置细节请见 Patroni 高可用。
三者的选择逻辑可以概括为一句话:要声明式、开箱即用选 Operator(CNPG 或 Zalando);要最大控制权或已在用 Patroni,就 Patroni + 自建编排。无论哪条路,下面这些运维知识都是通用的:PGDATA 要落在 PersistentVolume 上、大版本升级要演练 pg_upgrade 或逻辑复制方案、备份要真正做恢复演练。
云托管服务选型考量
如果自运维不是核心竞争力,托管服务(DBaaS)通常是起步阶段的正确答案。市面上常见的几类:
| 服务 | 形态 | 备注 |
|---|---|---|
| AWS RDS for PostgreSQL / Aurora PostgreSQL | 全托管 | 大版本上线节奏、参数白名单由厂商控制 |
| 阿里云 RDS PostgreSQL | 全托管 | 国内访问合规与生态集成友好 |
| Google Cloud SQL for PostgreSQL | 全托管 | 与 GCP 数据栈集成方便 |
| Supabase / Neon | 面向应用开发者的 Postgres 平台 | 分别以「应用平台套件」「存算分离/分支」为特色 |
选型时真正需要对比的是运营细节而不是功能清单:
- 大版本可用性:PG18 能不能选、什么时候可选,完全由厂商决定;参数白名单同样如此——
io_method、idle_replication_slot_timeout这类 PG18 新参数在托管环境未必开放设置。 - 扩展可用性:PostGIS、TimescaleDB、pg_stat_statements、pgvector 等是否预装、版本多新(见本系列第三页)。
- 备份与 PITR 能力:保留窗口、能否恢复到独立实例、跨区域复制。
- 连接与网络模型:直连还是走代理、连接数上限、VPC/私网打通方式。
- 监控导出:能否把指标接到你自己的 Prometheus(见下文可观测性)。
具体性能指标与价格本页不做断言——这类数字随规格、区域、时间变化太快,任何「抄来的结论」都会误导选型,请以各厂商官方文档与自己的压测为准。还有一条容易被忽略的约束:托管服务把「参数调优的自由」连同「调优的责任」一起收走了——你无法 SSH 进去改 postgresql.conf,PG18 新参数是否开放、何时开放,全在厂商的参数白名单里。如果你的工作负载高度依赖底层调优(大内存排序、并行维护作业、AIO 配置),自建或发行版形态反而更合适;反之,多数中小规模应用在托管服务上能拿到更好的总体拥有成本。
12-factor 与配置管理
12-factor 应用的第三条是「配置存储在环境里」。数据库侧的对应实践分两层:
连接与应用配置进环境。 连接串、凭据通过环境变量或 Secret 注入,不落镜像、不进代码仓库。官方镜像的 POSTGRES_* 变量就是这套约定在数据库初始化上的体现。
数据库参数分层管理。 PG 的参数有四个来源层级(优先级从高到低):会话级 SET → 角色级/库级 ALTER ROLE / ALTER DATABASE → postgresql.auto.conf(ALTER SYSTEM 写入)→ postgresql.conf。云原生的正确姿势是:把「需要持久的基础参数」写进配置文件或启动参数(随镜像/清单进版本管理),把「运维期的热调整」用 ALTER SYSTEM:
-- 热调整示例:写进 postgresql.auto.conf 并 reload,重启后依然生效
ALTER SYSTEM SET idle_replication_slot_timeout = '1h';
SELECT pg_reload_conf();
-- 查看参数的当前值与来源
SELECT name, setting, source FROM pg_settings WHERE name = 'io_workers';参数管理的方法论与更多示例见 ALTER SYSTEM 与参数管理。
Secret 管理。 POSTGRES_PASSWORD 这类值应来自 K8s Secret / 密钥管理服务,而不是写死在 compose 文件或 CI 变量里;镜像文档也提醒:POSTGRES_HOST_AUTH_METHOD=trust 可以绕过密码要求,但等于对同网段开放免密连接,生产禁止。
initdb 校验和默认值带来的部署影响
PG18 的 initdb 默认启用数据校验和(可用 --no-data-checksums 关闭)。对部署流程的影响有三处:
- 新建集群的默认安全基线提高了。官方镜像初始化出的 PG18 数据目录默认带 checksums,页面损坏能在读取时被发现。
pg_upgrade要求新旧集群 checksum 设置一致。把「老的无 checksum 集群」升级到 PG18 时,要么新集群显式--no-data-checksums保持一致,要么借机开启 checksums(推荐,但必须演练)。- ** checksums 有 CPU 开销**,但通常远小于其带来的数据完整性收益;有疑虑就在压测环境对比。
云上备份与快照
云环境给了你两种备份手段,必须分清它们不是一回事:
- 存储快照(volume snapshot):秒级、便宜、适合「快速回滚整个数据目录」。但快照抓的是块设备在某瞬间的状态,如果应用还在写库,快照恢复出来的是「崩溃恢复视角」的数据——PG 能靠 WAL 把它推到一致点,但要配合
pg_backup_start()/pg_backup_stop()(旧名pg_start_backup()/pg_stop_backup())这样的 API 或直接停写,才能构成应用级一致的备份。 - 逻辑/物理备份 + WAL 归档:
pg_dump系列与基础备份 + WAL 归档(pgBackRest、WAL-G 等)构成 PITR 能力,可以恢复到任意时间点,是真正的灾备手段。异地容灾的完整方法(offsite 策略、RPO/RTO 权衡)见专页:
延伸阅读
异地备份与灾备策略请见 异地容灾与 offsite 策略。
在 K8s Operator 形态里,备份通常由 Operator 调度到对象存储(CNPG 与 Zalando Operator 都内置了这类能力),但「Operator 帮你备份了」不等于「恢复演练做过了」——后者无法自动化,只能排进日程。
可观测性
云原生环境的可观测性三件套:指标(Prometheus)、日志(stdout + 采集器)、告警。PG18 在这三条线上都有增量:
- 指标:
postgres_exporter抓取的查询要适配 PG18 的视图变化——pg_stat_io新增read_bytes/write_bytes/extend_bytes,pg_stat_wal删除了wal_write等四列(迁移到pg_stat_io),pg_stat_checkpointer新增num_done。监控体系搭建详见 Prometheus 监控方案。 - 日志:容器里 PG 日志走 stdout,由采集器(如 Loki、Fluent Bit)收集。PG18 的
log_connections支持更细粒度并可报告连接各阶段耗时,log_line_prefix新增%L输出客户端 IP——在「连接来自 NAT 后的 Sidecar/代理」的云环境里,这个转义符对排障价值极高。 - 每后端视图:
pg_stat_get_backend_io()与pg_stat_get_backend_wal()让「定位单个连接的 I/O/WAL 消耗」成为可能。 - 进度视图:
pg_stat_progress_vacuum/pg_stat_progress_analyze现在包含代价延迟(cost delay)时间(需track_cost_delay_timing),长作业的「在等什么」一目了然。
最后提醒一点:云原生环境的可观测性是「三层拼图」——PG 的统计视图、容器的资源指标、网络与存储的延迟指标缺一不可。数据库看起来慢时,先确认慢在等待 I/O(pg_stat_io)、等待 CPU(资源限额)、还是网络往返(pg_stat_activity 的 wait event),再动手优化——在容器里,很多「数据库问题」的真相在数据库之外。
大版本升级在云上的路径
PG18 把升级工具箱补强到历年来最齐:
pg_upgrade保留优化器统计:升级完第一天不再「全表顺序扫描等待 autovacuum 救场」;注意扩展统计(extended statistics)不保留。--jobs并行检查、--swap交换目录:升级窗口进一步压缩;配合官方镜像的版本化 PGDATA,pg_upgrade --link的硬链接加速在容器卷上更易实现。- 逻辑复制在线迁移:跨大版本、跨云、跨自建/托管的通用解法,方法见 PG16 逻辑复制增强。
- 托管服务的版本升级:通常由厂商提供蓝绿或快照回滚方案,但 PG 主版本升级多数仍需用户主动触发并停写。
无论哪条路,升级专页的流程与检查清单依然适用:大版本升级实战。
网络与连接管理
容器与 K8s 环境「连接」一层的特殊之处在于:客户端到 PG 之间几乎总隔着代理层(Service、负载均衡、Sidecar、连接池),过去排障时「日志里看到的 IP 是谁」因此变得模糊。PG18 在这条线上给了三件武器:
| 能力 | 说明 |
|---|---|
log_line_prefix 新增 %L | 日志前缀可直接输出客户端 IP——穿过代理后依然可知源头 |
log_connections 细化 | 支持更细粒度取值,并可报告连接各阶段耗时(握手/认证/启动各花多久) |
search_path 变更通知 | 服务端主动把 search_path 变化告知客户端,连接池复用连接时的 schema 漂移有了协议层解法 |
TLS 侧也有两个配置更新:ssl_ecdh_curve 更名为 ssl_groups(旧名仍可用),允许配置多组 ECDH 曲线,且默认值包含 X25519;新增 ssl_tls13_ciphers 用于指定 TLS 1.3 的密码套件(冒号分隔多个)。云上做传输加密合规整改时,这两个参数就是你要找的开关。
连接池本身(PgBouncer 等)不属于 PG 内核范畴,但 PG18 的每后端视图(pg_stat_get_backend_io() / pg_stat_get_backend_wal())让「池里哪个连接在消耗资源」变得可查——池化环境下排障的视角从「实例级」细化到了「连接级」。顺带一提,search_path 变更通知对连接池场景是隐性但重要的修复:过去池子复用连接后「上一位用户设置的 search_path 还在生效」这类问题,如今协议层就会把变化告知客户端。
Kubernetes 有状态部署的通用要点
无论选哪个 Operator 或自建 StatefulSet,下面这份清单都适用(定性描述,具体做法以所用平台文档为准):
| 要点 | 常见做法 | 与 PG 的关联 |
|---|---|---|
| 稳定存储 | StatefulSet + PersistentVolumeClaim,存储类选 SSD/高性能网络盘 | PGDATA 必须落在 PV 上;存储延迟直接影响 AIO 收益 |
| 优先级与驱逐 | PriorityClass、PodDisruptionBudget | 数据库 Pod 不应被节点维护「顺手」驱逐 |
| 反亲和 | Pod anti-affinity / topology spread | 一主两备不要落在同一台宿主机或同一可用区 |
| 资源请求 | requests/limits 明确 CPU 与内存 | io_workers、并行 worker 都吃额外进程与内存 |
| 健康检查 | readiness 区分「进程活着」与「主库可写」 | failover 期间的探针语义需要精细设计 |
| 备份调度 | Operator 内置或 pgBackRest 定时任务 | 备份目标通常是对象存储 |
| 大版本升级 | Operator 编排的 pg_upgrade 或逻辑复制迁移 | PG18 的统计保留/--jobs/--swap 直接受益 |
通用原则只有一条:有状态负载的每一项配置都要回答「Pod 被杀后会发生什么」——数据在 PV 里、拓扑在 Operator 里、凭据在 Secret 里,Pod 本身应当是「随时可重建」的。
镜像选择的细节:变体、标签与不可变部署
官方镜像提供 Debian 与 Alpine 两类变体(postgres:18 与 postgres:18-alpine)。两者的差异是基础 C 库(glibc 与 musl)与包生态的差异,对 PG 的实际影响主要体现在区域设置(locale/collation 行为)与扩展兼容性上——生产环境选型后保持一致,避免「开发用 Alpine、生产用 Debian」这类隐性漂移。
标签策略上有两个层次:
- 次级标签(
postgres:18)跟随 minor 更新:适合开发与测试环境,minor 升级是官方定义的零风险操作; - 摘要固定(
postgres@sha256:...):生产环境可固定到具体摘要实现不可变部署,升级变成显式的、可审计的变更。
如果出于合规需要自建镜像,注意两点:一是保持与官方 entrypoint 的兼容(docker-entrypoint-initdb.d 等约定已内化到无数部署脚本);二是想要 io_method = io_uring 就必须确认构建参数里带了 liburing 支持——不确认的话,老老实实用默认的 worker。
安全基线:认证、校验和与密钥
PG18 时代的安全基线可以这样概括:
- 认证:默认 SCRAM-SHA-256(官方镜像 14+ 即默认),MD5 进入弃用通道(设置 MD5 密码会告警,可用
md5_password_warnings = off关闭,但未来大版本将移除);有企业 IdP 的团队可以把 OAuth 认证列入规划——pg_hba.conf的oauth方法、oauth_validator_libraries验证器库与 libpq 的 OAuth 选项构成完整链路,短期令牌与集中撤销在容器化环境尤其有价值。 - 数据完整性:PG18 的
initdb默认开启校验和,官方镜像初始化的集群天然受益;老集群升级时用pg_controldata对比新旧设置,保持一致。 - 密钥管理:
POSTGRES_PASSWORD等凭据一律来自 Secret 系统;POSTGRES_HOST_AUTH_METHOD=trust在生产环境视为事故级别配置。 - 取消密钥加固:线协议 3.2 下取消请求密钥提升为 256 位——需要服务端与客户端同时支持 3.2,属于「升级两端后自动获得」的改进。
- 传输加密可调:
ssl_groups与ssl_tls13_ciphers让 TLS 曲线与套件的选择权回到运维手里,合规基线可以精确声明。
io_workers 与并行度的协调
容器里做 I/O 相关调优时,要分清两套「worker」的分工:
| 组件 | 数量由谁定 | 干什么 |
|---|---|---|
I/O worker(io_workers) | io_method = worker 时的专用进程,默认 3 | 替后端执行异步 I/O 操作本身 |
并行查询 worker(max_parallel_maintenance_workers 等) | 计划与维护操作的并行度 | 承担扫描/构建等计算的并行化 |
两者是互补关系:并行 worker 发起 I/O 请求,I/O worker 负责把请求变成数据。 sizing 的定性原则是——io_workers 覆盖「同时排队 I/O 的吞吐」,io_workers 不足表现为请求排队(可从 pg_aios 观察到队列形态);并行度则决定「多少个执行流在同时发起请求」。先用默认值跑基准,再按瓶颈逐项调整,比拍脑袋配大值靠谱。
备份工具链速览
云上把「备份」讲清楚,先分清四类工具的分工(均为定性描述):
| 工具 | 类型 | 定位 |
|---|---|---|
pg_dump / pg_dumpall | 逻辑备份 | 跨版本可移植,恢复慢于物理备份,适合小库与对象级抽取 |
pg_basebackup | 物理基础备份 | 内置的整实例基础备份,PITR 的原料 |
| pgBackRest / WAL-G | 物理备份 + 归档 | 生产级并行备份、压缩、加密、对象存储集成的事实标准 |
| 卷快照 | 存储层 | 秒级、便宜,做回滚与快速克隆,但要按上文方式圈备份窗口 |
PG18 对这条链路的增量集中在工具细节上:pg_verifybackup 支持 tar 格式校验;pg_combinebackup -k/--link 在合并增量备份时硬链接共享文件;pg_dump --statistics 系列让统计可以独立迁移;vacuumdb --missing-stats-only 只补缺失统计。这些能力在「备份即代码」的流水线里都能直接用。
云上的最小权限与网络隔离
部署视角的安全清单(定性,逐项展开见安全与权限阶段):
- 网络层:数据库端口不对公网暴露;K8s 里用 NetworkPolicy 限定「哪些 Pod 能连 5432」;跨可用区/跨区域副本链路走内网或专线。
- 认证层:默认 SCRAM-SHA-256;不使用
trust;机器账号与应用账号分离;凭据轮换纳入 Secret 管理。 - 授权层:应用账号只授予所需库表的最小权限;模式与扩展安装使用独立的管理账号。
- 传输层:强制 TLS(
ssl_groups、ssl_tls13_ciphers是 PG18 的配置入口),内部链路同样加密。 - 审计层:利用 PG18 的
log_connections细粒度与%L客户端 IP,把「谁、从哪、何时连上」记全。
上线前的十项检查
把本章内容收敛成一份可以贴在工单里的清单:
- 卷挂载对准
/var/lib/postgresql,PGDATA显式声明; POSTGRES_PASSWORD来自 Secret,未使用trust;initdb校验和设置与升级路径一致(新旧集群对齐);io_method已按存储形态选定并写入清单(启动级参数);idle_replication_slot_timeout与max_slot_wal_keep_size已配置;- 备份流水线含基础备份 + WAL 归档,恢复演练在日程内;
- 监控查询已适配 PG18 视图(
pg_stat_io字节列、num_done等); - 日志采集接好 stdout,
%L与连接阶段耗时已启用; - 大版本升级路径(pg_upgrade 参数组合或逻辑复制)已演练;
- 托管环境(如适用)确认 PG18 可用性与参数白名单。
云托管选型的决策路径
把选型收敛成一个简单的决策链,避免「功能清单焦虑」:
需要多少运维责任?
├── 全部自己扛 → 官方镜像/源码 + Patroni/pgBackRest 自建(对照本页与高可用阶段)
├── 有人兜底,但保留控制权 → 发行版 + 支持合同,跑在自己的虚拟机/专有网络
└── 尽量不管 → DBaaS(托管服务)
├── 数据规模与负载可预测 → 通用托管(RDS/阿里云 RDS/Cloud SQL)
└── 强弹性、存算分离诉求 → 云原生形态(如 Aurora 类架构、Neon)决策之后,用下面四个问题做最终验证:
- PG18 现在能用吗? 不能的话,路线图是什么;
- 我需要的参数能设吗? 逐个核对
io_method、idle_replication_slot_timeout等新参数; - 我需要的扩展装了吗? 版本多新(见第三页);
- 指标能导出来吗? 能否接入自己的 Prometheus 与告警体系。
四个问题都过关,再谈价格——顺序反了通常要多付一次迁移成本。
Operator 形态的日常运维(day-2)
Operator 解决的是「装起来」,day-2 运维仍然是你的责任,只是工具变了:
| 日常动作 | Operator 时代的做法 | 依赖的 PG 能力 |
|---|---|---|
| 主从切换演练 | 触发 Operator 的 failover 流程 | 流复制 + Patroni 类组件(见 Patroni) |
| 扩缩读副本 | 改 CRD 实例数字段 | 流复制、热备 |
| minor 升级 | Operator 滚动重启 | minor 兼容、热备排队 |
| major 升级 | Operator 编排 pg_upgrade / 逻辑复制 | PG18 统计保留、--jobs、--swap |
| 备份验证 | 定期恢复到临时集簇跑校验 | 基础备份 + WAL 归档 |
| 容量观测 | 接 Prometheus 的自定义查询 | pg_stat_io 字节级指标 |
核心心法:Operator 把「操作」自动化了,把「验证」留给了你——failover 演练、恢复演练这类动作无法外包,只能排进日程。
实战示例
示例一:用 docker compose 跑一套可持久化的 PG18
# docker-compose.yml
services:
pg18:
image: postgres:18
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?required}
POSTGRES_DB: appdb
# 显式写 PGDATA,不依赖镜像默认值,避免跨版本歧义
PGDATA: /var/lib/postgresql/18/docker
volumes:
- pg18-data:/var/lib/postgresql
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pg18-data: {}export POSTGRES_PASSWORD='s3cret'
docker compose up -d
docker compose exec pg18 psql -U postgres -c "SELECT version();"PostgreSQL 18.x (Debian ...) on x86_64-pc-linux-gnu, ...验证数据持久化:docker compose down && docker compose up -d 后数据仍在——因为卷挂在 /var/lib/postgresql,覆盖了版本化的 18/docker 子目录。再把 POSTGRES_PASSWORD 换成 Secret 引用(K8s 或 compose 的 secrets 配置),这套最小部署就具备了进入准生产的基本形态。
示例二:容器内验证异步 I/O
docker compose exec pg18 psql -U postgres -c "SHOW io_method;" io_method
-----------
worker
(1 row)-- 一个会话跑顺序扫描,另一个会话观察 AIO 队列
SELECT count(*) FROM big_table;SELECT backend_pid, io_task, count(*)
FROM pg_aios
GROUP BY 1, 2
ORDER BY 3 DESC;若想尝试 io_uring,先确认两件事:镜像里的二进制构建时带了 liburing(官方 Debian 镜像近年已默认启用,但请以镜像说明为准),且内核与容器运行时放行 io_uring 系统调用;任一不满足就退回 worker。
示例三:声明一个 Operator 管理的 PG18 集群(示意)
以 CNPG 风格为例(字段名与版本以官方文档为准,此处只表达「声明式」的形态):
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: app-db
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:18
instances: 3
storage:
size: 100Gi
backup:
barmanObjectStore:
destinationPath: s3://app-db-backups/
postgresql:
parameters:
idle_replication_slot_timeout: "2h"Operator 会据此拉起一主两备、配置流复制、接备份管道——具体字段、镜像地址与备份写法请以所用 Operator 的官方文档为准,不要照抄本文示例。
示例四:升级前后的一致性检查
-- 1) 校验和设置(pg_upgrade 前必须确认新旧一致)
SELECT * FROM pg_control_checkpoint(); -- 只示意;更直接的方式是查 pg_controldata 输出# 新旧集群各跑一次,对比 Data page checksum 字段
pg_controldata /var/lib/postgresql/17/docker | grep -i checksum
pg_controldata /var/lib/postgresql/18/docker | grep -i checksum-- 2) 升级后确认优化器统计被保留(PG18 起应为非空)
SELECT count(*) FROM pg_stats WHERE schemaname = 'public';若统计缺失,用 vacuumdb --missing-stats-only --all 快速补齐(PG18 新选项),而不是全库 ANALYZE。
示例五:应用级一致的快照备份
存储快照要成为「可恢复的备份」,必须圈定 PG 的备份窗口:
-- 会话 1:进入在线备份状态(PG15+ 的函数名;更早版本为 pg_start_backup)
SELECT pg_backup_start(label => 'daily-snapshot', fast => true);# 会话 2(或脚本):对数据卷打快照(以各云平台 CLI 为准,此处仅示意流程)
# <snapshot the volume>-- 会话 1:结束备份状态
SELECT * FROM pg_backup_stop();pg_backup_start() 到 pg_backup_stop() 之间打的快照才带有一致性元数据,恢复时 PG 能把数据推到一致点。纯快照(不打备份标记)只适合「接受崩溃恢复语义」的回滚场景。
示例六:把监控查询对准 PG18
-- pg_stat_io:字节级 I/O 观测(PG18 新增 read_bytes/write_bytes/extend_bytes)
SELECT backend_type, context, object,
read_bytes, write_bytes, extend_bytes
FROM pg_stat_io
WHERE backend_type = 'client backend'
AND read_bytes > 0
ORDER BY read_bytes DESC
LIMIT 5;-- 检查点完成数(PG18 新增 num_done:只统计真正完成的检查点)
SELECT num_timed, num_requested, num_done, slru_written
FROM pg_stat_checkpointer;-- 定位单个连接的 I/O/WAL 消耗(PG18 新增每后端统计)
SELECT pid, io_summary.*
FROM pg_stat_activity a
CROSS JOIN LATERAL pg_stat_get_backend_io(a.pid) AS io_summary
LIMIT 5;托管环境把自定义查询贴进 postgres_exporter 的查询配置前,先在 PG18 实例上跑通——列名对不上是升级后监控报警的第一来源。
注意事项
PGDATA 路径变更是硬性断裂。 PG18 官方镜像的数据目录与 VOLUME 都变了。沿用「挂 /var/lib/postgresql/data」老配置启动 PG18 镜像,数据会写进匿名卷或错误位置,容器重建即「丢数据」(数据其实还在匿名卷里,但不再被复用)。迁移时按官方指引把数据挪进 18/docker 子目录。
POSTGRES_PASSWORD 是必填项。 除非显式设置 POSTGRES_HOST_AUTH_METHOD=trust(生产禁止),镜像拒绝在无密码时初始化。凭据应来自 Secret 管理系统,而非明文 compose/CI。
新镜像默认 checksums,旧集群升级要显式对齐。 pg_upgrade 要求新旧集群 checksum 设置一致,这是 PG18 升级失败的新增经典原因,升级演练时先跑 pg_controldata 对比。
io_method 只能启动时设置。 在容器/K8s 里改动它意味着重建 Pod 或重启容器,要提前写进镜像环境变量或 Operator 的参数区,别指望热改。
io_uring 是三重门槛。 构建期要有 liburing、内核要支持 io_uring(该接口自 Linux 5.1 引入)、容器运行时的安全策略要放行相关系统调用。三者缺一则回退 worker——它本来就是默认值。
托管服务的参数白名单不完全开放。 PG18 新参数(如 io_method、idle_replication_slot_timeout)在云厂商环境未必可设;迁移到托管前先确认关键参数的可用性。
快照不等于备份。 卷快照若没有配合 pg_backup_start()/pg_backup_stop() 或停写窗口,恢复结果的一致性责任在你;PITR 必须依赖基础备份 + WAL 归档。
升级后监控面板要同步改。 pg_stat_wal 四列被移除、op_bytes 消失、pg_backend_memory_contexts.parent 被删——升级完成后导出的自定义 SQL 查询会直接报错,属于「升级成功但监控挂了」的典型。
镜像变体与自建镜像的默认值可能不同。 Alpine 与 Debian 变体的 locale 行为、自建镜像的编译参数(如是否带 liburing)都会改变运行时表现;判断「某个特性在容器里能不能用」,以镜像的构建说明为准,不要按官方源码包的默认假设推理。
托管环境的 minor 与 major 责任不同。 minor 升级通常由厂商自动滚动执行,major 升级多数需要你主动触发并停写;把「下一个大版本什么时候可用、怎么触发」写进与厂商的沟通清单,而不是等到 EOL 前。
初始化脚本只在首次启动执行。 /docker-entrypoint-initdb.d/ 下的脚本只在数据目录为空(首次 initdb)时运行;容器重建或换镜像不会重跑它们。初始化逻辑应当幂等并纳入版本管理,不要依赖「容器每次启动都执行一遍」。
别用 fsync=off 换性能。 容器与云盘的延迟问题应通过存储类选择与 I/O 配置(含 PG18 的 AIO)解决;关闭刷盘等于把持久性换成速度,数据库领域几乎没有合理场景。
最佳实践
- 挂载卷对准
/var/lib/postgresql,并显式声明PGDATA。 官方镜像 PG18 起的版本化目录设计就是为多版本并存与pg_upgrade --link服务的;显式写PGDATA让配置自文档化,跨镜像版本不踩坑。 - 把
io_method与io_workers写进部署清单而不是靠默认值。 它们只能启动时设置,改一次等于重启一次;提前想清楚容器环境的存储形态再定值,用sync做过对照再谈优化。 - checksums 开启,升级前对齐设置。 新集群享受 PG18 默认开启的完整性保护;升级老集群时把「是否借机开启 checksums」作为一个显式决策写进升级方案,而不是留给
pg_upgrade报错。 - 给所有复制槽配
idle_replication_slot_timeout。 云上副本随时可能被调度走或失联,槽的空闲超时与max_slot_wal_keep_size一起构成 WAL 磁盘的最后一道护栏。 - K8s 上优先选 Operator 而不是手写 StatefulSet。 failover、备份、滚动升级这些运维知识已经沉淀在 CNPG / Zalando Operator / Patroni 里,手写一遍不会更好,还会漏掉恢复演练环节。
- 托管服务选型先验证三件事:PG 大版本可用性、参数与扩展白名单、监控能否导出到自己的 Prometheus——功能列表会骗人,这三项不会。
- 凭据只走 Secret,配置分四层管理。 基础参数进配置文件与版本管理,热调整用
ALTER SYSTEM,会话/角色级微调用ALTER ROLE/ALTER DATABASE;每层的「谁在什么时候改了什么」都可追溯。 - 快照做回滚、备份做灾备,两条线都要有恢复演练。 快照 +
pg_backup_start()/pg_backup_stop()保证应用级一致;WAL 归档到对象存储保证 PITR;演练恢复流程的频率以季度为单位。 - 升级走「pg_upgrade 保留统计 + --jobs + --swap/--link」的组合拳。 配合版本化 PGDATA,把大版本升级窗口压到分钟级;升级后用
pg_stats验证统计,用vacuumdb --missing-stats-only查漏。 - 监控查询随版本走。 把自定义巡检 SQL 纳入版本管理并在 PG18 环境回归一遍,替换掉
pg_stat_wal旧列与op_bytes引用,补上pg_stat_io字节级指标与每后端 I/O/WAL 视图。 - 容器日志按 stdout 交付,日志格式为采集器优化。 利用
log_connections的新粒度与%L(客户端 IP)转义符,让「连接从哪来、哪个阶段慢」在日志系统里可查。 - 所有「云上能不能这样跑」的结论,以官方文档与自己的演练为准。 镜像文档、Operator 文档、云厂商文档都在演进;本页给出的是已核实的基线,落地前请对准你所用版本的最新文档。
下一节
继续学习:PG18:生态与展望