第 12 课:为真实项目编写 Dockerfile 和 Compose
第四阶段 · 真实 student-management-api · 面向 Docker 初学者
本课完成标准
你能够:
- 读懂真实项目 Dockerfile;
- 让代码、static 和 migrations 进入镜像;
- 使用生产 .env 注入配置;
- 构建并启动 API 容器;
- 通过登录页面和静态 CSS 验证容器;
- 理解为什么不在 Compose 中创建 MySQL。
1. 先看最终关系
Dockerfile(制作 FastAPI 镜像)
↓ build
镜像(代码 + 依赖 + static + migrations)
↓ run
容器(读取 .env,监听 8000)
本课只构建 api 服务。MySQL 在 ECS 主机上运行,所以 Compose 不写 mysql 服务,也不创建数据库卷。
2. 编写 Dockerfile
在项目根目录创建 Dockerfile:
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["fastapi", "run", "main.py", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]
| 指令 | 本项目中的含义 |
|---|---|
| FROM | 使用 Python 3.12 slim 作为基础环境 |
| WORKDIR /app | 让 main.py 的相对路径可以找到 static/ |
| COPY requirements.txt | 先复制依赖清单,便于 Docker 缓存 |
| RUN pip install | 在镜像内部安装依赖,不使用本机 venv |
| COPY . . | 复制源码、static、migrations 等内容 |
| CMD | 容器启动时运行真实 FastAPI |
为什么先复制 requirements?代码经常修改,依赖通常不常修改,这样可以复用依赖安装缓存。
3. workers 先从 1 开始
本课使用一个 worker,先保证能启动和排错。生产环境是否使用更多 worker,要结合 ECS 内存、请求量和数据库连接池测试。
fastapi run main.py --host 0.0.0.0 --port 8000 --workers 2
每个 worker 都是一个 Python 进程,会增加内存和数据库连接。不要把 workers 数量当成越大越好。
4. 编写 compose.yaml
在同一个项目根目录创建 compose.yaml:
services:
api:
build:
context: .
dockerfile: Dockerfile
env_file:
- .env
ports:
- "127.0.0.1:8000:8000"
restart: unless-stopped
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/login', timeout=3)"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
当前项目没有专门的 /health 路由,但 /login 会返回登录页面,所以健康检查访问它。它只检查 HTTP 服务和静态文件是否能响应,不代表数据库迁移已经完成。
| 配置 | 作用 |
|---|---|
| env_file | 把 ECS 上的 .env 注入容器,镜像本身不保存密码 |
| 127.0.0.1:8000:8000 | 只允许 ECS 本机访问,后续由 Nginx 对外提供服务 |
| restart: unless-stopped | 异常退出自动重启,手动停止后保持停止 |
| healthcheck | 让 Docker 报告服务是否能响应登录页面 |
5. 检查构建上下文
cd /www/wwwroot/student-management-api
docker compose config
docker build --check .
如果 Docker 版本不支持 docker build --check,可以跳过它,继续执行 docker compose config。确认 .dockerignore 没有排除 static、migrations 和 requirements.txt。
6. 构建并启动真实项目
docker compose build
docker compose up -d
docker compose ps
docker compose logs --tail=100 api
首次构建会下载 Python 基础镜像和依赖,可能需要几分钟。状态理想情况下显示 Up,健康状态可能显示 healthy。
在 ECS 本机测试:
curl -i http://127.0.0.1:8000/login
curl -I http://127.0.0.1:8000/static/css/common.css
服务器终端使用 curl 即可。Windows 本地不负责测试 ECS 的 127.0.0.1;后续通过 SSH 或 Nginx 域名访问。
7. 不要在启动命令中自动迁移
本课的容器启动只负责启动 FastAPI,不把 alembic upgrade head 拼到 CMD 中。数据库迁移需要单独检查和执行,下一课会先解决容器到 MySQL 的连接,第 14 课再正式操作 Alembic。
如果每次容器重启都自动迁移,迁移失败可能让应用无法启动,也很难判断是应用问题还是数据库问题。
8. 常见错误排查
| 现象 | 先检查 | 常见原因 |
|---|---|---|
| ModuleNotFoundError | requirements.txt、构建日志 | 依赖未列出或未重新构建 |
| FileNotFoundError: static | WORKDIR、COPY . .、.dockerignore | 工作目录错误或静态文件未进入镜像 |
| 容器 Exited | docker compose logs api | 配置、导入或启动命令错误 |
| 健康检查 unhealthy | /login 和日志 | 应用还在启动、静态文件缺失或端口不对 |
| 数据库连接失败 | 容器内环境和 MySQL 地址 | 第 13 课专门排查,不要开放 3306 公网 |
9. 一分钟自测
问题 1:为什么 compose.yaml 使用 127.0.0.1:8000:8000?
- A. 让 FastAPI 只被 ECS 本机和 Nginx 访问
- B. 让 Docker 自动安装 MySQL
- C. 让容器不需要端口
问题 2:为什么 healthcheck 访问 /login?
- A. 当前项目已有登录页面,能验证 FastAPI 和 static 是否响应
- B. /login 是 MySQL 管理接口
- C. /login 会自动执行数据库迁移
答案:1A,2A。
本课通关证据
docker compose config
docker compose build
docker compose up -d
docker compose ps
docker compose logs --tail=100 api
curl -i http://127.0.0.1:8000/login
curl -I http://127.0.0.1:8000/static/css/common.css
完成标准:真实镜像构建成功,api 容器运行中,登录页面和静态 CSS 能响应,8000 只绑定 ECS 本机,并且 Compose 没有创建 MySQL 容器。
如果遇到错误,请贴完整命令和输出,但隐藏密码、Token 与私钥。
想系统学习这门课程?
你已阅读完本文。需要更完整的知识体系,可以继续了解「Vibe Coding 实战课程」。
查看课程详情