腾讯云 Ubuntu 24.04 上用 Docker 部署 PostgreSQL 完整指南
这段时间在腾讯云上给团队搭了一套项目环境核心需求是把 PostgreSQL 跑起来供多个业务模块使用。折腾了一圈最终选了 Ubuntu 24.04 Docker 的方式来完成部署。整个过程走下来发现网上一堆教程都是“复制粘贴”式的很多关键细节没讲到尤其是 Ubuntu 24.04 这个版本和 Docker 的组合踩了不少坑才跑通。所以干脆把这套部署流程从头到尾整理出来包括我自己的选型思考、每一步操作的原因、参数怎么配置以及排错经验希望对正在做同样事情的你有点帮助。1. 为什么在腾讯云上我坚持用 Docker 部署 PostgreSQL1.1 容器化和传统安装方式的真实差异很多人习惯直接在 Ubuntu 上用 apt 装 PostgreSQL这是我强烈不建议在生产服务器上做的事。apt 安装的 PostgreSQL 通常不是最新稳定版而且一旦系统升级或者你手动改了配置文件整个环境就变得不可控了。容器化部署最大的优势在于环境隔离和一致性——你在本地 Ubuntu 上能跑起来的 PostgreSQL在腾讯云上同样能跑不会因为系统底包、glibc 版本、依赖库的差异导致各种莫名其妙的问题。另外Docker 容器的启动、停止、重启速度比 systemctl 管理原生服务要快得多这在日常运维中体验非常明显。举个例子我调整 postgresql.conf 参数后需要重启数据库让配置生效用 Docker 的方式就一句话docker restart pg-container两三秒搞定。原生安装你还得先sudo systemctl restart postgresql然后看日志确认有没有启动失败处理起来要啰嗦不少。还有一个很实际的理由未来不管你换服务器还是做多机部署镜像一套过去就行数据放数据卷里业务代码和数据库配置完全分离。这种“基础设施即代码”的思路在团队协作时特别有用——新同事加入直接把 docker-compose.yml 给他一条命令起来一个一模一样的数据库环境省去了写一堆初始化文档的时间。1.2 数据安全与迁移Volume 解决换服务器的核心痛点容器化部署最容易被新手忽略的是数据卷的概念。如果没有把 PostgreSQL 的数据目录挂载到宿主机容器一删除数据库里的所有数据就彻底没了而且找不回来——这是很多第一次用 Docker 跑数据库的人踩过的最痛的坑。我的建议是初始化第一秒就规划好数据目录。在腾讯云服务器上数据尽量不要放在系统盘里因为系统盘在实例重装或升级时会有被格式化的风险。我会单独买一块云硬盘挂载到/data目录然后让 PostgreSQL 容器把数据写到/data/postgresql下面。这样一来即使整个服务器实例挂了把云硬盘卸载后挂到新实例上重新执行 docker run 挂载同一个目录数据完好无损。这个操作对数据库这种“生命线”级别的服务来说价值远高于那点挂载配置的成本。除了数据目录还要注意容器本身的“一次性”属性。PostgreSQL 容器可以随时删掉重新创建只要数据卷还在删除容器不会影响数据库里的内容。这听起来好像没什么实际操作中意义很大——你升级 PostgreSQL 镜像、调整启动参数、修改端口映射都只需要用一个新容器替换旧容器旧容器删掉就行根本不用操心数据迁移问题。1.3 不用云数据库 RDS自己部署的核心原因肯定有人会问腾讯云不是有现成的云数据库 PostgreSQL 吗直接用不就行了干嘛还要自己折腾说实话云数据库确实省心但有几个场景让我最终选择了自建。第一成本问题。云数据库 RDS 的定价比纯云服务器要高不少尤其是对数据量增长快的业务存储费用和备份费用都是额外支出。如果只是个人项目或中小团队内部系统自己部署能把成本压得很低。第二控制权和灵活性。云数据库有各种限制比如某些参数不允许修改、插件安装受限、超级用户权限拿不到。自建的话postgresql.conf 里每个参数都能调想装什么扩展就装什么扩展出了问题也能直接用超级用户登录排查不会被平台的管控策略卡住。第三数据迁移的便利性。云数据库要做跨平台迁移或者迁移回自建环境过程相对复杂。但自建数据库的数据要带到任何地方都简单——pg_dump 导出或者直接拷贝整个数据目录到哪都能恢复。当然如果你的业务对可用性的要求极高比如金融、电商秒杀这些场景那云数据库的自动容灾和 SLA 保障还是很有价值的。但作为一般业务部署自己在腾讯云上用 Docker 跑 PostgreSQL兼顾了成本、灵活性和可控性是性价比很高的选择。2. 腾讯云实例与 Ubuntu 24.04 的初始化细节2.1 实例规格与镜像选择2C4G 够不够用腾讯云上创建实例的时候配置选择直接决定了后面的使用体验。我这次用的是 2核4G 的 Standard S5对大多数中小型业务来说已经完全够用了。如果你是要跑并发量比较高的业务我建议至少 4核8G 起步但如果是团队内部系统、个人项目或者测试环境2C4G 完全没问题。系统镜像方面我选择的是 Ubuntu 24.04 LTS也就是代码名称 Nobl。选择 LTS 版本一个最重要的原因就是维护周期长标准支持到 2029 年安全更新不断档不像非 LTS 版本一年就要催你升级。另外 Ubuntu 24.04 的软件仓库更新比较及时对 Docker 和 PostgreSQL 这类常用组件的兼容性也测得多踩坑概率小。还有一个容易忽略的是系统盘大小。腾讯云默认给的是 50G 系统盘我直接改成了 100G因为 Docker 镜像、容器日志、PostgreSQL 的 WAL 日志、备份文件都会占空间。系统盘不够用再去扩既麻烦还可能短暂的不可用不如一开始就留足余量。登录方式建议直接用密钥对而不是密码登录。腾讯云控制台创建实例的时候可以绑定密钥日常 SSH 登录就不用输密码了也更安全。如果实在要用密码尽量设置一个足够强度的复杂密码并且不要用默认的 root 账号直接登录建议创建一个普通用户加上 sudo 权限。这一套下来服务器的基础环境才算是稳了。2.2 安全组规则与腾讯云的两层防火墙理解腾讯云的网络安全策略和普通机房不太一样它的安全组相当于在虚拟机外面的第一层防火墙。你的 Ubuntu 系统内部可能还有 ufw 或者 iptables构成了第二层防护。这两层之间的关系是“与”的关系——流量必须同时通过两层放行才能到达你的服务。对于 PostgreSQL 来说默认端口是 5432。很多人部署完成后发现宿主机怎么都连不上数据库腰果大半都出在安全组没放行。腾讯云安全组规则里入站规则必须添加一条“TCP:5432”的放行规则而且我建议在“来源”那一栏填你办公网络的公网 IP不要图省事填0.0.0.0/0。数据库这种东西暴露在公网上等于把自己的数据开放给全世界的扫描器分分钟被爆破或者勒索。等我部署好之后业务服务器的内网 IP 也会加进安全组来源这样只能内网访问更安全。系统内部的防火墙我的建议是直接关闭 ufw理由后面具体说。因为 Docker 在操作 iptables 的方式比较特殊如果开了 ufw 再叠加 Docker很容易出现规则相互覆盖造成端口看起来放行了却连不上或者相反——你以为关了端口结果公网还能访问。在腾讯云这种已经有安全组外置防护的环境下把防火墙这一层交给云平台服务器内部就别再叠加了逻辑简单还不容易出问题。2.3 系统初始化时区、源、基础工具一次搞定拿到新服务器后我通常先做一套基础初始化流程避免后面部署的时候各种小问题。Ubuntu 24.04 的 apt 源默认用的是官方镜像国内访问有时候会比较慢建议直接换成腾讯云镜像源。操作方式是编辑/etc/apt/sources.list.d/ubuntu.sources把archive.ubuntu.com和security.ubuntu.com替换成mirrors.cloud.tencent.com然后执行apt update。如果你的服务器是在腾讯云买的内网访问腾讯云镜像源速度更快这点很关键。时区设置也需要处理。默认情况下 Ubuntu 用的是 UTC 时间数据库记录日志、时间戳都会比北京时间慢 8 小时调试问题时很容易产生混淆。执行timedatectl set-timezone Asia/Shanghai把时区调回来同时确认一下系统时间是否和北京时间一致。PostgreSQL 容器内部也要做同样的时区配置这个后面启动容器的时候再设置。基础工具方面我一般会装vim、curl、wget、git、htop、tree、net-tools这些日常排查问题的时候都能用上。另外推荐装一个unzip因为后面如果要从备份文件恢复数据处理压缩包是避不开的操作。3. Docker 引擎安装Ubuntu 24.04 的坑和官方源的正确姿势3.1 不要用 apt 自带的 docker.io直接上官方仓库Ubuntu 24.04 的软件源里面确实有 docker.io 这个包但我不建议通过这种方式安装 Docker。原因有几个第一apt 仓库里的 Docker 版本相对滞后可能不是最新稳定版第二docker.io 包和 Docker 官方仓库安装的引擎在某些插件、网络特性上有差异遇到问题你查 Stack Overflow 都可能因为版本不一致而对不上号。正确姿势是添加 Docker 官方 apt 源然后安装 Docker Engine。在 Ubuntu 24.04 上执行下面的命令# 安装依赖 sudo apt update sudo apt install -y ca-certificates curl gnupg # 添加 Docker 官方 GPG 密钥 sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg # 添加 Docker apt 仓库 echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release echo $VERSION_CODENAME) stable | \ sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 安装 Docker Engine sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里有个容易踩的坑curl 下载 GPG 密钥时如果网络不好或者 DNS 有问题有可能下载到空文件或者不完整的文件然后添加到 apt 源的时候会报错。解决办法是先把密钥文件下载看下大小如果ls -l看到文件很小比如只有几字节基本就是下载出了问题重新执行一次 curl 就好。安装完成后执行sudo systemctl enable --now docker让 Docker 开机自启然后运行docker version确认安装成功。如果看到 Client 和 Server 都有版本号输出说明 Docker 引擎正常在运行。Server 部分显示不出来的时候多半是 daemon 没有起来检查一下/var/log/syslog里 dockerd 相关的报错。3.2 Ubuntu 24.04 的 iptables-nft 与 Docker 的兼容性问题Ubuntu 从 22.04 开始就把默认防火墙从 iptables 切换到了 nftables24.04 延续了这个设定。Docker 引擎的端口映射和网络隔离底层是通过 iptables 规则实现的而 Ubuntu 24.04 的 iptables 命令实际上是一个指向 nftables 的兼容层。绝大多数情况下这个兼容层工作得很好但你如果手动写了 iptables 规则或者开了 ufw就很可能和 Docker 的网络规则打架。我遇到过的具体情况是ufw 的规则和 Docker 的 FORWARD 链规则相互覆盖导致 Docker 映射出来的端口从外面访问不通。排查了半天发现是 ufw 默认 DROP 策略把 Docker 的流量也拦了。这个问题的根源在于 Docker 会在 iptables 的 DOCKER 链里插入规则但 ufw 的规则加载顺序会覆盖掉一部分。所以我的建议非常明确在使用腾讯云安全组的前提下直接在 Ubuntu 内部禁用 ufw让 Docker 完全接管 iptables 规则。执行sudo systemctl stop ufw sudo systemctl disable ufw如果你实在需要在系统层面做防火墙控制建议用 Docker 的--publish端口映射暴露范围来限制不要再用 ufw 叠加。比如只映射到某个内网 IP 上-p 192.168.1.10:5432:5432这样只有访问该 IP 的流量才会到达 PostgreSQL 容器比 ufw 规则直观多了。3.3 让非 root 用户直接管理 Docker安装完 Docker 后你会发现直接运行docker ps会报权限错误提示无法连接 Docker daemon因为 Docker 要求 root 权限。官方推荐的做法是把你的用户加入 docker 用户组这样每次不用 sudo 前缀也能执行 docker 命令。sudo groupadd docker sudo usermod -aG docker $USER newgrp docker执行完后重新登录服务器运行docker ps就能正常看到容器列表了。这一步看似简单但很多人会忽略一个安全细节docker 组的用户其实等同于 root 权限因为 Docker 客户端可以通过各种方式提权。所以在团队服务器上不要随便把人加进 docker 组最好是只给真正需要管理容器的人。4. PostgreSQL 容器启动参数逐项解析4.1 数据目录规划与专属 Docker 网络万事俱备开始重头戏启动 PostgreSQL 容器。第一步先规划好数据目录我习惯在/data下建一个postgresql目录sudo mkdir -p /data/postgresql这个目录将作为数据卷挂载到容器内部。PostgreSQL 官方镜像的数据目录是/var/lib/postgresql/data所以启动命令里会有-v /data/postgresql:/var/lib/postgresql/data这一段。然后是网络规划。如果你只是跑一个数据库容器直接用 Docker 默认的 bridge 网络也没问题。但如果后面还有其他容器要一起协作比如应用容器需要连接数据库我建议创建一个自定义网络。自定义网络的好处是容器之间可以用服务名互相访问而不是依赖每次可能变化的 IP 地址。docker network create app-network这样后续启动其他容器的时候只要都加入app-network在应用容器里直接连postgres这个主机名就能访问数据库非常方便。4.2 首次启动必设参数密码、数据目录、时区、字符集现在正式启动容器。我用的命令是docker run -d \ --name postgres \ --restart always \ --network app-network \ -e POSTGRES_USERmyuser \ -e POSTGRES_PASSWORDYour_Strong_Password \ -e POSTGRES_DBmydb \ -e TZAsia/Shanghai \ -e PGTZAsia/Shanghai \ -p 5432:5432 \ -v /data/postgresql:/var/lib/postgresql/data \ postgres:16逐项解释一下每个参数的含义。-d表示后台运行--name postgres给容器起名叫 postgres方便后续操作。--restart always让容器在意外退出或服务器重启时自动拉起这是生产环境必备的否则服务器一重启数据库不会自动启动业务直接挂掉。环境变量方面POSTGRES_USER和POSTGRES_PASSWORD是启动时初始化超级用户用的。这里有个容易忽略的点这两个变量只在数据目录为空、首次初始化的时候生效。如果你的数据目录里已经有一份旧数据容器启动时会完全忽略这两个变量直接用数据目录里的用户信息。所以在数据目录是空的第一次启动时务必把密码设成强密码并且记好。POSTGRES_DB指定初始化时额外创建的数据库。默认情况下镜像会创建一个和POSTGRES_USER同名的数据库这个变量让你额外创建一个业务数据库。TZ和PGTZ设置时区不设置的话容器默认 UTC数据库里now()返回的时间和北京时间差 8 小时后面查日志、统计数据会非常别扭。-p 5432:5432把宿主机的 5432 端口映射到容器的 5432 端口。如果担心安全可以改成-p 127.0.0.1:5432:5432这样数据库只在服务器本地可访问外部连不进来但这样应用容器也得在同一个宿主机上才能连跨服务器的场景就不适用了。我一般会根据实际需求判断如果数据库只给同服务器的应用用就绑定 127.0.0.1如果有其他服务器需要远程访问就按实际来源 IP 配置安全组放行。镜像版本我用的postgres:16。PostgreSQL 16 是目前发布比较久的稳定版了在性能、逻辑复制、Vacuum 等方面都有了明显改进。如果你之前用的是 14 或 15建议尽快升级到 16如果是全新部署直接上 16 肯定是不会错的选择。4.3 端口映射与连接测试启动完成后先用一条命令确认容器的运行状态docker ps | grep postgres看到状态是Up就说明容器正常在运行。接下来可以测试本地连接docker exec -it postgres psql -U myuser -d mydb能进入 psql 交互界面说明容器内部一切正常。然后测试宿主机到容器的连接psql -h 127.0.0.1 -p 5432 -U myuser -d mydb这一步如果你本机没有安装 psql 客户端也可以直接用 Python 或者别的工具连一下试试。如果在宿主机上连接失败优先排查两个地方第一端口映射是否生效ss -tlnp | grep 5432看端口有没有监听第二PostgreSQL 的监听地址是否正确。默认情况下镜像里的listen_addresses是*也就是监听所有网卡如果这个参数被改成了localhost那么从宿主机用 127.0.0.1 连接反而会失败注意区分。最后是从你自己的电脑上远程连接测试。先在腾讯云安全组里确认放行了 5432 端口的入站规则然后本地用 Navicat、DBeaver 这些工具连服务器的公网 IP能连上就算是大功告成。5. 针对腾讯云规格的 PostgreSQL 参数调优5.1 shared_buffers、effective_cache_size 与内存预算PostgreSQL 跑起来之后如果不调优就上线性能大概率不理想。默认配置是为最小环境设计的相当于把一台跑车的发动机限速在 30 码。不同的服务器规格需要不同参数配置在 2C4G 的腾讯云实例上我建议这样设置。先看shared_buffers这是 PostgreSQL 的共享内存缓冲区用来缓存数据页。官方文档推荐设置为系统内存的 25%2C4G 就是 1GB。设置太大会导致系统内存不足触发 swap 反而拖慢性能。effective_cache_size是一个告诉优化器“操作系统层还有多少缓存可用”的估算值不是实际分配的内存。一般设置为系统内存的 50%75%。2C4G 我设置为 3GB。这个值主要影响查询计划器对索引扫描和顺序扫描的选择设置合理了优化器更容易选择使用索引加快查询速度。还有work_mem这个参数控制排序和哈希操作的内存上限。注意它不是全局内存而是每个排序操作都会用这么多所以并发高的时候总内存消耗会成倍上涨。2C4G 我设置为 16MB因为并发连接不多所以 16MB 是安全的如果并发很高建议更低一点比如 8MB避免内存暴涨。安全修改参数的方式是编辑容器内的postgresql.confdocker exec -it postgres bash -c echo shared_buffers 1GB /var/lib/postgresql/data/postgresql.conf不过我不太推荐直接 docker exec 进去改文件因为容器重建之后你得重新执行一遍。更优雅的做法是把postgresql.conf所在目录整体挂载到宿主机或者在宿主机上写好一个自定义配置文件启动时用-c参数覆盖。比如docker exec -it postgres psql -U myuser -d mydb -c ALTER SYSTEM SET shared_buffers 1GB; docker restart postgresALTER SYSTEM SET会修改 PostgreSQL 的postgresql.auto.conf文件重启后自动生效这样既不用进容器改文件也能保持配置的统一管理。我实际生产环境用的就是这种方式比手动改 postgresql.conf 规范得多。5.2 WAL 与 Checkpointer 参数写入密集场景的核心如果你是要跑写入比较频繁的业务WAL 相关的参数会直接影响性能。WALWrite-Ahead Logging是 PostgreSQL 的预写日志相当于操作日志所有的数据修改先写 WAL 再落盘保证崩溃时能恢复。max_wal_size和min_wal_size控制 WAL 文件的大小范围。默认的max_wal_size 1GB对写入密集场景偏小会导致 checkpoint 更频繁地触发而 checkpoint 本身是耗时操作频繁触发会影响插入和更新性能。2C4G 我设置为max_wal_size 2GBmin_wal_size 80MB。checkpoint_timeout默认是 5 分钟意思是每 5 分钟至少触发一次 checkpoint。如果 WAL 文件增速很快5 分钟一到就 checkpoint有可能跟不上写入速度。我把这个参数调到 15 分钟配合加大max_wal_size大幅减少 checkpoint 频率。wal_buffers默认是 16MB对于大多数场景够用一般不需要动。如果你遇到大量写操作争抢 WAL 缓冲区锁可以考虑提高到 32MB但这种情况通常是连接数过高先查连接池再调参数。另外如果你的业务对数据丢失零容忍可以开启synchronous_commit on但这会降低写入性能。对自己部署的数据库通常用默认的on就好因为 Docker 跑在同一台宿主机上丢失数据的概率很小。5.3 连接数、客户端认证与日志参数max_connections默认是 100对一般场景够用。但要注意每个连接在 PostgreSQL 内部都是一个进程会占用一定内存。2C4G 的实例如果开满 100 个连接内存压力会很大。我建议把应用层的连接池配好比如 pgbouncer把实际活跃连接控制在 2030 个以内远比盲目调大max_connections靠谱。client_encoding和lc_messages建议都设为UTF8尤其是有中文数据存储需求的时候。如果初始化集群时没有用 UTF8后面要转编码非常麻烦数据可能乱码。启动容器的时候可以加一个-e POSTGRES_INITDB_ARGS--encodingUTF8 --localeC.UTF-8确保初始化的数据库就是 UTF8 编码。日志方面的参考配置是logging_collector on log_directory log log_filename postgresql-%Y-%m-%d.log log_statement ddl log_min_duration_statement 1000log_statement ddl只记录 DDL 操作不至于日志爆炸log_min_duration_statement 1000把超过 1 秒的慢查询记录下来方便后续做性能优化时定位问题。日志文件默认在/var/lib/postgresql/data/log目录正好也在我们挂载的宿主机数据卷里备份和查看都很方便。5.4 这些参数我不建议动前面推荐的都是有明确收益的调整但有些参数在 2C4G 这种规格下最好别随便改。autovacuum保持默认开启即可这是 PostgreSQL 自动清理死行、维护数据健康的核心机制关闭它会让表膨胀得越来越严重查询越来越慢。fsync保持 on虽然关了能提升安全性但一旦断电或系统崩溃数据文件和 WAL 不一致整个数据库可能直接无法启动这种风险绝对不能冒。full_page_writes也不要关它和 crash recovery 直接相关关闭后若系统崩溃可能发生数据页损坏。还有一点容易被忽略Docker 容器默认的存储驱动是 overlay2如果底层文件系统本身性能一般数据库的 IO 会明显受限。腾讯云 SSD 云硬盘的性能还可以但不建议买那种低配的 HDD 云硬盘来跑数据库IO 延迟会让人抓狂。6. 日常运维备份、升级与故障恢复6.1 用 pg_dump 做定时逻辑备份避免灾难性损失PostgreSQL 跑起来只是第一步备份策略才是日常运维的重头戏。最常用的备份工具是pg_dump它导出的是逻辑数据——即 SQL 语句可以跨版本恢复。我习惯把备份任务写到宿主机的一个脚本里然后用 cron 定时执行。脚本如下#!/bin/bash BACKUP_DIR/data/backup/postgresql DATE$(date %Y%m%d_%H%M%S) DATABASE_NAMEmydb # 确保备份目录存在 mkdir -p $BACKUP_DIR # 执行备份 docker exec postgres pg_dump -U myuser -d $DATABASE_NAME | gzip $BACKUP_DIR/${DATABASE_NAME}_${DATE}.sql.gz # 保留最近 7 天备份清理更早的文件 find $BACKUP_DIR -name *.sql.gz -mtime 7 -exec rm -f {} \;给脚本加执行权限然后crontab -e添加一行0 2 * * * /data/scripts/backup_postgres.sh /data/scripts/backup.log 21这个方案每天晚上 2 点做一次全量逻辑备份保留 7 天足够应对大部分误删数据、升级失败等场景。恢复的时候执行gunzip /data/backup/postgresql/mydb_xxxx.sql.gz | docker exec -i postgres psql -U myuser -d mydb逻辑备份适合中小规模数据如果数据量上了 TB 级别就要考虑用物理备份方案了比如 pg_basebackup 或者第三方工具这里先不展开。6.2 容器镜像升级的完整步骤和注意事项PostgreSQL 出新版本或者你想从 16 升级到 17Docker 容器的方式有清晰的步骤。但有一点必须反复强调绝对不要直接 docker pull 新镜像然后 restart 容器因为数据目录的版本不兼容容器可能直接启动失败。完整升级流程是这样的备份当前数据按上面的脚本来一次全量备份。停止应用写入确保没有业务连接数据库。如果业务无法完全暂停至少要把连接降到最低。记录当前容器配置把 docker inspect 里的运行参数记下来尤其是挂载卷、端口映射、环境变量这些。建议平时就把启动命令写进 docker-compose.yml 或者脚本里省得到时候手忙脚乱。停止并删除旧容器docker stop postgres docker rm postgres。注意只要数据卷目录还在数据就不会丢。用新镜像启动新容器比如把postgres:16改成postgres:17同样的挂载卷和环境变量。启动后看日志如果报数据版本不兼容那就继续用旧镜像或者用 pg_upgrade 走正式的版本升级流程。PostgreSQL 的跨大版本升级不能靠数据目录直接替换因为系统表结构会变数据格式也可能不同。如果是从 16 升 17个人推荐直接用 pg_dump 导出再导入新库简单可靠数据量实在太大才考虑 pg_upgrade。6.3 日志查看、容器健康检查和自动重启日常运维中日志是最重要的第一手排错信息。PostgreSQL 容器的日志可以用docker logs查看docker logs postgres docker logs --tail 100 -f postgres--tail 100只看最近 100 行-f持续跟踪输出。容器崩溃的时候先用 docker logs 看日志往往能直接定位到原因比猜谜语效率高得多。如果希望 Docker 能更智能地判断容器状态可以配置健康检查。修改启动命令加一段docker run -d \ ... --health-cmdpg_isready -U myuser -d mydb \ --health-interval30s \ --health-timeout5s \ --health-retries3 \ postgres:16pg_isready是 PostgreSQL 自带的健康检查工具能确认数据库是否能够接受连接。配置之后docker ps里会显示 healthy 状态容器异常时也能快速被主机发现。--restart always已经保证了容器会随机器启动但如果 PostgreSQL 进程挂了而容器没挂仅靠 restart 策略是不够的。健康检查配合 Docker 的自动重启策略或者用系统 systemd unit 管理容器服务才能做到自动拉起。有精力的话可以把容器交给 systemd 管理用systemctl restart docker-postgres控制逻辑会更清晰。7. 踩坑实录从“容器退出”到“数据丢失”的完整排查链路7.1 容器一启动就退出PostgreSQL 日志正在告诉你原因我遇到的第一个问题是按照网上教程执行 docker run 之后容器状态从 Up 变成 Exited一两秒就退出了。这种情况新手最容易慌但其实八成是启动参数或者数据目录权限的问题先看日志docker logs postgres日志里出现FATAL: data directory /var/lib/postgresql/data has wrong ownership时说明宿主机挂载目录的属主不对。PostgreSQL 容器内是用 postgres 用户来运行数据库的UID 是 999它需要对你挂载出来的数据目录有读写权限。解决办法是sudo chown -R 999:999 /data/postgresql还有一种情况是数据目录不干净。如果之前有别的版本或别的方式初始化过这个目录里面残留了旧文件PostgreSQL 启动时会报错提示数据目录非空或者版本不匹配。对付这种问题把目录清空再启动即可——前提是你已经确认不需要旧数据了。我强烈建议第一次启动时先不要急着挂载数据卷直接用容器默认路径跑通确认业务没问题后再考虑挂载卷迁移。这样可以避免一上来就把权限、路径问题混在一起排查成本高。7.2 连接超时和拒绝访问安全组、listen_addresses、pg_hba.conf 三座大山第二个让我折腾最久的坑是部署完以后从本地电脑怎么都连不上数据库。通过docker ps看容器明明是 Up在服务器上用 127.0.0.1 也能连上但换成本地就超时或者访问被拒绝。这个问题的根源通常是三选一按排查顺序来腾讯云安全组没放行。这是最常见的。控制台 → 实例 → 安全组 → 入站规则检查 5432 端口是否有 TCP 放行规则。特别注意来源 IP 是不是包含了你本地电脑的公网 IP。很多人在这一步填了0.0.0.0/0那当然能连上但风险极大如果填了指定 IP就要确保你的地址没变过。PostgreSQL 只监听了 localhost。默认情况下 postgres 镜像的listen_addresses是*也就是说监听所有网卡。但如果你之前用其他方式改过配置它可能只在 127.0.0.1 上监听。在服务器上执行ss -tlnp | grep 5432看监听地址是什么。如果是 127.0.0.1:5432那就说明需要改listen_addresses。pg_hba.conf 的认证规则不允许远程登录。这个文件控制哪些 IP 可以用什么方式认证。默认镜像里的 pg_hba.conf 允许所有地址用scram-sha-256认证但如果你想从特定网段连确认没有规则拦截。检查方法是在容器里执行docker exec postgres cat /var/lib/postgresql/data/pg_hba.conf找到类似host all all 0.0.0.0/0 scram-sha-256的行。如果这三项都设置正确从本地用 psql 或者 DBeaver 连接理论上能正常通。还连不上就需要检查云服务器的外网带宽、路由是否有异常了。7.3 重启后数据没了永远不要把数据库文件放在容器可写层最后也是最重要的一坑有同事在服务器上执行docker rm -f postgres然后重新跑了一个容器发现数据库里的表全没了。我当时看着他操作屏幕简直想拍桌子——这就是没做数据卷挂载的后果。如果你只是在 docker run 的时候用了postgres:16镜像没有指定-v参数PostgreSQL 默认把数据写到容器内部的/var/lib/postgresql/data。这个路径在容器被删除时会连同容器一起消失。Docker 的设计理念是“容器是可丢弃的”但数据必须是持久的所以一切状态数据都必须放到卷里。如果你已经遇到这个问题了先冷静能找回来一部分的概率并不为零。如果容器还没被删除只是停止了可以先docker start postgres然后把数据目录拷贝出来docker cp postgres:/var/lib/postgresql/data /data/postgresql_data_backup如果容器已经被删了但 Docker 的 overlay2 层还没被清理没有执行过docker system prune理论上还能从/var/lib/docker/overlay2/下通过搜索PG_VERSION文件找到残留数据但这个过程很痛苦不是所有场景都能成功恢复。防患于未然的做法很简单启动命令里必须有-v /data/postgresql:/var/lib/postgresql/data并且用docker inspect postgres可以确认挂载是否正确。更方便的做法是把启动命令固化成 docker-compose.yml 文件每次启动都通过同一个文件来管理既能保留配置也天然避免了漏挂载的情况。这套部署方案我从零到一跑通了中间踩的每一个坑现在回想起来都值得。数据卷挂载和参数调优这两个点是我觉得最需要提前规划好的——数据库部署不像普通服务一旦数据量上去、业务跑起来再改底层结构代价就大了。如果你也在腾讯云上折腾 Ubuntu 24.04 Docker PostgreSQL 的组合按照这套流程走一遍至少能少走很多弯路。