
1. 部署前的核心认知与环境准备Ubuntu 24.04 搭配 Docker 跑 PostgreSQL是我在实际项目中反复验证过的一套组合。腾讯云服务器上这么干图的就是环境隔离和部署效率——数据库跑在容器里宿主机即使折腾坏了容器一删重来也就几分钟的事不会像传统方式那样把系统搞得一团糟。先说几个关键判断这些直接影响你后面的每一步操作。腾讯云 Ubuntu 24.04 默认是纯净的服务器版系统不带图形界面SSH 连接后就是一个终端。这个版本的内核是 6.8对 Docker 的支持非常成熟不像早期版本那样还需要额外处理内核兼容问题。另外 Ubuntu 24.04 的 apt 源里自带 Docker 相关软件包但你千万别图省事直接apt install docker.io原因后面我会细说。这个部署方案适合谁如果你是正在学习 PostgreSQL 的开发者想在云上有个自己的数据库练手或者你是小团队要快速搭一套测试环境、内部工具的后端存储再或者你是运维新手想搞明白容器化数据库到底怎么落地——这篇文章都能用得上。注意我默认你已经有一台腾讯云服务器CVMCloud Virtual Machine云服务器系统是 Ubuntu 24.04并且能用 SSH 登录到 root 用户或拥有 sudo 权限的普通用户。如果你是 Windows 本机建议先用 Xshell 或 Termius 这类终端工具连上服务器再操作如果你用的是腾讯云网页版自带的“OrcaTerm”终端也行但注意复制粘贴命令时格式可能有问题后面我会提到这个坑。2. 搭建 Docker 运行环境2.1 为什么不用 apt 自带的 docker.io很多人第一次装 Docker 都会掉进这个坑。Ubuntu 官方源里的 docker.io 包虽然能用但版本通常比较旧而且更新节奏慢。你在本机测试没问题一到生产环境要装 Docker Compose 插件、要配置国内镜像加速器、要跟最新的容器生态保持同步就会发现自己被绑死在老版本上进退两难。正确做法是使用 Docker 官方提供的 apt 源。这个源在腾讯云服务器上也能正常访问实测速度还可以。如果你发现拉取 Docker 官方源超时可以换成清华 TUNA 镜像源或阿里云镜像源配置方法我放在下文。2.2 完整安装步骤这里我给出一套完整、可直接复制执行的命令序列。全程大约 5 到 8 分钟取决于你的服务器带宽。第一步更新系统已有的软件包列表。这一步必须做否则后边装依赖时容易碰到索引过期的问题。sudo apt update sudo apt upgrade -y第二步安装 Docker 依赖的基础软件包。curl 用来下载密钥ca-certificates 用来验证 HTTPS 连接gnupg 用来导入 GPG 密钥。sudo apt install -y curl ca-certificates gnupg第三步创建 apt 源的 keyrings 目录并下载 Docker 的官方 GPG 密钥。这个密钥用来验证从 Docker 源下载的软件包是否官方可信。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 源配置。注意 Ubuntu 24.04 的代号是 noble下面的命令里已经写对了不要照抄网上那些给 20.04 或 22.04 用的旧命令。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/keyrings/docker.list /dev/null第五步再执行一次 apt update 让新源生效然后安装 Docker 本体和相关插件。注意这里装的 docker-compose-plugin 就是 Docker Compose 的官方插件版本比单独装 python-pip 版的 docker-compose 要新得多而且用法是docker compose中间有空格。sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin第六步启动 Docker 服务并设置开机自启。sudo systemctl enable docker --now sudo systemctl status docker看到active (running)的输出就说明 Docker 已经跑起来了。2.3 换国内镜像加速器这一步几乎必做。腾讯云服务器虽然在国内但默认的 Docker Hub 拉取速度时快时慢尤其拉 PostgreSQL 这种体积几百 MB 的镜像时速度不稳定会让人很崩溃。修改 Docker 的守护进程配置文件/etc/docker/daemon.json加入镜像加速地址{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com, https://docker.mirrors.ustc.edu.cn ] }写完后执行sudo systemctl restart docker。这里要提醒你一句镜像加速器属于“公共资源”很多公开的加速地址会因为负载过高或合规调整而失效。如果你的拉取速度还是慢最稳妥的办法是使用腾讯云自家提供的容器镜像服务Tencent Container Registry作为中转但这需要你先把镜像推到自己账号下操作稍复杂这里不展开。提示不要在 daemon.json 里写一堆失效的加速器地址Docker 会挨个尝试反而更慢。保持两到三个就够。3. PostgreSQL 镜像选型与容器创建3.1 版本选择到底该用 16 还是 17截至这篇博文写作时PostgreSQL 官方在 Docker Hub 上主要维护 16 和 17 两个大版本15 及更早的版本也还在但已进入维护后期。我的建议是如果你的项目是全新的直接用 16 或 17 都可以如果你将来要迁移老项目先确认好你本地的 PostgreSQL 主版本尽量保持一致避免pg_dump导出导入时遇到版本兼容问题。PostgreSQL 16 是当前非常成熟的版本15 的pg_stat_io等监控视图得到完善logical replication的逻辑复制性能也有明显提升。17 是最新大版本增加了不少查询优化和内置功能但生态里的第三方工具适配度还在追赶。我的个人建议是生产环境求稳选 16测试环境想尝鲜可以上 17。拉取镜像的命令sudo docker pull postgres:16如果拉取超时检查一下 2.3 小节的镜像加速器是否配置正确或者换一个加速地址再试。3.2 通过 docker run 快速启动一个实例先用最简单的方式把 PostgreSQL 跑起来不加 Compose 编排方便你理解每个参数的含义。sudo docker run -d \ --name postgres-test \ -e POSTGRES_USERmyuser \ -e POSTGRES_PASSWORDmypassword \ -e POSTGRES_DBmydb \ -p 5432:5432 \ -v pgdata:/var/lib/postgresql/data \ postgres:16逐个解释这些参数-d后台运行容器。--name postgres-test给容器起个名字方便后续管理。-e POSTGRES_USERmyuser设置超级用户名。注意这里设置的用户是超级用户不是普通用户。-e POSTGRES_PASSWORDmypassword设置该用户的密码。生产环境千万别用这么弱的密码至少 16 位混合大小写和特殊字符。-e POSTGRES_DBmydb自动创建一个默认数据库。容器启动时会自动执行初始化脚本。-p 5432:5432把容器内的 5432 端口映射到宿主机的 5432 端口这样外部才能访问。-v pgdata:/var/lib/postgresql/data数据持久化。把容器里的数据目录挂载到 Docker 卷pgdata上。执行完后用sudo docker ps查看容器状态。正常情况下STATUS列显示Up就说明容器已经跑起来了。3.3 数据持久化为什么如此重要PostgreSQL 的容器镜像设计得很精简容器里的文件系统是临时的。如果不做数据持久化一旦你执行docker rm删除容器或者容器因故障被重建所有数据库数据都会丢得一干二净。上面的命令里用了 Docker Volume命名卷的方式即pgdata这个卷的数据存放在宿主机/var/lib/docker/volumes/pgdata/_data目录下。哪怕容器删了卷里的数据文件还在下次创建容器时重新挂载同一个卷数据就都回来了。除了命名卷还可以用 bind mount绑定挂载的方式把数据库数据存到你指定的宿主机目录比如-v /opt/postgres/data:/var/lib/postgresql/data这种方式的好处是数据文件路径可控备份时直接打包/opt/postgres/data就行。缺点是如果你手动改过目录权限可能导致 PostgreSQL 启动时无法读写数据文件报Permission denied的错误。对比下来日常开发和测试用命名卷更省心生产环境建议用 bind mount 配合定期备份脚本。注意千万不要把数据库数据直接放在容器可写层也就是不挂载任何卷这是新手最容易犯的致命错误。容器一升级或崩溃数据直接蒸发。4. Docker Compose 编排与配置优化4.1 用 compose.yaml 固化部署配置docker run适合快速测试但如果你要稳定部署建议把配置写成compose.yaml文件。好处很明显配置即代码版本可控复制到另一台机器就能一键拉起整套环境。在服务器上建一个目录比如/opt/postgres在里面创建compose.yamlservices: postgres: image: postgres:16 container_name: postgres restart: always environment: POSTGRES_USER: myuser POSTGRES_PASSWORD: mypassword POSTGRES_DB: mydb TZ: Asia/Shanghai PGTZ: Asia/Shanghai ports: - 5432:5432 volumes: - pgdata:/var/lib/postgresql/data - /etc/localtime:/etc/localtime:ro healthcheck: test: [CMD-SHELL, pg_isready -U myuser -d mydb] interval: 10s timeout: 5s retries: 5 logging: driver: json-file options: max-size: 50m max-file: 3 volumes: pgdata:说几个关键配置的用意restart: always容器异常退出时Docker 会自动拉起进程。对于数据库这种核心服务这个设置非常必要。服务器重启后容器也会跟着自动启动。TZ和PGTZ设置时区。PostgreSQL 官方镜像默认 UTC 时区如果你的业务在国内日志时间和数据库时间会差 8 个小时排查问题时会非常别扭。healthcheck健康检查。Docker 会定期执行pg_isready命令探测数据库是否可用。后续做自动化部署或负载均衡时这个检查结果会被用来判断容器是否健康。logging控制日志大小。PostgreSQL 在业务量上来后日志增长很快不限制的话会蚕食磁盘空间。50MB 一个文件、保留 3 份是实践经验值。4.2 启动与管理命令在/opt/postgres目录下执行sudo docker compose up -d查看状态和日志sudo docker compose ps sudo docker compose logs -f postgres停止和删除sudo docker compose down注意docker compose down不会删除数据卷数据还在。如果连数据一起删加-v参数sudo docker compose down -v。这条命令非常危险执行前务必确认你确实要清空数据。4.3 通过自定义 postgresql.conf 调整参数PostgreSQL 官方镜像在启动时可以使用command参数覆盖默认配置或者把自定义的postgresql.conf挂载进容器。我常用的方式是用command传递关键参数简单直接services: postgres: image: postgres:16 container_name: postgres command: - postgres - -c - shared_buffers256MB - -c - max_connections200 - -c - work_mem16MB如果你的服务器内存是 4GB建议shared_buffers设置为总内存的 25% 左右也就是 1GB。这个参数控制 PostgreSQL 共享内存池的大小设置得太小会导致频繁读写磁盘太大又会导致系统内存不足。8GB 内存的机器可以设置为 2GB。max_connections要根据业务并发量来定默认 100 通常够用但如果你用连接池工具如 PgBouncer可以适当调低。work_mem是每个排序和哈希操作可用的内存设得越大复杂查询越快但并发高时会成倍消耗内存16MB 到 32MB 是保守的起步值。提示不要盲目照搬网上的“优化参数”优化必须基于你的真实负载。先用默认配置跑起来观察一段时间再逐步调整每次只改一个参数效果才可控。5. 客户端连接与日常管理5.1 从宿主机连接容器内的 PostgreSQLPostgreSQL 官方镜像里自带 psql 客户端可以直接在容器内执行 SQL 命令。进入容器sudo docker exec -it postgres psql -U myuser -d mydb你会看到 psql 的交互式提示符在这里面可以直接执行 SQL。比如查看版本SELECT version();列出所有数据库\l退出 psql\q5.2 从腾讯云服务器外部连接数据库要在你的本机比如 Windows 上的 Navicat、DBeaver或者你用代码连访问云上的 PostgreSQL需要打通两个层面的“门”。第一道门腾讯云安全组。登录腾讯云控制台找到你的 CVM 实例进入“安全组”配置添加入站规则协议 TCP、端口 5432、来源 IP 建议设置为你的办公网 IP用curl ifconfig.me查询不要设置为0.0.0.0/0否则全世界都能扫到你的数据库端口。第二道门PostgreSQL 自身的访问控制。默认情况下PostgreSQL 只监听本机回环地址localhost容器里跑的时候又叠加了网络隔离。你需要在容器里确认 PostgreSQL 是否监听在了所有地址上。官方镜像默认配置了listen_addresses *所以外网理论上可以连。但这里有个关键的坑即便监听正常pg_hba.conf 里的认证规则也可能挡住你。检查一下当前的规则sudo docker exec postgres cat /var/lib/postgresql/data/pg_hba.conf如果里面只有local all all trust和host all all 127.0.0.1/32 trust这类规则那外部 IP 连进来会被拒绝。最简单的方式是通过环境变量或 command 参数临时追加规则不过更规范的做法是挂载自定义的 pg_hba.conf 文件。挂载一个自定义 pg_hba.conf 到容器里的方法在宿主机/opt/postgres下创建pg_hba.conf内容参考host all all 0.0.0.0/0 scram-sha-256然后在 compose.yaml 里挂载volumes: - pgdata:/var/lib/postgresql/data - /opt/postgres/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro同时把数据目录里的 pg_hba.conf 替换掉。实际操作中我更建议直接用命令参数指明自定义配置文件路径command: - postgres - -c - hba_file/etc/postgresql/pg_hba.conf然后在 compose.yaml 里把宿主机上的/opt/postgres/pg_hba.conf挂载到容器内的/etc/postgresql/pg_hba.conf。改完之后重启容器sudo docker compose restart postgres5.3 开启 pgAdmin 或使用可视化工具很多人习惯用 pgAdmin 4 管理 PostgreSQL你可以用 Docker 再跑一个 pgAdmin 容器pgadmin: image: dpage/pgadmin4 container_name: pgadmin restart: always environment: PGADMIN_DEFAULT_EMAIL: adminexample.com PGADMIN_DEFAULT_PASSWORD: adminpassword ports: - 8080:80 volumes: - pgadmin-data:/var/lib/pgadmin启动后访问http://你的服务器IP:8080用邮箱和密码登录再添加 PostgreSQL 服务器连接。注意 Host 要填postgres也就是 compose 里的服务名因为 pgAdmin 容器和 PostgreSQL 容器在同一个 Docker 网络里可以通过服务名互访。如果你只是日常执行 SQL 和看数据DBeaver Community 这个免费工具比 pgAdmin 轻量很多推荐下载使用。5.4 数据库备份与恢复备份这件事很多人都是在出了事故才想起。提前养成习惯会省很多麻烦。PostgreSQL 提供了pg_dump逻辑备份工具可以在容器内直接执行sudo docker exec postgres pg_dump -U myuser -d mydb /opt/postgres/backup_$(date %Y%m%d).sql恢复时把备份文件拷贝到容器内再执行 psqlsudo docker cp /opt/postgres/backup_20240101.sql postgres:/tmp/ sudo docker exec postgres psql -U myuser -d mydb -f /tmp/backup_20240101.sql也可以不用docker cp直接用管道符cat /opt/postgres/backup_20240101.sql | sudo docker exec -i postgres psql -U myuser -d mydb注意exec后面要加-i参数才能把标准输入传进去很多人会漏掉。提示可以用 cron 定时执行备份脚本里保留最近 7 天的备份文件避免磁盘被备份文件占满。这里不再展开 cron 的写法实际项目中这是必须补上的一环。6. 常见问题与排查技巧实录6.1 端口被占用5432 无法启动如果你在启动容器时遇到类似提示Error starting userland proxy: listen tcp4 0.0.0.0:5432: bind: address already in use说明宿主机上已经有进程占用了 5432 端口。很可能是你以前用 apt 装过 PostgreSQL残留了系统服务。排查方法sudo ss -tlnp | grep 5432找到占用进程的 PID确认是旧的 PostgreSQL 服务后停掉sudo systemctl stop postgresql sudo systemctl disable postgresql然后重新启动容器。腾讯云上有些镜像市场提供的系统盘可能预装了 PostgreSQL所以这个情况并不少见。6.2 密码认证失败password authentication failed for user这个报错太常见了。原因一般有三种可能一是环境变量没传对。POSTGRES_PASSWORD 设置后仅在数据目录第一次初始化时生效。如果你的数据卷已经存在且里面的数据是以前用别的密码初始化的再改环境变量不会改变已有用户的密码。解决方式有两种直接删卷重新初始化测试环境可以或者进入容器用 psql 改密码sudo docker exec -it postgres psql -U myuser -d mydb -c ALTER USER myuser PASSWORD newpassword;二是 pg_hba.conf 的认证方式太严格比如要求scram-sha-256但你的客户端工具用的是旧的md5。在 pg_hba.conf 里改成md5或scram-sha-256保持一致。三是客户端连接时填的用户名和密码不对。检查连接字符串确认端口、数据库名、用户名、密码都是最新的。6.3 数据目录权限问题Permission denied用了 bind mount 方式挂载数据目录后启动容器时报chmod: changing permissions of /var/lib/postgresql/data: Operation not permitted或者FATAL: data directory /var/lib/postgresql/data has invalid permissions这通常是宿主机上用来挂载的目录权限不对。PostgreSQL 容器内部默认以 postgres 用户运行UID 是 999。你在宿主机上创建的/opt/postgres/data目录归属 root 用户容器里的 postgres 用户无法写入。解决方式把宿主机目录的属主改成 UID 999sudo mkdir -p /opt/postgres/data sudo chown -R 999:999 /opt/postgres/data注意不要用chmod 777那样虽然能用但会造成安全隐患数据库数据目录的权限尽可能收紧。6.4 容器能启动但健康检查一直失败如果 compose.yaml 里配置了 healthcheck但docker compose ps看到的 STATUS 一直是unhealthy排查思路先手动在容器内执行健康检查要执行的命令sudo docker exec postgres pg_isready -U myuser -d mydb如果输出接受连接或英文accepting connections说明数据库本身正常问题出在健康检查的命令参数上。常见的坑是环境变量里的用户名和数据库名不一致比如用户创建了myuser但健康检查里写的是POSTGRES_USER环境变量的默认值用户名对不上就会一直失败。另外注意 healthcheck 的执行频率和超时时间数据库在启动初期会有十几秒的初始化时间interval和retries要留足余量避免刚启动时就被判定为不健康。6.5 容器日志疯狂输出磁盘被写满PostgreSQL 在默认配置下会记录大量日志尤其当业务表频繁更新时log_statement或数据库的 log_min_duration_statement 设置不合理可能导致日志文件急速膨胀。解决方式就是 4.1 小节里提到的 logging 配置Docker 的 json-file 驱动控制日志文件的大小和个数logging: driver: json-file options: max-size: 50m max-file: 3已经写满的日志怎么清把 compose.yaml 改好后执行sudo docker compose up -d --force-recreate重新创建容器后旧的日志文件就会被滚动清理。注意这个操作不会影响数据卷里的数据库文件可以放心执行。7. 我在实际部署中积累的经验腾讯云 Ubuntu 24.04 上用 Docker 跑 PostgreSQL整套流程走下来最深的体会是不要把容器当虚拟机用也不要把容器当临时玩具用。它介于两者之间——初始化、升级、回滚、迁移都变得很方便但数据安全和配置管理需要你多花心思。我建议你在上生产环境之前至少要做到三件事一是用 Docker Compose 固化部署配置不要再用零散的 docker run 命令碰运气二是开启健康检查并设置合适的日志轮转让运维状态可观测三是把pg_dump备份脚本和恢复文档提前写好真出事的时候能按步骤执行而不是临时翻命令。还有一个很少人提的小技巧部署完成后用docker compose config命令校验一下 compose.yaml 的语法和最终生效配置。这个命令不会创建任何资源但能看到你写的配置被 Docker 解析后的完整形态很多隐藏的默认值和无意的缩进错误都能在这一步暴露出来。这套环境跑稳定之后后续再想扩展可以考虑在同一台服务器上用 Docker 部署 Redis、Nginx 之类的服务统一纳入 Compose 管理。容器化这条路走通了一次后面的路就顺了。