如何编写 distroless 镜像?

解读

在国内云原生面试中,“写 distroless 镜像”并不是让你从零编译 Google 的 distroless 基础镜像,而是考察能否把 Rust 二进制打包成最小化、无壳、无包管理器、无调试工具的容器镜像,并满足“编译通过即正确”的 Rust 质量文化。
面试官常跟问:

  1. 镜像体积能压到多少 MB?
  2. 是否用到了国内源加速?
  3. 有没有解决 glibc 与 musl 的兼容性?
  4. 是否做了静态链接、SBOM、签名?
    答不到这些细节,会被认为“只跑过 hello-world”。

知识点

  1. 多阶段构建(multi-stage build):Rust 阶段只负责编译,最终阶段只拷二进制。
  2. 静态链接
    • x86_64-unknown-linux-musl 完全静态,无外部 libc 依赖,可直接丢进 scratchgcr.io/distroless/static
    • x86_64-unknown-linux-gnu 默认动态链接 glibc,需保留 distroless 的 libc6 层。
  3. 国内加速
    • 替换 registry.docker-cn.comdocker.mirrors.ustc.edu.cn 或阿里云 ACR 缓存。
    • $CARGO_HOME/config 里写 source.crates-io.replace-with = 'tuna' 指向清华源。
  4. 最小化裁剪
    • cargo build --release -Z strip=symbolsstrip 工具去掉符号表。
    • upx --best --lzma 可选,但国内金融场景常禁用(防扫描误报)。
  5. 安全加固
    • 非 root 用户(USER 65532:65532)。
    • 只读根文件系统、CAP_DROP=ALLseccomp=runtime/default
  6. 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 即可通过安全扫描。

拓展思考

  1. 交叉编译与国产化:在鲲鹏 ARM 环境,把 x86_64-unknown-linux-musl 换成 aarch64-unknown-linux-musl,并用华为 openEuler 提供的 distroless 基础镜像,即可实现信创场景的零依赖交付。
  2. 动态链接的权衡:若必须依赖 libssl.so(例如使用 native-tls),可把第二阶段换成 gcr.io/distroless/cc,但体积涨到 20~30 MB;此时需用 ldd 确认无多余 .so,并开启 RPATH=$ORIGIN 防止加载恶意库。
  3. 运行时调试:distroless 没有 shell,线上排障可旁挂 ephemeral-debugger 容器,利用 kubectl debugshareProcessNamespace attach 进去,符合国内金融“不可变基础设施”合规要求
  4. 供应链合规:把 cargo auditable 生成的 .json 打进镜像层,再用 trivy image --sbom 扫描,可在阿里 ACR 的“云安全中心”直接出等保报告,让 Rust 的“编译通过即正确”延伸到“镜像交付即合规”