讲解
镜像是用手写命令堆出来的吗?不是——镜像是用 Dockerfile「声明」出来的。Dockerfile 是一个纯文本脚本,每条指令生成镜像的一层,docker build 按顺序执行并把结果固化。它的最大价值是可重复:同一个 Dockerfile 在任何机器上构建出同样的镜像(前提是指令本身确定,比如基础镜像锁了版本),环境配置从此进了版本库,和代码一起评审、一起回溯。
最小可用的 Dockerfile 只需要三条指令:FROM 指定基础镜像(一切镜像的起点,scratch 是空镜像,alpine 是最常用的最小系统);COPY 把构建上下文里的文件复制进镜像;CMD 指定容器启动时的默认命令。构建命令是 docker build -t 名字:标签 上下文目录——最后的上下文目录极其重要:Docker 会把整个目录打包发给守护进程,COPY 只能引用上下文内的文件,这也是大目录构建慢的原因(.dockerignore 章节解决)。
构建成功后用 docker run 验证,是最朴素的开发闭环:改代码 → build → run → 看效果。注意 CMD 有两种写法:exec 形式 CMD ["node", "app.js"](JSON 数组,进程直接成为 1 号进程,能正确接收信号,推荐)和 shell 形式 CMD node app.js(经 /bin/sh 包一层,信号传递有坑)。ENTRYPOINT 与 CMD 的组合下一章细讲。
示例
先准备构建上下文:一个放文件的目录(bash 块在沙盒目录中执行,dockerfile 块会写入同一目录并被真实构建):
mkdir -p helloctx && printf '你好, 这是镜像里的文件\n构建时间演示\n' > helloctx/message.txt
一个最小 Dockerfile——FROM + COPY + CMD:
# context: helloctx
FROM alpine:3.21
WORKDIR /app
COPY message.txt .
CMD ["cat", "message.txt"]
构建并运行(镜像名带教程前缀,跑完即焚):
cd helloctx && docker build -t ohmydocs-verify-hello:1.0 .
docker run --rm ohmydocs-verify-hello:1.0
看一眼镜像的分层历史,每条指令对应一层:
docker history ohmydocs-verify-hello:1.0
常见坑
- COPY 引用上下文外的文件:COPY ../secrets . 直接报错——构建上下文之外的文件够不着,这是安全边界。
- 上下文目录选太大:在项目根目录构建却没配 .dockerignore,几百 MB 的 node_modules 每次都打包上传。
- CMD 用 shell 形式:主进程变成 sh,docker stop 的 SIGTERM 到不了真正的应用,优雅退出失效。
- 以为构建是联网实时的:构建有缓存,同样的指令层直接复用——改了文件 COPY 层才会重建(缓存章节细讲)。
小结
Dockerfile 声明式定义镜像:FROM 定基座、COPY 放文件、CMD 给默认命令;docker build -t 名 . 构建,上下文目录决定 COPY 能见范围。下一章把常用指令补齐。