🧩 The big picture in one breath一口气看懂全局
In the ComfyUI lesson you downloaded a ready-made box (ddtraveller/comfyui-mac) and opened it with docker run. But who packs those boxes? Anyone who can write a Dockerfile — including you, in about 20 minutes. A Dockerfile is a short text file, usually 5–10 lines, that tells Docker exactly how to pack a program and everything it needs into one sealed box. Write it once, and your program runs the same on your Mac, your friend's Windows PC, or a giant server in a data center.
在ComfyUI 课程里,你下载了一个现成的盒子(ddtraveller/comfyui-mac),用 docker run 打开了它。可这些盒子是谁打包的呢?任何会写 Dockerfile 的人——包括你,大约只要 20 分钟。Dockerfile 是一个很短的文本文件,通常只有 5–10 行,它精确地告诉 Docker 如何把一个程序和它需要的一切打包进一个密封的盒子。写好一次,你的程序就能在你的 Mac、朋友的 Windows 电脑、或数据中心的大型服务器上以完全相同的方式运行。
1 What you need你需要准备什么
- Docker Desktop installed and the whale 🐳 awake — if you did the ComfyUI lesson, you're already set. If not, do its steps 1–3 first.
- A text editor — VS Code (free) is great, but even TextEdit/Notepad works.
- About 1 GB of free disk space (this project is tiny compared to last time!)
- Internet for two small downloads — after that, your app builds and runs offline
- 装好 Docker Desktop、小鲸鱼 🐳 已苏醒——如果你做过 ComfyUI 课程,这一步早就完成了;如果没有,先做它的第 1–3 步。
- 一个文本编辑器——VS Code(免费)很棒,TextEdit / 记事本也凑合。
- 大约 1 GB 空闲磁盘空间(和上次比,这个项目小多了!)
- 联网下载两个小东西——之后你的应用可以完全离线构建和运行
🍱 Quick recap of the lunchbox words快速复习"饭盒"词汇
An image is a sealed, ready-made box. A container is a box that's open and running. A port is a numbered door on the box. Today we add the most powerful word of all: a Dockerfile — the recipe that Docker follows to pack a brand-new box.
image(镜像)是密封好的现成盒子;container(容器)是打开并正在运行的盒子;port(端口)是盒子上带编号的门。今天我们加上最强大的一个词:Dockerfile——Docker 用来打包全新盒子的配方。
2 What is a Dockerfile? (the recipe card)什么是 Dockerfile?(配方卡)
A Dockerfile is a plain text file — literally named Dockerfile, no extension — containing a list of instructions. When you run docker build, Docker reads the recipe from top to bottom and follows every line. The result is an image: your very own sealed box.
Dockerfile 是一个纯文本文件——名字就叫 Dockerfile,没有扩展名——里面是一行行指令。当你运行 docker build 时,Docker 从上到下读配方、照着每一行做。结果就是一个镜像:完全属于你的密封盒子。
👩🍳 Think of it like…把它想象成……
A recipe card vs. a finished cake. The Dockerfile is the recipe — cheap to write, easy to share, easy to fix. The image is the finished cake — baked by following the recipe. And the container is a slice being eaten 🍰. You can bake the same cake a thousand times from one recipe card, and it comes out identical every time. That's the whole superpower.
配方卡 vs 烤好的蛋糕。Dockerfile 是配方——写起来便宜、容易分享、容易修改。镜像是烤好的蛋糕——照配方烤出来的。而容器是正在被吃的那一块 🍰。同一张配方卡可以烤一千个蛋糕,每一个都一模一样。这就是它的全部超能力。
| Thing | What it is | Kitchen version |
|---|---|---|
Dockerfile | text file of instructions | 📝 the recipe card |
| image | the sealed, ready-to-run box | 🎂 the finished cake |
| container | the box, opened and running | 🍰 a slice on a plate |
docker build | the command that follows the recipe | 👩🍳 the baking |
| 东西 | 它是什么 | 厨房版本 |
|---|---|---|
Dockerfile | 写满指令的文本文件 | 📝 配方卡 |
| 镜像 image | 密封的、随时能跑的盒子 | 🎂 烤好的蛋糕 |
| 容器 container | 打开并运行中的盒子 | 🍰 盘子里的一块 |
docker build | 照配方执行的命令 | 👩🍳 烘焙过程 |
3 The recipe, line by line — click to explore! 🖱️配方逐行讲解——点击探索!🖱️
Here is the entire Dockerfile we'll write today — just 7 lines. Click any line on the left (or any layer of the cake on the right) to see what it does. Notice how each instruction adds a layer to the cake, from the bottom up.
这就是今天要写的完整 Dockerfile——只有 7 行。点击左边的任意一行(或右边蛋糕的任意一层),看看它是干什么的。注意每条指令都会给蛋糕加一层,从下往上叠。
👆 Click a line of the Dockerfile👆 点击 Dockerfile 的某一行
Each instruction gets its own explanation here. Start with line 1 — every Dockerfile starts with FROM.每条指令的讲解会出现在这里。从第 1 行开始吧——每个 Dockerfile 都以 FROM 开头。
💡 Did you notice you didn't start from zero?注意到了吗?你并不是从零开始
FROM python:3.12-slim means your recipe starts from someone else's finished cake — a small box that already has Python installed, packed by the Python team and shared on Docker Hub. You only add your layers on top. Almost every Dockerfile in the world begins by standing on someone else's shoulders. (The comfyui-mac box you used last time? Its recipe starts with FROM python:3.12-slim too!)
FROM python:3.12-slim 的意思是:你的配方从别人烤好的蛋糕开始——一个已经装好 Python 的小盒子,由 Python 团队打包、放在 Docker Hub 上共享。你只需要在上面加你自己的几层。世界上几乎每个 Dockerfile 都是站在别人的肩膀上起步的。(你上次用的 comfyui-mac 盒子?它的配方同样以 FROM python:3.12-slim 开头!)
4 The cache — why line order is a secret superpower缓存——行的顺序是隐藏的超能力
Docker remembers every layer it has already built. When you rebuild, it checks each line from the top: "has anything this line depends on changed?" If not — CACHED, reused instantly, zero seconds. The moment one line changes, that line and every line below it must rebuild.
Docker 会记住每一个已经构建过的层。重新构建时,它从上往下逐行检查:"这一行依赖的东西变了吗?"没变——CACHED(已缓存),瞬间复用,零秒。一旦某一行变了,那一行以及它下面的每一行都必须重建。
🎂 Think of it like…把它想象成……
You can't swap a cake's middle layer without re-doing all the layers above it. But the layers below the change? Untouched, still perfect. Docker thinks exactly the same way — which is why smart bakers put the layers that change often (your code) near the top of the recipe.
你没法只换蛋糕中间的一层,而不重做它上面的所有层。但变化那层下面的层呢?原封不动,依然完美。Docker 的思路一模一样——所以聪明的烘焙师会把经常变化的层(你的代码)放在配方靠上的位置。
Try it yourself. This simulator behaves exactly like a real docker build. Press the buttons and watch which layers rebuild:
自己试试。这个模拟器的行为和真正的 docker build 一模一样。按下按钮,观察哪些层需要重建:
🔨 Build simulator构建模拟器
🕵️ The mystery of lines 3 & 4, solved第 3、4 行之谜,破案了
Now you know why the recipe copies requirements.txt before app.py, instead of copying everything at once. The slow step is pip install. You'll edit app.py fifty times a day, but the requirements list almost never changes — so by keeping them on separate lines, editing your code never re-runs the slow install. Rebuilds drop from ~40 seconds to ~2. This one trick is used in practically every professional Dockerfile on Earth.
现在你知道为什么配方先复制 requirements.txt、再复制 app.py,而不是一次全部复制了。最慢的一步是 pip install。你一天会改五十次 app.py,但购物清单几乎从不变化——把它们分成两行,改代码就永远不会重新执行慢吞吞的安装。重建时间从约 40 秒降到约 2 秒。地球上几乎每一个专业的 Dockerfile 都在用这一招。
5 Build the Joke Machine — the three files做"笑话机"——三个文件
Enough theory — let's pack a real box. The Joke Machine is a tiny website that tells a random programming joke every time you refresh. Open Terminal and make a project folder:
理论够了——来打包一个真盒子。笑话机是一个小网站,每次刷新都讲一个随机的编程笑话。打开终端,建一个项目文件夹:
mkdir -p ~/joke-machine && cd ~/joke-machine
Now create three files inside that folder with your text editor. (In VS Code: File → Open Folder → joke-machine, then make each file.) First, the app itself:
现在用文本编辑器在这个文件夹里创建三个文件。(VS Code:文件 → 打开文件夹 → joke-machine,然后逐个新建。)首先是应用本身:
app.pyfrom http.server import HTTPServer, BaseHTTPRequestHandler
import pyjokes
class JokeHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
joke = pyjokes.get_joke()
page = f"""
<body style="font-family:sans-serif;text-align:center;
padding:80px 20px;background:#0f172a;color:#fff;">
<h1 style="max-width:640px;margin:0 auto;">😂 {joke}</h1>
<p style="margin-top:30px;">
<a href="/" style="color:#38bdf8;">Tell me another one</a>
</p>
</body>"""
self.wfile.write(page.encode("utf-8"))
print("😂 Joke Machine running on port 8000...")
HTTPServer(("0.0.0.0", 8000), JokeHandler).serve_forever()
Second, the shopping list — the one Python package our app needs:
然后是购物清单——我们的应用需要的唯一一个 Python 包:
requirements.txtpyjokes
And third, the recipe you already explored in step 3 — type it exactly:
最后是你在第 3 步已经探索过的配方——一字不差地输入:
DockerfileFROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
EXPOSE 8000
CMD ["python", "app.py"]
⚠️ Naming matters名字很重要
The recipe file must be named Dockerfile — capital D, no .txt ending. If TextEdit sneakily saves it as Dockerfile.txt, the build will say it can't find a Dockerfile. (In TextEdit also use Format → Make Plain Text first.) Check with ls in Terminal — you should see exactly: Dockerfile app.py requirements.txt
配方文件必须叫 Dockerfile——大写 D,没有 .txt 后缀。如果 TextEdit 偷偷把它存成 Dockerfile.txt,构建时就会说找不到 Dockerfile。(用 TextEdit 还要先 格式 → 制作纯文本。)在终端里用 ls 检查——你应该恰好看到:Dockerfile app.py requirements.txt
6 Bake it: docker build开烤:docker build
From inside the joke-machine folder, run:
在 joke-machine 文件夹里运行:
docker build -t joke-machine .
Three pieces, three meanings:
三个部分,三层含义:
| Piece | What it means |
|---|---|
docker build | "follow the recipe and bake an image" |
-t joke-machine | tag — the name written on the box, so you can run it by name later |
. (the dot!) | "the kitchen is this folder" — Docker may only use files from here when it COPYs. It's called the build context |
| 部分 | 含义 |
|---|---|
docker build | "照配方烤一个镜像" |
-t joke-machine | tag(标签)——写在盒子上的名字,以后可以按名字运行它 |
.(那个点!) | "厨房就是这个文件夹"——COPY 的时候 Docker 只能用这里的文件。它叫构建上下文(build context) |
Watch the output — you'll see Docker walk your recipe line by line: [2/4] WORKDIR /app, [3/4] RUN pip install… Exactly like the simulator in step 4, but real. When it finishes, see your creation sitting on the shelf:
盯着输出看——你会看到 Docker 逐行执行你的配方:[2/4] WORKDIR /app、[3/4] RUN pip install… 和第 4 步的模拟器一模一样,只不过这次是真的。结束后,看看架子上你的作品:
docker images # joke-machine should be in the list, ~130 MB# 列表里应该有 joke-machine,约 130 MB
🐢➡️🐇 Run the build twice把构建跑两遍
Run the exact same docker build command again, right now. The first build took maybe a minute; the second finishes in about one second, and every step says CACHED. That's the layer cache from step 4 — now you've seen it for real.
现在就把同一条 docker build 命令原样再跑一次。第一次也许要一分钟;第二次大约一秒完成,每一步都显示 CACHED。这就是第 4 步讲的层缓存——这回你亲眼见到真的了。
7 Open the box: docker run开盒:docker run
Same command you learned in the ComfyUI lesson — but this time the box is yours:
和你在 ComfyUI 课程里学的命令一样——但这次的盒子是你自己的:
docker run -d --name jokes -p 8000:8000 joke-machine
Then open your browser and visit:
然后打开浏览器访问:
http://localhost:8000
🎉 A joke, served by your app, running inside your container, built from your recipe. Refresh for another. You are now officially someone who ships software in boxes.
🎉 一个笑话,由你的应用提供,跑在你的容器里,从你的配方构建出来。刷新一下,再来一个。从现在起,你正式成为用盒子发布软件的人了。
🤯 Stop and appreciate what just happened停下来,体会一下刚才发生了什么
Your Mac doesn't have pyjokes installed. It might not even have the right Python. It doesn't matter. Everything the app needs lives inside the box. You could send this image to a friend on Windows, a Linux server in Bangkok, or a Raspberry Pi — same box, same jokes. That "works on my machine… and every other machine" magic is why the whole software world runs on containers.
你的 Mac 没装 pyjokes,甚至可能没有合适的 Python 版本。这都无所谓。应用需要的一切都在盒子里。你可以把这个镜像发给用 Windows 的朋友、曼谷的一台 Linux 服务器、或者一块树莓派——同一个盒子,同样的笑话。这种"在我电脑上能跑……在所有电脑上都能跑"的魔法,正是整个软件世界都在用容器的原因。
8 The edit → rebuild → rerun loop 🔁修改 → 重建 → 重启的循环 🔁
Real developers change code constantly. Here's the loop. Try it: open app.py and change the 😂 emoji to 🤖 (or restyle the whole page — it's HTML, you know HTML!). Then:
真正的开发者无时无刻不在改代码。循环是这样的。试试看:打开 app.py,把 😂 换成 🤖(或者干脆给整个页面换个样式——那是 HTML,你懂 HTML!)。然后:
# 1. re-bake (watch lines 1–4 say CACHED — only your code layer rebuilds)# 1. 重新烤(看着第 1–4 行显示 CACHED——只有你的代码层重建)
docker build -t joke-machine .
# 2. throw away the old running box (the old slice of cake)# 2. 扔掉旧的运行中盒子(旧的那块蛋糕)
docker rm -f jokes
# 3. open a fresh box from the new image# 3. 从新镜像开一个新盒子
docker run -d --name jokes -p 8000:8000 joke-machine
Refresh localhost:8000 — there's your change. The whole loop takes about five seconds, thanks to the cache.
刷新 localhost:8000——你的修改出现了。多亏缓存,整个循环大约只要五秒。
🧊 Why step 2? Containers don't update themselves为什么要第 2 步?容器不会自己更新
A running container was opened from the old image and keeps running the old code forever — rebuilding the image doesn't touch it. You must remove the old container and start a new one from the new image. Forgetting this is the #1 "why isn't my change showing up?!" mystery for every new Docker user. Now it won't be for you.
运行中的容器是从旧镜像开出来的,会永远跑着旧代码——重建镜像碰不到它。你必须删掉旧容器、从新镜像启动一个新的。忘记这一步,就是每个 Docker 新手的头号谜题:"为什么我的修改没生效?!"现在它难不倒你了。
9 .dockerignore — don't pack your dirty laundry.dockerignore——别把脏衣服打包进去
Remember the dot — the build context? Docker hands your whole folder to the builder. As projects grow, folders collect junk you'd never want in the box: editor settings, giant videos, passwords. A .dockerignore file (a sibling of your Dockerfile) lists what to leave out:
还记得那个点——构建上下文吗?Docker 会把你的整个文件夹交给构建器。项目变大后,文件夹里会积攒你绝不想装进盒子的杂物:编辑器配置、巨大的视频、密码。.dockerignore 文件(和 Dockerfile 放在一起)列出要排除的东西:
__pycache__/
.DS_Store
*.mp4
secrets.txt
.git/
🧦 Think of it like…把它想象成……
Packing a suitcase for camp with a "do NOT pack" list taped to the lid: no dirty socks, no diary, no 4-kg encyclopedia. The box stays small, builds stay fast, and your secrets stay home. It works just like the .gitignore file you may know from GitHub.
给夏令营行李箱贴一张"禁止打包"清单:不带脏袜子、不带日记、不带 4 公斤重的百科全书。盒子保持小巧、构建保持快速、秘密留在家里。它和你可能在 GitHub 上见过的 .gitignore 文件工作方式一样。
10 Level up: share your box with the world 🌍进阶:把你的盒子分享给全世界 🌍
The ddtraveller/comfyui-mac box you downloaded last time? It got onto Docker Hub with exactly three commands — and yours can too. Make a free account at hub.docker.com, then:
上次你下载的 ddtraveller/comfyui-mac 盒子?它就是靠三条命令登上 Docker Hub 的——你的也可以。先在 hub.docker.com 注册一个免费账号,然后:
docker login
docker tag joke-machine YOUR_USERNAME/joke-machine
docker push YOUR_USERNAME/joke-machine
Now anyone on the planet can run your Joke Machine with one command: docker run -p 8000:8000 YOUR_USERNAME/joke-machine. You started this lesson as a person who downloads boxes. You're ending it as a person who publishes them.
现在地球上的任何人都能用一条命令运行你的笑话机:docker run -p 8000:8000 YOUR_USERNAME/joke-machine。这节课开始时,你是下载盒子的人;结束时,你已经是发布盒子的人。
🧹 Cleaning up when you're done玩完后的清理
docker rm -f jokes removes the running container; docker rmi joke-machine removes the image from the shelf. Your three recipe files stay in ~/joke-machine — and that's the beauty: as long as you keep the 7-line recipe, you can re-bake the box anytime, anywhere.
docker rm -f jokes 删除运行中的容器;docker rmi joke-machine 把镜像从架子上拿掉。你的三个配方文件还好好地待在 ~/joke-machine 里——这正是美妙之处:只要留着这 7 行配方,你随时随地都能把盒子重新烤出来。
? Pop quiz — are you a box-packer now? 🧠突击测验——你现在是打包工了吗?🧠
No grades, no pressure — each answer explains itself.不打分、没压力——每个答案都会自己解释。
✅ Your box-packer checklist打包工进度清单
Tick each box as you go. Your progress saves in this browser automatically.完成一项勾一项,进度自动保存在这个浏览器里。
Saved on this device only. Nothing is sent anywhere — just like your jokes.仅保存在本设备。不会上传任何内容——和你的笑话一样。