腾讯云Ubuntu 24.04上用Docker部署PostgreSQL实战指南
发布时间:2026/9/16 9:40:28 锦皓数字建站

腾讯云上买了一台 Ubuntu 24.04 的服务器头一件事就是把数据库环境给安排了。这次我毫不犹豫选了 Docker 方案部署 PostgreSQL整个流程跑下来比直接在系统里手动 apt 安装省心太多而且后续维护、升级、迁移都方便。这篇文章就把我这次在腾讯云 Ubuntu 24.04 上通过 Docker 部署 PostgreSQL 的完整过程写出来包括从服务器初始化、Docker 安装、容器启动、数据持久化、日常备份到问题排查每一步都附上我当时实际操作时的思考希望能给正要干同样事情的朋友一些参考。1. 为什么我坚持用 Docker 跑 PostgreSQL1.1 不直接 apt 安装的理由很多人刚接触云服务器习惯性apt install postgresql装完再用pg_ctl或者systemctl去管服务。这条路不是不行而且 Ubuntu 官方仓库里的 PostgreSQL 版本也挺稳但它有几个问题在云服务器场景下特别明显。第一个是升级麻烦。系统自带的 PostgreSQL 版本基本锁死在系统发版时的版本比如 Ubuntu 24.04 默认仓库的 PostgreSQL 是 16.x 系列以后想要升到 17 或者 18就得自己加 PostgreSQL 官方 APT 源然后处理数据目录格式升级一不小心就把服务搞挂。而 Docker 镜像的版本切换就是docker pull postgres:17加一次容器重建数据目录升级步骤反而更可控。第二个是迁移不灵活。系统里直接装的 PostgreSQL配置分散在/etc/postgresql/、/var/lib/postgresql/、系统日志目录等好几个地方想原样搬到另一台机器要把依赖、配置、数据目录一起拷过去。Docker 方案更干净一个docker-compose.yml文件加一个数据卷目录到新机器上一条命令就恢复服务这个优势在做灾备或者换服务器的时候特别值钱。第三个是隔离性。云服务器上往往不只跑一个服务如果数据库版本或者其他依赖跟业务环境冲突直接装在系统里就会互相踩。Docker 把 PostgreSQL 独立在一个容器里宿主机怎么折腾都不影响数据库运行。当然Docker 方案也不是没有代价。多了一层容器排查网络问题时多一个环节数据库性能虽然损耗极小但如果你对性能有极致追求、或者要做很复杂的系统级调优直接用物理机部署会更直接。对我来说云服务器场景下 Docker 的收益远远大于代价。1.2 版本选择和镜像挑选PostgreSQL 版本方面我这次选了postgres:16而不是最新版 17。主要是 16 已经经过了足够长时间的线上检验生态里的扩展、监控工具、备份工具都跟得很稳。17 发布之后新增了不少好东西比如增量备份、更细粒度的 WAL 管理但它的生态成熟度还不算最佳我个人的习惯是让新版本先飞一会儿确认没问题再迁移。如果你有明确需求必须用 17官方镜像同样支持命令换一下就行。镜像本身我建议用官方镜像postgres:16不要用postgres:16-alpine这类精简版。Alpine 镜像体积确实小很多但里面用的 musl libc跟系统自带的 glibc 在行为上有微妙差异而且编译 PostgreSQL 扩展时经常缺依赖遇到问题解决起来比多占那几百 MB 磁盘要痛苦得多。官方标准镜像是基于 Debian 的生态兼容性最好出问题网上资料也齐全。这里顺便给个腾讯云服务器配置参考。如果是个人项目或者中小业务2核4G的轻量应用服务器足够跑 PostgreSQL 加两三个业务容器如果数据量大、并发高建议 4核8G 以上并且一定要单独挂一块云硬盘存数据库文件别把数据库放到系统盘里系统盘出问题会连带数据一起遭殃。2. 腾讯云环境基础准备2.1 系统初始化和用户配置新买的腾讯云服务器拿到手第一件事不是装 Docker而是把系统基础环境收拾干净。我是通过 SSH 登录服务器操作的先更新系统软件包sudo apt update sudo apt upgrade -yUbuntu 24.04 是 LTS 版本腾讯云镜像源一般已经配置好了更新速度还行。接下来我习惯创建一个普通用户用于日常操作不建议一直用 root 直接干活误操作风险太大。腾讯云 Ubuntu 镜像默认是没有 ubuntu 用户的需要自己动手建sudo adduser ubuntu sudo usermod -aG sudo ubuntu然后把本机的 SSH 公钥加到~/.ssh/authorized_keys里之后就能用ssh ubuntu服务器IP免密登录。这里有一个细节创建用户后要测试能正常sudo再退出当前 root 会话我之前有次忘了测结果退出后发现新用户 sudo 权限配错了只能回腾讯云控制台用 VNC 登录救场来回折腾大半天。顺便把时区设置了业务系统里的时间错乱比你想的更常见sudo timedatectl set-timezone Asia/Shanghai timedatectl2.2 安装 Docker 引擎Ubuntu 24.04 上装 Docker 有两条路一条是直接apt install docker.io简单但装出来的是 Ubuntu 仓库里打好的 Docker 版本更新频率低另一条是添加 Docker 官方 APT 源装docker-ce版本新功能全。服务器生产环境我推荐后者。由于国内网络环境访问 Docker 官方 APT 源速度不稳定我建议先把源替换成国内镜像站这里以清华源为例sudo apt install -y ca-certificates curl gnupg 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 echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \ $(. /etc/os-release echo $VERSION_CODENAME) stable | \ sudo tee /etc/apt/sources.list.d/docker.list /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin装完以后把 Docker 服务设为开机自启并立即启动sudo systemctl enable docker sudo systemctl start docker docker version能看到 Client 和 Server 两段信息Server 段正常显示版本号就说明装好了。顺便把手头这个用户加进 docker 组以后就不用每条命令前都加 sudo 了sudo usermod -aG docker ubuntu newgrp docker这里有个容易忽略的坑如果你是在 Ubuntu 桌面版或者某些云镜像上跑 Docker可能会遇到cannot connect to the Docker daemon的错误大概率是当前用户不在 docker 组或者 Docker daemon 没起来用systemctl status docker查一下就行。2.3 拉取官方 PostgreSQL 镜像环境就绪后直接拉取 PostgreSQL 官方镜像docker pull postgres:16国内访问 Docker Hub 的速度不稳定如果拉取超时或者非常慢可以给 Docker 配置镜像加速。腾讯云用户可以在控制台的容器镜像服务页面找到专属加速地址本地配置就是在/etc/docker/daemon.json里加一行registry-mirrors{ registry-mirrors: [https://mirror.ccs.tencentyun.com] }配置完记得重启 Dockersudo systemctl restart docker这个加速只对腾讯云内网访问有效速度快很多。镜像拉完以后先别急着docker run我建议先在本地做一次简单的启动测试确认镜像本身没问题docker run --rm -e POSTGRES_PASSWORDtest123 postgres:16看到数据库初始化日志、最后出现database system is ready to accept connections就说明镜像没问题CtrlC停掉这个测试容器会自动删除不会留垃圾。3. Docker 部署 PostgreSQL 核心实操3.1 docker run 快速启动一个实例先来一个最直接的docker run命令把实例跑起来看看效果docker run -d \ --name postgres \ -e POSTGRES_USERmyuser \ -e POSTGRES_PASSWORDMypassword123! \ -e POSTGRES_DBmydb \ -p 5432:5432 \ -v /data/postgres:/var/lib/postgresql/data \ --restartalways \ postgres:16这行命令每个参数都是什么意思我拆开说-d后台运行容器。--name postgres容器名字后续操作都靠这个名字指代。-e POSTGRES_USERmyuser初始化的超级用户名默认是postgres。我习惯显式改成业务账号名字方便后面权限管理。-e POSTGRES_PASSWORDMypassword123!这个账号的密码。官方镜像要求必须设置否则会拒绝启动并给出告警日志。-e POSTGRES_DBmydb初始化的数据库名不设的话默认跟 POSTGRES_USER 同名。-p 5432:5432端口映射宿主机 5432 端口转发到容器 5432 端口。左边是宿主机端口右边是容器内端口左右可以不一样比如-p 5433:5432但实际用的时候为了省心两边保持一致。-v /data/postgres:/var/lib/postgresql/data数据卷挂载宿主机目录对应容器内 PostgreSQL 数据目录这是数据持久化的关键没有这一行容器一删数据就全没了。--restartalways容器退出后自动重启服务器重启后也会自动拉起数据库。启动后验证一下状态docker ps docker logs postgresdocker logs postgres能看到 PostgreSQL 初始化全过程包括生成超级用户、创建数据库、启动 WAL 进程等最后出现database system is ready to accept connections就是启动成功。3.2 数据持久化的原理和正确姿势刚才的-v /data/postgres:/var/lib/postgresql/data是关键中的关键这里多说几句。PostgreSQL 官方镜像的数据目录是/var/lib/postgresql/data所有数据文件、WAL 日志、配置文件都在这个目录下。把宿主机/data/postgres挂载过去后容器内的写入实际落盘在宿主机这样哪怕容器被删除、重建只要挂载同一个目录数据都在。但有三个坑我实测踩过必须提醒你。第一个坑是挂载目录的权限。官方镜像容器内跑进程的用户postgres的 uid 是 999宿主机上如果挂载目录的属主不对容器启动时可能因为无法写入而报错。解决方式简单粗暴sudo mkdir -p /data/postgres sudo chown -R 999:999 /data/postgres第二个坑是环境变量只在首次初始化时生效。也就是说容器首次创建时如果/var/lib/postgresql/data目录是空的它才会执行 initdb 并根据POSTGRES_USER、POSTGRES_PASSWORD创建账号一旦初始化完成你再改环境变量重启容器账号密码都不会变。网上很多改了密码不起作用的求助帖基本都是这个原因。要改密码正确姿势是进入容器内用 SQL 改或者删除数据目录重新初始化前提是不要旧数据。第三个坑是PGDATA。官方镜像的数据目录是可以通过PGDATA环境变量改位置的但默认已经指向/var/lib/postgresql/data了你在宿主机挂载时挂载点和PGDATA路径必须匹配否则会出现挂载了目录但数据实际写到别处的情况。比如你设置了PGDATA/var/lib/postgresql/data/pgdata那得把宿主机目录挂载到/var/lib/postgresql/data/pgdata才正确。3.3 生产环境推荐用 docker compose 编排单容器场景用docker run够用但为了以后配置可复用、改动可追踪我推荐一步到位使用docker-compose.yml。下面是我实际在用的精简版services: postgres: image: postgres:16 container_name: postgres restart: always environment: POSTGRES_USER: myuser POSTGRES_PASSWORD: Mypassword123! POSTGRES_DB: mydb TZ: Asia/Shanghai PGTZ: Asia/Shanghai LANG: C.UTF-8 ports: - 5432:5432 volumes: - /data/postgres:/var/lib/postgresql/data - /data/backups:/backups healthcheck: test: [CMD-SHELL, pg_isready -U myuser -d mydb] interval: 10s timeout: 5s retries: 5 shm_size: 256m这个配置里要注意几个点TZ和PGTZ环境变量控制容器时区不设置的话容器默认 UTC日志时间跟北京时间差 8 小时排查问题时候看着特别别扭。LANG设置字符集环境避免出现中文乱码。healthcheck定义健康检查用pg_isready探测数据库是否就绪docker ps里的 STATUS 列会显示(healthy)这在后面接容器编排、自动重启、负载均衡时很有用。shm_size设置共享内存大小默认 64MB 在某些并发场景可能不够如果看到could not resize shared memory这类报错调大这个值就行。启动编排文件docker compose up -d docker compose ps docker compose logs -f postgres以后要升级 PostgreSQL 小版本两步搞定docker compose pull docker compose up -d这种配置即代码的方式对团队协作和故障恢复都有很大价值。3.4 服务器安全组和防火墙的配置数据库跑起来之前先把防火墙和安全组理清楚不然连接不上、或者被公网随意扫描都是麻烦事。腾讯云有两层网络控制一层是云平台层面的安全组CVM或者防火墙轻量服务器另一层是操作系统自身的防火墙UFW。我个人原则是数据库端口 5432 绝对不对全网开放。如果业务服务器和数据库在同一台机器那 5432 只需要监听本地连安全组都不用放行直接用localhost或内网 IP 连接即可。但在云服务器场景下往往需要从本地电脑用其他工具去连库这时候可以放行但一定要限制来源 IP。在腾讯云控制台的操作是CVM 云服务器进入「安全组」配置添加入站规则协议端口填 TCP:5432来源填你自己的公网 IP/32。轻量应用服务器进入「防火墙」页面添加规则 TCP:5432限制来源 IP 或 IP 段。系统层面Ubuntu 24.04 默认没有启用 UFW我一般启用它并放行必要端口sudo ufw allow 22/tcp sudo ufw allow from 你的IP to any port 5432 proto tcp sudo ufw enable sudo ufw status注意顺序很重要先放行 SSH 22 端口再启用 UFW。我之前真的见过有人没放行 22 就开防火墙然后把自己锁在外面只好去控制台用 VNC 登录救场非常尴尬。4. 备份、恢复与日常维护4.1 用 pg_dump 做逻辑备份PostgreSQL 容器跑起来之后第一件该做的事不是往里灌数据而是把备份方案想好。最常用的备份方式是逻辑备份用pg_dump把数据库导出成 SQL 文件或者自定义格式文件。在 Docker 环境下命令是在容器外部执行通过docker exec进到容器里跑mkdir -p /data/backups docker exec postgres pg_dump -U myuser -d mydb -F c -f /backups/mydb_$(date %Y%m%d).dump这里-F c表示输出为自定义压缩格式比纯 SQL 文件体积小并且支持pg_restore灵活恢复。/backups是容器内路径通过刚才 compose 文件里的/data/backups:/backups挂载映射到了宿主机。如果需要备份所有数据库用pg_dumpalldocker exec postgres pg_dumpall -U myuser -f /backups/all_$(date %Y%m%d).dumppg_dumpall会把所有数据库、角色、表空间的定义都导出来适合整机级别的备份。备份还有一个细节建议用自定义格式而不是纯 SQL原因是恢复时pg_restore可以指定只恢复某些表选择更灵活纯 SQL 要么整库跑、要么手拆文件效率差很多。4.2 用 pg_restore 恢复数据恢复数据也走docker exec。先确保目标数据库存在然后把 dump 文件恢复到库里docker exec -i postgres pg_restore -U myuser -d mydb --clean --if-exists /backups/mydb_20250101.dump--clean会在恢复前删除已存在的同名对象--if-exists避免删除不存在的对象时报错中断。如果恢复过程中报权限或者对象冲突加个--no-owner忽略 owner 信息docker exec -i postgres pg_restore -U myuser -d mydb --clean --if-exists --no-owner /backups/mydb_20250101.dump如果是纯 SQL 文件用 psql 恢复docker exec -i postgres psql -U myuser -d mydb mydb.sql恢复前一定确认目标库是空的或者你准备好了覆盖否则数据会叠加出现主键冲突。4.3 写一个自动备份脚本并交给 crontab手动备份容易忘生产环境必须自动化。我写了个简单可靠的备份脚本/usr/local/bin/backup_postgres.sh#!/bin/bash BACKUP_DIR/data/backups DATE$(date %Y%m%d_%H%M%S) RETENTION_DAYS7 docker exec postgres pg_dump -U myuser -d mydb -F c -f /backups/mydb_${DATE}.dump if [ $? -eq 0 ]; then echo [$(date %Y-%m-%d %H:%M:%S)] Backup succeeded: mydb_${DATE}.dump /var/log/backup_postgres.log else echo [$(date %Y-%m-%d %H:%M:%S)] Backup FAILED /var/log/backup_postgres.log fi find ${BACKUP_DIR} -name mydb_*.dump -mtime ${RETENTION_DAYS} -delete给脚本加执行权限然后扔进 crontabsudo chmod x /usr/local/bin/backup_postgres.sh crontab -e在 crontab 里加一行每天凌晨 2 点执行0 2 * * * /usr/local/bin/backup_postgres.sh /dev/null 21这个脚本做了三件事执行 pg_dump 备份、记录日志、清理 7 天前的旧备份。备份文件别一直堆着既占磁盘空间又难管理保留最近一周就够更早的可以转移到对象存储归档。这里我说句实打实的话备份脚本写了、crontab 配了不等于备份一定有效。我见过太多定时任务跑了几个月结果从没真正恢复过的例子。建议每个月手动做一次恢复演练把备份文件恢复到临时库验证数据完整性这一步关键时刻能救命。5. 常见问题排查与实战经验5.1 连接不上数据库的排查思路这个是最常见的问题没有之一。现象是本地用 psql 或者客户端工具连不上腾讯云服务器的 5432 端口。排查顺序很重要我按经验排列第一先确认容器是否在运行docker ps docker logs --tail 50 postgres如果容器没起来看日志是启动报错还是直接退出常见的有密码未设置、数据目录权限错误、磁盘空间不足等。第二测试宿主机本地是否能连docker exec -it postgres psql -U myuser -d mydb容器内能连说明数据库本身没问题问题在网络层容器内都连不上问题在数据库配置或者容器本身。第三检查监听地址和端口。官方镜像默认listen_addresses*如果之前手动改过postgresql.conf确认没有限制成localhostdocker exec postgres psql -U myuser -d mydb -c SHOW listen_addresses; docker exec postgres psql -U myuser -d mydb -c SHOW port;第四检查云平台安全组和系统防火墙是否放行了来源 IP 的 5432 访问步骤见上文 3.4。第五检查pg_hba.conf认证配置。镜像默认允许所有来源、密码认证如果你动过这个文件注意确认没有把远端连接全部拒绝。查看生效配置docker exec postgres psql -U myuser -d mydb -c SELECT * FROM pg_hba_file_rules;这套排查流程走下来90% 的连接问题都能定位。这里还有个本地客户端容易忽略的细节psql连接时如果没指定主机名它不会默认走到 TCP/IP而是走 Unix socket。我在本地一直psql -U myuser -d mydb连不上想了半天才想起来少了-h 服务器IP加上之后就通了。这个坑虽然基础但真的很常见。5.2 容器启动失败和数据目录权限问题刚刚说过官方镜像容器内用户postgres的 uid 是 999。宿主机挂载目录如果权限不对容器初始化数据目录时会报类似这样的错误chmod: changing permissions of /var/lib/postgresql/data: Operation not permitted或者initdb: error: could not change permissions of directory /var/lib/postgresql/data: Operation not permitted解决方式很明确sudo chown -R 999:999 /data/postgres如果是 Ubuntu 桌面版并启用了 AppArmor偶尔还会遇到容器被限制的诡异情况表现为容器启动后马上退出dmesg里能看到 AppArmor 相关日志。这种在云服务器上很少见真遇到可以临时用--security-opt apparmorunconfined测试但生产环境不建议这么干优先从权限和挂载找原因。5.3 容器重启后发现数据没了这个问题的根源基本就是数据卷没挂好。一种情况是docker run时忘了加-v参数数据全写到容器可写层里容器一删数据跟着没了。这是 Docker 数据持久化的核心知识点官方镜像README里反复强调但还是有很多人踩。另一种情况是挂载了但挂载的宿主机目录是空的容器重新初始化了一个空库。比如你原来挂的是/data/postgres后来改配置挂到/data/pgdata数据其实还在老目录里只是新容器没读到。判断当前容器挂载情况docker inspect postgres | grep -A 5 MountsMounts 列表会明确显示源路径和目标路径一眼就能看出来数据目录挂载对了没有。假如数据已经跟着旧容器走了而旧容器还没删还有机会把数据拷出来# 先把旧容器停下来别继续写入 docker stop old-postgres # 把数据目录从容器里拷到宿主机 docker cp old-postgres:/var/lib/postgresql/data /data/postgres如果旧容器已经被删了但只要镜像还在理论上还能从镜像层里恢复一部分数据不过难度大很多。所以再次强调数据卷挂载一定要在创建容器时就配好这是最基本的底线。5.4 性能调优的几个常用参数PostgreSQL 容器跑稳后可以做一些基础性能调优。别一上来就乱调先看几个关键参数SHOW shared_buffers; SHOW effective_cache_size; SHOW work_mem; SHOW max_connections;shared_buffers是共享缓冲区官方推荐设为物理内存的 25% 左右。比如 4G 内存的机器设 1GB 比较合理。这个参数在容器内只认容器的内存限制如果宿主机内存充足按实际需求调就行。effective_cache_size是给查询规划器参考的操作系统文件缓存 shared_buffers的总量估计可以设成物理内存的 75%。它不真正占用内存只是优化器决策用设置太小时会让优化器偏向走索引而非顺序扫描变大点没坏处。work_mem是单个排序操作可以用的内存建议控制在 4MB-64MB 之间并发高时设太大会导致内存耗尽设太小排序会走磁盘临时文件性能暴跌。这个参数没有万能值要根据业务查询特点针对性调。修改参数推荐用 SQL 方式而不是去容器里改配置文件ALTER SYSTEM SET shared_buffers 1GB; ALTER SYSTEM SET effective_cache_size 3GB;改完执行SELECT pg_reload_conf();即可部分参数需要重启容器才会生效比如shared_buffers。注意ALTER SYSTEM改的是postgresql.auto.conf优先级高于postgresql.conf所以不用再手动改容器里的配置。5.5 几个值得养成的实践习惯第一个习惯是给容器命名固定化。不要用 Docker 随机生成的容器名比如stoic_goldberg这种根本记不住。用--name postgres或者 compose 里的container_name后续所有命令、脚本、监控都统一用这个名字省心。第二个习惯是留意时区和字符集。数据库容器里设置TZAsia/Shanghai数据库连接时也尽量让客户端带着时区信息。PostgreSQL 的timestamptz类型会保存 UTC 时间并按会话时区显示如果容器时区不对日志和业务数据会出现 8 小时偏移查问题的时候特别容易误判。第三个习惯是别把数据库端口对公网放开。如果只是本地调试甚至可以用 SSH 隧道访问远程数据库端口根本不暴露到公网。这样就算数据库账号密码泄露外部也没法直连多一层安全保险。举个例子本地想用 pgAdmin 连腾讯云上的数据库可以这样ssh -L 127.0.0.1:5433:127.0.0.1:5432 ubuntu服务器IP -N然后本地连接127.0.0.1:5433实际请求通过 SSH 隧道转发到服务器的 5432 端口公网完全碰不到数据库端口。这次部署过程中我印象最深的还是那个数据目录权限问题。当时挂载目录建好、容器启动正常我还挺得意结果重启服务器后容器一直起不来日志一片红最后定位到就是/data/postgres目录的属主不对。chown 999:999那行命令下去服务立刻恢复正常。从那以后我只要写挂载相关的配置第一步就先把宿主机目录权限理顺省得后面折腾。另一个想特别提的是备份脚本。我有一套 PostgreSQL 服务跑了两三年中间经历过一次磁盘故障靠着自动备份才把数据完整恢复回来。那次经历后我给自己定了个规矩不仅要有备份还要定期做恢复演练。数据这东西不验证恢复流程就等于没有备份。希望这篇实操记录能帮你少走一些弯路。如果你打算在腾讯云、Ubuntu 24.04 上用 Docker 部署 PostgreSQL照着上面的步骤来应该能顺利跑起来。过程中遇到其他问题也建议先看docker logs它会把大部分线索都明明白白摆在你面前。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。