3、第一个Docker镜像:编写Dockerfile并运行Hello World容器
好,我们开始动手。第一个镜像,惯例就是 Hello World。别小看它,它能把整个构建链路跑通,让你亲眼看到「从代码到容器」是怎么一回事。
3.1 到底什么是 Dockerfile
说白了,Dockerfile 就是一张「购物清单 + 安装说明书」。你告诉 Docker:
- 从哪个基础镜像开始(比如 Ubuntu、Alpine 或者空白的 scratch)
- 拷贝哪些文件进去
- 执行什么命令
- 容器启动时运行什么程序
它是一个纯文本文件,名字固定叫 Dockerfile(首字母大写)。每一行是一条指令,Docker 会从上到下逐条执行。
我个人的习惯:项目根目录放一个 Dockerfile,和源代码放一起。这样你一眼就知道这个项目能容器化。
3.2 编写第一个 Dockerfile
最简版本长这样:
FROM alpine:latest
CMD ["echo", "Hello World"]
解释一下:
FROM alpine:latest– 基于 Alpine Linux 这个极小镜像,才 5 MB。CMD– 容器启动时默认执行的命令。这里就是打印 Hello World。
你可能会问,为什么不用 FROM scratch?scratch 是空镜像,你需要自己塞一个可执行文件进去,对于初学者反而麻烦。Alpine 自带 shell 和常用工具,调试方便。
我曾经踩过一个坑:直接在
CMD 里写 echo Hello World,结果容器跑起来秒退,什么输出都没看到。原因是 CMD 的两种写法:CMD echo Hello World 会被当作 shell 格式,而 CMD ["echo", "Hello"] 是 exec 格式。我建议新手统一用 exec 格式,更安全。
3.3 构建镜像:docker build
有了 Dockerfile,执行这条命令:
docker build -t hello-world:v1 .
-t给镜像打标签,格式名字:版本。这里名字是hello-world,版本是v1。.是构建上下文路径。Docker 会把当前目录所有文件发给守护进程。
你会看到类似输出:
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM alpine:latest
latest: Pulling from library/alpine
...
Step 2/2 : CMD ["echo", "Hello World"]
---> Running in abc123def
---> 4a5b6c7d8e9f
Successfully built 4a5b6c7d8e9f
Successfully tagged hello-world:v1
每一行 Step 对应 Dockerfile 的一条指令,每条指令都会生成一个镜像层。
注意:构建上下文不是 Dockerfile 所在目录,而是你指定的路径。如果你 Dockerfile 在 subdir 里,可以用
-f subdir/Dockerfile,但上下文还是要指向根目录。
3.4 运行容器:docker run
镜像构建成功,运行它:
docker run --rm hello-world:v1
终端应该输出:
Hello World
--rm表示容器退出后自动删除,避免残留。- 不加
--rm也行,但你的docker ps -a会留下大量已停止容器。
如果你看到这个输出,恭喜你,第一个 Docker 镜像和容器完整跑通了!
3.5 镜像层缓存与可重复构建
为什么我强调「可重复计算」?你看 Docker 构建时,每一层都有 hash。如果 Dockerfile 没变,上一层缓存就能复用。这个机制保证了:同样的 Dockerfile + 同样的上下文,永远产生同样的镜像(前提是基础镜像版本固定)。
举个例子:你加一行 RUN apk add curl,Docker 会重新跑这一层,但之前的 FROM 层和 COPY 层都从缓存拿。这就是可重复构建的基石。
一个小技巧:把变化频繁的指令(比如
COPY 源代码)放后面,把不常变的指令(比如安装系统包)放前面。这样能最大化缓存命中率。
嗯,这段流程你亲手跑一遍,比看十遍文档都管用。