3、Dockerfile实战:多阶段构建、基础镜像选择、层缓存优化、安全扫描

好,咱们直接进入正题。Dockerfile 写得好不好,直接决定了你镜像的“身材”和“安全性”。我见过太多人把镜像搞到 2 个 G,跑起来还一堆漏洞。今天咱们就把这几个硬骨头啃下来。

核心要点:多阶段构建解决镜像体积,基础镜像选择决定安全基线,层缓存优化提升构建速度,安全扫描守住最后防线。

3.1 多阶段构建:瘦身利器

说白了,多阶段构建就是“用完就扔”。你想想看,编译环境需要 gcc、make、各种头文件,但运行时根本不需要这些。我以前在项目里见过一个 Java 镜像,把整个 JDK 和 Maven 都塞进去了,足足 1.2G。后来改成多阶段构建,只留 JRE,瞬间降到 200M。

来看个实际例子。这是咱们《因子工厂》后端服务的 Dockerfile:

# 第一阶段:编译环境
FROM golang:1.21-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server

# 第二阶段:运行环境
FROM alpine:3.19

RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/server /server

EXPOSE 8080
CMD ["/server"]

看到了吗?第一阶段用 golang 镜像编译,第二阶段只拿编译好的二进制文件。最终镜像里连 Go 编译器都没有,干净得很。

我的习惯:第一阶段镜像名用 AS builder,语义清晰。第二阶段用 alpinedistroless,体积小、攻击面少。

3.2 基础镜像选择:别乱用

基础镜像选错了,后面全是坑。我见过有人直接用 ubuntu:latest,结果镜像里一堆用不上的工具,还经常有高危漏洞。

我个人常用的几个选择:

场景 推荐基础镜像 体积 说明
Go 编译 golang:1.21-alpine ~300MB 编译环境,用完即弃
Java 运行时 eclipse-temurin:17-jre-alpine ~180MB 只带 JRE,不带 JDK
Python 应用 python:3.12-slim ~120MB slim 版本,够用
极致安全 gcr.io/distroless/base ~20MB 连 shell 都没有

避坑指南:我曾经用 alpine:latest 跑 Node.js 应用,结果发现 musl libc 和 glibc 不兼容,折腾了半天。后来老老实实用了 node:20-slim。记住:不要盲目追求最小镜像,兼容性更重要

3.3 层缓存优化:别让 Docker 白干活

Docker 构建时,每一层都会缓存。如果你把经常变动的文件放在前面,那缓存就全废了。我刚开始写 Dockerfile 时,经常把 COPY . . 写在最前面,结果每次改一行代码都要重新下载依赖,气得不行。

正确的做法是:把不常变动的指令放在前面

# 错误示范:依赖每次都重新下载
COPY . .
RUN npm install

# 正确示范:先复制依赖文件,再安装
COPY package.json package-lock.json ./
RUN npm install
COPY . .

为什么会这样?因为 Docker 检查每一层的文件是否变化。如果 package.json 没变,那 RUN npm install 这层就直接用缓存,省了十几秒甚至几分钟。

我的经验:在《因子工厂》项目中,我们用了 .dockerignore 排除 node_modules.git 等目录。这样 COPY . . 时不会把没用的文件带进去,缓存命中率更高。

3.4 安全扫描:上线前的最后一道关

镜像构建完了,别急着推。先扫一遍漏洞。我见过一个项目,上线后才发现镜像里有 Log4j 漏洞,紧急回滚,那叫一个狼狈。

现在常用的扫描工具有:

  • Trivy:开源、快、支持多种语言。我个人最常用。
  • Grype:Anchore 出品,集成度高。
  • Docker Scout:Docker 官方工具,适合 Docker Desktop 用户。

在 CI/CD 里加个扫描步骤,很简单:

# 用 Trivy 扫描镜像
trivy image myapp:latest --severity HIGH,CRITICAL

# 如果发现高危漏洞,直接让构建失败
trivy image myapp:latest --exit-code 1 --severity CRITICAL

重要:安全扫描不是一次性的。基础镜像会更新,新漏洞会不断出现。我建议每周至少扫描一次生产环境的镜像,或者每次构建时都扫。

3.5 本章知识体系

下面这张图,把咱们今天讲的内容串起来了。你一看就明白:

Dockerfile 实战核心流程 基础镜像选择 alpine / slim / distroless 多阶段构建 builder → runtime 层缓存优化 指令顺序 / .dockerignore 安全扫描 Trivy / Grype / Docker Scout 四个环节环环相扣,缺一不可

嗯,到这里 Dockerfile 实战的核心内容就讲完了。记住:基础镜像选对、多阶段构建瘦身、层缓存优化提速、安全扫描守住底线。这四步走下来,你的镜像基本就稳了。

最后说一句:别嫌麻烦。我刚开始也觉得多阶段构建、安全扫描这些步骤多余,直到有一次线上事故让我彻底改了。现在每次构建都走这套流程,心里踏实。


公众号:蓝海资料掘金营,微信deep3321