直接用多阶段构建+Distroless基础镜像可打造无shell、体积小、攻击面窄的安全镜像,关键在于只保留运行必需组件:Go/Rust选static-debian13(约2MiB),Java选java17/21-debian13,Python选python3-debian12,Node.js选nodejs22-debian13,.NET选runtime-deps:10.0-noble-chiseled;所有镜像不含/bin/sh和包管理器,须用多阶段构建——第一阶段用完整SDK镜像编译,第二阶段COPY产物至Distroless镜像并用exec格式ENTRYPOINT(如["java","-jar","/app.jar"])启动。

直接用多阶段构建 + Distroless 基础镜像,就能做出没有 shell、体积小、攻击面窄的安全镜像。关键不是“能不能跑”,而是“只保留运行必需的东西”。
选对 Distroless 镜像类型
Distroless 不是万能通用镜像,得按语言和运行方式匹配:
- Go/Rust 等静态编译程序 → 用 gcr.io/distroless/static-debian13(约 2 MiB)
- Java 应用(JAR 包) → 选 gcr.io/distroless/java17-debian13 或 java21-debian13
- Python 应用 → 对应 gcr.io/distroless/python3-debian12(含 Python 解释器和基础库)
- Node.js → gcr.io/distroless/nodejs22-debian13
- .NET 自包含应用 → 推荐 mcr.microsoft.com/dotnet/runtime-deps:10.0-noble-chiseled
注意:所有镜像都不含 /bin/sh,也不支持 apt 或 apk,所以不能在运行时装包或调试。
必须用多阶段构建
因为 Distroless 镜像里没有编译工具链,你得在构建阶段用完整镜像(如 maven、python:3.11-slim、node:20),再把产物复制过去:
- 第一阶段:FROM 完整 SDK 镜像 → 编译、打包、生成可执行文件或 JAR/whl 等
- 第二阶段:FROM Distroless 镜像 → COPY 构建产物 → 设置 ENTRYPOINT
- 避免 COPY 整个源码或构建缓存,只复制最终运行所需的二进制或包
例如 Java 项目中,COPY --from=builder /app/target/app.jar /app.jar 就足够,不带 src/ 或 pom.xml。
ENTRYPOINT 必须用 exec 格式
Distroless 没有 shell,所以不能写 ENTRYPOINT java -jar /app.jar(这是 shell 形式,会调用 /bin/sh -c)。
正确写法是:
ENTRYPOINT ["java", "-jar", "/app.jar"]ENTRYPOINT ["python3", "/app/main.py"]-
ENTRYPOINT ["./mybinary"](Go/Rust 静态二进制)
如果需要传参,用 CMD 配合:比如 ENTRYPOINT ["java", "-jar", "/app.jar"] + CMD ["--server.port=8080"],容器启动时自动拼接。
验证和调试的小技巧
虽然进不去容器,但仍有办法确认是否正常:
- 构建后运行
docker run --rm your-image:tag,看日志是否输出启动成功信息 - 用
docker image inspect your-image:tag查看Config.Entrypoint和Size,确认体积在预期范围(比如 Python 镜像应 ≤ 40 MiB) - 扫描漏洞:
trivy image your-image:tag,Distroless 通常只有极少数 CVE(集中在 glibc 或 OpenSSL 版本) - 若需临时调试,可在构建阶段保留一个带 shell 的临时镜像(仅用于本地验证),但生产镜像一定用 Distroless


















