scratch是零基础镜像,不继承任何文件或依赖,仅适用于静态编译的二进制(如cgo_enabled=0的go程序);它无shell、无libc、无系统路径,cmd必须用exec格式,镜像体积即二进制大小。

直接写 FROM scratch 就是制作零基础镜像的唯一方式。它不继承任何文件、用户、环境变量或系统库,真正从“空”开始——但前提是你的应用二进制文件必须是静态编译、无外部依赖的。
为什么 scratch 是“零基础”而非“空白报错”
scratch 不是一个真实存在的镜像,Docker 内部将其识别为一个保留关键字,代表空层起点。你无法 docker pull scratch,也不能在它上面运行 ls 或 sh——因为里面真的什么都没有。
- 没有 /bin/sh,所以不能用 shell 格式 CMD(如
CMD ./app) - 没有 libc、/dev、/proc、/sys,所有路径和系统调用都得靠程序自己满足
- 镜像体积 = 你 COPY 进去的那个可执行文件大小(通常几 MB)
Go 程序最适配 scratch 的原因
Go 默认静态链接,只要关掉 CGO 就能打包出完全自包含的二进制:
- 编译前设置:
CGO_ENABLED=0 - 编译命令:
go build -a -ldflags '-extldflags "-static"' -o app main.go - 验证是否静态:
file app输出含 statically linked;ldd app显示 not a dynamic executable
实际 Dockerfile 写法(多阶段构建推荐)
避免把编译工具链打进最终镜像,用多阶段分离构建与运行环境:
FROM golang:1.22-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o /app/app . FROM scratch COPY --from=builder /app/app /app EXPOSE 8000 CMD ["/app"]
- 第一阶段用完整 Go 环境编译,确保依赖解析正确
- 第二阶段只 COPY 编译结果,不带 SDK、源码、mod 缓存
- CMD 必须用 exec 格式(数组写法),否则会因缺少 sh 启动失败
其他语言能否用 scratch?要看能不能静态化
不是只有 Go 可以,但门槛明显更高:
-
Rust:默认静态链接,加
--target x86_64-unknown-linux-musl更稳妥 - C/C++:需用 musl-gcc 或静态链接所有依赖(包括 OpenSSL、zlib 等)
- Python/Java/Node.js:原生不支持——解释器本身就有强动态依赖,强行塞进 scratch 会启动失败;应改用 alpine 或 distroless 镜像











