教程Rust

部署 Rust 应用程序

Zeabur 原则上支持所有类型的 Rust 应用程序——默认情况下会自动查找运行文件;如果项目有多个运行文件,也可以在“Service 名称”一栏(或者是使用 ZBPACK_RUST_ENTRY)自行指定要运行的文件名称。

如果你手边没有项目,可以 fork zeabur/axum-template 仓库到你的 GitHub 账户,然后在 Zeabur 上试着部署。

支持的项目类型

  • 单运行文件项目:即运行文件只有 src/main.rs 一个入口的 Rust 项目。Zeabur 会自动查找 cargo install 之后的产出的第一个运行文件。
  • 多运行文件项目:如果除了 src/main.rs,项目在 src/bin 也有其他运行文件,请在“Service 名称”一栏填写“你想运行的最终产物的名称”,或者是在 zbpack.json 填写这个设置
    {
      "rust": {
        "entry": "your-entry"
      }
    }
    你也可以使用环境变量 ZBPACK_RUST_ENTRY 达到同样的效果。
  • Cargo Workspace monorepo:如果你的项目是由多个 crate 组成,请在“Service 名称”一栏填写“你想运行的最终产物的名称”。默认情况下会 cargo install workspace 下的 所有项目,你除了可以通过上述 ZBPACK_RUST_ENTRY 的方式指定运行文件名称外,也可以在 zbpack.json 直接指定这个应用程序 workspace 所在的位置。 如果你的应用程序在 Repository 根目录的 rest-api 文件夹下,你可以这样填写:
    {
      "rust": {
        "app_dir": "rest-api"
      }
    }
    你也可以使用环境变量 ZBPACK_RUST_APP_DIR 达到同样的效果。

如果你填写的运行文件不存在,则会改用自动查找的形式。“自动查找”会查找编译出的所有运行文件,并运行找到的第一个文件。因为这种模式在多项目情况下很容易猜错,因此在复杂的项目场景下(如第二和第三点提到的多运行文件项目),建议你根据〈查找最终产物名称〉的方式填写正确的“Service 名称”。

查找最终产物名称

最终产物可以在你项目的 target/release 中找到。具体查找的方式:

  1. 你需要 Rust toolchain 才能编译项目,可到 Rust 官方网站 下载。

  2. clone 你要部署的项目,并且进到项目目录。

  3. 运行 cargo build --release 编译项目。

    $ cargo run --release
    Compiling zeabur v0.1.0 (/project)
      Finished release [optimized] target(s) in 0.65s
      Running `target/release/zeabur`
  4. 找到 Running target/release/zeabur 那一行,提取最后的运行文件名称 (zeabur) 即可

  5. 在 Service 名称或 ZBPACK_RUST_ENTRY 填入 zeabur

設定的最终产物最后会被命名为 /app/main

复制其他文件

如果你除了运行文件之外,还依赖代码库的其他文件。举例来说:

*
|`-- public  <-- 你的程序依赖这个文件夹
|`-- src
|  `-- main.rs
|`-- Cargo.toml
|`-- Cargo.lock
`--- config.toml   <-- 你的程序依赖这个文件

你可以在 zbpack.json 中填写 rust.assets,里面填入你想保留的文件夹或文件,文件夹可以选择用 / 结尾。范例如下:

{
  "rust": {
    "assets": [
      "public/",
      "config.toml"
    ]
  }
}

自定义编译和启动命令

Zeabur 在 Rust 中提供了「编译命令」、「启动命令」和「启动前命令」,方便你在 Rust 项目的各个构建阶段插入所需的依赖、写入必要的文件。

编译命令

你可以使用 ZBPACK_BUILD_COMMAND 指定 编译前要运行的命令,例如:

ZBPACK_BUILD_COMMAND=apt update && apt install -y sqlite3 && rm -rf /var/lib/apt/lists/*

也可以使用 zbpack.json 指定:

{
  "build_command": "apt update && apt install -y sqlite3 && rm -rf /var/lib/apt/lists/*"
}

启动命令

你可以使用 ZBPACK_START_COMMAND 覆盖 Zeabur 默认指定的启动命令,例如:

ZBPACK_START_COMMAND="/app/main --debug"

也可以使用 zbpack.json 指定:

{
  "start_command": "/app/main --debug"
}

启动前命令

如果你需要在启动前安装一些依赖,可以使用 ZBPACK_PRE_START_COMMAND 指定要在启动前运行的命令:

ZBPACK_PRE_START_COMMAND="apt update && apt install -y sqlite3 && rm -rf /var/lib/apt/lists/*"

也可以使用 zbpack.json 指定:

{
  "pre_start_command": "apt update && apt install -y sqlite3 && rm -rf /var/lib/apt/lists/*"
}

ZBPACK_START_COMMAND 相比,它会持久运行的内容,不会在每次重新启动时重新执行,从而提高速度。

自动安装依赖

如果你的项目依赖 OpenSSL 且有在 Cargo.toml 中显式依赖(或 Cargo.lock 间接依赖)openssl,Zeabur 会自动安装 openssl 运行时库。

运行环境

Zeabur 目前使用 rust:1-silm 作为运行镜像。你的运行文件会放在 /app 目录,并将 /(根目录)作为运行目录。

rust.assets 中的文件会放在 /app 目录下,即与可运行文件在同一目录。如果你有读取 assets 的需求,通常实务上会从 相对于可运行文件的路径 读取文件。如下例子会获取你 images 资源的所在位置。

use std::env;
use std::path::Path;
 
fn main() {
    let exe_path = env::current_exe().expect("Failed to get current executable path");
    let exe_dir = exe_path.parent().expect("Failed to get executable directory");
    let relative_path = exe_dir.join("images");
 
    println!("config.toml path: {:?}", relative_path);
}

注意,如果是配置文件,实务上通常从运行目录获取。考虑到运行目录是根目录,在使用 Config Editor 新增配置文件时,请填写 /config.toml(而不是 /app/config.toml),然后从运行目录读取文件:

use std::env;
use std::fs::File;
use std::io::{self, Read};
use std::path::PathBuf;
 
fn main() -> io::Result<()> {
    let current_dir = env::current_dir()?;
    let config_path: PathBuf = current_dir.join("config.toml");
 
    // 这里推荐使用 serde 进行反序列化。这里只是为了演示所以使用 read_to_string。
    let mut file = File::open(config_path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
 
    println!("Config file contents:\n{}", contents);
 
    Ok(())
}

Serverless 部署

不同于其他 PaaS 平台是直接将你的 Rust 应用程序打包在 Docker 容器中运行,Zeabur 同时支持使用 Serverless 部署 Rust 的方式,这可以让你的 Rust 应用程序只有在收到请求的时候才启动,而不是像容器一样一直运行,浪费资源。

如果想要用 Serverless 部署 Rust 应用程序,请在环境变量中加入 ZBPACK_SERVERLESS=1