scratch 是什么
scratch 是 Dockerfile 里的特殊保留字,表示从一个完全空的文件系统开始构建:镜像里没有 shell、没有 libc、没有包管理器,只有你显式 COPY 进去的文件。它只适合放静态编译产物(Go、Rust、C,且关闭 CGO),换来镜像可以小到几 MB、攻击面最小;代价是 CA 证书、时区、passwd 这些运行期数据要自己带进去。
基本信息
| 用途 | 构建极致精简的静态二进制镜像 |
|---|---|
| 语法 | FROM scratch |
| 适用范围 | Go / Rust / C 等静态编译产物 |
| 镜像内缺失 | shell、libc、包管理器、CA 证书、时区数据 |
| 运行限制 | 只能用 exec 形式的 ENTRYPOINT / CMD |
典型使用场景
- Go 服务容器化:CGO_ENABLED=0 编译后直接放进空镜像
- 安全敏感组件:无 shell 无包管理器,减少攻击者可用工具
- 边缘与分发场景:镜像越小,拉取与分发成本越低
- 极短启动的一次性任务与批处理程序
- 作为体积基线,验证产物是否真的静态链接
多阶段构建示例
多阶段构建示例
# 构建阶段:静态编译
FROM golang:1.23 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -o /out/app ./cmd/app
# 运行阶段:空镜像只放必要文件
FROM scratch
COPY --from=build /out/app /app
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
USER 10001:10001
ENTRYPOINT ["/app"]缺失项与补救
| 缺失项 | 典型表现 | 补救方式 |
|---|---|---|
| CA 证书 | 访问 HTTPS 报 x509: certificate signed by unknown authority | 从构建阶段 COPY ca-certificates.crt |
| 时区数据 | 日志时间错乱、本地时间计算错误 | COPY /usr/share/zoneinfo |
| shell | docker exec 进不去,HEALTHCHECK 无法执行 | 不适合需要交互排障的负载 |
| libc 与动态库 | 启动立即报 not found 或 exec format error | 改为静态编译,或退回 alpine/debian 基础镜像 |
scratch 镜像无法 docker exec 进去排查,只能依赖宿主侧的 docker inspect、docker logs 以及应用自身输出到标准输出的日志。
HEALTHCHECK 的 CMD-SHELL 形式在 scratch 里必然失败,因为镜像中没有 shell;如需健康检查只能用可执行文件的 exec 形式。
部分运行时会读取系统的名称解析与用户数据库文件,空镜像里这些文件并不存在,遇到按用户名解析或特定 DNS 查询失败的情况,按实际报错把对应文件一并拷入再验证。