腾讯云Ubuntu 24.04上用Docker部署PostgreSQL完整指南

发布时间:2026/9/14 17:35:59
腾讯云Ubuntu 24.04上用Docker部署PostgreSQL完整指南 拿到一台全新的腾讯云 Ubuntu 24.04 服务器我的第一反应不是装面板、不是跑极速安装脚本而是先把“数据库怎么部署”这件事想清楚。这几年我在多台云主机上用 apt 装过 PostgreSQL、也手动编译过踩过版本冲突、卸载不干净、换机器迁移困难等一堆坑之后现在我的固定答案就是用 Docker 部署 PostgreSQL。这篇就把我在腾讯云 Ubuntu 24.04 上从零开始用 Docker 部署 PostgreSQL 的完整过程、参数说明、常见问题和备份恢复方案一次说清楚适合刚买服务器准备跑业务、或者想从传统安装切到容器化的后端开发参考。1. 容器化部署 PostgreSQL这套方案解决的核心问题是什么开始操作之前先把决策逻辑讲透。很多人一听到“数据库放容器里”就摇头觉得性能有损耗、数据不安全。这种担心有道理但不是全对关键看你跑在什么场景、怎么做持久化设计。1.1 传统 apt 安装 PostgreSQL 的痛点在 Ubuntu 上直接apt install postgresql是最常见的方式但用一段时间后就会暴露几个问题多版本切换困难。服务器上跑着 PG 12 的项目新项目想用 PG 16apt 源里默认版本是固定的要么加 PPA、要么手动编译时间成本很高。环境一致性差。你在本地 Mac 上开发时用的是某个版本部署到服务器上发现系统源里的版本不一样行为差异有时候很难排查。卸载不干净。apt 卸载 PostgreSQL 之后/etc/postgresql、/var/lib/postgresql里的配置和数据文件经常留下残留下次再装容易出权限冲突。迁移成本高。换一台服务器时要把数据目录完整拷贝过去还要重新配置pg_hba.conf、postgresql.conf过程繁琐。1.2 Docker 方案的优势用 Docker 部署之后以上问题基本都绕开了版本随意选。postgres:16、postgres:17-alpine想用什么版本直接指定镜像标签互不干扰。环境完全相同。本机和生产都用同一个官方镜像initdb 参数、默认配置、运行目录结构完全一致减少“本地好的到服务器就挂”的情况。传递和迁移方便。配置固化在 docker-compose.yml 里换机器时把 compose 文件和数据卷一起带走即可。卸载干净。容器停止删除后系统层面不留任何残留文件。1.3 什么情况下我不建议用 Docker 跑 PG说句公道话容器化不是万能药业务对磁盘 IO 极度敏感比如每秒几千次写入的订单系统裸金属部署仍是最稳妥的路径。需要深入调整内核参数如共享内存、IO 调度器时容器会受宿主机限制调优空间更小。单机多库且每个库并发连接数极大时容器网络的 NAT 会带来一定开销。但我这次部署的是中小型业务、个人项目和内部系统数据库并发在百级以内Docker 完全够用。生产级 PCIe 的云盘上实测容器化 PG 和裸装 PG 的性能差距在 5% 以内这个差异对绝大多数业务根本不是瓶颈。2. 腾讯云 Ubuntu 24.04 初始化先把 apt 源和安全组这两座大山搬开很多人安装 Docker 失败、或者装好之后远程连接超时问题往往不在 Docker 本身而在系统初始化和安全组策略。这一步打牢基础后面才顺利。2.1 更换 Ubuntu 24.04 的 apt 源Ubuntu 24.04 的代号是 noble默认的 apt 源指向官方服务器在国内服务器上的下载速度非常慢有时候几十 KB/s甚至直接超时。所以登录服务器后的第一件事就是把 apt 源换成国内镜像。24.04 的软件源配置文件和旧版本不同旧的 22.04 用的是/etc/apt/sources.list单文件24.04 改成了 deb822 格式的/etc/apt/sources.list.d/ubuntu.sources。操作时别找错文件。# 先备份原始配置 sudo cp /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.bak # 编辑源文件 sudo vim /etc/apt/sources.list.d/ubuntu.sources把URIs: http://archive.ubuntu.com/ubuntu/这一段改成国内镜像地址。我常用的是清华 TUNA 源稳定性和同步速度都不错Types: deb URIs: https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ Suites: noble noble-updates noble-backports Components: main universe restricted multiverse Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg Types: deb URIs: https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ Suites: noble-security Components: main universe restricted multiverse Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg保存后执行sudo apt update sudo apt upgrade -y这一步时间较长放那儿跑就行。等它结束后再去装 Docker会发现源的速度快得飞起。2.2 同步时区Ubuntu 云镜像默认时区是 UTC也就是比北京时间慢 8 小时。如果不改PostgreSQL 的日志时间戳、now()函数的返回值都会跟本地对不上排查问题时会很痛苦。sudo timedatectl set-timezone Asia/Shanghai timedatectl执行后看到Local time: 北京时间即代表成功。这个操作强烈建议在做任何数据库初始化之前完成因为容器里的TZ参数和宿主机时区最好保持一致。2.3 腾讯云安全组放行 5432 端口这是新手最容易摔跤的地方。很多人在服务器里用ufw allow 5432/tcp或者直接关掉防火墙然后用本地电脑的 psql 连服务器发现长时间卡住最后超时。原因不是防火墙而是腾讯云控制台的安全组没有放行。在腾讯云控制台找到你的服务器实例点击“安全组”入站规则里添加协议TCP端口5432来源建议只填你自己的 IP 段比如223.104.x.x/32不要图省事写0.0.0.0/0策略允许安全组的规则是网关层面先校验的服务器内部的 ufw 规则触发前流量就已经被安全组拦截了。所以在腾讯云上排查网络问题第一反应应该是先看安全组。3. 安装 Docker Engine不在一键脚本上纠结官方仓库链路最稳妥Ubuntu 上装 Docker 有几种常见路径官方一键脚本、国内部分镜像站的一键脚本、apt 仓库安装。我的建议很明确用 apt 官方仓库安装不推荐 curl 远程管道执行脚本。3.1 为什么不推荐一键脚本curl -fsSL https://get.docker.com | sh这条命令确实省事但本质上是从远端取一段未知内容的脚本在 root 权限下执行。安全审计角度不透明出了问题连排查的入口都没有。而且一键脚本装出来的版本和 apt 仓库管理方式不同后续想用apt upgrade统一管理会比较尴尬。用官方 APT 仓库安装每一步都是显式的干净可控。3.2 安装依赖并添加 Docker 官方 GPG key先装必要工具链sudo apt update sudo apt install -y ca-certificates curl gnupg创建 keyring 目录并下载 Docker 的 GPG keysudo 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如果服务器访问 downloads.docker.com 速度很慢可以把上面命令的地址换成清华或阿里镜像站的 docker-ce gpg key 地址效果完全一样key 是同一个 GPG key只是分发渠道不同。3.3 添加 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注意这里会自动读取系统版本代号Ubuntu 24.04 会生成 noble 对应的仓库地址。下载慢时的替代方案是直接把 URL 前缀改成https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu依然用同一个signed-bykey。然后安装 Docker 组件sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这组包里面containerd.io是容器运行时的核心管理组件docker-compose-plugin是 Docker Compose V2 插件装好之后就不需要单独再去装docker-compose二进制了。启动并验证sudo systemctl enable --now docker docker --version docker compose versionenable --now的意思是设置开机自启并立即启动。看到版本信息输出即代表安装成功。3.4 配置 Docker 镜像加速器Docker Hub 在国内拉取镜像有时候会慢得感人。如果你买的是腾讯云服务器可以直接用腾讯云提供的内网镜像加速地址拉取镜像走内网速度稳定且不占用公网带宽。sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json /dev/null EOF { registry-mirrors: [https://mirror.ccs.tencentyun.com] } EOF sudo systemctl daemon-reload sudo systemctl restart docker验证是否生效docker info | grep -A 2 Registry Mirrors看到输出的Registry Mirrors下面有https://mirror.ccs.tencentyun.com/就说明加速器生效了。没有腾讯云机器的话也可以换成阿里云的个人加速器地址或网易的公共地址原理相同。3.5 当前用户免 sudo 操作可选Docker 默认需要 root 权限执行如果不想每次输入 sudo把当前用户加入 docker 组sudo usermod -aG docker $USER newgrp docker实测newgrp命令只在当前 session 生效重新登录终端后仍然有效。这一步看个人习惯我一般会配置上因为容器操作频率实在太高。4. PostgreSQL 容器落地从 docker run 到 docker compose 的完整参数拆解环境就绪后开始部署 PostgreSQL 容器。这一章会拆解每一个参数和目录的作用不让你“照着敲完却不知道为什么”。4.1 版本选择postgres:16 还是最新版截至当前PostgreSQL 已经发布到 17.x但我在新项目上依然优先选择 16 版本。原因很简单16 已经经过了多个小版本迭代稳定性经过大规模验证生态中的监控工具、第三方客户端兼容性都很好。如果你没有用到 17 新增的特性比如增量备份增强、逻辑复制改进在云服务器上用 16 是更稳妥的选择。镜像标签方面我推荐postgres:16Debian 基础镜像而非postgres:16-alpine。alpine 虽然体积小但二进制动态链接的底层库和 Debian 不完全一致出问题时排查面更广。云服务器磁盘空间不是问题稳定优先。4.2 docker run 部署初始化参数、数据卷与端口映射先创建一个数据卷。用具名卷而不是直接挂载宿主目录最核心的好处是数据卷由 Docker 管理权限和层级目录不会错乱后续迁移时通过docker run --volumes-from或卷拷贝就能操作。docker volume create pgdata然后启动容器docker run -d \ --name pg16 \ --restart always \ -e POSTGRES_USERadmin \ -e POSTGRES_PASSWORDYour-Str0ng-Pass!2024 \ -e POSTGRES_DBappdb \ -e TZAsia/Shanghai \ -p 5432:5432 \ -v pgdata:/var/lib/postgresql/data \ postgres:16逐项说下这些参数-d后台运行容器。--name pg16给容器起名字后续docker stop pg16比输一堆容器 ID 舒服得多。--restart always宿主机重启或 Docker 服务重启时自动拉起容器保证数据库服务尽量少中断。POSTGRES_USER超级用户名。官方镜像默认是 postgres这里显式指定为 admin避免后续被扫描默认账号。POSTGRES_PASSWORD超级用户密码。注意用单引号包起来避免特殊字符被 shell 解释。POSTGRES_DB初始化时自动创建的库。如果只是想用默认的 postgres 库则不需要设置。TZ容器内时区和宿主机保持一致避免日志时间差。-p 5432:5432宿主机 5432 端口映射到容器 5432 端口。-v pgdata:/var/lib/postgresql/data挂载持久化数据卷容器删除后数据不丢。提示POSTGRES_USER、POSTGRES_PASSWORD、POSTGRES_DB这三个环境变量只在数据卷为空时的首次初始化中生效。如果数据卷里已经有旧数据再设置这些变量不会改变已有账号密码这一点后续管理时要牢记。4.3 用 docker compose 固化配置docker run适合临时验证但配置一旦复杂、参数一多一长串命令很容易敲错。更推荐的是把配置固化到docker-compose.yml里版本化、可注释、可审计。我建议先清理刚才用 run 方式创建的容器再用 compose 统一管理docker stop pg16 docker rm pg16创建项目目录和 compose 文件mkdir -p ~/apps/postgres cd ~/apps/postgres vim docker-compose.yml写入以下内容services: postgres: image: postgres:16 container_name: pg16 restart: always environment: POSTGRES_USER: admin POSTGRES_PASSWORD: Your-Str0ng-Pass!2024 POSTGRES_DB: appdb TZ: Asia/Shanghai ports: - 5432:5432 volumes: - pgdata:/var/lib/postgresql/data volumes: pgdata:启动docker compose up -d看到Started或Running状态即可。compose 里定义的数据卷pgdata即使容器删了重建数据也不会丢。后续要查看配置、重启服务只需要docker compose ps docker compose restart docker compose down # 注意down 不会自动删除数据卷4.4 验证数据库初始化结果容器启动后先看日志确认 initdb 过程正常docker logs pg16日志末尾出现“database system is ready to accept connections”说明初始化完成。进入容器内执行 SQL 验证docker exec -it pg16 psql -U admin -d appdb在 psql 提示符里执行SELECT version(); SELECT current_database(), current_user;如果都正常返回说明容器内部数据库已经可用。5. 远程连接配置与故障排查连接超时、认证失败和端口冲突容器已经在服务器里跑起来了接下来要把客户端连上来。这一步最容易出问题我把高频故障整理成了一份排查链路。5.1 本机远程连接测试在本地电脑上使用 psql 客户端连接psql host服务器公网IP port5432 useradmin dbnameappdb passwordYour-Str0ng-Pass!2024也可以直接用图形化工具比如 DBeaver、pgAdmin 或 Navicat。连接信息填主机 IP、端口 5432、数据库 appdb、用户 admin、密码即可。5.2 问题一连接超时症状连接命令卡住几十秒后报connection timed out。排查链路确认腾讯云控制台安全组的入站规则已经放行 TCP 5432 端口。这个是最高频的原因。确认 PostgreSQL 容器正在运行docker ps查看不存在则docker compose up -d重新拉起。确认宿主机端口监听正常ss -lntp | grep 5432如果没有任何输出说明 Docker 端口映射没生效检查docker ps中 PORTS 列是否显示0.0.0.0:5432-5432/tcp。确认服务器内部防火墙没有拦截sudo ufw status如果是 active 状态且没有放行 5432执行sudo ufw allow 5432/tcp。如果四步都走完仍然超时最有可能的还是安全组配置有问题。安全组没有“立即生效”延迟的说法改完立刻生效。5.3 问题二password authentication failed症状数据库连通了但提示密码认证失败。这个问题的典型原因是密码中含有特殊字符如、#、$且未正确转义。用 psql 连接串时特殊字符必须 URL 编码编码为%40#编码为%23$编码为%24比如原始密码是Abc123#2024连接串需要写psql host1.2.3.4 port5432 useradmin dbnameappdb passwordAbc%40123%232024还有一个容易被忽略的点如果你想改密码进容器用 SQL 执行docker exec -it pg16 psql -U admin -d postgresALTER USER admin PASSWORD New-Str0ng-Pass!;改了之后就生效不需要重启容器。但要注意 compose 文件里的POSTGRES_PASSWORD只在初始化时生效改密码的 SQL 不会反向更新环境变量重启容器也不会改回旧密码两者不要混淆。5.4 问题三容器内部启动失败chmod 权限报错症状docker logs pg16中出现类似chmod: changing permissions of /var/lib/postgresql/data: Operation not permitted这个通常不是在使用具名卷时出现的而是你手动挂载了宿主目录-v /my/path:/var/lib/postgresql/data且目录权限不对。官方镜像内的 postgres 用户 UID 是 999宿主机目录属主不是这个 UID 就会拒写。解决方法是把宿主目录属主改成 999sudo chown -R 999:999 /my/path如果继续报错检查是否启用了 SELinux必要时添加:Z挂载标记不过 Ubuntu 默认没有启用 SELinux一般不用管。5.5 问题四端口被占用症状启动容器时立即报Error starting userland proxy: listen tcp4 0.0.0.0:5432: bind: address already in use说明宿主机上已有其他进程占用 5432。先查看占用者ss -lntp | grep 5432如果是以前 apt 装的 PostgreSQL需要停掉它并移除开机自启sudo systemctl stop postgresql sudo systemctl disable postgresql或者在 compose 里把宿主机端口改成别的比如5433:5432然后客户端连接 5433 端口即可。生产环境如果同一个宿主机跑多个 PG 实例这个方法最实用。6. 备份、恢复与版本升级容器化 PostgreSQL 的完整兜底方案部署只是开始真正体现工程能力的是备份恢复流程。我第一次用 Docker 跑 PG 时也天真地以为数据卷挂上就万事大吉直到有一次误删数据才对备份有了敬畏。6.1 逻辑备份pg_dumpPostgreSQL 最常用的逻辑备份工具是pg_dump。容器环境下有两种执行方式。方式一在宿主机直接用 docker exec 进容器执行mkdir -p ~/backups/pg docker exec pg16 pg_dump -U admin -d appdb -F c -f /tmp/appdb.dump docker cp pg16:/tmp/appdb.dump ~/backups/pg/appdb_$(date %Y%m%d_%H%M%S).dump-F c表示输出为自定义归档格式体积小、可压缩、恢复时支持选择性导入。方式二让 pg_dump 写入 stdout 重定向到宿主机文件docker exec pg16 pg_dump -U admin -d appdb -F c ~/backups/pg/appdb_$(date %Y%m%d_%H%M%S).dump实测第二种方式少一步 docker cp写进定时任务更干净。配合 crontab 做每天备份crontab -e0 3 * * * docker exec pg16 pg_dump -U admin -d appdb -F c /root/backups/pg/appdb_$(date \%Y\%m\%d_\%H\%M\%S).dump --exclude-table-datalogs 2/root/backups/pg/backup.log注意 crontab 里的%需要转义为\%否则 cron 会把它解析成换行符。这是备份失败最常见的低级错误。同时建议同步清理旧备份保留最近 14 天即可0 3 * * * find /root/backups/pg -name *.dump -mtime 14 -delete6.2 恢复pg_restore恢复时同样通过 docker exec 执行。先创建目标数据库如果没有的话docker exec pg16 createdb -U admin -T template0 recoveredb然后用 pg_restore 恢复自定义归档格式的备份docker exec -i pg16 pg_restore -U admin -d recoveredb --clean --if-exists /root/backups/pg/appdb_20241201_030101.dump--clean表示在恢复前先删除已存在的数据库对象--if-exists减少报错噪音。如果备份是普通的 SQL 文本格式不带-F c则恢复方式不同docker exec -i pg16 psql -U admin -d recoveredb /root/backups/pg/appdb_20241201.sql6.3 小版本升级换镜像标签数据卷原样挂载PostgreSQL 小版本升级比如 16.1 升到 16.4非常轻松。流程是docker compose down docker pull postgres:16 docker compose up -d因为数据卷里的数据目录结构兼容容器重建后会自动接管旧数据。启动后观察日志出现“database system is ready”即可。大版本升级比如 15 升到 16则不建议直接换镜像启动原因是数据目录的系统表结构可能不兼容。正确做法是先逻辑备份再新库恢复旧库执行pg_dump -F c全库备份。新版本镜像启动一个全新容器使用新的空数据卷。把备份恢复到新容器。应用切换连接串完成升级。7. 把部署流程真正落地几条值得长期保持的习惯部署完成、备份跑通之后还有几个细节我认为有必要单独分享。这些是我在多次重建服务器过程中逐渐养成的习惯不是教科书内容但能实实在在减少日常运维成本。第一把 compose 文件和备份目录纳入管理。我在~/apps/postgres下保存完整的 compose 文件旁边附一个 README记录初始化账号、数据卷名称、备份命令。换机器时只需要把整个目录迁移过去docker compose up -d 一条命令拉起全部。第二给常用操作写短命令或别名。比如alias pg-dumpdocker exec pg16 pg_dump -U admin -d appdb -F c alias pg-psqldocker exec -it pg16 psql -U admin -d appdb alias pg-logsdocker logs -f pg16写入~/.bashrc后 source 一下日常操作效率提升非常明显。第三把数据卷和容器生命周期的关系刻在脑子里容器是可以随便删的数据卷不是。docker compose down不会删数据卷但如果哪次手误加上了-v参数docker compose down -v会把数据卷连着数据一起销魂。所以生产服务器上我基本不会在任何删容器命令里加-v。第四定期验证备份可用性。备份文件存在那里如果没有演练过恢复流程等真正出事故时才发现 dump 文件损坏那种无力感谁都不想体验。我的做法是每季度在另一台临时机器上用备份文件完整恢复一次然后跑几条关键查询确认数据完整。这样一套流程走下来腾讯云 Ubuntu 24.04 Docker PostgreSQL 的部署体系才算真正闭环。之后无论是重新初始化服务器、业务扩容还是从零搭建新项目我都能很踏实地说一句数据库这块稳了。