如何编写 distroless 镜像?
解读
在国内云原生面试中,“写 distroless 镜像”并不是让你从零编译 Google 的 distroless 基础镜像,而是考察能否把 Rust 二进制打包成最小化、无壳、无包管理器、无调试工具的容器镜像,并满足“编译通过即正确”的 Rust 质量文化。
面试官常跟问:
- 镜像体积能压到多少 MB?
- 是否用到了国内源加速?
- 有没有解决 glibc 与 musl 的兼容性?
- 是否做了静态链接、SBOM、签名?
答不到这些细节,会被认为“只跑过 hello-world”。
知识点
- 多阶段构建(multi-stage build):Rust 阶段只负责编译,最终阶段只拷二进制。
- 静态链接:
x86_64-unknown-linux-musl完全静态,无外部 libc 依赖,可直接丢进scratch或gcr.io/distroless/static。x86_64-unknown-linux-gnu默认动态链接 glibc,需保留 distroless 的libc6层。
- 国内加速:
- 替换
registry.docker-cn.com、docker.mirrors.ustc.edu.cn或阿里云 ACR 缓存。 - 在
$CARGO_HOME/config里写source.crates-io.replace-with = 'tuna'指向清华源。
- 替换
- 最小化裁剪:
cargo build --release -Z strip=symbols或strip工具去掉符号表。upx --best --lzma可选,但国内金融场景常禁用(防扫描误报)。
- 安全加固:
- 非 root 用户(
USER 65532:65532)。 - 只读根文件系统、
CAP_DROP=ALL、seccomp=runtime/default。
- 非 root 用户(
- SBOM 与签名:
cargo auditable生成嵌入依赖清单的二进制。cosign sign --key aliyun-kms://对接国内 KMS 做镜像签名,满足等保 2.0 要求。
答案
下面给出一份可直接在阿里 ACR 云原生流水线跑通的 Dockerfile,兼顾体积、合规与国内源加速,编译通过即可上线。
# 1) 编译阶段
FROM rust:1.78-slim AS builder
# 国内源加速
RUN mkdir -p /root/.cargo && \
echo '[source.crates-io]\nreplace-with = "tuna"\n[source.tuna]\nregistry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"' \
> /root/.cargo/config.toml
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
# 提前做依赖缓存层
RUN cargo fetch
COPY src ./src
# 静态 musl 工具链
RUN rustup target add x86_64-unknown-linux-musl
RUN cargo build --release --target x86_64-unknown-linux-musl --bin app
RUN strip /app/target/x86_64-unknown-linux-musl/release/app
# 2) 运行阶段:真正 distroless
FROM gcr.io/distroless/static:nonroot
# nonroot 用户 uid=65532
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/app /app
ENTRYPOINT ["/app"]
构建命令(国内集群)
DOCKER_BUILDKIT=1 docker build -t registry.cn-hangzhou.aliyuncs.com/yourrepo/app:1.0.0 .
镜像体积仅 3~5 MB(不含业务逻辑),docker run --read-only --cap-drop=ALL 即可通过安全扫描。
拓展思考
- 交叉编译与国产化:在鲲鹏 ARM 环境,把
x86_64-unknown-linux-musl换成aarch64-unknown-linux-musl,并用华为 openEuler 提供的 distroless 基础镜像,即可实现信创场景的零依赖交付。 - 动态链接的权衡:若必须依赖
libssl.so(例如使用 native-tls),可把第二阶段换成gcr.io/distroless/cc,但体积涨到 20~30 MB;此时需用ldd确认无多余.so,并开启RPATH=$ORIGIN防止加载恶意库。 - 运行时调试:distroless 没有 shell,线上排障可旁挂
ephemeral-debugger容器,利用kubectl debug的shareProcessNamespaceattach 进去,符合国内金融“不可变基础设施”合规要求。 - 供应链合规:把
cargo auditable生成的.json打进镜像层,再用trivy image --sbom扫描,可在阿里 ACR 的“云安全中心”直接出等保报告,让 Rust 的“编译通过即正确”延伸到“镜像交付即合规”。