收官章不教新知识,把前十二章的零件组装成三个完整的实战项目:一套本地 Web 服务栈、一个自包含的数据库、一条从 Dockerfile 到发布的完整流水线。每个案例都按「目标 → 命令 → 验证 → 翻车点」展开,这些流程我全部真实跑过,注释里的坑是踩出来的。
「快速要一个干净的 Linux 环境试个东西」,容器是最快的答案(对比第 3 章:开 VM 要分钟级,容器秒级):
docker run -it --rm ubuntu:24.04 bash # 用完即删,不留垃圾
apt update && apt install -y curl # 在里面随便折腾
exit # 退出即销毁
-it --rm 是「试验场」姿势的标准组合。要保留环境就去掉 --rm,明天 start 再 exec。这个姿势的日常用途:试一个软件包的安装过程、验证某个配置写法、跑别人的脚本前隔离一下。配合第 9 章 docker save/load,甚至能把这个环境打包带走。
组合第 10 章的网络和第 11 章的挂载,搭一个双服务站点:nginx 做入口、第二个容器做 API,同网络内用名字互通:
docker network create site
# 静态站:挂本地目录,改代码即时生效(第 11 章开发流)
docker run -d --name web --network site \
-p 8080:80 \
-v "$PWD/html:/usr/share/nginx/html:ro" \
nginx:1.27
# API 容器(演示用 python http server)
docker run -d --name api --network site \
python:3.12-alpine \
python -m http.server 8000
验证名字互通(第 10 章):进 web 容器 curl api:
docker exec web curl -s http://api:8000/ # 容器名即域名!
一个真实的静态站反向代理的写法(挂自定义 nginx 配置进容器,配置里 proxy_pass http://api:8000;),是「同网络名字解析」在生产里的最小复刻。翻车点记录:挂配置文件用 bind mount 后 nginx 不热加载,docker exec web nginx -s reload;容器里 curl 不了宿主,用第 10 章 host.docker.internal。
数据库是容器化的重灾区,标准姿势(第 11 章的卷 + 第 9 章的环境变量):
docker volume create mysql-data
docker run -d --name mysql \
--network site \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=appdb \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.4
# 等半分钟让数据库初始化完(docker logs -f mysql 看到 ready for connections)
docker exec -it mysql mysql -uroot -psecret appdb \
-e "CREATE TABLE t(id INT); INSERT INTO t VALUES (42); SELECT * FROM t;"
然后走第 11 章的「删容器保数据」验证:rm -f 容器重挂卷,表还在。再演示一次反面:不带 -v 跑一次,rm 后数据蒸发(这门课的第一个震撼实验)。
把第 12 章的 Dockerfile 接上发布动作,模拟一次真实交付:
# ① 构建(多段构建,第 12 章)
docker build -t myreg/myapp:1.0 .
# ② 本地测试
docker run -d --name test --rm -p 9999:8080 myreg/myapp:1.0
curl localhost:9999/health && docker stop test
# ③ 打标签并推送(第 8 章)
docker tag myreg/myapp:1.0 myreg/myapp:latest
docker push myreg/myapp:1.0
# ④ 生产侧拉起(服务器上)
docker pull myreg/myapp:1.0
docker run -d --name app --restart unless-stopped \
-v appdata:/data -p 80:8080 myreg/myapp:1.0
「build → test → push → pull → run」五步,就是没有 Kubernetes 时代所有公司的发布流程(今天它仍是单机部署和 CI 的核心骨架)。每一步出错的样子也记一下:build 失败看日志哪层断的、push 被拒是没 docker login 或仓库不存在、run 秒退用 logs 查(第 9 章)。
-v html:... 被当成卷名(第 11 章),nginx 显示的是默认页;绝对路径或 $PWD。docker stats 观察三个容器的资源占用;给 mysql 限内存(--memory 512m),故意压测看它被 OOM kill 后 --restart 拉起(dmesg 里找 OOM 记录,呼应第 7 章 cgroup)。