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,语义清晰。第二阶段用 alpine 或 distroless,体积小、攻击面少。
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 实战的核心内容就讲完了。记住:基础镜像选对、多阶段构建瘦身、层缓存优化提速、安全扫描守住底线。这四步走下来,你的镜像基本就稳了。
最后说一句:别嫌麻烦。我刚开始也觉得多阶段构建、安全扫描这些步骤多余,直到有一次线上事故让我彻底改了。现在每次构建都走这套流程,心里踏实。
公众号:蓝海资料掘金营,微信deep3321