Docker Compose 管理 PostgreSQL:容器可以重建,数据不能丢
前面的用户数据保存在 Python 列表里,只要 FastAPI 重启,已经创建的用户就会消失
要让数据真正持久化,需要引入数据库。这一阶段先不写 SQLAlchemy,也不急着改业务代码,只解决一个更基础的问题:怎样稳定地启动 PostgreSQL,以及删除并重建容器后,数据为什么还能保留
一、先分清镜像、容器与 Compose
这三个概念经常一起出现,但职责不同:
text
Image
→ 创建容器的只读模板
Container
→ 镜像的一次运行实例
Docker Compose
→ 根据 compose.yaml 统一描述和管理一组服务
postgres:17.10 是镜像,运行它后得到 PostgreSQL 容器;docker compose 则负责按配置创建、启动、停止和删除这个容器
可以把镜像理解为系统安装模板,容器是用模板创建出来的运行环境
二、用 compose.yaml 描述 PostgreSQL
项目根目录创建 compose.yaml:
yaml
services:
postgres:
image: postgres:17.10
environment:
POSTGRES_USER: ai_workspace
POSTGRES_PASSWORD: ai_workspace_dev
POSTGRES_DB: ai_workspace
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
这里声明了一个名为 postgres 的服务,使用 PostgreSQL 17.10 镜像
三个环境变量只在数据库第一次初始化时使用:
text
POSTGRES_USER
→ 创建数据库用户
POSTGRES_PASSWORD
→ 设置该用户的密码
POSTGRES_DB
→ 创建默认数据库
这里的密码只适合本地学习环境,不能照搬到生产环境
三、端口映射的方向
配置中的端口格式是:
text
宿主机端口:容器端口
因此:
yaml
ports:
- "5432:5432"
表示访问电脑上的 5432,Docker 会把连接转发到 PostgreSQL 容器内部的 5432
如果电脑上的 5432 已经被占用,可以改成:
yaml
ports:
- "15432:5432"
这时,在宿主机运行的 FastAPI 应连接:
text
localhost:15432
PostgreSQL 容器内部仍然监听默认的 5432
四、启动并检查服务
在项目根目录执行:
bash
cd /d/code/ai-workspace-rebuild # 进入包含 compose.yaml 的项目根目录
docker compose config # 解析并显示 Compose 最终配置
docker compose up -d # 创建并在后台启动服务
docker compose ps # 查看当前 Compose 项目的容器状态
docker compose logs postgres # 查看 postgres 服务日志
up 表示创建并启动服务,-d 是 --detach 的缩写,让容器在后台运行
config 适合在启动前检查 YAML 是否能够被 Compose 正确解析;ps 用来确认容器是否运行;logs postgres 用来判断 PostgreSQL 是否已经完成初始化并开始接受连接
五、数据究竟保存在哪里
最关键的是这一行:
yaml
volumes:
- postgres_data:/var/lib/postgresql/data
格式是:
text
命名卷:容器内目录
PostgreSQL 把数据库文件写入 /var/lib/postgresql/data。这个目录挂载了命名卷 postgres_data,所以文件实际由 Docker 的 volume 保存
命名卷的生命周期独立于容器:容器可以删除,卷仍然保留
六、进入 PostgreSQL 做持久化实验
进入正在运行的数据库容器:
bash
docker compose exec postgres \
psql -U ai_workspace -d ai_workspace
参数含义:
text
exec postgres
→ 在 postgres 服务容器中执行命令
psql
→ 打开 PostgreSQL 命令行客户端
-U ai_workspace
→ 使用 ai_workspace 用户
-d ai_workspace
→ 连接 ai_workspace 数据库
\
→ Bash 命令尚未结束,下一行继续输入
在 psql 中创建测试表:
sql
CREATE TABLE persistence_test (
message text NOT NULL
);
写入一条容易辨认的数据:
sql
INSERT INTO persistence_test (message)
VALUES ('volume keeps this row');
查询确认:
sql
SELECT * FROM persistence_test;
SQL 可以换行输入,直到分号 ; 出现后才会执行;如果未完成的 SQL 不想继续,可以按 Ctrl+C 取消;输入 \q 退出 psql
七、删除容器,再重新创建
退出 psql 后执行:
bash
docker compose down # 删除当前项目的容器和默认网络,保留命名卷
docker compose ps -a # 确认当前项目已经没有容器
docker volume ls --filter name=ai-workspace-rebuild # 查看项目相关命名卷
容器已经被删除,但类似下面的命名卷仍然存在:
text
ai-workspace-rebuild_postgres_data
重新创建容器:
bash
docker compose up -d # 使用同一份配置重新创建 PostgreSQL 容器
再次进入数据库:
bash
docker compose exec postgres \
psql -U ai_workspace -d ai_workspace
重新查询:
sql
SELECT * FROM persistence_test;
结果仍然包含:
text
volume keeps this row
这证明保留数据的不是旧容器,而是重新挂载到新容器上的同一个命名卷
八、down 与 down -v 的区别
bash
docker compose down # 删除容器和默认网络,保留命名卷
如果加上 -v:
bash
docker compose down -v # 同时删除 Compose 管理的命名卷
-v 是 --volumes 的缩写。数据库文件保存在卷里,卷被删除后,当前数据库数据也会消失
因此,日常停止和重建开发容器时,不应该随手添加 -v
九、宿主机与容器中的数据库地址不同
当 FastAPI 在宿主机运行时,连接地址使用宿主机暴露的端口:
text
localhost:5432
如果 FastAPI 以后也放进 Compose,localhost 将指向 FastAPI 容器自己,不再指向电脑,更不会指向 PostgreSQL 容器
同一个 Compose 网络中的服务可以通过服务名互相访问:
text
FastAPI 容器 → postgres:5432 → PostgreSQL 容器
这里的 postgres 来自:
yaml
services:
postgres:
因此,两种运行方式对应两种地址:
text
FastAPI 在宿主机运行
→ localhost:5432
FastAPI 在 Compose 容器中运行
→ postgres:5432
阶段结果
现在 PostgreSQL 已经能够稳定启动,删除并重建容器后数据也不会丢失
但业务接口还没有真正连接数据库:
- 用户仍然保存在 Python 内存列表中
- FastAPI 还不知道数据库连接地址
- 还没有 SQLAlchemy Engine
- 还没有 ORM 模型和数据库迁移
- 还没有用 Repository 替换内存存储
下一阶段将使用 SQLAlchemy 和 psycopg 建立 Python 到 PostgreSQL 的连接,再逐步把用户数据迁移到数据库中