Rust Rocket构建小体积Docker镜像运行报exit code 126
错误原因
docker exec 退出码126 不是二进制没复制——二进制不存在会返回127。126的本质是:系统找到了对应路径的文件,但无法执行,常见触发原因只有三类:
- 文件没有可执行权限
- 文件格式/依赖和运行环境不兼容(架构不匹配、动态链接库缺失、不是合法可执行文件)
- 路径指向了非可执行文件
你两个构建方案分别踩了不同的坑:
多阶段构建+distroless方案问题
- 依赖缺失:官方
rust基础镜像是Debian系,默认编译出的二进制动态链接glibc,虽然gcr.io/distroless/cc带了基础glibc,但Rocket如果开启TLS、依赖OpenSSL,distroless/cc默认不带OpenSSL库和CA证书,会导致二进制加载失败。 - 权限隐患:COPY默认继承源文件权限,如果构建阶段二进制的可执行位丢失,复制后就无法运行。
- 路径不严谨:运行阶段没有显式设置WORKDIR,用相对路径
./arb_data容易出现路径偏差。
musl静态编译+scratch方案核心错误
这个方案的问题非常明确:你定义的默认二进制名是arbdata(无下划线),但项目Cargo.toml配置的项目名是arb_data(中间带下划线),cargo build生成的二进制名和项目名一致,也就是说你构建阶段复制文件、运行阶段执行的文件名从一开始就写错了。
除此之外你还漏了CA证书复制、没有显式给二进制加执行权限,就算文件名写对也可能碰到执行失败的问题。
修复方案
二选一即可,都能把镜像体积从原来的1GB+压缩到50MB以内,静态编译方案可以做到5MB左右。
方案1:修正版distroless构建(兼容性最好)
不需要处理静态编译的C库兼容问题,适合快速上线:
# 构建阶段 FROM rust:1.75-slim-bookworm as builder WORKDIR /app # 安装编译依赖 RUN apt update && apt install -y --no-install-recommends libssl-dev pkg-config ca-certificates # 依赖缓存层:先复制依赖文件编译空项目,避免每次改代码都重编依赖 COPY Cargo.toml Cargo.lock ./ RUN mkdir src && echo "fn main(){}" > src/main.rs RUN cargo build --release # 复制真实源码编译 COPY src ./src RUN touch src/main.rs && cargo build --release # 运行阶段 FROM gcr.io/distroless/cc-debian12:nonroot # 复制CA证书,需要调用外部HTTPS接口时必须加 COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ WORKDIR /app # 显式加可执行权限复制二进制 COPY --from=builder --chmod=755 /app/target/release/arb_data /app/arb_data USER nonroot EXPOSE 8000 CMD ["/app/arb_data"]
方案2:修正版musl静态编译+scratch(体积极致小)
完全空的基础镜像,体积最小,适合对镜像大小有严格要求的场景:
# 二进制名和项目实际输出保持一致,带下划线 ARG BINARY_NAME_DEFAULT=arb_data FROM clux/muslrust:stable as builder RUN groupadd -g 10001 -r dockergrp && useradd -r -g dockergrp -u 10001 dockeruser ARG BINARY_NAME_DEFAULT ENV BINARY_NAME=$BINARY_NAME_DEFAULT # 依赖缓存层 COPY Cargo.lock Cargo.toml ./ RUN mkdir src && echo "fn main() {print!(\"Dummy main\");}" > src/main.rs RUN cargo build --target x86_64-unknown-linux-musl --release # 清理dummy main的编译缓存,匹配正确的二进制名 RUN rm -f target/x86_64-unknown-linux-musl/release/deps/arb_data* # 编译真实二进制 COPY src ./src RUN touch src/main.rs && cargo build --target x86_64-unknown-linux-musl --release RUN mkdir -p /build-out RUN cp target/x86_64-unknown-linux-musl/release/$BINARY_NAME /build-out/ # 运行阶段 FROM scratch COPY --from=builder /etc/passwd /etc/passwd # 复制CA证书 COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ USER dockeruser ARG BINARY_NAME_DEFAULT ENV BINARY_NAME=$BINARY_NAME_DEFAULT ENV RUST_LOG="error,$BINARY_NAME=info" # 显式加可执行权限 COPY --from=builder --chmod=755 /build-out/$BINARY_NAME /$BINARY_NAME EXPOSE 8000 CMD ["/arb_data"]
前置配置与排查方法
- 构建前必须在项目根目录新建
.dockerignore文件,写入以下内容,避免把本地不同架构的编译产物、冗余文件复制进构建上下文:
target/ .git/ .gitignore Dockerfile
- 如果改完仍报错,可以在构建后执行以下命令验证二进制状态:
- 对distroless镜像:
docker run --rm --entrypoint ls <你的镜像名> -l /app,看二进制是否存在、权限位是否包含x(可执行标识) - 本地验证编译产物:在构建阶段容器里执行
ldd /app/target/release/arb_data,动态编译的话会列出所有依赖库,静态编译会提示not a dynamic executable,如果有库找不到就是依赖缺失
- 对distroless镜像:
- 不要依赖COPY的默认权限继承,复制可执行文件时始终加
--chmod=755显式赋予执行权限,避免不同构建环境的权限配置差异导致问题。
内容的提问来源于stack exchange,提问作者streetRAT
相关产品推荐
相关产品推荐

