简单记录一下UXCI 这个Rust命令行工具的发布过程中遇到的问题以及解决方案.
问题: 运行环境中GLIBC缺失
问题的表现是: Linux下的编译结果无法在部分其他环境执行
报错内容为:
1
| 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中运行.
让运行环境适配可执行文件也是一种选项, 但这个一般是对使用者来说的, 因此这次的需求中可以不用考虑.
而为了让二进制文件能适配更多的运行环境, 大致上有这几种思路: 1. 使用静态链接 2. 降低glibc版本
使用静态链接
StackOverflow中的
这里 还提到了静态编译glibc, 之后有需要的话可以尝试一下
降低glibc版本
由于Docker的存在, 这个选项实际执行起来还是比较方便的.
最终文件如下:
ci/cargo_config
1 2 3 4 5
| [source.crates-io] replace-with = 'mirror'
[source.mirror] registry = "sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/"
|
ci/dockerfile.builder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| 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
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
RUN curl https://sh.rustup.rs -sSfo rustup.sh && \ bash rustup.sh -y
ENV PATH="/root/.cargo/bin:$PATH"
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
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
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 的镜像进行编译.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 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
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官方镜像 , 优化一下构建镜像中的安装方式.