公司每一个服务,最后都会变成一个镜像、被集群拉起来跑。
今天你自己走一遍这条路 —— 从写 Dockerfile 到把它推进你自己的仓库。
浏览器访问你自己电脑上的 localhost:8000,看到你的服务。
镜像推进你自己的仓库,换台机器 docker pull 能拉下来。
改一行代码重新构建,体会分层缓存带来的秒级重建。
术语第一次听没关系:先做出来 → 再回头理解。今天所有命令都在你自己电脑上跑。
| 词 | 是什么 | 类比 |
|---|---|---|
| 镜像 image | 只读的模板:代码 + 运行环境 + 依赖,一次性打包好 | 一张安装光盘 |
| 容器 container | 镜像的一次运行实例,可以起很多个、删了不留痕 | 光盘装出来的系统 |
| 仓库 registry | 集中存放镜像的地方,谁都能来拉 | 应用商店 |
镜像不变,容器随时可起可删;仓库负责把镜像送到别的机器上。
所以"轻"的代价是:容器里跑的本质上就是一个被隔离开的普通进程。 隔离开它、限制住它、打包好它 —— 靠的是三样内核能力。
PID 看不到别的进程NET 有自己的网卡/IPMNT 有自己的文件系统UTS 有自己的主机名USER 自己的用户体系# 容器里看到的进程,只有它自己那一个$ docker run --rm alpine ps auxPID USER TIME COMMAND 1 root 0:00 ps aux # 宿主机上看到的却是完整的一大串
“容器里的 1 号进程”和宿主机的 1 号进程,根本不是同一个东西。
--cpus)--memory)超了会怎样?直接被杀掉——这就是 K8s 里最常见的 OOMKilled。
$ docker run --rm --memory=100m python:3.12-slim \ python -c "a='x'*(200*1024*1024)"Killed # 容器被内核杀掉,不是程序自己报错# 排查时先看状态:docker inspect 里的 OOMKilled
docker history myapp:v1
docker diff <容器名>
每一层只记录增量。所以:没变的层直接复用,构建就快。
容器 = 被 namespace 隔开、被 cgroup 限住、 用 分层文件系统 打包好的 —— 一个普通进程。
这三样能力照样是底座,只是变成了集群帮你管。
限制内存写 resources.limits.memory,超了也是同一个下场。
镜像小、层复用得好,拉取和滚动更新都快。
registry.dev.oaiai.ai/你的用户名/hello:v1
镜像存在哪台服务器
你的命名空间,靠约定自律
版本,别只用 latest
registry.dev.oaiai.ai —— 你们自己的练习田,随你推
registry.akria.net —— 公司线上服务用,新人只读
# 从哪来 FROM python:3.12-slim # 工作目录 WORKDIR /app # 先依赖,后代码(关键顺序) COPY requirements.txt . RUN pip install -r requirements.txt COPY . . # 启动命令 CMD ["uvicorn","main:app","--host","0.0.0.0","--port","8000"]
FROM基础镜像,别人的成果
COPY / RUN构建时执行,每一行就是一层
CMD容器启动时才执行
顺序有讲究:依赖文件先 COPY,改代码就不会重装依赖。
$ docker build -t hello:v1 .[+] Building 12.4s (9/9) FINISHED $ docker run -d -p 8000:8000 --name hello hello:v1$ curl -s localhost:8000/health{"status":"ok"}
-p 8000:8000宿主机端口 : 容器端口。左半边是你电脑,右半边是容器。
D2 讲的端口概念,在这里第一次上岗。
-d后台运行,不加它日志就直接刷屏
| 命令 | 干什么 |
|---|---|
docker ps -a | 看容器在不在、退出码是多少 |
docker logs -f hello | 看输出,排障第一件事 |
docker exec -it hello bash | 进容器里看文件、试命令 |
docker inspect hello | 看完整配置(端口、挂载、OOMKilled) |
常见坑:容器一起来就退出?多半是 CMD 写的命令前台跑不住(比如写成了 service nginx start 这类后台命令)。容器里的主进程必须待在前台。
# 1 登录(账号密码课上发放)$ docker login registry.dev.oaiai.ai # 2 给镜像起一个“带仓库地址”的名字$ docker tag hello:v1 registry.dev.oaiai.ai/<你的用户名>/hello:v1 # 3 推上去$ docker push registry.dev.oaiai.ai/<你的用户名>/hello:v1v1: digest: sha256:9f2c… size: 2841
验证方式很重要:先 docker rmi 删掉本地镜像,再 docker pull 拉回来能跑 —— 才算真的推成功了。
构建完 → docker login → docker push
凭据存在 ~/.docker/config.json
要拉你的私有镜像 → 也得有凭据
在 K8s 里这份凭据叫 imagePullSecret
ImagePullBackOff —— Pod 起来了但镜像拉不到。十有八九是镜像名写错或没给拉取凭据。下一讲会亲手修一次。
FROM你引用别人的镜像当底座
FROM python:3.12-slim
composecompose 按名字拉起一堆容器
image: postgres:16
k8s 清单集群照这个名字去仓库拉
image: registry.dev.oaiai.ai/…
"引用"就是把这条链黏起来的东西:代码 → 镜像 → 仓库 → 集群,全靠镜像名对齐。
COPY . .
RUN pip install -r requirements.txt
改一个字母 → 这层失效 → 依赖全部重装
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
改代码 → 依赖层命中缓存 → 秒级重建
这就是"改一次"的作业要你亲眼看到的东西:看构建输出里那一串 CACHED。
# 第一阶段:编译前端(需要 node,但产物只要 dist) FROM node:22-slim AS web-build RUN npm ci && npm run build # 第二阶段:运行时只留 python + 编译好的产物 FROM python:3.11-slim COPY --from=web-build /app/dist ./web/dist
最终镜像里没有 node_modules、编译工具链 → 体积可能小一半以上
攻击面更小:镜像里没有的东西,漏洞也带不进去
Apple 芯片默认构建 arm64,而集群节点是 amd64,拉过去直接起不来。
docker build --platform linux/amd64 -t hello:v1 .
本地 .venv/、node_modules/、.git/ 会一起进镜像,又慢又大。
# .dockerignore
.venv/
node_modules/
.git/
记住:镜像体积是运维成本 —— 拉取、拷贝、滚动更新、备份,每一环都在为它买单。
| 单机上的痛 | 你只能怎么办 |
|---|---|
| 一台机器跑满 | 手工换机器、重新部署一遍 |
| 进程挂了 | 半夜爬起来 docker start |
| 要升级版本 | 停服 → 换镜像 → 起回来(客户看得见) |
| 端口冲突 | 手工排端口,还得记住谁占了哪个 |
| 要扩到 3 个副本 | 手工起 3 次,再想办法给它们做负载均衡 |
这些事,K8s 全都替你干了 —— 下一讲一个痛点一个对象地对上。
机器跑满
手工换机器
Scheduler / Node
自动挑节点调度
进程挂了
半夜爬起来重启
Deployment
副本掉了自动补
升级要停服
客户看得见
滚动更新
新起旧摘,全程可用
端口冲突 / 对外暴露
Service + Ingress
稳定入口 + 域名路由
配置、密码写死在镜像里
ConfigMap / Secret
配置与镜像分离
| 现象 | 先试这个 |
|---|---|
docker login 失败 | 确认账号密码、仓库地址写全(要带域名,不是 IP) |
push 403 / denied | 镜像名少了 <用户名>/ 这一段,或拼错仓库地址 |
| 容器起来立刻退出 | docker logs 看报错;检查 CMD 是不是后台命令 |
| 浏览器访问不到 | -p 没写、端口写反、服务没监听 0.0.0.0 |
| 镜像 800MB 起 | 用了完整基础镜像 + 没写 .dockerignore |
| 本地跑得好,集群起不来 | 多半是 --platform 没指定(Mac 芯片) |
通用三招:看日志 → 看 inspect → 把报错原文丢给 Claude Code。
Dockerfile,本地构建并跑通(curl 有响应)CACHED 截图改成多阶段构建,比较前后镜像体积(docker images)
docs/training/学员/<姓名>/D7/,按 D6 的 Git 规范提 PR,提交信息中文
你会拿到自己的集群账号,把今天推上去的那个镜像,部署到集群里,
挂上属于你自己的域名 <你的用户名>.dev.oaiai.ai —— 然后亲手修一次 ImagePullBackOff。