简单记录一下UXCI 这个Rust命令行工具的发布过程中遇到的问题以及解决方案.
问题: 运行环境中GLIBC缺失
问题的表现是: Linux下的编译结果无法在部分其他环境执行
报错内容为:
uxcirs: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by uxcirs)
这个错误是因为rust使用了动态链接, 因此依赖于系统的glibc库.
编译时使用的GLIBC_2.28
(或者其他版本)在运行时的机器上不存在, 因此无法正常运行.
获取可用的 GLIBC 版本:
ldd --version | head -1
各环境下的版本分别为:
- 镜像
ubuntu:18.04
:ldd (Ubuntu GLIBC 2.27-3ubuntu1.6) 2.27
(sha256:8aa9c2798215f99544d1ce7439ea9c3a6dfd82de607da1cec3a8a2fae005931b) - VPS:
ldd (Debian GLIBC 2.28-10+deb10u2) 2.28
- 镜像
rust:latest
:ldd (Debian GLIBC 2.31-13+deb11u5) 2.31
(sha256:557ff96cf0d2bed8fe24aded88a5dabbca8d71ff4fa66b696ed8a295247c92cc)
因此, VPS上编译得到的文件无法在 Ubuntu 18.04 中运行.
使用rust:latest
镜像编译得到的文件无法在VPS和Ubuntu 18.04中运行.
让运行环境适配可执行文件也是一种选项, 但这个一般是对使用者来说的, 因此这次的需求中可以不用考虑.
而为了让二进制文件能适配更多的运行环境, 大致上有这几种思路:
- 使用静态链接
- 降低glibc版本
使用静态链接
StackOverflow中的 这里 还提到了静态编译glibc, 之后有需要的话可以尝试一下
降低glibc版本
由于Docker的存在, 这个选项实际执行起来还是比较方便的.
最终文件如下:
ci/cargo_config
[source.crates-io]
replace-with = 'mirror'
[source.mirror]
registry = "sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/"
ci/dockerfile.builder
# syntax = docker/dockerfile:experimental
FROM ubuntu:18.04 AS linux-old-glibc
ENV LANG=C.UTF-8 XDG_CACHE_HOME=/var/cache PIP_CACHE_DIR=/var/cache/pip TZ=Asia/Shanghai DEBIAN_FRONTEND=noninteractive
# apt换源 & 避免apt缓存清理
RUN sed -i "s/\\w\+.ubuntu.com/mirrors.aliyun.com/g" /etc/apt/sources.list && \
cat /etc/apt/sources.list && \
mkdir -p $PIP_CACHE_DIR && rm -f /etc/apt/apt.conf.d/docker-clean
# 安装依赖
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && \
apt-get install -y --no-install-recommends \
build-essential curl zip ca-certificates
# 安装rust
RUN curl https://sh.rustup.rs -sSfo rustup.sh && \
bash rustup.sh -y
# 更新PATH
ENV PATH="/root/.cargo/bin:$PATH"
# 配置cargo镜像
COPY ci/cargo_config ./
RUN cat cargo_config >> /root/.cargo/config
# 配置权限
RUN chmod 755 -R /root/.cargo
RUN mkdir -p /app/dist
# 配置工作目录
WORKDIR /app
FROM rust:latest AS linux
ENV LANG=C.UTF-8 XDG_CACHE_HOME=/var/cache PIP_CACHE_DIR=/var/cache/pip TZ=Asia/Shanghai DEBIAN_FRONTEND=noninteractive
# apt换源 & 避免apt缓存清理
RUN sed -i "s/\\w\+.debian.org/mirrors.aliyun.com/g" /etc/apt/sources.list && \
cat /etc/apt/sources.list && \
mkdir -p $PIP_CACHE_DIR && rm -f /etc/apt/apt.conf.d/docker-clean
# 配置cargo镜像
COPY ci/cargo_config $CARGO_HOME/config
WORKDIR /app
RUN mkdir -p /app/dist
FROM linux AS windows
# 安装依赖
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && \
apt-get install -y --no-install-recommends \
g++-mingw-w64-x86-64 curl zip
RUN --mount=type=cache,target=/usr/local/cargo/registry \
rustup target add x86_64-pc-windows-gnu && \
rustup toolchain install stable-x86_64-pc-windows-gnu
WORKDIR /app
RUN mkdir -p /app/dist
然后分别使用 linux-old-glibc 和 windows 的镜像进行编译.
# syntax = docker/dockerfile:experimental
FROM rust-builder:linux-old-glibc AS linux-build
# 安装依赖
COPY Cargo.lock Cargo.toml ./
RUN --mount=type=cache,target=/usr/local/cargo/registry \
cargo fetch
# 执行编译
COPY src ./src
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/app/target \
cargo build --release && \
cp target/release/uxcirs dist/
FROM rust-builder:windows AS build
# 安装依赖
COPY Cargo.lock Cargo.toml ./
RUN --mount=type=cache,target=/usr/local/cargo/registry \
cargo fetch
# 复制代码
COPY src ./src
# 编译windows版本
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/app/target \
cargo build --release --target=x86_64-pc-windows-gnu && \
cp target/x86_64-pc-windows-gnu/release/*.exe dist/
几个可以注意一下的点:
Cargo.toml
中配置了[[bin]]
之后, 可以直接使用cargo fetch
. 或者, 可以写一个空的src/main.rs
cargo build
指定了 –target 时, 生成的文件需要到target/{target}/release/
目录下去获取.
后续可以再参考rust官方镜像, 优化一下构建镜像中的安装方式.