核心 · Key Idea
一句话:Dockerfile 每条指令是一层,层是可缓存的、按顺序追加的。理解层 + 缓存 + 多阶段构建,能把镜像从 1.2 GB 砍到 80 MB。
一个好 Dockerfile(Node 例)#
# ---- builder ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# ---- runtime ----
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
USER node
CMD ["node", "node_modules/next/dist/bin/next", "start"]构建:
docker build -t myapp:0.1 .
docker build --platform linux/amd64,linux/arm64 -t myapp:0.1 . --push # buildx打个比方#
打个比方 · Analogy
镜像像一摞透明胶片:一层叠一层,最终透出最上面的画面(容器内的文件系统)。改了下层,上层全部要重画 → 缓存失效;改了上层(如代码),下层(依赖)不动 → 秒级重建。
关键概念#
层Layer
RUN / COPY / ADD 各产生一层。MAGIC 是 layer 之间可缓存。
缓存键Cache Key
由本条指令 + 上层校验和决定。所以**先 COPY package.json 再 install** 比一起 COPY 强。
多阶段构建Multi-stage
FROM ... AS builder + 最后阶段只 COPY 产物 → 把工具链丢掉,镜像极小。
.dockerignore忽略文件
node_modules、.git、构建产物 —— 不要拷进 builder。
BuildKit / buildx现代构建后端
并行构建、跨架构、缓存挂载。生产必开。
Distroless / scratch极小基底
只放二进制 + 必需库,无 shell。**安全 + 小**,但调试不便。
怎么工作#
每层都有自己的 sha256,远端 push 只传新层。
实操要点#
- 顺序:变化最少的放上层(基础镜像 / 依赖),最常改的放下层(源码)。
COPY优于ADD:ADD 有自动解压 / URL 下载等魔法,容易踩坑。- 少用
RUN:每条 RUN 一层。多个 shell 命令用&&串成一条。 - 删完才小:
apt-get install -y X && apt-get clean && rm -rf /var/lib/apt/lists/*—— 不删依赖文件层照样存在。 HEALTHCHECK:写在 Dockerfile 里,平台层(Compose / K8s)能自动判定。ENV NODE_ENV=production+--frozen-lockfile:可重现构建。- 不打
latest:CI 打:1.2.3+:1.2+:1三条标签,方便回滚。 - 多架构:
docker buildx create --use,然后--platform linux/amd64,linux/arm64。
常见反模式#
- 直接用
node:20:~1 GB;改node:20-alpine或node:20-slim。 - 构建期装的 gcc / make 进了运行时:上多阶段构建。
- 代码改一行重新装依赖:先 COPY 锁文件再 install。
- secrets 硬编码到镜像:用 BuildKit
--mount=type=secret,绝不写进层。
易混点#
docker build
传统单架构构建。
无并行、缓存简单。
无并行、缓存简单。
docker buildx
BuildKit 加持,**多平台 + 远端缓存**。
现在的默认。
现在的默认。