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 源代码)放后面,把不常变的指令(比如安装系统包)放前面。这样能最大化缓存命中率。
Dockerfile → 镜像 → 容器 流程 Dockerfile docker build -t hello-world:v1 . 本地镜像 docker run hello-world:v1 容器(Hello World) 读取指令 生成层缓存 启动 运行 关键:Dockerfile 每一步 → 镜像层 → 复用缓存 → 可重复构建

嗯,这段流程你亲手跑一遍,比看十遍文档都管用。