本节摘要:docker build 把 Dockerfile 与构建上下文变成一叠镜像层。本节从最小构建命令出发,讲清上下文是什么、-t 标签怎么起、缓存按什么规则命中,再逐个拆解 -f、--no-cache、--build-arg、--target 等高频义项,全程配可复现的构建会话与缓存失效排查现场。
改完一行 Dockerfile,重新 build,结果十分钟后还在下载依赖包——这个场景几乎每个团队都遇到过。问题不在构建本身,而在对"上下文"与"缓存"这两件事的理解。本节就从现场出发,把 docker build 的词条逐项展开:它读什么、算什么、缓存什么。
先给一个能跑的完整例子。目录里放这样的 Dockerfile:
FROM debian:12-slim WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends curl \ && rm -rf /var/lib/apt/lists/* COPY app.py . CMD ["python3", "app.py"]
构建命令与真实输出:
$ docker build -t myweb:0.1 . [+] Building 12.4s (10/10) FINISHED => [1/4] FROM docker.io/library/debian:12-slim => [2/4] WORKDIR /app => [3/4] RUN apt-get update && apt-get install -y ... curl => [4/4] COPY app.py . => exporting to image => => naming to docker.io/library/myweb:0.1 $ docker images myweb REPOSITORY TAG IMAGE ID CREATED SIZE myweb 0.1 8c4e21ab77d2 5 seconds ago 112MB
命令末尾那个点不是"文件在哪"的省写,它是构建上下文——docker 客户端把这个目录整个打包发给守护进程,Dockerfile 里的 COPY 只能从这个包里取文件。理解这点能解释很多怪现象:上下文目录里塞了几个 GB 的日志,构建一开始就卡住不动,因为光打包上传就要跑半天。
docker build [OPTIONS] PATH | URL | -
义项(高频选项):
| 选项 | 含义 | 使用时机 |
|---|---|---|
-t 名字:标签 |
给结果打标签,可重复多次 | 几乎总是要写 |
-f 路径 |
指定 Dockerfile 位置 | 文件不叫默认名时 |
--no-cache |
全量重建,忽略缓存 | 怀疑缓存陈旧时 |
--build-arg K=V |
传入构建期变量 | 配合 ARG 指令 |
--target 阶段名 |
只构建到某阶段 | 多阶段构建调试 |
--platform |
指定目标平台 | 交叉构建 |
构建上下文除了目录,还可以是远端地址或管道输入,日常九成场景就是那个点号。标签可一次打多个:-t myweb:0.1 -t myweb:latest,推送仓库时很省事。
$ ls -lh -rw-r--r-- 1 ops ops 2.3G app-logs.tar -rw-r--r-- 1 ops ops 156 Dockerfile -rw-r--r-- 1 ops ops 892 app.py $ docker build -t myweb:0.2 . ...(进度条在 Sending build context to Docker daemon 阶段停滞)
在上下文根放 .dockerignore,语法与常见忽略文件一致:
app-logs.tar *.log .git node_modules
再构建时上下文只有几 KB,Sending 阶段瞬间完成。这个文件不是可选项,而是 build 词条的固定搭配——它同时还是安全边界:密钥文件被 ignore 掉,才不会糊里糊涂烧进镜像层。

把现场跑出来对照:
$ echo "# touch" >> app.py $ docker build -t myweb:0.2 . => [2/4] WORKDIR /app CACHED => [3/4] RUN apt-get update && ... CACHED => [4/4] COPY app.py . 0.4s $ docker build -t myweb:0.2 --no-cache . => [3/4] RUN apt-get update && ... 48.2s
改了 app.py 后,前两层照样 CACHED,只有 COPY 及其后重建——这正是把"爱变的放后面"换来的效率。而 --no-cache 连依赖安装都重来,适合怀疑基础镜像或系统源出了变化的场合,代价是全程真跑。
ARG NODE_VERSION=20 FROM node:${NODE_VERSION}-slim
$ docker build --build-arg NODE_VERSION=18 -t legacy:18 . $ docker build -t current:20 .
ARG 是构建期变量,镜像落地后即消失,与运行期的 ENV 不是一回事。用它可以不改编译器版本号,维护两条构建线。注意 ARG 必须在用到它的 FROM 之前声明才对 FROM 生效,这条规则是 build 报"invalid reference format"之外的另一类高频翻车点。
⚠️ 常见坑:把密码或令牌写进 ARG 传进去——它虽然不进最终环境变量,却会留在构建历史里,docker history 一查便知。凭据类输入交给构建器的 secret 机制或运行期挂载,别走 ARG。
镜像造出来之后总有退役的一天:哪些能删、怎么删才不伤及无辜、离线机器上怎么把它送过去,2.3 的清理与搬运词条接手。