玩转Docker 08 --- 实战:容器化真实后端并编排
一、本课目标
把前 7 课的拼图合体成真实工作流 :给一个 Python/FastAPI 应用写 Dockerfile、用 Compose 的 build: 从源码构建、environment: 注入配置、up -d --build 走"改代码→重新构建→上线"的日常循环。
二、项目:FastAPI + Redis 计数器
scss
浏览器 → api(自建镜像, FastAPI, 8100 对外) ⇄ redis(现成镜像, 纯内网)
GET /→ 应用信息(证明在跑、环境变量注入生效)GET /count→ 计数 +1(存在 redis 里,证明 api 连上了 redis)
四个文件:
bash
lesson8-api/
├── app/main.py # FastAPI 应用(读环境变量、连 redis)
├── requirements.txt # fastapi / uvicorn / redis
├── Dockerfile # 按"先依赖后代码"排序
└── docker-compose.yml # build: + environment: + redis(无 ports)
三、本课新知识点
| 新东西 | 说明 |
|---|---|
Compose 的 build: . |
不用现成镜像,从当前目录 Dockerfile 现场构建 |
environment: |
环境变量注入配置 ------代码 os.getenv() 读,值由 yml 给(生产标配) |
up -d --build |
先构建再启动------改代码后的日常部署命令 |
| pip 国内源 | pip install -i https://pypi.tuna.tsinghua.edu.cn/simple(国内快数倍) |
--host 0.0.0.0 |
Web 服务容器化的头号坑:uvicorn 默认只听 127.0.0.1(容器内部),容器外访问不到;必须 0.0.0.0 |
Dockerfile 核心排序(第 4 课铁律实战):
sql
COPY requirements.txt → RUN pip install → COPY app/代码
(稳定,缓存保住) (耗时,命中缓存) (常变,放最后)
四、实验全过程(含目的、命令详解、结果解读)
实验 1:首次构建并启动
【目的】 验证"build: + up"全流程:拉基础镜像 → 逐层构建(含 pip 装依赖)→ 起两个容器。
【命令】
bash
docker compose -f ~/桌面/work/Docker-lab/lesson8-api/docker-compose.yml up -d --build
【命令详解】 up -d 后台起全部服务;--build = 先按 build: 构建镜像再起容器。
【过程与结果】
ini
[+] Building 29.7s (12/12) FINISHED
=> [1/5] FROM python:3.12-slim 21.1s ← 拉基础镜像(sha256:... = 分层下载)
=> [2/5] WORKDIR /app 0.1s
=> [3/5] COPY requirements.txt . 0.0s
=> [4/5] RUN pip install ... 4.6s ← 清华源,快!
=> [5/5] COPY app/ ./app/ 0.1s
=> naming to lesson8-api-api:latest ← 构建出的镜像自动命名:项目名-服务名
[+] up 14/14
✔ Image lesson8-api-api Built
✔ Container lesson8-api-redis-1 Started ← 先 redis(depends_on)
✔ Container lesson8-api-api-1 Started
【结论】 5 个构建步骤 = Dockerfile 的 5 条指令,每条一层;镜像自动命名 lesson8-api-api;redis:alpine(4MB)与 python:3.12-slim(50MB)拉取耗时正常。
实验 2:浏览器验证 API 与 Redis 链路
【目的】 确认应用在跑、环境变量注入生效、api 真的连上了 redis。
【命令】 浏览器访问 localhost:8100/ 与 localhost:8100/count(或 curl)。
【过程与结果】
json
{"app":"demo-api","status":"running","redis_host":"redis"} ← GET /
{"app":"demo-api","hits":1} ← GET /count
{"app":"demo-api","hits":2} ← 再刷一次
【结果解读】
"app":"demo-api"→ FastAPI 在跑"redis_host":"redis"→ 环境变量注入生效,值=服务名(compose 网络自动解析成 redis 容器 IP)hits递增 → 计数真的写进了 redis 容器------api 与另一容器协作干活成功
【结论】 后端 + 依赖服务的容器化架构打通;"配置从环境读、地址用服务名"的生产模式落地。
实验 3:改代码 → rebuild(含一次"没生效"的教训)
【目的】 验证排序铁律的实战价值:改代码后 rebuild,pip install 层应保持 CACHED、只有 COPY 代码层重建。
【过程】 三个版本,完整记录:
版本 1(踩坑) :在编辑器里改了代码 → rebuild → 输出全 CACHED (连 COPY app/ 都是)→ 浏览器无变化。
- 解读 :
CACHED [5/5] COPY app/是铁证------rebuild 那一刻磁盘上的 main.py 没变(编辑器改了但没保存落盘,或保存晚于 build)。 - 教训 :Docker 只认磁盘,编辑器画面不算数。"改了没生效"两大元凶:①没保存 ②忘了 rebuild。
版本 2(验证落盘):
bash
grep -n version ~/桌面/work/Docker-lab/lesson8-api/app/main.py
有输出(显示带 v2 的行)= 磁盘上确实改了。
版本 3(成功):rebuild 后------
ruby
=> CACHED [4/5] RUN pip install ... ← 依赖层缓存稳住(铁律的价值)
=> [5/5] COPY app/ ./app/ ← 只有它重新执行(代码哈希变了)
✔ Container lesson8-api-api-1 Recreated ← api 被重建
(redis 无动静 ------ 没改它就不碰)
浏览器刷新 → "version":"v2" 出现 → 上线完成。
【结论】 ① 排序铁律省时间(依赖层不重装);② Compose 智能增量更新(只重建变化的服务);③ build 缓存是"改动检测器"------全 CACHED 说明源文件没变。
五、完整时序:改代码 → 上线(标准动作,值得背下来)
css
① 编辑器改代码 → ② Ctrl+S 落盘(Docker 只认磁盘)
→ ③ docker compose up -d --build
├─ BuildKit 逐层构建+查缓存(依赖层 CACHED,仅代码层重建)
├─ 新镜像 = 旧层复用 + 顶部新代码层
└─ Compose 智能更新(api Recreated,redis 不动)
→ ④ 新容器跑起来(环境变量照常注入)
→ ⑤ 浏览器刷新验证 → 新功能上线 ✅
| 时序环节 | 对应知识 |
|---|---|
| ② 保存落盘 | 编辑器≠磁盘(本课坑) |
| 逐层构建 | 第 3 课(镜像=层) |
| 缓存命中/失效 | 第 4 课(缓存键=父层+输入;排序铁律) |
| 必须重新 build | 第 4 课(镜像不可变) |
| 删旧起新容器 | 第 4 课(容器一次性) |
| Recreate/不碰 redis | 第 7 课(compose 增量更新) |
| 服务名连 redis | 第 6 课(自定义网络 DNS) |
| 8100→8000 | 第 1 课(端口映射) |
六、关键认知
build:vsimage::自研代码用 build(现场构建),第三方服务用 image(现成镜像)------一个项目通常两者混用。- 配置注入 :代码只
os.getenv(),值由 composeenvironment:给;REDIS_HOST: redis= 服务名当地址。 --host 0.0.0.0必写:否则容器外访问不到(Web 容器化头号坑)。- Docker 只认磁盘:改完必须保存 + 必须 rebuild,缺一环就"没生效"。
- 缓存=改动检测器:rebuild 全 CACHED → 先怀疑"改动没进构建目录/没保存"。
七、命令清单
| 命令 | 作用 |
|---|---|
docker compose up -d --build |
构建镜像 + 启动全部服务(改代码后的部署命令) |
sed -i 's/旧/新/' 文件 |
命令行文本替换(流编辑器;-i 直接改文件;脚本自动化必备) |
grep -n 关键词 文件 |
确认改动落盘(改完验证的好习惯) |
八、易错点
- uvicorn/flask 不监听 0.0.0.0 → 端口映射了也访问不到。
- 编辑器改了没保存 → 磁盘没变 → build 全 CACHED → "没生效"。
- 改了代码忘了 rebuild → 镜像冻结,容器还是旧的(第 4 课老坑)。
- pip 不加国内源 → 构建慢好几倍。
- requirements.txt 放在代码后面拷 → 改代码就重装依赖(排序铁律违反)。
九、一句话总结
真实后端容器化 = 好的 Dockerfile(先依赖后代码)+ Compose(build 构建、environment 注入、服务名互连)+ up -d --build 循环。改代码后的标准动作:保存 → rebuild → 验证。
十、本课产物
- 项目
~/桌面/work/Docker-lab/lesson8-api/(可反复实验) - 运行中:
lesson8-api-api-1(8100)、lesson8-api-redis-1;镜像lesson8-api-api:latest - 清理:
docker compose -f ~/桌面/work/Docker-lab/lesson8-api/docker-compose.yml down
十一、下一课预告
第 9 课(最后一课):最佳实践与进阶 🎓
- 多阶段构建(multi-stage build):构建工具不进最终镜像,瘦身 50%+
.dockerignore实战:构建上下文瘦身 + 防密钥泄漏- 镜像瘦身技巧:slim/alpine 基础镜像、层合并
- 依赖固定版本(requirements 锁版本)
- CI/CD 概览:把"build→push→部署"自动化(push 代码自动构建镜像、自动部署)