PostgreSQL 工程化部署深度解析
📖 本文档定位 :本文是
PostgreSQL数据库部署安装流程.md的配套解析文档。部署流程文档回答「怎么做」,本文档回答「为什么这么做、每条命令的原理是什么、不这么做会怎样」。每一条命令、每一个配置字段都会被逐行拆解,力求不遗漏、不遗留。
目录
- 一、整体架构与部署全景
- 二、第一章「配置需求」解析
- 三、第三章「验证环节」逐条解析
- 四、第四章「具体流程」逐条深度解析
- 五、第五章「常用管理命令」逐条解析
- 六、第六章「验证连接」逐条解析
- 七、第七章「备份与恢复」逐条解析
- 八、第八章「监控与告警」逐条解析
- 九、第九章「故障排查」逐条解析
- 十、第十章「升级与回滚」逐条解析
- 十一、附录解析
- 十二、全流程串联总览
一、整体架构与部署全景
1.1 部署架构图
#mermaid-svg-TgZWnjj2Z55p014y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TgZWnjj2Z55p014y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TgZWnjj2Z55p014y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TgZWnjj2Z55p014y .error-icon{fill:#552222;}#mermaid-svg-TgZWnjj2Z55p014y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TgZWnjj2Z55p014y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TgZWnjj2Z55p014y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TgZWnjj2Z55p014y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TgZWnjj2Z55p014y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TgZWnjj2Z55p014y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TgZWnjj2Z55p014y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TgZWnjj2Z55p014y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TgZWnjj2Z55p014y .marker.cross{stroke:#333333;}#mermaid-svg-TgZWnjj2Z55p014y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TgZWnjj2Z55p014y p{margin:0;}#mermaid-svg-TgZWnjj2Z55p014y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TgZWnjj2Z55p014y .cluster-label text{fill:#333;}#mermaid-svg-TgZWnjj2Z55p014y .cluster-label span{color:#333;}#mermaid-svg-TgZWnjj2Z55p014y .cluster-label span p{background-color:transparent;}#mermaid-svg-TgZWnjj2Z55p014y .label text,#mermaid-svg-TgZWnjj2Z55p014y span{fill:#333;color:#333;}#mermaid-svg-TgZWnjj2Z55p014y .node rect,#mermaid-svg-TgZWnjj2Z55p014y .node circle,#mermaid-svg-TgZWnjj2Z55p014y .node ellipse,#mermaid-svg-TgZWnjj2Z55p014y .node polygon,#mermaid-svg-TgZWnjj2Z55p014y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TgZWnjj2Z55p014y .rough-node .label text,#mermaid-svg-TgZWnjj2Z55p014y .node .label text,#mermaid-svg-TgZWnjj2Z55p014y .image-shape .label,#mermaid-svg-TgZWnjj2Z55p014y .icon-shape .label{text-anchor:middle;}#mermaid-svg-TgZWnjj2Z55p014y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TgZWnjj2Z55p014y .rough-node .label,#mermaid-svg-TgZWnjj2Z55p014y .node .label,#mermaid-svg-TgZWnjj2Z55p014y .image-shape .label,#mermaid-svg-TgZWnjj2Z55p014y .icon-shape .label{text-align:center;}#mermaid-svg-TgZWnjj2Z55p014y .node.clickable{cursor:pointer;}#mermaid-svg-TgZWnjj2Z55p014y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TgZWnjj2Z55p014y .arrowheadPath{fill:#333333;}#mermaid-svg-TgZWnjj2Z55p014y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TgZWnjj2Z55p014y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TgZWnjj2Z55p014y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TgZWnjj2Z55p014y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TgZWnjj2Z55p014y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TgZWnjj2Z55p014y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TgZWnjj2Z55p014y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TgZWnjj2Z55p014y .cluster text{fill:#333;}#mermaid-svg-TgZWnjj2Z55p014y .cluster span{color:#333;}#mermaid-svg-TgZWnjj2Z55p014y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TgZWnjj2Z55p014y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TgZWnjj2Z55p014y rect.text{fill:none;stroke-width:0;}#mermaid-svg-TgZWnjj2Z55p014y .icon-shape,#mermaid-svg-TgZWnjj2Z55p014y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TgZWnjj2Z55p014y .icon-shape p,#mermaid-svg-TgZWnjj2Z55p014y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TgZWnjj2Z55p014y .icon-shape .label rect,#mermaid-svg-TgZWnjj2Z55p014y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TgZWnjj2Z55p014y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TgZWnjj2Z55p014y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TgZWnjj2Z55p014y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 远程 Windows 客户端
Ubuntu 22.04 宿主机
Docker Engine
/opt/postgresql/data
TCP 5432
md5认证
数据读写
docker compose up -d 读取
volumes 挂载
/opt/postgresql/compose
docker-compose.yml
部署配置文件
PG数据文件
postgresql.conf
pg_hba.conf
PG_VERSION
postgres_db 容器
test01.py
psycopg2
1.2 部署流程总览
#mermaid-svg-3R4JrFX45AslrMKu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3R4JrFX45AslrMKu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3R4JrFX45AslrMKu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3R4JrFX45AslrMKu .error-icon{fill:#552222;}#mermaid-svg-3R4JrFX45AslrMKu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3R4JrFX45AslrMKu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3R4JrFX45AslrMKu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3R4JrFX45AslrMKu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3R4JrFX45AslrMKu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3R4JrFX45AslrMKu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3R4JrFX45AslrMKu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3R4JrFX45AslrMKu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3R4JrFX45AslrMKu .marker.cross{stroke:#333333;}#mermaid-svg-3R4JrFX45AslrMKu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3R4JrFX45AslrMKu p{margin:0;}#mermaid-svg-3R4JrFX45AslrMKu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3R4JrFX45AslrMKu .cluster-label text{fill:#333;}#mermaid-svg-3R4JrFX45AslrMKu .cluster-label span{color:#333;}#mermaid-svg-3R4JrFX45AslrMKu .cluster-label span p{background-color:transparent;}#mermaid-svg-3R4JrFX45AslrMKu .label text,#mermaid-svg-3R4JrFX45AslrMKu span{fill:#333;color:#333;}#mermaid-svg-3R4JrFX45AslrMKu .node rect,#mermaid-svg-3R4JrFX45AslrMKu .node circle,#mermaid-svg-3R4JrFX45AslrMKu .node ellipse,#mermaid-svg-3R4JrFX45AslrMKu .node polygon,#mermaid-svg-3R4JrFX45AslrMKu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3R4JrFX45AslrMKu .rough-node .label text,#mermaid-svg-3R4JrFX45AslrMKu .node .label text,#mermaid-svg-3R4JrFX45AslrMKu .image-shape .label,#mermaid-svg-3R4JrFX45AslrMKu .icon-shape .label{text-anchor:middle;}#mermaid-svg-3R4JrFX45AslrMKu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3R4JrFX45AslrMKu .rough-node .label,#mermaid-svg-3R4JrFX45AslrMKu .node .label,#mermaid-svg-3R4JrFX45AslrMKu .image-shape .label,#mermaid-svg-3R4JrFX45AslrMKu .icon-shape .label{text-align:center;}#mermaid-svg-3R4JrFX45AslrMKu .node.clickable{cursor:pointer;}#mermaid-svg-3R4JrFX45AslrMKu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3R4JrFX45AslrMKu .arrowheadPath{fill:#333333;}#mermaid-svg-3R4JrFX45AslrMKu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3R4JrFX45AslrMKu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3R4JrFX45AslrMKu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3R4JrFX45AslrMKu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3R4JrFX45AslrMKu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3R4JrFX45AslrMKu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3R4JrFX45AslrMKu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3R4JrFX45AslrMKu .cluster text{fill:#333;}#mermaid-svg-3R4JrFX45AslrMKu .cluster span{color:#333;}#mermaid-svg-3R4JrFX45AslrMKu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3R4JrFX45AslrMKu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3R4JrFX45AslrMKu rect.text{fill:none;stroke-width:0;}#mermaid-svg-3R4JrFX45AslrMKu .icon-shape,#mermaid-svg-3R4JrFX45AslrMKu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3R4JrFX45AslrMKu .icon-shape p,#mermaid-svg-3R4JrFX45AslrMKu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3R4JrFX45AslrMKu .icon-shape .label rect,#mermaid-svg-3R4JrFX45AslrMKu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3R4JrFX45AslrMKu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3R4JrFX45AslrMKu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3R4JrFX45AslrMKu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1.配置需求
2.流程简述
3.验证环节
Docker/镜像源
4.具体流程
compose部署
5.常用管理命令
6.验证连接
本地+Python
7.备份与恢复
8.监控与告警
9.故障排查
10.升级与回滚
1.3 核心设计理念
| 设计理念 | 在本部署中的体现 |
|---|---|
| 声明式配置 | 用 docker-compose.yml 描述「要什么」,而不是用 docker run 描述「怎么做」 |
| 数据与计算分离 | 数据落宿主机 /opt/postgresql/data,容器可随时销毁重建,数据不丢 |
| 最小权限 | 容器内以非 root 用户(UID 999)运行 PostgreSQL |
| 可观测性 | 配置 healthcheck + logging,容器假活可被发现,日志不会撑满磁盘 |
| 可恢复性 | 提供 pg_dumpall 备份 + 定时任务 + 恢复命令的完整闭环 |
二、第一章「配置需求」解析
2.1 系统要求
- 一台装了 Docker 的服务器
- 该服务器能与本地 win 电脑 ping 通
- 操作系统:Ubuntu 22.04(推荐)
逐条解析:
| 要求 | 原理 |
|---|---|
| 装了 Docker | 整个部署基于容器化,Docker Engine 是前提。Docker Compose 是 Docker 的子命令(docker compose),V2 已内置,无需单独安装 |
| 能 ping 通 | 后续第六章要用 Python 从 Windows 远程连接 PostgreSQL。如果网络不通,所有远程验证都会失败。这一条把网络问题前置排除 |
| Ubuntu 22.04 | LTS 长期支持版,内核 5.15+,对 Docker 和 cgroup v2 支持完善。非强制,CentOS/Rocky 也可,但命令细节(包管理器、防火墙命令)会不同 |
2.2 资源需求
- PostgreSQL 15.4 镜像(Docker官方仓库)
- 国内的能用来拉取镜像的docker镜像源
为什么需要国内镜像源:
Docker Hub 官方仓库服务器在海外,国内直连拉取速度极慢甚至超时。需要配置 daemon.json 中的 registry-mirrors 指向国内加速节点(如 DaoCloud、1ms.run 等),第三章会详细讲配置方法。
为什么选 PostgreSQL 15.4:
- PostgreSQL 15 是稳定的大版本(2022 年 10 月发布)
.4是该大版本的小版本补丁,修复了已知安全与稳定性问题- 指定精确版本号(而非
latest)是工程化最佳实践:避免未来latest指向新大版本导致不兼容
2.3 预备配置参数
| 参数 | 值 | 解析 |
|---|---|---|
| 端口 | 5432 | PostgreSQL 官方默认端口,IANA 注册的标准端口 |
| 用户名 | postgres | PostgreSQL 镜像约定的超级用户名,镜像初始化时自动创建 |
| 密码 | postgres | 教程示例值。生产环境必须替换为强密码 |
| 默认数据库 | postgres | 镜像初始化时自动创建的默认库,POSTGRES_DB 环境变量控制 |
⚠️ 安全提醒 :
postgres/postgres是公开的示例凭证。生产环境中,任何人知道你的 IP 就能用这个密码登录你的数据库。务必修改。
三、第三章「验证环节」逐条解析
本章的作用是前置排障:在正式部署前,先确认 Docker 环境和镜像拉取能力正常,避免部署到一半才发现环境问题。
3.1 验证 Docker 环境
命令 1:检查 Docker 是否安装
bash
sudo docker --version
| 片段 | 作用 |
|---|---|
sudo |
以 root 权限执行。Docker 守护进程监听 /var/run/docker.sock,默认只有 root 和 docker 组用户可访问 |
docker |
Docker CLI 客户端 |
--version |
打印 Docker 版本号并退出(不连接守护进程) |
预期输出: Docker version 24.x.x, build xxxxxxx
如果失败: 说明 Docker 未安装,需先执行 curl -fsSL https://get.docker.com | sh 安装。
命令 2:检查 Docker 服务是否运行
bash
sudo systemctl status docker
| 片段 | 作用 |
|---|---|
systemctl |
Systemd 服务管理工具(Ubuntu 22.04 默认使用 systemd) |
status |
查看服务运行状态 |
docker |
服务名 |
预期输出: 绿色 active (running)
如果显示 inactive: 执行 sudo systemctl start docker 启动。
3.2 验证镜像源是否能进行拉取镜像
bash
sudo docker pull hello-world
| 片段 | 作用 |
|---|---|
docker pull |
从 registry 拉取镜像 |
hello-world |
Docker 官方提供的最小测试镜像(约 13KB),仅打印一段欢迎信息 |
原理:
用最小镜像测试网络通路,而不是直接拉 PostgreSQL(约 400MB)。如果 hello-world 都拉不下来,说明镜像源有问题,拉大镜像必然超时。
如果失败(超时/报错):
需要配置国内镜像源加速,见下方步骤。
3.3 配置 Docker 镜像源
命令 1:创建配置目录
bash
sudo mkdir -p /etc/docker
| 片段 | 作用 |
|---|---|
mkdir -p |
-p 表示递归创建,目录已存在时不报错(幂等操作) |
/etc/docker |
Docker 约定的配置目录,daemon.json 必须放在这里 |
命令 2:编辑配置文件
shellscript
sudo vim /etc/docker/daemon.json
| 片段 | 作用 |
|---|---|
vim |
Vim 文本编辑器 |
/etc/docker/daemon.json |
Docker 守护进程的配置文件 |
配置文件内容:
json
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.1ms.run",
"https://docker.anyhub.us.kg"
]
}
| 字段 | 作用 |
|---|---|
registry-mirrors |
镜像加速地址列表。Docker 拉取镜像时会依次尝试列表中的地址,第一个成功即停止 |
原理:
Docker 默认从 registry-1.docker.io 拉取镜像。配置 registry-mirrors 后,Docker 会优先通过镜像加速节点拉取,加速节点本质是 Docker Hub 的反向代理/缓存。
命令 3:重新加载 systemd 配置
bash
sudo systemctl daemon-reload
原理:
daemon-reload 让 systemd 重新读取所有 service 文件。修改了 Docker 的配置后需要这一步,让 systemd 感知到 Docker 服务配置的变化。
命令 4:重启 Docker
bash
sudo systemctl restart docker
原理:
Docker 守护进程在启动时才会读取 daemon.json。修改配置文件后必须重启守护进程才能生效。
重启后: 重新执行 sudo docker pull hello-world 验证镜像源是否生效。
四、第四章「具体流程」逐条深度解析
本章是整个部署的核心。从拉取镜像到配置远程访问,每一步都关系到部署能否成功。
4.1 拉取 PostgreSQL 镜像
bash
sudo docker pull postgres:15.4
| 片段 | 作用 |
|---|---|
docker pull |
从 registry 拉取镜像到本地 |
postgres |
镜像名(官方 PostgreSQL 镜像) |
:15.4 |
版本标签。精确指定大版本 15 + 小版本 4 |
为什么不用 latest:
latest 是一个浮动的指针------今天指向 15.4,明天可能指向 16.0。生产环境用 latest 会导致:
- 不可复现:不同时间拉取的镜像版本不同
- 意外升级:新大版本可能不兼容旧数据格式
- 无法回滚:不知道之前用的是什么版本
镜像分层原理:
#mermaid-svg-4w3iyGRgGCRZ4urG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4w3iyGRgGCRZ4urG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4w3iyGRgGCRZ4urG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4w3iyGRgGCRZ4urG .error-icon{fill:#552222;}#mermaid-svg-4w3iyGRgGCRZ4urG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4w3iyGRgGCRZ4urG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4w3iyGRgGCRZ4urG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4w3iyGRgGCRZ4urG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4w3iyGRgGCRZ4urG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4w3iyGRgGCRZ4urG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4w3iyGRgGCRZ4urG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4w3iyGRgGCRZ4urG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4w3iyGRgGCRZ4urG .marker.cross{stroke:#333333;}#mermaid-svg-4w3iyGRgGCRZ4urG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4w3iyGRgGCRZ4urG p{margin:0;}#mermaid-svg-4w3iyGRgGCRZ4urG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4w3iyGRgGCRZ4urG .cluster-label text{fill:#333;}#mermaid-svg-4w3iyGRgGCRZ4urG .cluster-label span{color:#333;}#mermaid-svg-4w3iyGRgGCRZ4urG .cluster-label span p{background-color:transparent;}#mermaid-svg-4w3iyGRgGCRZ4urG .label text,#mermaid-svg-4w3iyGRgGCRZ4urG span{fill:#333;color:#333;}#mermaid-svg-4w3iyGRgGCRZ4urG .node rect,#mermaid-svg-4w3iyGRgGCRZ4urG .node circle,#mermaid-svg-4w3iyGRgGCRZ4urG .node ellipse,#mermaid-svg-4w3iyGRgGCRZ4urG .node polygon,#mermaid-svg-4w3iyGRgGCRZ4urG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4w3iyGRgGCRZ4urG .rough-node .label text,#mermaid-svg-4w3iyGRgGCRZ4urG .node .label text,#mermaid-svg-4w3iyGRgGCRZ4urG .image-shape .label,#mermaid-svg-4w3iyGRgGCRZ4urG .icon-shape .label{text-anchor:middle;}#mermaid-svg-4w3iyGRgGCRZ4urG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4w3iyGRgGCRZ4urG .rough-node .label,#mermaid-svg-4w3iyGRgGCRZ4urG .node .label,#mermaid-svg-4w3iyGRgGCRZ4urG .image-shape .label,#mermaid-svg-4w3iyGRgGCRZ4urG .icon-shape .label{text-align:center;}#mermaid-svg-4w3iyGRgGCRZ4urG .node.clickable{cursor:pointer;}#mermaid-svg-4w3iyGRgGCRZ4urG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4w3iyGRgGCRZ4urG .arrowheadPath{fill:#333333;}#mermaid-svg-4w3iyGRgGCRZ4urG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4w3iyGRgGCRZ4urG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4w3iyGRgGCRZ4urG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4w3iyGRgGCRZ4urG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4w3iyGRgGCRZ4urG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4w3iyGRgGCRZ4urG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4w3iyGRgGCRZ4urG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4w3iyGRgGCRZ4urG .cluster text{fill:#333;}#mermaid-svg-4w3iyGRgGCRZ4urG .cluster span{color:#333;}#mermaid-svg-4w3iyGRgGCRZ4urG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4w3iyGRgGCRZ4urG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4w3iyGRgGCRZ4urG rect.text{fill:none;stroke-width:0;}#mermaid-svg-4w3iyGRgGCRZ4urG .icon-shape,#mermaid-svg-4w3iyGRgGCRZ4urG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4w3iyGRgGCRZ4urG .icon-shape p,#mermaid-svg-4w3iyGRgGCRZ4urG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4w3iyGRgGCRZ4urG .icon-shape .label rect,#mermaid-svg-4w3iyGRgGCRZ4urG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4w3iyGRgGCRZ4urG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4w3iyGRgGCRZ4urG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4w3iyGRgGCRZ4urG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} postgres:15.4 镜像
基础层: debian-bullseye-slim
运行时层: 安装 PostgreSQL 15.4 二进制
入口层: docker-entrypoint.sh
配置层: postgresql.conf 模板
pg_hba.conf 模板
镜像内的 docker-entrypoint.sh 是关键------它在容器首次启动时执行 initdb 初始化数据目录,并根据环境变量(POSTGRES_USER、POSTGRES_PASSWORD 等)自动创建用户和数据库。
4.2 准备部署目录
bash
sudo mkdir -p /opt/postgresql/compose
cd /opt/postgresql/compose
逐行解析:
| 命令 | 作用 |
|---|---|
sudo mkdir -p /opt/postgresql/compose |
在 /opt/ 下创建 postgresql/compose 目录。-p 递归创建父目录,已存在不报错 |
cd /opt/postgresql/compose |
切换工作目录到 compose 目录 |
为什么用 /opt/ 目录:
Linux 文件系统层次标准(FHS)规定:
/opt/------ 第三方独立软件/大型应用的安装目录/var/------ 系统运行时可变数据(日志、缓存等)/usr/------ 系统自带软件包
PostgreSQL 作为独立的第三方生产数据库,放在 /opt/ 语义最合适,便于备份、迁移、识别。
为什么需要专门的 compose 目录:
Docker Compose 的工作方式是:在哪个目录执行 docker compose 命令,就以当前目录的 docker-compose.yml 为部署描述文件。如果没有固定目录:
- 每次执行命令都要带
-f /path/to/docker-compose.yml,容易写错路径 - 文档和脚本无法统一
- 团队协作时找不到配置文件
4.3 创建数据持久化目录并授权
bash
# 创建数据目录
sudo mkdir -p /opt/postgresql/data
# 授权给容器内的 postgres 用户(UID/GID 均为 999)
sudo chown -R 999:999 /opt/postgresql/data
这两行命令分别解决两个完全不同的问题:
① mkdir ------ 数据持久化(容器与数据分离)
核心原理:Docker 容器是「无状态、可丢弃」的------容器一删,里面的数据全部消失。
#mermaid-svg-lFZpZhNpVHqLkJms{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lFZpZhNpVHqLkJms .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lFZpZhNpVHqLkJms .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lFZpZhNpVHqLkJms .error-icon{fill:#552222;}#mermaid-svg-lFZpZhNpVHqLkJms .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lFZpZhNpVHqLkJms .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lFZpZhNpVHqLkJms .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lFZpZhNpVHqLkJms .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lFZpZhNpVHqLkJms .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lFZpZhNpVHqLkJms .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lFZpZhNpVHqLkJms .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lFZpZhNpVHqLkJms .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lFZpZhNpVHqLkJms .marker.cross{stroke:#333333;}#mermaid-svg-lFZpZhNpVHqLkJms svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lFZpZhNpVHqLkJms p{margin:0;}#mermaid-svg-lFZpZhNpVHqLkJms .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lFZpZhNpVHqLkJms .cluster-label text{fill:#333;}#mermaid-svg-lFZpZhNpVHqLkJms .cluster-label span{color:#333;}#mermaid-svg-lFZpZhNpVHqLkJms .cluster-label span p{background-color:transparent;}#mermaid-svg-lFZpZhNpVHqLkJms .label text,#mermaid-svg-lFZpZhNpVHqLkJms span{fill:#333;color:#333;}#mermaid-svg-lFZpZhNpVHqLkJms .node rect,#mermaid-svg-lFZpZhNpVHqLkJms .node circle,#mermaid-svg-lFZpZhNpVHqLkJms .node ellipse,#mermaid-svg-lFZpZhNpVHqLkJms .node polygon,#mermaid-svg-lFZpZhNpVHqLkJms .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lFZpZhNpVHqLkJms .rough-node .label text,#mermaid-svg-lFZpZhNpVHqLkJms .node .label text,#mermaid-svg-lFZpZhNpVHqLkJms .image-shape .label,#mermaid-svg-lFZpZhNpVHqLkJms .icon-shape .label{text-anchor:middle;}#mermaid-svg-lFZpZhNpVHqLkJms .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lFZpZhNpVHqLkJms .rough-node .label,#mermaid-svg-lFZpZhNpVHqLkJms .node .label,#mermaid-svg-lFZpZhNpVHqLkJms .image-shape .label,#mermaid-svg-lFZpZhNpVHqLkJms .icon-shape .label{text-align:center;}#mermaid-svg-lFZpZhNpVHqLkJms .node.clickable{cursor:pointer;}#mermaid-svg-lFZpZhNpVHqLkJms .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lFZpZhNpVHqLkJms .arrowheadPath{fill:#333333;}#mermaid-svg-lFZpZhNpVHqLkJms .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lFZpZhNpVHqLkJms .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lFZpZhNpVHqLkJms .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lFZpZhNpVHqLkJms .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lFZpZhNpVHqLkJms .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lFZpZhNpVHqLkJms .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lFZpZhNpVHqLkJms .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lFZpZhNpVHqLkJms .cluster text{fill:#333;}#mermaid-svg-lFZpZhNpVHqLkJms .cluster span{color:#333;}#mermaid-svg-lFZpZhNpVHqLkJms div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lFZpZhNpVHqLkJms .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lFZpZhNpVHqLkJms rect.text{fill:none;stroke-width:0;}#mermaid-svg-lFZpZhNpVHqLkJms .icon-shape,#mermaid-svg-lFZpZhNpVHqLkJms .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lFZpZhNpVHqLkJms .icon-shape p,#mermaid-svg-lFZpZhNpVHqLkJms .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lFZpZhNpVHqLkJms .icon-shape .label rect,#mermaid-svg-lFZpZhNpVHqLkJms .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lFZpZhNpVHqLkJms .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lFZpZhNpVHqLkJms .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lFZpZhNpVHqLkJms :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 宿主机(持久存储)
postgres_db 容器(可随时销毁)
volumes 挂载
读写都落这里
PostgreSQL 进程
/opt/postgresql/data/
所有表数据、索引、WAL日志
compose 文件中的挂载配置:
yaml
volumes:
- /opt/postgresql/data:/var/lib/postgresql/data
| 路径 | 含义 |
|---|---|
/opt/postgresql/data(冒号左) |
宿主机路径 |
/var/lib/postgresql/data(冒号右) |
容器内 PostgreSQL 的标准数据目录(PGDATA) |
效果: 容器内 PG 进程读写的数据,实际上落在宿主机的 /opt/postgresql/data 下。即使容器被 docker compose down 删除,数据依然完整保留。重新 docker compose up -d 挂载同一目录即可恢复。
② chown ------ 解决容器内外 UID 不一致问题
核心原理:容器内外的「用户身份」是隔离的,但文件权限是共享的。
| 角色 | UID:GID | 说明 |
|---|---|---|
宿主机 sudo mkdir 创建的目录 |
0:0(root) |
sudo 意味着以 root 执行,创建的目录属主是 root |
| 容器内 PostgreSQL 进程 | 999:999 |
官方镜像安全最佳实践:不以 root 运行,用专用用户 postgres(UID 999) |
冲突场景:
#mermaid-svg-O9LG65MhxwxFNBiX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-O9LG65MhxwxFNBiX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-O9LG65MhxwxFNBiX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-O9LG65MhxwxFNBiX .error-icon{fill:#552222;}#mermaid-svg-O9LG65MhxwxFNBiX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-O9LG65MhxwxFNBiX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-O9LG65MhxwxFNBiX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-O9LG65MhxwxFNBiX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-O9LG65MhxwxFNBiX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-O9LG65MhxwxFNBiX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-O9LG65MhxwxFNBiX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-O9LG65MhxwxFNBiX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-O9LG65MhxwxFNBiX .marker.cross{stroke:#333333;}#mermaid-svg-O9LG65MhxwxFNBiX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-O9LG65MhxwxFNBiX p{margin:0;}#mermaid-svg-O9LG65MhxwxFNBiX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-O9LG65MhxwxFNBiX .cluster-label text{fill:#333;}#mermaid-svg-O9LG65MhxwxFNBiX .cluster-label span{color:#333;}#mermaid-svg-O9LG65MhxwxFNBiX .cluster-label span p{background-color:transparent;}#mermaid-svg-O9LG65MhxwxFNBiX .label text,#mermaid-svg-O9LG65MhxwxFNBiX span{fill:#333;color:#333;}#mermaid-svg-O9LG65MhxwxFNBiX .node rect,#mermaid-svg-O9LG65MhxwxFNBiX .node circle,#mermaid-svg-O9LG65MhxwxFNBiX .node ellipse,#mermaid-svg-O9LG65MhxwxFNBiX .node polygon,#mermaid-svg-O9LG65MhxwxFNBiX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-O9LG65MhxwxFNBiX .rough-node .label text,#mermaid-svg-O9LG65MhxwxFNBiX .node .label text,#mermaid-svg-O9LG65MhxwxFNBiX .image-shape .label,#mermaid-svg-O9LG65MhxwxFNBiX .icon-shape .label{text-anchor:middle;}#mermaid-svg-O9LG65MhxwxFNBiX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-O9LG65MhxwxFNBiX .rough-node .label,#mermaid-svg-O9LG65MhxwxFNBiX .node .label,#mermaid-svg-O9LG65MhxwxFNBiX .image-shape .label,#mermaid-svg-O9LG65MhxwxFNBiX .icon-shape .label{text-align:center;}#mermaid-svg-O9LG65MhxwxFNBiX .node.clickable{cursor:pointer;}#mermaid-svg-O9LG65MhxwxFNBiX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-O9LG65MhxwxFNBiX .arrowheadPath{fill:#333333;}#mermaid-svg-O9LG65MhxwxFNBiX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-O9LG65MhxwxFNBiX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-O9LG65MhxwxFNBiX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O9LG65MhxwxFNBiX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-O9LG65MhxwxFNBiX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O9LG65MhxwxFNBiX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-O9LG65MhxwxFNBiX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-O9LG65MhxwxFNBiX .cluster text{fill:#333;}#mermaid-svg-O9LG65MhxwxFNBiX .cluster span{color:#333;}#mermaid-svg-O9LG65MhxwxFNBiX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-O9LG65MhxwxFNBiX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-O9LG65MhxwxFNBiX rect.text{fill:none;stroke-width:0;}#mermaid-svg-O9LG65MhxwxFNBiX .icon-shape,#mermaid-svg-O9LG65MhxwxFNBiX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O9LG65MhxwxFNBiX .icon-shape p,#mermaid-svg-O9LG65MhxwxFNBiX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-O9LG65MhxwxFNBiX .icon-shape .label rect,#mermaid-svg-O9LG65MhxwxFNBiX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O9LG65MhxwxFNBiX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-O9LG65MhxwxFNBiX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-O9LG65MhxwxFNBiX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 目录属主是 root:root
目录属主是 999:999
容器启动
PG 进程以 UID 999
尝试写入数据目录
权限不足
Permission denied
容器退出
Exited (1)
写入成功
数据库正常启动
chown -R 999:999 把目录属主改为 999,使容器内 postgres 用户有写权限。
| 参数 | 作用 |
|---|---|
chown |
change owner,修改文件/目录属主 |
-R |
递归处理,包括所有子目录和文件 |
999:999 |
UID:GID。这里 999 不是随便写的,是官方 postgres 镜像内部 postgres 用户的固定 UID |
常见官方镜像的固定 UID 速查:
| 镜像 | 内部用户 | UID |
|---|---|---|
postgres |
postgres | 999 |
mysql |
mysql | 999 |
redis |
redis | 999 |
nginx |
nginx | 101 |
elasticsearch |
elasticsearch | 1000 |
不执行 chown 会怎样: 容器启动后立即退出,日志报 initdb: error: could not access directory "/var/lib/postgresql/data": Permission denied。这是新手最常踩的坑。
4.4 编写 docker-compose.yml
这是整个部署的「单一事实源」(Single Source of Truth)。所有运行时参数都声明在这个文件里。
yaml
# /opt/postgresql/compose/docker-compose.yml
services:
postgres:
image: postgres:15.4
container_name: postgres_db
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
TZ: Asia/Shanghai
ports:
- "5432:5432"
volumes:
- /opt/postgresql/data:/var/lib/postgresql/data
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
逐字段深度解析:
services:
Compose 文件的顶级键。下面定义所有要启动的服务。本例只有一个 postgres 服务。
postgres:
服务名(自定义)。后续 docker compose logs postgres、docker compose exec postgres bash 都用这个名字引用服务。
image: postgres:15.4
| 字段 | 作用 |
|---|---|
image |
指定使用哪个镜像 |
postgres:15.4 |
镜像名:标签。如果本地没有,Compose 会自动拉取 |
container_name: postgres_db
| 字段 | 作用 |
|---|---|
container_name |
固定容器名 |
为什么固定名字: 不指定时 Docker 会自动生成 compose目录名-服务名-序号 格式的名字(如 compose-postgres-1),难以记忆和引用。固定为 postgres_db 后,所有 docker exec、docker logs 命令都可以直接用这个名字。
restart: unless-stopped
| 值 | 行为 |
|---|---|
no |
容器退出后不重启(默认) |
always |
总是重启,即使手动 docker stop 也会在 Docker 重启时被拉起 |
unless-stopped |
宕机/异常退出自动重启;但手动 docker stop 后不会被拉起 |
on-failure |
仅在非零退出码时重启 |
选 unless-stopped 的原因: 既保证数据库崩溃后自动恢复,又不会在你主动维护(手动 stop)时被意外拉起。
environment:
环境变量注入到容器内。PostgreSQL 官方镜像约定了一系列 POSTGRES_* 环境变量,在首次启动 (数据目录为空)时由 docker-entrypoint.sh 读取并执行初始化:
| 变量 | 作用 |
|---|---|
POSTGRES_USER: postgres |
创建超级用户,用户名为 postgres |
POSTGRES_PASSWORD: postgres |
设置该用户的密码 |
POSTGRES_DB: postgres |
自动创建名为 postgres 的默认数据库 |
TZ: Asia/Shanghai |
设置容器时区,影响 PostgreSQL 的时间函数(now()、current_timestamp) |
⚠️ 重要 :这些环境变量仅在数据目录为空时生效。如果数据目录已有数据(非首次启动),修改这些变量不会改变已有的用户/密码/库。
ports:
yaml
ports:
- "5432:5432"
| 片段 | 含义 |
|---|---|
左边 5432 |
宿主机端口 |
右边 5432 |
容器内端口 |
"5432:5432" |
将宿主机 5432 映射到容器内 5432 |
原理: 容器有独立的网络命名空间,外部无法直接访问容器内端口。ports 在宿主机上创建一个端口转发规则,把宿主机的 5432 流量转发到容器内的 5432。
变体: 如改为 "127.0.0.1:5432:5432",则只有宿主机本机能连,远程无法连接。适合「仅本机应用访问数据库」的场景。
volumes:
yaml
volumes:
- /opt/postgresql/data:/var/lib/postgresql/data
| 片段 | 含义 |
|---|---|
| 左边 | 宿主机路径(绑定挂载,bind mount) |
| 右边 | 容器内路径 |
: |
分隔符 |
已在 4.3 详解,此处不重复。
deploy:
yaml
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
| 字段 | 作用 |
|---|---|
limits.cpus: '2.0' |
限制容器最多使用 2 个 CPU 核心 |
limits.memory: 2G |
限制容器最多使用 2GB 内存 |
原理: 通过 Linux cgroup(控制组)机制限制容器的资源使用上限。防止数据库因异常查询(如全表扫描)吃光宿主机内存,导致其他服务被 OOM Killer 杀掉。
为什么是 2G: PostgreSQL 的 shared_buffers 默认 128MB,加上连接池、排序、临时表等,2G 对于中小规模应用足够。生产环境需根据实际数据量和并发量调整。
healthcheck:
yaml
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
| 字段 | 作用 |
|---|---|
test |
健康检查命令。pg_isready 是 PostgreSQL 自带的就绪检测工具,检查服务器是否可接受连接 |
interval: 30s |
每 30 秒检查一次 |
timeout: 5s |
单次检查超时时间。超过 5 秒未响应视为失败 |
retries: 3 |
连续 3 次失败后标记为 unhealthy |
start_period: 30s |
启动后 30 秒内的失败不计入 retries(给数据库初始化留缓冲时间) |
原理与作用:
#mermaid-svg-RnJ2fh5C1HOBT7hw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RnJ2fh5C1HOBT7hw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RnJ2fh5C1HOBT7hw .error-icon{fill:#552222;}#mermaid-svg-RnJ2fh5C1HOBT7hw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RnJ2fh5C1HOBT7hw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RnJ2fh5C1HOBT7hw .marker.cross{stroke:#333333;}#mermaid-svg-RnJ2fh5C1HOBT7hw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RnJ2fh5C1HOBT7hw p{margin:0;}#mermaid-svg-RnJ2fh5C1HOBT7hw defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-RnJ2fh5C1HOBT7hw g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-RnJ2fh5C1HOBT7hw g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-RnJ2fh5C1HOBT7hw g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-RnJ2fh5C1HOBT7hw g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-RnJ2fh5C1HOBT7hw g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-RnJ2fh5C1HOBT7hw .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-RnJ2fh5C1HOBT7hw .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-RnJ2fh5C1HOBT7hw .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RnJ2fh5C1HOBT7hw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RnJ2fh5C1HOBT7hw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RnJ2fh5C1HOBT7hw .edgeLabel .label text{fill:#333;}#mermaid-svg-RnJ2fh5C1HOBT7hw .label div .edgeLabel{color:#333;}#mermaid-svg-RnJ2fh5C1HOBT7hw .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-RnJ2fh5C1HOBT7hw .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-RnJ2fh5C1HOBT7hw .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-RnJ2fh5C1HOBT7hw .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-RnJ2fh5C1HOBT7hw .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-RnJ2fh5C1HOBT7hw .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RnJ2fh5C1HOBT7hw #statediagram-barbEnd{fill:#333333;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .cluster-label,#mermaid-svg-RnJ2fh5C1HOBT7hw .nodeLabel{color:#131300;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-RnJ2fh5C1HOBT7hw .note-edge{stroke-dasharray:5;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-note text{fill:black;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram-note .nodeLabel{color:black;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagram .edgeLabel{color:red;}#mermaid-svg-RnJ2fh5C1HOBT7hw #dependencyStart,#mermaid-svg-RnJ2fh5C1HOBT7hw #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-RnJ2fh5C1HOBT7hw .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RnJ2fh5C1HOBT7hw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} docker compose up
pg_isready 返回 0
start_period 后仍失败
连续 3 次检查失败
检查恢复成功
docker compose down
starting
healthy
unhealthy
没有 healthcheck 会怎样: Docker 只能判断进程是否存活(Up),但无法判断数据库是否真正可用。进程活着但数据库还在做 crash recovery 时,连接会被拒绝------这就是「假活」。healthcheck 解决了这个问题。
logging:
yaml
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
| 字段 | 作用 |
|---|---|
driver: json-file |
日志以 JSON 格式写入文件(Docker 默认驱动) |
max-size: "10m" |
单个日志文件最大 10MB |
max-file: "3" |
最多保留 3 个日志文件 |
原理: Docker 默认会无限增长容器日志(存在 /var/lib/docker/containers/<id>/<id>-json.log),不做轮转最终会撑满磁盘。配置后,日志达到 10MB 会自动轮转:json.log → json.log.1 → json.log.2 → 删除最旧的。
总日志上限: 10MB × 3 = 30MB,不会失控。
4.5 启动服务
bash
# 后台启动
sudo docker compose up -d
# 查看状态(等待片刻后 STATUS 应显示 healthy)
sudo docker compose ps
# 查看实时日志
sudo docker compose logs -f postgres
命令 1:docker compose up -d
| 片段 | 作用 |
|---|---|
docker compose |
Compose V2 语法(作为 Docker 的子命令) |
up |
根据当前目录的 docker-compose.yml 创建并启动容器 |
-d |
detach,后台运行,不占用当前终端 |
执行流程:
#mermaid-svg-eP099nsAmAyikBbV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eP099nsAmAyikBbV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eP099nsAmAyikBbV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eP099nsAmAyikBbV .error-icon{fill:#552222;}#mermaid-svg-eP099nsAmAyikBbV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eP099nsAmAyikBbV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eP099nsAmAyikBbV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eP099nsAmAyikBbV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eP099nsAmAyikBbV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eP099nsAmAyikBbV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eP099nsAmAyikBbV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eP099nsAmAyikBbV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eP099nsAmAyikBbV .marker.cross{stroke:#333333;}#mermaid-svg-eP099nsAmAyikBbV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eP099nsAmAyikBbV p{margin:0;}#mermaid-svg-eP099nsAmAyikBbV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eP099nsAmAyikBbV .cluster-label text{fill:#333;}#mermaid-svg-eP099nsAmAyikBbV .cluster-label span{color:#333;}#mermaid-svg-eP099nsAmAyikBbV .cluster-label span p{background-color:transparent;}#mermaid-svg-eP099nsAmAyikBbV .label text,#mermaid-svg-eP099nsAmAyikBbV span{fill:#333;color:#333;}#mermaid-svg-eP099nsAmAyikBbV .node rect,#mermaid-svg-eP099nsAmAyikBbV .node circle,#mermaid-svg-eP099nsAmAyikBbV .node ellipse,#mermaid-svg-eP099nsAmAyikBbV .node polygon,#mermaid-svg-eP099nsAmAyikBbV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eP099nsAmAyikBbV .rough-node .label text,#mermaid-svg-eP099nsAmAyikBbV .node .label text,#mermaid-svg-eP099nsAmAyikBbV .image-shape .label,#mermaid-svg-eP099nsAmAyikBbV .icon-shape .label{text-anchor:middle;}#mermaid-svg-eP099nsAmAyikBbV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eP099nsAmAyikBbV .rough-node .label,#mermaid-svg-eP099nsAmAyikBbV .node .label,#mermaid-svg-eP099nsAmAyikBbV .image-shape .label,#mermaid-svg-eP099nsAmAyikBbV .icon-shape .label{text-align:center;}#mermaid-svg-eP099nsAmAyikBbV .node.clickable{cursor:pointer;}#mermaid-svg-eP099nsAmAyikBbV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eP099nsAmAyikBbV .arrowheadPath{fill:#333333;}#mermaid-svg-eP099nsAmAyikBbV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eP099nsAmAyikBbV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eP099nsAmAyikBbV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eP099nsAmAyikBbV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eP099nsAmAyikBbV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eP099nsAmAyikBbV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eP099nsAmAyikBbV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eP099nsAmAyikBbV .cluster text{fill:#333;}#mermaid-svg-eP099nsAmAyikBbV .cluster span{color:#333;}#mermaid-svg-eP099nsAmAyikBbV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eP099nsAmAyikBbV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eP099nsAmAyikBbV rect.text{fill:none;stroke-width:0;}#mermaid-svg-eP099nsAmAyikBbV .icon-shape,#mermaid-svg-eP099nsAmAyikBbV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eP099nsAmAyikBbV .icon-shape p,#mermaid-svg-eP099nsAmAyikBbV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eP099nsAmAyikBbV .icon-shape .label rect,#mermaid-svg-eP099nsAmAyikBbV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eP099nsAmAyikBbV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eP099nsAmAyikBbV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eP099nsAmAyikBbV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
docker compose up -d
镜像是否在本地?
自动 docker pull
创建容器
挂载 volumes
设置端口映射
启动容器进程
数据目录是否为空?
执行 initdb 初始化
创建用户/库/配置文件
跳过初始化
直接启动 PostgreSQL
PostgreSQL 开始接受连接
幂等性: docker compose up -d 是幂等的------重复执行不会创建多个容器。如果配置没变,它什么也不做;如果修改了 compose 文件,它会重建受影响的容器。
命令 2:docker compose ps
| 片段 | 作用 |
|---|---|
ps |
列出当前 compose 项目中所有服务的运行状态 |
重点看 STATUS 列:
| 状态值 | 含义 | 是否可继续 |
|---|---|---|
Up |
容器已启动,但 healthcheck 尚未完成或未配置 | 等待 |
Up (healthy) |
健康检查通过,数据库可接受连接 | ✅ 可继续 |
Up (unhealthy) |
健康检查连续失败 | ❌ 需排查 |
Exited (1) |
启动失败(常见原因:权限问题、端口占用) | ❌ 需排查 |
Restarting |
反复重启中 | ❌ 需排查 |
命令 3:docker compose logs -f postgres
| 片段 | 作用 |
|---|---|
logs |
查看服务日志 |
-f |
follow,持续跟踪新输出(类似 tail -f) |
postgres |
服务名(compose 文件中 services: 下的 key) |
首次启动的正常日志关键行:
database system was shut down at ... # 正常
database system is ready to accept connections # ← 这行出现说明启动成功
4.6 验证数据库连接
bash
sudo docker exec -it postgres_db psql -U postgres
逐参数解析:
| 片段 | 作用 |
|---|---|
sudo |
su peru ser do,临时以 root 权限执行这一条命令。普通用户访问 Docker 守护进程的 socket 文件需要 root 权限 |
docker exec |
在运行中 的容器内执行一条命令(区别于 docker run 启动新容器) |
-i |
--interactive,保持标准输入(STDIN)打开,让 psql 能接收键盘输入 |
-t |
--tty,分配伪终端(pseudo-TTY),让交互体验类似 SSH |
postgres_db |
目标容器名(来自 compose 的 container_name) |
psql |
PostgreSQL 命令行客户端 |
-U postgres |
u ser,以 postgres 这个用户身份登录数据库 |
💡 关于
sudo:已经进入 root 还需要加吗?不需要。 文档中统一写了
sudo是为了兼容「以普通用户登录服务器」的情况。实际上:
场景 提示符 是否需要 sudo以普通用户登录(如 aw@ubuntu:~$)$需要 ,执行 root 权限操作时必须加 sudo已通过 sudo -i或su -切到 root(如root@ubuntu:~#)#不需要,你已经是 root,加了等于没加 判断方法:看终端提示符末尾 ------
$是普通用户(要加 sudo),#是 root(不用加)。本文档所有命令前的
sudo,如果你已经切到 root,直接去掉即可。
-it 的作用:
-i 和 -t 是「进容器做交互操作」的标配组合。不加的话,psql 启动后立刻退出(因为没有终端、没有输入源),根本无法交互。加上后才能看到 psql 的交互提示符 postgres=# 并输入 SQL。
进入 psql 后的命令:
| 命令 | 作用 |
|---|---|
\l |
list,列出所有数据库 |
\du |
列出所有用户/角色 |
\q |
quit,退出 psql |
为什么先在容器内验证:
容器内验证是「零依赖」测试------不依赖网络、IP、防火墙、pg_hba.conf。只要这一步通过,就证明数据库本身完全正常。之后任何「连不上」的问题都可以缩小到网络层或配置层,排查效率大幅提升。
#mermaid-svg-VHQPxnL13HOJqoSW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VHQPxnL13HOJqoSW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VHQPxnL13HOJqoSW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VHQPxnL13HOJqoSW .error-icon{fill:#552222;}#mermaid-svg-VHQPxnL13HOJqoSW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VHQPxnL13HOJqoSW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VHQPxnL13HOJqoSW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VHQPxnL13HOJqoSW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VHQPxnL13HOJqoSW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VHQPxnL13HOJqoSW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VHQPxnL13HOJqoSW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VHQPxnL13HOJqoSW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VHQPxnL13HOJqoSW .marker.cross{stroke:#333333;}#mermaid-svg-VHQPxnL13HOJqoSW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VHQPxnL13HOJqoSW p{margin:0;}#mermaid-svg-VHQPxnL13HOJqoSW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VHQPxnL13HOJqoSW .cluster-label text{fill:#333;}#mermaid-svg-VHQPxnL13HOJqoSW .cluster-label span{color:#333;}#mermaid-svg-VHQPxnL13HOJqoSW .cluster-label span p{background-color:transparent;}#mermaid-svg-VHQPxnL13HOJqoSW .label text,#mermaid-svg-VHQPxnL13HOJqoSW span{fill:#333;color:#333;}#mermaid-svg-VHQPxnL13HOJqoSW .node rect,#mermaid-svg-VHQPxnL13HOJqoSW .node circle,#mermaid-svg-VHQPxnL13HOJqoSW .node ellipse,#mermaid-svg-VHQPxnL13HOJqoSW .node polygon,#mermaid-svg-VHQPxnL13HOJqoSW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VHQPxnL13HOJqoSW .rough-node .label text,#mermaid-svg-VHQPxnL13HOJqoSW .node .label text,#mermaid-svg-VHQPxnL13HOJqoSW .image-shape .label,#mermaid-svg-VHQPxnL13HOJqoSW .icon-shape .label{text-anchor:middle;}#mermaid-svg-VHQPxnL13HOJqoSW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VHQPxnL13HOJqoSW .rough-node .label,#mermaid-svg-VHQPxnL13HOJqoSW .node .label,#mermaid-svg-VHQPxnL13HOJqoSW .image-shape .label,#mermaid-svg-VHQPxnL13HOJqoSW .icon-shape .label{text-align:center;}#mermaid-svg-VHQPxnL13HOJqoSW .node.clickable{cursor:pointer;}#mermaid-svg-VHQPxnL13HOJqoSW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VHQPxnL13HOJqoSW .arrowheadPath{fill:#333333;}#mermaid-svg-VHQPxnL13HOJqoSW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VHQPxnL13HOJqoSW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VHQPxnL13HOJqoSW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VHQPxnL13HOJqoSW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VHQPxnL13HOJqoSW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VHQPxnL13HOJqoSW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VHQPxnL13HOJqoSW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VHQPxnL13HOJqoSW .cluster text{fill:#333;}#mermaid-svg-VHQPxnL13HOJqoSW .cluster span{color:#333;}#mermaid-svg-VHQPxnL13HOJqoSW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VHQPxnL13HOJqoSW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VHQPxnL13HOJqoSW rect.text{fill:none;stroke-width:0;}#mermaid-svg-VHQPxnL13HOJqoSW .icon-shape,#mermaid-svg-VHQPxnL13HOJqoSW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VHQPxnL13HOJqoSW .icon-shape p,#mermaid-svg-VHQPxnL13HOJqoSW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VHQPxnL13HOJqoSW .icon-shape .label rect,#mermaid-svg-VHQPxnL13HOJqoSW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VHQPxnL13HOJqoSW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VHQPxnL13HOJqoSW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VHQPxnL13HOJqoSW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 通过
失败
失败
4.6 容器内验证
数据库本身 OK
数据库有问题
检查日志/权限/镜像
4.7 远程访问配置
远程连接验证
问题在网络层或配置层
排查 pg_hba/listen/防火墙
4.7 配置远程访问
PostgreSQL 出于安全考虑,默认只允许本机连接。要让远程客户端连进来,必须改两个配置。
bash
# 1) 让 PostgreSQL 监听所有网卡
sudo docker exec -it postgres_db bash -c "sed -i \"s/^#listen_addresses = 'localhost'/listen_addresses = '*'/\" /var/lib/postgresql/data/postgresql.conf"
# 2) 放行远程访问(示例:放行 192.168.1.0/24 网段)
sudo docker exec -it postgres_db bash -c "echo \"host all all 192.168.1.0/24 md5\" >> /var/lib/postgresql/data/pg_hba.conf"
# 3) 重启容器使配置生效
sudo docker compose restart
命令 1:修改 listen_addresses(监听地址)
bash
sudo docker exec -it postgres_db bash -c \
"sed -i \"s/^#listen_addresses = 'localhost'/listen_addresses = '*'/\" /var/lib/postgresql/data/postgresql.conf"
拆解:
| 片段 | 作用 |
|---|---|
docker exec -it postgres_db bash -c "..." |
在容器内执行 bash 命令 |
sed -i |
stream editor,-i 表示 in-place 直接修改文件 |
s/旧文本/新文本/ |
替换操作 |
^#listen_addresses = 'localhost' |
匹配以 # 开头的注释行(PostgreSQL 默认把 listen_addresses 注释掉) |
listen_addresses = '*' |
替换为监听所有网卡 |
listen_addresses 的作用:
PostgreSQL 进程监听 TCP 套接字,listen_addresses 决定它绑定在哪些网络接口(IP)上:
| 值 | 含义 | 效果 |
|---|---|---|
'localhost' |
仅绑定 127.0.0.1 | 只有容器内进程能连 |
'*' |
绑定所有可用 IP | 外部网络可达 |
'192.168.1.10' |
只绑定指定 IP | 精细控制 |
为什么是注释行: postgresql.conf 中 listen_addresses 默认被 # 注释掉(表示用默认值 'localhost')。用 sed 把 # 去掉并改值,是最小修改方式。
为什么不用环境变量改: PostgreSQL 镜像的 POSTGRES_* 环境变量只控制初始化的用户/密码/库,不控制监听地址。监听地址必须改配置文件。
命令 2:修改 pg_hba.conf(访问控制白名单)
bash
sudo docker exec -it postgres_db bash -c \
"echo \"host all all 192.168.1.0/24 md5\" >> /var/lib/postgresql/data/pg_hba.conf"
拆解:
| 片段 | 作用 |
|---|---|
echo "..." >> |
追加一行到文件末尾 |
>> |
追加(不覆盖原内容) |
host all all 192.168.1.0/24 md5 |
pg_hba.conf 规则行 |
规则行各字段含义:
host all all 192.168.1.0/24 md5
│ │ │ │ │
│ │ │ │ └─ 认证方式
│ │ │ └───────────────── 来源 IP 段
│ │ └───────────────────── 数据库用户(all = 所有用户)
│ └────────────────────────── 目标数据库(all = 所有库)
└──────────────────────────────── 连接类型(host = TCP/IP 远程)
| 认证方式 | 含义 |
|---|---|
md5 |
密码认证(MD5 加密传输) |
scram-sha-256 |
更安全的密码认证(SCRAM 协议,推荐生产使用) |
trust |
无需密码直接通过(极度危险) |
peer |
仅限本机,操作系统用户与数据库用户一致时通过 |
为什么用 >> 追加而不是覆盖: pg_hba.conf 已有 localhost 的规则,覆盖会丢失本机连接能力。追加一行新规则在文件末尾,PostgreSQL 自上而下匹配,新规则优先生效。
⚠️ 注意:
echo >>是非幂等操作------每次执行都会追加一行。多次执行会产生重复行。工程化更好的做法是用 compose 挂载自定义 pg_hba.conf 文件,但教程为简化流程使用了 echo 追加。
命令 3:重启容器
bash
sudo docker compose restart
原理: postgresql.conf 和 pg_hba.conf 不支持热加载 ,必须重启 PostgreSQL 进程才能重新读取。docker compose restart 等价于「先 stop 再 start」------容器内的 PG 进程重启,但容器本身和数据不动。
重启后数据会丢吗: 不会。重启只是重启进程,数据目录里的 PG_VERSION 等标识文件还在,PG 不会重新 initdb。这正是 4.3 数据持久化的价值。
两个配置的关系图
#mermaid-svg-wcQmeclH6z95bLJR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wcQmeclH6z95bLJR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wcQmeclH6z95bLJR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wcQmeclH6z95bLJR .error-icon{fill:#552222;}#mermaid-svg-wcQmeclH6z95bLJR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wcQmeclH6z95bLJR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wcQmeclH6z95bLJR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wcQmeclH6z95bLJR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wcQmeclH6z95bLJR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wcQmeclH6z95bLJR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wcQmeclH6z95bLJR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wcQmeclH6z95bLJR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wcQmeclH6z95bLJR .marker.cross{stroke:#333333;}#mermaid-svg-wcQmeclH6z95bLJR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wcQmeclH6z95bLJR p{margin:0;}#mermaid-svg-wcQmeclH6z95bLJR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wcQmeclH6z95bLJR .cluster-label text{fill:#333;}#mermaid-svg-wcQmeclH6z95bLJR .cluster-label span{color:#333;}#mermaid-svg-wcQmeclH6z95bLJR .cluster-label span p{background-color:transparent;}#mermaid-svg-wcQmeclH6z95bLJR .label text,#mermaid-svg-wcQmeclH6z95bLJR span{fill:#333;color:#333;}#mermaid-svg-wcQmeclH6z95bLJR .node rect,#mermaid-svg-wcQmeclH6z95bLJR .node circle,#mermaid-svg-wcQmeclH6z95bLJR .node ellipse,#mermaid-svg-wcQmeclH6z95bLJR .node polygon,#mermaid-svg-wcQmeclH6z95bLJR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wcQmeclH6z95bLJR .rough-node .label text,#mermaid-svg-wcQmeclH6z95bLJR .node .label text,#mermaid-svg-wcQmeclH6z95bLJR .image-shape .label,#mermaid-svg-wcQmeclH6z95bLJR .icon-shape .label{text-anchor:middle;}#mermaid-svg-wcQmeclH6z95bLJR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wcQmeclH6z95bLJR .rough-node .label,#mermaid-svg-wcQmeclH6z95bLJR .node .label,#mermaid-svg-wcQmeclH6z95bLJR .image-shape .label,#mermaid-svg-wcQmeclH6z95bLJR .icon-shape .label{text-align:center;}#mermaid-svg-wcQmeclH6z95bLJR .node.clickable{cursor:pointer;}#mermaid-svg-wcQmeclH6z95bLJR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wcQmeclH6z95bLJR .arrowheadPath{fill:#333333;}#mermaid-svg-wcQmeclH6z95bLJR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wcQmeclH6z95bLJR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wcQmeclH6z95bLJR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wcQmeclH6z95bLJR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wcQmeclH6z95bLJR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wcQmeclH6z95bLJR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wcQmeclH6z95bLJR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wcQmeclH6z95bLJR .cluster text{fill:#333;}#mermaid-svg-wcQmeclH6z95bLJR .cluster span{color:#333;}#mermaid-svg-wcQmeclH6z95bLJR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wcQmeclH6z95bLJR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wcQmeclH6z95bLJR rect.text{fill:none;stroke-width:0;}#mermaid-svg-wcQmeclH6z95bLJR .icon-shape,#mermaid-svg-wcQmeclH6z95bLJR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wcQmeclH6z95bLJR .icon-shape p,#mermaid-svg-wcQmeclH6z95bLJR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wcQmeclH6z95bLJR .icon-shape .label rect,#mermaid-svg-wcQmeclH6z95bLJR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wcQmeclH6z95bLJR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wcQmeclH6z95bLJR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wcQmeclH6z95bLJR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. TCP 连接 5432
2. 连接到达 PG
3. 匹配规则
是
否
md5
trust
密码正确
密码错误
远程客户端
192.168.1.100
listen_addresses = '*'
PostgreSQL 监听所有网卡
pg_hba.conf
来源 IP 在白名单中?
4. 认证方式
拒绝连接
要求输入密码
直接通过
五、第五章「常用管理命令」逐条解析
以下命令均在
/opt/postgresql/compose/目录下执行。
5.1 查看容器状态
bash
sudo docker compose ps
| 片段 | 作用 |
|---|---|
compose ps |
列出当前 compose 项目中所有服务的容器状态 |
输出重点: NAME、STATUS(含 healthcheck 结果)、PORTS。
5.2 查看实时日志
bash
sudo docker compose logs -f postgres
| 片段 | 作用 |
|---|---|
logs |
查看日志输出 |
-f |
follow,持续跟踪新输出 |
postgres |
服务名 |
5.3 停止服务
bash
sudo docker compose stop
作用: 停止容器进程,但不删除容器 。容器状态变为 Exited,数据完整保留。
与 down 的区别: stop 只是停下,down 会停止并删除容器(数据卷保留)。
5.4 启动服务
bash
sudo docker compose start
作用: 启动已停止的容器。与 stop 配对使用。
5.5 重启服务
bash
sudo docker compose restart
作用: 先 stop 再 start。用于修改配置文件后使其生效。
5.6 进入容器内的 psql
bash
sudo docker exec -it postgres_db psql -U postgres
已在 4.6 详解,此处不重复。
5.7 进入容器 shell
bash
sudo docker exec -it postgres_db bash
| 片段 | 作用 |
|---|---|
bash |
在容器内启动 bash shell |
与 5.6 的区别: 5.6 直接进入 psql(数据库客户端),5.7 进入容器的操作系统 shell,可以执行任意 Linux 命令(ls、cat、ps 等),用于深度排查。
5.8 停止并删除容器
bash
sudo docker compose down
作用: 停止并删除容器、网络。数据目录 /opt/postgresql/data 保留,不会丢数据。
什么时候用:
- 升级镜像版本前
- 完全重建容器
- 清理环境
5.9 修改 compose 文件后重新生效
bash
sudo docker compose up -d
作用: Compose 会对比当前运行状态与 compose 文件的差异,只重建有变化的部分。如果配置没变,什么也不做。
stop / start / restart / down / up 的区别总览:
#mermaid-svg-JUKvZEwxoqbK1NEw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JUKvZEwxoqbK1NEw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JUKvZEwxoqbK1NEw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JUKvZEwxoqbK1NEw .error-icon{fill:#552222;}#mermaid-svg-JUKvZEwxoqbK1NEw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JUKvZEwxoqbK1NEw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JUKvZEwxoqbK1NEw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JUKvZEwxoqbK1NEw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JUKvZEwxoqbK1NEw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JUKvZEwxoqbK1NEw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JUKvZEwxoqbK1NEw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JUKvZEwxoqbK1NEw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JUKvZEwxoqbK1NEw .marker.cross{stroke:#333333;}#mermaid-svg-JUKvZEwxoqbK1NEw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JUKvZEwxoqbK1NEw p{margin:0;}#mermaid-svg-JUKvZEwxoqbK1NEw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JUKvZEwxoqbK1NEw .cluster-label text{fill:#333;}#mermaid-svg-JUKvZEwxoqbK1NEw .cluster-label span{color:#333;}#mermaid-svg-JUKvZEwxoqbK1NEw .cluster-label span p{background-color:transparent;}#mermaid-svg-JUKvZEwxoqbK1NEw .label text,#mermaid-svg-JUKvZEwxoqbK1NEw span{fill:#333;color:#333;}#mermaid-svg-JUKvZEwxoqbK1NEw .node rect,#mermaid-svg-JUKvZEwxoqbK1NEw .node circle,#mermaid-svg-JUKvZEwxoqbK1NEw .node ellipse,#mermaid-svg-JUKvZEwxoqbK1NEw .node polygon,#mermaid-svg-JUKvZEwxoqbK1NEw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JUKvZEwxoqbK1NEw .rough-node .label text,#mermaid-svg-JUKvZEwxoqbK1NEw .node .label text,#mermaid-svg-JUKvZEwxoqbK1NEw .image-shape .label,#mermaid-svg-JUKvZEwxoqbK1NEw .icon-shape .label{text-anchor:middle;}#mermaid-svg-JUKvZEwxoqbK1NEw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JUKvZEwxoqbK1NEw .rough-node .label,#mermaid-svg-JUKvZEwxoqbK1NEw .node .label,#mermaid-svg-JUKvZEwxoqbK1NEw .image-shape .label,#mermaid-svg-JUKvZEwxoqbK1NEw .icon-shape .label{text-align:center;}#mermaid-svg-JUKvZEwxoqbK1NEw .node.clickable{cursor:pointer;}#mermaid-svg-JUKvZEwxoqbK1NEw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JUKvZEwxoqbK1NEw .arrowheadPath{fill:#333333;}#mermaid-svg-JUKvZEwxoqbK1NEw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JUKvZEwxoqbK1NEw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JUKvZEwxoqbK1NEw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JUKvZEwxoqbK1NEw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JUKvZEwxoqbK1NEw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JUKvZEwxoqbK1NEw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JUKvZEwxoqbK1NEw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JUKvZEwxoqbK1NEw .cluster text{fill:#333;}#mermaid-svg-JUKvZEwxoqbK1NEw .cluster span{color:#333;}#mermaid-svg-JUKvZEwxoqbK1NEw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JUKvZEwxoqbK1NEw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JUKvZEwxoqbK1NEw rect.text{fill:none;stroke-width:0;}#mermaid-svg-JUKvZEwxoqbK1NEw .icon-shape,#mermaid-svg-JUKvZEwxoqbK1NEw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JUKvZEwxoqbK1NEw .icon-shape p,#mermaid-svg-JUKvZEwxoqbK1NEw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JUKvZEwxoqbK1NEw .icon-shape .label rect,#mermaid-svg-JUKvZEwxoqbK1NEw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JUKvZEwxoqbK1NEw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JUKvZEwxoqbK1NEw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JUKvZEwxoqbK1NEw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据影响
stop/start/restart/down/up
数据目录 /opt/postgresql/data
容器生命周期
stop
start
restart
up -d
down
up -d
容器存在且运行
容器存在但停止
容器被删除
重新创建并启动
| 命令 | 容器 | 数据 |
|---|---|---|
stop |
停止,不删除 | 保留 |
start |
启动已停止的 | 保留 |
restart |
重启进程 | 保留 |
down |
停止并删除 | 保留(在宿主机目录) |
up -d |
创建并启动 | 保留 |
六、第六章「验证连接」逐条解析
6.1 服务器本地验证
bash
sudo docker exec -it postgres_db psql -U postgres
已在 4.6 详解。 这一步是「容器内零依赖验证」,确认数据库本身可用。
6.2 Windows 本地连接(Python 测试)
安装依赖
bash
pip install psycopg2-binary
| 片段 | 作用 |
|---|---|
pip |
Python 包管理器 |
install |
安装包 |
psycopg2-binary |
PostgreSQL 的 Python 驱动(预编译版本,无需编译 C 扩展) |
为什么用 psycopg2-binary 而非 psycopg2: psycopg2 需要在安装时编译 C 代码,依赖 pg_config 和编译器。psycopg2-binary 提供预编译的 wheel 包,直接安装即可,适合开发和快速验证。
Python 测试脚本
python
# test01.py
import psycopg2
try:
conn = psycopg2.connect(
host="192.168.1.128",
port="5432",
database="postgres",
user="postgres",
password="postgres"
)
print("连接成功!")
cursor = conn.cursor()
cursor.execute("SELECT version();")
print("PostgreSQL 版本:", cursor.fetchone())
cursor.close()
conn.close()
except Exception as e:
print("连接失败:", e)
逐行解析:
| 行 | 代码 | 作用 |
|---|---|---|
| 1 | # test01.py |
文件注释(脚本名) |
| 3 | import psycopg2 |
导入 PostgreSQL 驱动 |
| 5 | try: |
开始异常捕获块。网络连接可能失败,必须用 try-except |
| 6 | conn = psycopg2.connect(...) |
建立到 PostgreSQL 的 TCP 连接 |
| 7 | host="192.168.1.128" |
服务器 IP。⚠️ 需替换为你的实际 IP |
| 8 | port="5432" |
端口,与 compose 中 ports 映射一致 |
| 9 | database="postgres" |
连接的数据库名 |
| 10 | user="postgres" |
用户名 |
| 11 | password="postgres" |
密码 |
| 13 | print("连接成功!") |
连接成功提示 |
| 14 | cursor = conn.cursor() |
创建游标对象,用于执行 SQL |
| 15 | cursor.execute("SELECT version();") |
执行 SQL 查询,获取 PostgreSQL 版本 |
| 16 | print(... cursor.fetchone()) |
fetchone() 获取第一行结果并打印 |
| 17 | cursor.close() |
关闭游标,释放资源 |
| 18 | conn.close() |
关闭连接,释放资源 |
| 19-20 | except Exception as e: |
捕获所有异常 |
| 20 | print("连接失败:", e) |
打印错误信息 |
连接验证流程:
PostgreSQL 服务器 5432 Windows 客户端 PostgreSQL 服务器 5432 Windows 客户端 #mermaid-svg-aLdXUO3BrkMqE2tu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aLdXUO3BrkMqE2tu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aLdXUO3BrkMqE2tu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aLdXUO3BrkMqE2tu .error-icon{fill:#552222;}#mermaid-svg-aLdXUO3BrkMqE2tu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aLdXUO3BrkMqE2tu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aLdXUO3BrkMqE2tu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aLdXUO3BrkMqE2tu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aLdXUO3BrkMqE2tu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aLdXUO3BrkMqE2tu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aLdXUO3BrkMqE2tu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aLdXUO3BrkMqE2tu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aLdXUO3BrkMqE2tu .marker.cross{stroke:#333333;}#mermaid-svg-aLdXUO3BrkMqE2tu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aLdXUO3BrkMqE2tu p{margin:0;}#mermaid-svg-aLdXUO3BrkMqE2tu .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-aLdXUO3BrkMqE2tu text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-aLdXUO3BrkMqE2tu .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-aLdXUO3BrkMqE2tu .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-aLdXUO3BrkMqE2tu .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-aLdXUO3BrkMqE2tu .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-aLdXUO3BrkMqE2tu #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-aLdXUO3BrkMqE2tu .sequenceNumber{fill:white;}#mermaid-svg-aLdXUO3BrkMqE2tu #sequencenumber{fill:#333;}#mermaid-svg-aLdXUO3BrkMqE2tu #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-aLdXUO3BrkMqE2tu .messageText{fill:#333;stroke:none;}#mermaid-svg-aLdXUO3BrkMqE2tu .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-aLdXUO3BrkMqE2tu .labelText,#mermaid-svg-aLdXUO3BrkMqE2tu .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-aLdXUO3BrkMqE2tu .loopText,#mermaid-svg-aLdXUO3BrkMqE2tu .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-aLdXUO3BrkMqE2tu .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-aLdXUO3BrkMqE2tu .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-aLdXUO3BrkMqE2tu .noteText,#mermaid-svg-aLdXUO3BrkMqE2tu .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-aLdXUO3BrkMqE2tu .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-aLdXUO3BrkMqE2tu .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-aLdXUO3BrkMqE2tu .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-aLdXUO3BrkMqE2tu .actorPopupMenu{position:absolute;}#mermaid-svg-aLdXUO3BrkMqE2tu .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-aLdXUO3BrkMqE2tu .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-aLdXUO3BrkMqE2tu .actor-man circle,#mermaid-svg-aLdXUO3BrkMqE2tu line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-aLdXUO3BrkMqE2tu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} TCP SYN (192.168.1.128:5432) TCP SYN-ACK TCP ACK (连接建立) 启动协议 (user=postgres, database=postgres) 检查 pg_hba.conf AuthenticationRequest (md5) 密码 (postgres) AuthenticationOk SELECT version() "PostgreSQL 15.4 ..." 关闭连接
运行脚本
bash
python test01.py
预期输出:
连接成功!
PostgreSQL 版本: ('PostgreSQL 15.4 ...',)
⚠️ 注意 :请将代码中的
192.168.1.128替换为您自己的服务器 IP 地址。
七、第七章「备份与恢复」逐条解析
7.1 手动备份
备份单个数据库
bash
sudo docker exec postgres_db pg_dump -U postgres postgres > backup_$(date +%F).sql
逐段解析:
| 片段 | 作用 |
|---|---|
docker exec postgres_db |
在容器内执行命令(注意没有 -it,因为这是非交互式管道操作) |
pg_dump |
PostgreSQL 逻辑备份工具,将数据库导出为 SQL 文本 |
-U postgres |
以 postgres 用户执行 |
postgres(最后的参数) |
要备份的数据库名 |
> backup_$(date +%F).sql |
将输出重定向到文件。$(date +%F) 生成当前日期如 2026-06-26 |
为什么不加 -it: -i 和 -t 是为交互式终端设计的。这里用 > 重定向输出到文件,不需要终端,去掉 -it 更干净。
pg_dump 的原理: 逻辑备份------遍历数据库的所有表、索引、函数等对象,生成等价的 CREATE TABLE、INSERT 等 SQL 语句。恢复时就是重新执行这些 SQL。
备份所有数据库与角色
bash
sudo docker exec postgres_db pg_dumpall -U postgres > full_backup_$(date +%F).sql
| 差异 | 说明 |
|---|---|
pg_dumpall |
备份所有数据库 + 角色(用户/组)+ 表空间定义 |
pg_dump |
只备份单个数据库 |
为什么推荐 pg_dumpall: pg_dump 不备份角色信息(用户和密码)。如果你有多个数据库和自定义用户,用 pg_dumpall 才能完整恢复。
备份并压缩
bash
sudo docker exec postgres_db pg_dumpall -U postgres | gzip > full_backup_$(date +%F).sql.gz
| 片段 | 作用 | |
|---|---|---|
| ` | ` | 管道符,把前一个命令的输出作为后一个命令的输入 |
gzip |
GNU Zip 压缩工具 | |
> .sql.gz |
压缩后写入 .gz 文件 |
原理: pg_dumpall 输出的 SQL 文本有大量重复模式(如 INSERT INTO),gzip 压缩率通常可达 70%-90%。100MB 的 SQL 文件压缩后约 10-30MB。
7.2 定时备份
cron
30 2 * * * docker exec postgres_db pg_dumpall -U postgres | gzip > /opt/postgresql/backup/full_$(date +%F).sql.gz && find /opt/postgresql/backup -name "full_*.sql.gz" -mtime +14 -delete
cron 表达式解析:
30 2 * * *
│ │ │ │ │
│ │ │ │ └─ 星期 (0-7, 0和7都是周日)
│ │ │ └──── 月份 (1-12)
│ │ └─────── 日期 (1-31)
│ └────────── 小时 (0-23)
└────────────── 分钟 (0-59)
→ 每天 02:30 执行
命令链解析:
| 片段 | 作用 |
|---|---|
docker exec postgres_db pg_dumpall -U postgres |
全量备份 |
| ` | gzip > .../full_$(date +%F).sql.gz` |
&& |
前一条成功才执行下一条 |
find ... -name "full_*.sql.gz" |
查找备份目录中所有备份文件 |
-mtime +14 |
修改时间超过 14 天的 |
-delete |
删除匹配的文件 |
整体效果: 每天凌晨 2:30 全量备份,保留最近 14 天,超过 14 天的自动删除。
7.3 恢复
恢复全量备份
bash
gunzip -c full_backup_2026-06-26.sql.gz | sudo docker exec -i postgres_db psql -U postgres
| 片段 | 作用 | |
|---|---|---|
gunzip -c |
解压 gzip 文件,-c 输出到标准输出(不修改原文件) |
|
| ` | ` | 管道,把解压后的 SQL 送给 psql 执行 |
docker exec -i |
-i 保持标准输入开启(接收管道输入),不加 -t(无终端) |
|
psql -U postgres |
执行 SQL |
原理: 解压备份文件 → 管道传输 → psql 逐行执行 SQL 语句恢复数据。
恢复单库
bash
cat backup_2026-06-26.sql | sudo docker exec -i postgres_db psql -U postgres -d postgres
| 差异 | 说明 |
|---|---|
cat |
直接读取未压缩的 SQL 文件 |
-d postgres |
指定恢复到哪个数据库 |
八、第八章「监控与告警」逐条解析
8.1 容器层面
实时资源占用
bash
sudo docker stats postgres_db
| 片段 | 作用 |
|---|---|
stats |
实时显示容器的 CPU、内存、网络、磁盘 IO 使用情况 |
postgres_db |
容器名 |
输出类似 top 命令,实时刷新。
健康状态
bash
sudo docker inspect --format='{{.State.Health.Status}}' postgres_db
| 片段 | 作用 |
|---|---|
inspect |
查看容器的详细元数据(JSON 格式) |
--format='{``{.State.Health.Status}}' |
Go 模板语法,只提取健康状态字段 |
| 输出 | healthy / unhealthy / starting / 空(未配置 healthcheck) |
8.2 数据库层面
sql
-- 当前连接数
SELECT count(*) FROM pg_stat_activity;
| 函数/视图 | 作用 |
|---|---|
pg_stat_activity |
PostgreSQL 内置视图,记录每个连接的会话信息 |
count(*) |
统计总连接数 |
sql
-- 长事务(运行超过 5 分钟)
SELECT pid, now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active' AND now() - query_start > interval '5 minutes';
| 片段 | 作用 |
|---|---|
pid |
进程 ID |
now() - query_start AS duration |
当前时间减去查询开始时间 = 已运行时长 |
state = 'active' |
正在执行中的查询(非空闲) |
> interval '5 minutes' |
运行超过 5 分钟 |
sql
-- 数据库大小
SELECT pg_size_pretty(pg_database_size('postgres'));
| 函数 | 作用 |
|---|---|
pg_database_size('postgres') |
返回指定数据库的字节数 |
pg_size_pretty() |
把字节数转为人类可读格式(如 45 MB) |
8.3 专业监控
postgres_exporter + Prometheus + Grafana 的监控架构:
#mermaid-svg-FiAyiMxqRsByOQCt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FiAyiMxqRsByOQCt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FiAyiMxqRsByOQCt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FiAyiMxqRsByOQCt .error-icon{fill:#552222;}#mermaid-svg-FiAyiMxqRsByOQCt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FiAyiMxqRsByOQCt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FiAyiMxqRsByOQCt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FiAyiMxqRsByOQCt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FiAyiMxqRsByOQCt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FiAyiMxqRsByOQCt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FiAyiMxqRsByOQCt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FiAyiMxqRsByOQCt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FiAyiMxqRsByOQCt .marker.cross{stroke:#333333;}#mermaid-svg-FiAyiMxqRsByOQCt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FiAyiMxqRsByOQCt p{margin:0;}#mermaid-svg-FiAyiMxqRsByOQCt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FiAyiMxqRsByOQCt .cluster-label text{fill:#333;}#mermaid-svg-FiAyiMxqRsByOQCt .cluster-label span{color:#333;}#mermaid-svg-FiAyiMxqRsByOQCt .cluster-label span p{background-color:transparent;}#mermaid-svg-FiAyiMxqRsByOQCt .label text,#mermaid-svg-FiAyiMxqRsByOQCt span{fill:#333;color:#333;}#mermaid-svg-FiAyiMxqRsByOQCt .node rect,#mermaid-svg-FiAyiMxqRsByOQCt .node circle,#mermaid-svg-FiAyiMxqRsByOQCt .node ellipse,#mermaid-svg-FiAyiMxqRsByOQCt .node polygon,#mermaid-svg-FiAyiMxqRsByOQCt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FiAyiMxqRsByOQCt .rough-node .label text,#mermaid-svg-FiAyiMxqRsByOQCt .node .label text,#mermaid-svg-FiAyiMxqRsByOQCt .image-shape .label,#mermaid-svg-FiAyiMxqRsByOQCt .icon-shape .label{text-anchor:middle;}#mermaid-svg-FiAyiMxqRsByOQCt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FiAyiMxqRsByOQCt .rough-node .label,#mermaid-svg-FiAyiMxqRsByOQCt .node .label,#mermaid-svg-FiAyiMxqRsByOQCt .image-shape .label,#mermaid-svg-FiAyiMxqRsByOQCt .icon-shape .label{text-align:center;}#mermaid-svg-FiAyiMxqRsByOQCt .node.clickable{cursor:pointer;}#mermaid-svg-FiAyiMxqRsByOQCt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FiAyiMxqRsByOQCt .arrowheadPath{fill:#333333;}#mermaid-svg-FiAyiMxqRsByOQCt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FiAyiMxqRsByOQCt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FiAyiMxqRsByOQCt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FiAyiMxqRsByOQCt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FiAyiMxqRsByOQCt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FiAyiMxqRsByOQCt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FiAyiMxqRsByOQCt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FiAyiMxqRsByOQCt .cluster text{fill:#333;}#mermaid-svg-FiAyiMxqRsByOQCt .cluster span{color:#333;}#mermaid-svg-FiAyiMxqRsByOQCt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FiAyiMxqRsByOQCt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FiAyiMxqRsByOQCt rect.text{fill:none;stroke-width:0;}#mermaid-svg-FiAyiMxqRsByOQCt .icon-shape,#mermaid-svg-FiAyiMxqRsByOQCt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FiAyiMxqRsByOQCt .icon-shape p,#mermaid-svg-FiAyiMxqRsByOQCt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FiAyiMxqRsByOQCt .icon-shape .label rect,#mermaid-svg-FiAyiMxqRsByOQCt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FiAyiMxqRsByOQCt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FiAyiMxqRsByOQCt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FiAyiMxqRsByOQCt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 指标暴露
/metrics 端点
查询数据
阈值触发
PostgreSQL
postgres_exporter
Prometheus
定时抓取&存储
Grafana
可视化面板
告警
邮件/钉钉/飞书
九、第九章「故障排查」逐条解析
9.1 故障对照表解析
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 容器启动后立即退出 | 数据目录权限不对 / 端口被占 | 见下方详解 |
docker pull 超时 |
镜像源失效 | 见下方详解 |
| 远程连接被拒绝 | pg_hba.conf 未放行 / 未监听 |
见下方详解 |
password authentication failed |
密码错误 / 用户名错误 | 见下方详解 |
| 数据目录初始化失败 | 目录非空且不是 PG 数据目录 | 见下方详解 |
| 容器健康检查 unhealthy | 数据库未就绪或资源不足 | 见下方详解 |
| 磁盘写满 | 日志/备份未轮转 | 见下方详解 |
容器启动后立即退出
排查命令:
bash
sudo docker compose logs postgres
查看日志中的错误信息。常见原因:
- 数据目录权限不对 :日志报
Permission denied→ 重新执行sudo chown -R 999:999 /opt/postgresql/data - 端口被占 :日志报
address already in use→ 执行ss -tlnp | grep 5432查看占用进程,停止它或改用其他端口
docker pull 超时
镜像源失效。修改 /etc/docker/daemon.json 更换可用镜像源,sudo systemctl restart docker 后重试。
远程连接被拒绝
进入容器检查配置:
bash
sudo docker exec -it postgres_db cat /var/lib/postgresql/data/pg_hba.conf
确认:
pg_hba.conf中有放行远程 IP 的规则postgresql.conf中listen_addresses = '*'- 修改后执行了
sudo docker compose restart
password authentication failed
用本地连接验证密码:
bash
sudo docker exec -it postgres_db psql -U postgres
如果本地能连、远程不能连,说明是 pg_hba.conf 的认证方式问题。如果本地也不能连,说明密码确实错了。
修改密码:
sql
ALTER USER postgres PASSWORD '新密码';
9.2 常用排查命令汇总
bash
# 看容器日志(最近 200 行)
sudo docker compose logs --tail 200 postgres
| 片段 | 作用 |
|---|---|
--tail 200 |
只显示最后 200 行(避免输出过多) |
postgres |
服务名 |
bash
# 进入容器排查
sudo docker exec -it postgres_db bash
进入容器的 Linux shell,可执行 ls、cat、ps 等命令。
bash
# 查看端口监听
ss -tlnp | grep 5432
| 片段 | 作用 | |
|---|---|---|
ss |
socket statistics,替代已弃用的 netstat |
|
-t |
TCP 连接 | |
-l |
listening,仅显示监听状态 | |
-n |
numeric,不解析端口名(显示 5432 而非 postgres) | |
-p |
process,显示占用端口的进程 | |
| ` | grep 5432` | 过滤出 5432 端口 |
bash
# 查看磁盘空间
df -h
| 片段 | 作用 |
|---|---|
df |
disk free,显示文件系统磁盘使用情况 |
-h |
human-readable,用 KB/MB/GB 显示 |
十、第十章「升级与回滚」逐条解析
10.1 升级前必做
bash
# 1) 完整备份(务必!)
sudo docker exec postgres_db pg_dumpall -U postgres > pre_upgrade_$(date +%F).sql
为什么必须备份: 升级有风险------新版本可能不兼容旧数据格式,升级失败后需要回滚到备份。
bash
# 2) 停止并删除旧容器(数据目录保留)
cd /opt/postgresql/compose
sudo docker compose down
为什么先 down: 避免新旧容器同时挂载同一数据目录导致数据损坏。
10.2 升级
修改 docker-compose.yml 中的 image: postgres:15.4 为新版本,然后:
bash
sudo docker compose up -d
升级类型与风险:
#mermaid-svg-OMOWAecUFoJJ3Ovn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OMOWAecUFoJJ3Ovn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OMOWAecUFoJJ3Ovn .error-icon{fill:#552222;}#mermaid-svg-OMOWAecUFoJJ3Ovn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OMOWAecUFoJJ3Ovn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OMOWAecUFoJJ3Ovn .marker.cross{stroke:#333333;}#mermaid-svg-OMOWAecUFoJJ3Ovn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OMOWAecUFoJJ3Ovn p{margin:0;}#mermaid-svg-OMOWAecUFoJJ3Ovn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OMOWAecUFoJJ3Ovn .cluster-label text{fill:#333;}#mermaid-svg-OMOWAecUFoJJ3Ovn .cluster-label span{color:#333;}#mermaid-svg-OMOWAecUFoJJ3Ovn .cluster-label span p{background-color:transparent;}#mermaid-svg-OMOWAecUFoJJ3Ovn .label text,#mermaid-svg-OMOWAecUFoJJ3Ovn span{fill:#333;color:#333;}#mermaid-svg-OMOWAecUFoJJ3Ovn .node rect,#mermaid-svg-OMOWAecUFoJJ3Ovn .node circle,#mermaid-svg-OMOWAecUFoJJ3Ovn .node ellipse,#mermaid-svg-OMOWAecUFoJJ3Ovn .node polygon,#mermaid-svg-OMOWAecUFoJJ3Ovn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OMOWAecUFoJJ3Ovn .rough-node .label text,#mermaid-svg-OMOWAecUFoJJ3Ovn .node .label text,#mermaid-svg-OMOWAecUFoJJ3Ovn .image-shape .label,#mermaid-svg-OMOWAecUFoJJ3Ovn .icon-shape .label{text-anchor:middle;}#mermaid-svg-OMOWAecUFoJJ3Ovn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OMOWAecUFoJJ3Ovn .rough-node .label,#mermaid-svg-OMOWAecUFoJJ3Ovn .node .label,#mermaid-svg-OMOWAecUFoJJ3Ovn .image-shape .label,#mermaid-svg-OMOWAecUFoJJ3Ovn .icon-shape .label{text-align:center;}#mermaid-svg-OMOWAecUFoJJ3Ovn .node.clickable{cursor:pointer;}#mermaid-svg-OMOWAecUFoJJ3Ovn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OMOWAecUFoJJ3Ovn .arrowheadPath{fill:#333333;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OMOWAecUFoJJ3Ovn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OMOWAecUFoJJ3Ovn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OMOWAecUFoJJ3Ovn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OMOWAecUFoJJ3Ovn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OMOWAecUFoJJ3Ovn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OMOWAecUFoJJ3Ovn .cluster text{fill:#333;}#mermaid-svg-OMOWAecUFoJJ3Ovn .cluster span{color:#333;}#mermaid-svg-OMOWAecUFoJJ3Ovn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OMOWAecUFoJJ3Ovn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OMOWAecUFoJJ3Ovn rect.text{fill:none;stroke-width:0;}#mermaid-svg-OMOWAecUFoJJ3Ovn .icon-shape,#mermaid-svg-OMOWAecUFoJJ3Ovn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OMOWAecUFoJJ3Ovn .icon-shape p,#mermaid-svg-OMOWAecUFoJJ3Ovn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OMOWAecUFoJJ3Ovn .icon-shape .label rect,#mermaid-svg-OMOWAecUFoJJ3Ovn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OMOWAecUFoJJ3Ovn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OMOWAecUFoJJ3Ovn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OMOWAecUFoJJ3Ovn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 升级
小版本升级
如 15.4 → 15.5
大版本升级
如 15.4 → 16.2
✅ 可直接升级
数据目录兼容
⚠️ 需要 pg_upgrade
迁移数据格式
直接挂载旧目录
容器启动失败
⚠️ 跨大版本升级 (如 15 → 16)不能直接挂载旧数据目录 ,必须使用
pg_upgrade迁移数据,否则容器会启动失败。小版本升级(15.4 → 15.5)可直接升级。
10.3 回滚
bash
cd /opt/postgresql/compose
# 1) 将 docker-compose.yml 的 image 改回旧版本(如 postgres:15.4)
# 2) 重新启动(数据目录未被新版本改写时可直接回退)
sudo docker compose up -d
# 若数据目录已被改写,则从 pre_upgrade_*.sql 恢复
gunzip -c pre_upgrade_2026-06-26.sql | sudo docker exec -i postgres_db psql -U postgres
回滚的两种情况:
| 情况 | 回滚方式 |
|---|---|
| 新版本未改写数据目录 | 改回旧 image → docker compose up -d 直接回退 |
| 新版本已改写数据目录 | 无法直接回退 → 从 pre_upgrade_*.sql 备份恢复 |
十一、附录解析
附录 A:安全加固清单
| 加固项 | 原理 |
|---|---|
| 修改默认密码 | postgres/postgres 是公开凭证,任何人知道 IP 就能登录 |
| 收紧 pg_hba.conf | 0.0.0.0/0 允许全球访问,应限制为具体网段 |
scram-sha-256 替代 md5 |
SCRAM 协议更安全,防止重放攻击 |
| 启用 SSL/TLS | 防止中间人截获密码和数据 |
| 网络隔离 | 防火墙/安全组限制 5432 端口来源 IP |
| 最小权限账号 | 应用不用 superuser,只授增删改查权限 |
| 凭证管理 | 密码通过环境变量/密钥管理服务注入 |
| 定期备份 + 演练恢复 | 有备份不等于能恢复,必须实际演练 |
| 日志审计 | 记录连接日志,发现异常登录 |
| 版本补丁 | 关注安全公告,及时升级小版本 |
附录 B:配置参数速查表
| 类别 | 参数 | 值 | 说明 |
|---|---|---|---|
| 连接 | 端口 | 5432 | PostgreSQL 默认端口 |
| 连接 | 用户名 | postgres | 教程默认,生产请修改 |
| 连接 | 密码 | postgres | 教程默认,生产必须修改 |
| 连接 | 默认库 | postgres | 镜像自动创建 |
| 持久化 | 数据目录(宿主机) | /opt/postgresql/data | 绑定挂载路径 |
| 持久化 | 数据目录(容器内) | /var/lib/postgresql/data | PG 标准 PGDATA 路径 |
| 持久化 | 数据卷属主 | 999:999 | 容器内 postgres 用户的 UID:GID |
| 部署 | compose 目录 | /opt/postgresql/compose | docker-compose.yml 所在目录 |
| 镜像 | 版本 | postgres:15.4 | 官方镜像 |
| 容器 | 名称 | postgres_db | 统一命名 |
| 重启 | 策略 | unless-stopped | 宕机自动拉起,手动停止不拉起 |
| 时区 | TZ | Asia/Shanghai | compose 中设置 |
十二、全流程串联总览
#mermaid-svg-a5xNqeNKKBkpAldy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a5xNqeNKKBkpAldy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a5xNqeNKKBkpAldy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a5xNqeNKKBkpAldy .error-icon{fill:#552222;}#mermaid-svg-a5xNqeNKKBkpAldy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a5xNqeNKKBkpAldy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a5xNqeNKKBkpAldy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a5xNqeNKKBkpAldy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a5xNqeNKKBkpAldy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a5xNqeNKKBkpAldy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a5xNqeNKKBkpAldy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a5xNqeNKKBkpAldy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a5xNqeNKKBkpAldy .marker.cross{stroke:#333333;}#mermaid-svg-a5xNqeNKKBkpAldy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a5xNqeNKKBkpAldy p{margin:0;}#mermaid-svg-a5xNqeNKKBkpAldy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-a5xNqeNKKBkpAldy .cluster-label text{fill:#333;}#mermaid-svg-a5xNqeNKKBkpAldy .cluster-label span{color:#333;}#mermaid-svg-a5xNqeNKKBkpAldy .cluster-label span p{background-color:transparent;}#mermaid-svg-a5xNqeNKKBkpAldy .label text,#mermaid-svg-a5xNqeNKKBkpAldy span{fill:#333;color:#333;}#mermaid-svg-a5xNqeNKKBkpAldy .node rect,#mermaid-svg-a5xNqeNKKBkpAldy .node circle,#mermaid-svg-a5xNqeNKKBkpAldy .node ellipse,#mermaid-svg-a5xNqeNKKBkpAldy .node polygon,#mermaid-svg-a5xNqeNKKBkpAldy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a5xNqeNKKBkpAldy .rough-node .label text,#mermaid-svg-a5xNqeNKKBkpAldy .node .label text,#mermaid-svg-a5xNqeNKKBkpAldy .image-shape .label,#mermaid-svg-a5xNqeNKKBkpAldy .icon-shape .label{text-anchor:middle;}#mermaid-svg-a5xNqeNKKBkpAldy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-a5xNqeNKKBkpAldy .rough-node .label,#mermaid-svg-a5xNqeNKKBkpAldy .node .label,#mermaid-svg-a5xNqeNKKBkpAldy .image-shape .label,#mermaid-svg-a5xNqeNKKBkpAldy .icon-shape .label{text-align:center;}#mermaid-svg-a5xNqeNKKBkpAldy .node.clickable{cursor:pointer;}#mermaid-svg-a5xNqeNKKBkpAldy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-a5xNqeNKKBkpAldy .arrowheadPath{fill:#333333;}#mermaid-svg-a5xNqeNKKBkpAldy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-a5xNqeNKKBkpAldy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-a5xNqeNKKBkpAldy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a5xNqeNKKBkpAldy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-a5xNqeNKKBkpAldy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a5xNqeNKKBkpAldy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-a5xNqeNKKBkpAldy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-a5xNqeNKKBkpAldy .cluster text{fill:#333;}#mermaid-svg-a5xNqeNKKBkpAldy .cluster span{color:#333;}#mermaid-svg-a5xNqeNKKBkpAldy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-a5xNqeNKKBkpAldy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-a5xNqeNKKBkpAldy rect.text{fill:none;stroke-width:0;}#mermaid-svg-a5xNqeNKKBkpAldy .icon-shape,#mermaid-svg-a5xNqeNKKBkpAldy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a5xNqeNKKBkpAldy .icon-shape p,#mermaid-svg-a5xNqeNKKBkpAldy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-a5xNqeNKKBkpAldy .icon-shape .label rect,#mermaid-svg-a5xNqeNKKBkpAldy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a5xNqeNKKBkpAldy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-a5xNqeNKKBkpAldy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-a5xNqeNKKBkpAldy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 运维阶段
验证阶段
部署阶段
准备阶段
一、配置需求
确认系统/资源/参数
二、流程简述
了解整体步骤
三、验证环节
检查 Docker + 配置镜像源
4.1 拉取镜像
docker pull postgres:15.4
4.2 准备部署目录
mkdir /opt/postgresql/compose
4.3 创建数据目录+授权
mkdir + chown 999:999
4.4 编写 docker-compose.yml
声明式配置
4.5 启动服务
docker compose up -d
4.6 容器内验证
docker exec psql
4.7 配置远程访问
改 listen_addresses + pg_hba + restart
五、常用管理命令
ps/logs/stop/start/exec
6.1 本地验证
容器内 psql
6.2 Python 远程连接
psycopg2 test01.py
七、备份与恢复
pg_dumpall + crontab + 恢复
八、监控与告警
docker stats + pg_stat_activity
九、故障排查
日志/端口/权限/磁盘
十、升级与回滚
备份→改版本→回滚
各步骤核心作用与不做会怎样
| 步骤 | 解决的核心问题 | 不做会怎样 |
|---|---|---|
| 4.1 拉取镜像 | 获取 PostgreSQL 运行时 | docker compose up 时才拉取,首次启动变慢 |
| 4.2 准备部署目录 | 给 compose 文件一个标准位置 | 每次执行命令都得带 -f 路径 |
| 4.3 数据目录+授权 | 持久化 + 解决 UID 不一致 | 容器一删数据全丢 / 容器直接起不来 |
| 4.4 写 compose.yml | 声明式配置,可复现可版本化 | 散落命令,难维护、难复现 |
| 4.5 启动服务 | 把描述变成运行的容器 | 没有运行实例 |
| 4.6 容器内验证 | 验证数据库自身可用 | 不知道是 DB 坏还是网络坏 |
| 4.7 远程访问配置 | 解决 PG 默认只允许本机连 | 远程客户端全部连不上 |
| 7. 备份与恢复 | 数据安全兜底 | 数据丢失无法恢复 |
| 8. 监控与告警 | 及时发现故障 | 问题发生后才知道 |
| 9. 故障排查 | 快速定位问题 | 出问题后无从下手 |
| 10. 升级与回滚 | 安全升级 + 出问题能退回 | 升级失败后数据丢失 |
📝 结语 :工程化部署的核心不是「能跑起来」,而是「可维护、可复现、可恢复、可观测」。
docker-compose.yml把部署配置文件化,数据目录持久化保证数据安全,healthcheck 提供可观测性,备份恢复提供兜底能力------这些共同构成了一个生产级的 PostgreSQL 部署方案。