Rust 二进制在 musl 下体积翻倍的一次排查
上周把一个内部 CLI 从 glibc 目标切到 x86_64-unknown-linux-musl,准备塞进 distroless 镜像。同一份代码,glibc 下 release 产物是 4.1 MB,musl 下变成 9.3 MB。不是可执行文件大小的问题,是它还要跑在内存只有 256 MB 的边车容器里,体积和启动时的页加载都得算。
先确认不是 debug 信息漏了。Cargo.toml 里已经写死:
[profile.release]
opt-level = "z"
lto = "fat"
codegen-units = 1
panic = "abort"
strip = "symbols"
file target/x86_64-unknown-linux-musl/release/cli 显示 not stripped。原因是 musl 目标默认的 linker 和 glibc 不同,strip = "symbols" 在交叉链接时没有作用到最终的静态链接产物。手动补一刀:
cargo build --release --target x86_64-unknown-linux-musl
x86_64-linux-musl-strip target/x86_64-unknown-linux-musl/release/cli
体积从 9.3 MB 降到 6.8 MB,还是比 glibc 大。剩下的差距来自 musl 自己:静态链接把 libc 整块链进来,而 glibc 版本是动态依赖。这不是配置写错,是取舍。
我试过用 -C link-arg=-s 在 .cargo/config.toml 里全局加:
[target.x86_64-unknown-linux-musl]
rustflags = ["-C", "link-arg=-s"]
有效,但会让 cargo build 的增量产物直接不可调试,本地排查 panic backtrace 时全是地址。后来改成只在 CI 的打包阶段用环境变量注入,本地保持带符号。
失效的地方有两个。一是如果项目依赖了需要动态加载的 crate,比如某些绑定系统库的包,musl 静态目标会在链接期报 undefined reference,这时候 -s 只会让错误更难读。二是 panic = "abort" 加上 strip 之后,线上 panic 只剩一个 abort 信号,日志里没有行号,只能靠版本号回滚定位。我现在的做法是 release 保留 panic = "unwind",只在最终镜像层 strip,多出来的 unwind 表大约 200 KB,换一次可用的 backtrace。
另外 opt-level = "z" 在这个 CLI 上比 "s" 小了 5%,但解析 JSON 的热路径慢了约 8%,用 criterion 跑本地基准能看出来。最后我改回 "s",因为体积收益不值得。
你们在 musl 静态目标上会保留 backtrace 吗,还是干脆用 panic = "abort" 加外部崩溃采集?
0 条评论
还没有评论,写下第一条。