教學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 asset 的所在位置。

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 反序列化。這裡只是為了 demo 所以使用 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