zzxworld

使用 Docker 搭建 Rust 开发环境

最近对 Rust 编程语言有了兴趣,特别是其标榜的无 GC 和内存安全特性。Rust 官网提供了一个 Playground,可以零成本的在线尝试这门语言。通过这个工具搭配官方教程掌握了一点基础语法后,我决定在本地搭建一个开发环境来深入了解一下 Rust。

我使用 macOS 系统,按照官网安装说明,只需要在终端执行下面这条命令即可完成 Rust 的安装:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

不过鉴于我目前的开发环境都是基于 Docker,所以我还是准备用 Docker 来搭建一个本地 Rust 开发环境,这样如果不想学了清理起来也简单,对系统文件的干扰也最小。

首先创建一个给 Rust 工具使用的服务目录:

mkdir -p ~/Service/rust

然后在这个目录下创建 Dockerfile,内容如下:

# 拉取最新的 rust 镜像
FROM rust:latest

# 设置 rustup 命令的国内镜像变量
ENV RUSTUP_UPDATE_ROOT="https://mirrors.ustc.edu.cn/rust-static/rustup" \
    RUSTUP_DIST_SERVER="https://mirrors.ustc.edu.cn/rust-static"

# 添加自定义镜像构建脚本
ADD build.sh /tmp/build.sh

# 执行镜像构建脚本
RUN chmod +x /tmp/build.sh && /tmp/build.sh

然后创建构建镜像的自定义脚本: build.sh,代码如下:

#!/bin/sh

set -x

# 创建 Cargo 软件包目录
mkdir -vp ${CARGO_HOME:-$HOME/.cargo}

# 设置 cargo 软件包的国内镜像
cat << EOF | tee -a ${CARGO_HOME:-$HOME/.cargo}/config.toml
[source.crates-io]
replace-with = 'ustc'

[source.ustc]
registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"

[registries.ustc]
index = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"
EOF

# 镜像内系统国内软件源配置
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources

# 安装依赖
apt-get update
apt-get install gcc-x86-64-linux-gnu -y

# 安装交叉编译目标所需对象
rustup target add x86_64-unknown-linux-musl

# 安装编译热更新工具
cargo install cargo-watch

# 清理镜像打包缓存和临时文件
apt-get autoclean
rm -rf /var/lib/apt/lists/*
rm -rf /tmp/*

上面的自定义镜像构建脚本代码有点多,都是根据我需要添加,主要涵盖三个目的:

  1. Rust 使用 cargo 命令安装第三方包,默认从国外下载速度较慢,所以需要使用国内的镜像,上面使用了中科大提供的镜像。
  2. Rust 作为编译语言,最终生成的是一个可执行文件,这个文件跟编译时的系统有关。比如我默认拉取的是 ARM 版本的系统,最终编译出来的可执行文件就只能在 ARM 环境运行。如果想在 x86 环境运行,就需要用到交叉编译,所以我通过 rustup 命令添加了 x86 的编译目标。
  3. Rust 作为编译语言,每次修改代码后都需要重新编译才能看到变更效果,用惯了动态语言的我对这点非常不习惯,所以通过 cargo 命令安装了 cargo-watch 这个神器,后续通过它可以实现修改后自动重新编译。

完成以上准备后,使用 docker 命令打包镜像:

docker build \
    --no-cache --progress plain \
    -t zzxworld/rust:dev .

至此我的 Rust 开发环境基本准备妥当,通过 docker 命令已经可以体验在本地编译运行 Rust 代码的乐趣:

docker run --rm -it \
    -v ${PWD}:/srv/app \
    -w /srv/app \
    zzxworld/rust:dev cargo run

但每次都要输入这么长的 Docker 命令也太麻烦了,所以我创建了一个自定义 cargo 命令:

#!/bin/bash

# 创建 cargo 命令本地软件库目录
CARGO_REGISTRY_PATH=$HOME/Services/rust/cargo/registry
if [[ ! -d $CARGO_REGISTRY_PATH ]]; then
    mkdir -p $CARGO_REGISTRY_PATH
fi

# 镜像名称
DOCKER_IMAGE="zzxworld/rust:dev"

# 容器运行参数
DOCKER_ARGS=" \
    -u $UID:$UID \
    -v ${PWD}:/srv/app \
    -w /srv/app \
    -v ${CARGO_REGISTRY_PATH}:/usr/local/cargo/registry"

if [[ $1 == 'stop' ]]; then
    # 停止运行的 rust 容器
    docker ps | grep 'rust-cargo' | awk '{print $1}' | xargs docker stop
elif [[ $1 == 'build' && $2 == '--to-x86' ]]; then
    # 交叉编译到 x86 环境的命令
    docker run --rm -it ${DOCKER_ARGS} \
        -e RUSTFLAGS="-C linker=x86_64-linux-gnu-gcc" \
        -e CC_x86_64_unknown_linux_musl="x86_64-linux-gnu-gcc" \
        ${DOCKER_IMAGE} cargo build -r --target=x86_64-unknown-linux-musl
else
    # 执行 cargo 命令
    docker run --rm -it ${DOCKER_ARGS} \
        -p 8000:8000 \
        --name rust-cargo \
        ${DOCKER_IMAGE} cargo $@
fi

把这个文件命令为 cargo.sh,然后添加可执行权限,再到系统环境变量配置文件中映射为 cargo 命令:

alias cargo='~/Services/rust/bin/cargo.sh'

这样我就可以在任何地方直接使用 cargo 命令,和官方安装后的效果一致,比如创建一个新项目:

cargo new zzx-blog

或是直接运行项目:

cargo run

当然,我还是觉得通过 cargo watch 检测文件修改后自动编译的体验更好:

cargo watch -x run

除了以上正常的 cargo 命令,上面脚本中我还添加了两个扩展命令,一个是:

cargo stop

添加这个命令的原因是容器中运行的 cargo 命令无法通过 Ctrl + c 快捷键终止,只能通过停止容器的方式来结束容器内程序进程。

另外一个是:

cargo build --to-x86

扩展这个命令是为了方便执行交叉编译命令,不然老是要输入一大堆交叉编译参数也是很难受的。Rust 还有一些其他工具命令,不过目前我都还用不到,所以先就这样吧。

上一篇

没有了