在GitLab CICD的流水线作业中,发现Rust编译速度是个瓶颈,因为耗费CPU,非常缓慢,一次构建居然需要20几分钟,这大大超出我们的心理预期。
sccache
经过调研,发现sccache可以优化二次编译的速度。本质上是将编译结果以某种方式缓存,下次编译时可以复用。
它可以使用cargo install sccache
直接安装,也可以使用二进制文件,在这里下载sccache-v0.3.0-x86_64-unknown-linux-musl.tar.gz
。也可以用代理加速:archive.fastgit.org/mozilla/scc…。
对于我们而言,我们可以将sccache的编译结果存储在NAS中,这样不同的工作空间就可以共享缓存。
优化
修改GitLab Runner运行时镜像,Rust默认的环境变量,其实就是缓存位置:
FROM rust:1.62.1
WORKDIR /app
ENV TZ "Asia/Shanghai"
# 更改cargo的默认位置,在物理机上通常是~/.cargo,而容器里是/usr/local/cargo
ENV CARGO_HOME="/rust-cache/cargo"
# 更改rustc的编译文件
ENV RUSTC_WRAPPER="/rust-cache/bin/sccache"
# 更改sccache的默认缓存位置,也就是~/.cache/sccache
ENV SCCACHE_DIR="/rust-cache/sccache"
再在K8S使用helm创建GitLab Runner用到的图——gitlab-runner中需要缓存的地方挂载出来:
[[runners.kubernetes.volumes.pvc]]
name = "runner-rust-cache"
mount_path = "/rust-cache/config"
sub_path = "config"
readOnly = true
[[runners.kubernetes.volumes.pvc]]
name = "runner-rust-cache"
mount_path = "/rust-cache/sccache"
sub_path = "sccache"
readOnly = true
[[runners.kubernetes.volumes.pvc]]
name = "runner-rust-cache"
mount_path = "/rust-cache/bin/sccache"
sub_path = "bin/sccache"
[[runners.kubernetes.volumes.pvc]]
name = "runner-rust-cache"
mount_path = "/rust-cache/registry/cache"
sub_path = "registry/cache"
[[runners.kubernetes.volumes.pvc]]
name = "runner-rust-cache"
mount_path = "/rust-cache/registry/index"
sub_path = "registry/index"
这里的runner-rust-cache,是需要挂载的pvc:
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: runner-rust-cache
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
storageClassName: runner-rust-cache
mountOptions:
- vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport
nfs:
server: "xx.amazonaws.com.cn"
path: "/mnt/runner/rust"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: runner-rust-cache
namespace: gitlab-runner
spec:
accessModes:
- ReadWriteMany
storageClassName: runner-rust-cache
resources:
requests:
storage: 10Gi
验证
挂载前,24分36秒:
挂载后7分14秒:
这个时间虽然仍不理想,但差强人意。
mold
mold是现有Unix链接器的更快的替代品。它比LLVM lld链接器快几倍,后者是第二快的开源链接器。mold旨在通过最小化构建时间,特别是在快速调试-编辑-重建周期中,提高开发人员的生产力。
所以重点是,它可以加快链接速度,也就是我们使用cargo build看到控制台一系列输出编译中的最后一步(前面都是编译具体某个包,最后一步是将这些内容整合)。在这里下载:github.com/rui314/mold…,或者换用代理加速:archive.fastgit.org/rui314/mold…。
需要在cargo目录的config中配置:
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/xx/mold-1.4.0-x86_64-linux/bin/mold"]
第一次耗时正常是23分14秒。
加了mold之后是20分30秒,节省了不到3分钟。
有了sccache缓存后,正常是7分54秒:
加了mold以后是6分18秒,节省了一分半。
以下是一个集成了sccache和mold的基础镜像,用来在流水线中构建产物(运行cargo build):
FROM rust:alpine3.17
WORKDIR /app
RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
&& apk update
&& apk add --no-cache musl-dev pkgconfig openssl-dev clang wget make git bzip2 ca-certificates python3 bash build-base coreutils gcompat libgcc libc-dev
RUN mkdir download && cd download
&& wget https://github.com/rui314/mold/releases/download/v1.4.0/mold-1.4.0-x86_64-linux.tar.gz && tar xzvf mold-1.4.0-x86_64-linux.tar.gz && mv mold-1.4.0-x86_64-linux ../mold
&& wget https://github.com/mozilla/sccache/releases/download/v0.3.0/sccache-v0.3.0-x86_64-unknown-linux-musl.tar.gz && tar xzvf sccache-v0.3.0-x86_64-unknown-linux-musl.tar.gz && mv sccache-v0.3.0-x86_64-unknown-linux-musl/sccache ../ && chmod +x /app/sccache
&& apk del -f wget
&& rm -rf /var/lib/apt/lists/* && rm -rf ../download
ENV TZ "Asia/Shanghai"
ENV CARGO_HOME="/rust-cache/cargo"
ENV RUSTC_WRAPPER="/app/sccache"
ENV SCCACHE_DIR="/rust-cache/sccache"
ENV RUSTFLAGS="-C target-feature=-crt-static"
CMD /bin/bash
这里安装了不少依赖,都是我们项目中用到的,读者可以视情况做些删减。
我们线上的cargo配置为:
[source.crates-io]
replace-with = 'tuna'
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/app/mold/bin/mold"]
调整CPU
我们本机构建Rust产物也没有那么慢,为什么流水线就那么慢呢?
相对于前端项目,Rust的构建可谓是非常消耗CPU了。我们的前端项目构建通常在3分钟左右,最长的有10分钟。
所以针对Rust项目,调大runner的CPU,原来是200m:
builds:
cpuLimit: 600m
cpuRequests: 200m
修改为1,相当于从0.2升到1:
builds:
cpuLimit: 1.5
cpuRequests: 1
第一次缩减到5分钟,使用sccache后缩减到3分钟左右,这样差不多就可以忍受了。
总结
在GitLab CICD的流水线作业中,Rust编译速度是个瓶颈,我们通过sccache共享缓存和使用mold加速链接,以及最终调整runner分配的CPU,将一个项目首次耗时从23分钟降到5分钟,后续耗时降到3分钟左右,极大地提高了我们的生产效率。