Deploy Rust Code Faster

Matthias Endler

更快速部署 Rust 程式碼

在技術這條路上,我已經走了很長一段,從處理裸機伺服器到探索雲端運算的世界。起初,一切看似如此簡單——啟動一台伺服器、部署一個容器,就大功告成了。但隨著深入研究,我才發現基礎設施的簡易性並非表面上看起來那麼單純。

雲端服務供應商提供了琳瑯滿目的工具,每一套都有各自的學習曲線:

  • Google Cloud / AWS
  • Kubernetes
  • Helm
  • Docker
  • Terraform
  • GitHub Actions

如果你喜歡冒險,甚至可能會嘗試像 EKS 或 GKE 這類的 Kubernetes 代管服務。只要點幾下,應用程式就能上線,確實很誘人。但當你開始同時處理監控、日誌、安全、擴展等工作時,現實的挑戰才真正浮現。

很快地,你會發現自己在不知不覺中帶領起一個 DevOps 團隊,而不是專注在產品本身。你需要聘請更多人力來管理基礎設施,而你的競爭對手卻在持續推出功能、不斷擴大用戶群。

我的挫折

雲端曾承諾讓基礎設施變得簡單,但繁多的工具與服務卻令人眼花撩亂。即使你不會全部使用,也必須知道它們的存在並學習基礎知識。結果是什麼?就是你對產品的專注度被削弱了。

我喜歡處理基礎設施,但我也熱愛打造產品。可惜的是,許多公司在基礎設施上浪費了寶貴的時間與金錢,不斷重蹈覆轍。

如果有辦法完全消除對基礎設施的顧慮,會怎麼樣呢?

Serverless(無伺服器)的魅力

Serverless 架構看起來很有前景——沒有伺服器、沒有容器,只有純粹的商業邏輯。然而,它也並非沒有挑戰:

  • 冷啟動時間
  • Lambda 大小限制
  • 記憶體問題
  • 長時間執行的處理程序
  • 除錯的複雜度
  • 缺乏本地測試

Serverless 在特定使用情境下有其優點,但對於較大型的應用程式,你可能仍然需要一些伺服器。

Platform-As-A-Service(平台即服務) (PaaS)

像 Heroku 和 Netlify 這類平台帶來了第三種選擇——為你處理所有基礎設施的代管服務。你不再需要擔心基礎設施,只要推送程式碼,就能完成部署。這些解決方案最棒的地方在於,它們與特定的程式語言生態系深度整合。

我一直在尋找一個為 Rust 開發者量身打造、旨在提供頂級開發體驗的平台。我希望它能與 Rust 生態系(serde、sqlx、axum……)深度整合。

前陣子,我在嘗試讓 Rust 開發流程更順暢的方法時,偶然發現了 Shuttle。它是一個能自然融入現有 Rust 生態系的工具,讓你照常使用 cargo,同時幫你省去一些繁重的基礎設施工作。當然,它不是能解決所有問題的魔法棒,但我欣賞 Shuttle 的簡潔之處。你不會被丟進一個全新、學習曲線陡峭的環境中。相反地,你只需專注於 Rust 程式碼,而 Shuttle 則在背後協助處理一些伺服器端的複雜性。所以,本質上,它是讓你堅守熟悉的事物,同時在部署與伺服器管理方面讓生活變得稍微輕鬆一些。這不是要徹底改變你寫程式的方式,而是以更細微的方式轉變那些有時令人頭痛的背景流程管理。

至今在 Shuttle 上的體驗

到目前為止,我已經用 Shuttle 打造了兩個較小型的 Rust 服務:Zerocal 和 Readable。

Shuttle 只要透過極少的標註,就能將你的 Rust 程式碼部署到雲端。考量到資源配置與部署通常是打造服務過程中最痛苦的環節,這樣的開發體驗已經相當接近理想。

相反地,你只需要加入幾行程式碼。眼見為憑。樣板程式碼就此消失,剩下的只有商業邏輯。

Zerocal - 無狀態行事曆魔法

Zerocal 是我在 Shuttle 上部署的第一個專案。它的原理非常簡單卻富有創意:直接將行事曆資料編碼到 URL 中。這意味著建立活動就像這樣簡單:

curl https://zerocal.shuttleapp.rs?start=2023-11-04+20:00&duration=3h&title=Birthday&description=paaarty

這會回傳一個 iCal 檔案,你可以將它加入到行事曆中。以下是如何在瀏覽器中建立活動:

我在 Shuttle 還在修復一些問題、API 也時常變動的階段就嘗試打造這個專案。即使有這些小問題,整體仍是一次不錯的體驗。短短幾分鐘內,我的應用程式就成功上線運作了。

以下是包含 axum 路由、用來啟動服務的程式碼:

#[shuttle_runtime::main]
async fn axum() -> shuttle_axum::ShuttleAxum {
    // just normal axum routes
    let router = Router::new()
        .route("/", get(calendar))
        .route("/", post(calendar));

    Ok(router.into())
}

我自己已經不太需要 Zerocal 了,所以希望能有其他人願意接手。我認為它對於在 GitHub 或 Discord 這類地方分享邀請會非常實用。如果你想更了解 Zerocal,可以閱讀這篇詳細解析

我也想提一下,有人受到 Zerocal 的啟發,用 Deno 打造了類似的專案:由 Mahesh Sundaram(馬赫什·桑達拉姆) 開發的 kiwi。這是一個很棒的成果。

為我的電子閱讀器打造的閱讀模式

我對 Firefox 閱讀檢視 的喜愛,催生了 Reader Mode Proxy,它提供極簡、無 JavaScript 的網頁閱讀體驗,特別為電子閱讀器量身打造。其目的是將冗長的網站轉換為更易於閱讀、讓人能專心閱讀的格式。

這個專案深深反映了我的個人偏好,因為我喜歡能解決問題的簡潔應用程式。只要加上少許標註,我的程式碼就能順利適配 Shuttle 的環境。起初,我有自己的本地模式,讓我能在本機上執行應用程式進行測試,但我發現沒有必要繼續維護它,因為 Shuttle 自身的本地模式運作得同樣出色。

在開發應用程式的過程中,也遇到了一些波折。服務停機需要對程式碼進行一些重構。然而,Shuttle 的演進簡化了我的部分流程,特別是在它推出原生的靜態檔案處理功能之後。

之前看起來像這樣:

#[shuttle_runtime::main]
async fn axum() -> shuttle_axum::ShuttleAxum {
    let router = Router::new()
        // Previously, I needed to manually serve static files
        .route(
            "/static/Crimson.woff2",
            get(|| async {
                static_content(
                    include_bytes!("../static/fonts/Crimson.woff2",),
                    HeaderValue::from_static("text/woff2"),
                )
            }),
        )
        .route(
            "/static/JetBrainsMono.woff2",
            get(|| async {
                static_content(
                    include_bytes!("../static/fonts/JetBrainsMono.woff2",),
                    HeaderValue::from_static("font/woff2"),
                )
            }),
        )
        .fallback(readable);

    Ok(router.into())
}

現在則簡化為:

#[shuttle_runtime::main]
async fn axum() -> shuttle_axum::ShuttleAxum {
   let router = Router::new()
        .nest_service("/static", ServeDir::new(PathBuf::from("static")))
        .fallback(readable);
    Ok(router.into())
}

想了解這個專案的細節,請參考這篇更完整的介紹

掌控與安全

一開始,我擔心為基礎設施而標註程式碼會導致廠商鎖定。我希望能對專案保有完全的掌控權。想搬走嗎?Shuttle 的巨集幫你省去了樣板程式碼,所以我只要移除加入的 2 個標註,就能恢復成原本的程式碼。Shuttle 的程式碼本身也是開源的,所以我甚至可以自行架設自託管的執行個體——雖然我並不想這麼做。

自建基礎設施的真正成本

基礎設施表面上看起來似乎很簡單,但維護它卻涉及各種複雜性與成本。更新、部署、可用性——這些都可能讓人難以招架。花在這些任務上的每一小時,都同時帶來直接成本與機會成本。

基礎設施就像一座迷宮,而 Shuttle 似乎很適合使用 Rust 的開發者。現在我對 Shuttle 的能力與限制已有了相當的了解,打算近期在 Shuttle 上嘗試一個更大的專案。如果你考慮試試看,不妨先查看他們的定價,確保它符合你的需求。

請留意基礎設施的真正成本!

如我先前提過的,這不僅僅是伺服器成本,還有更多。最大的因素可能是維護與除錯基礎設施所需的人力,而這是非常昂貴的。如果我要使用 infrastructure as code(基礎設施即程式碼),光是架設基礎設施就要花上許多小時,後續維護更要花上更多時間,以現今的薪資水準來看,這可是所費不貲。

即使只是為了興趣專案,對我來說也不值得如此大費周章。我寧願專注於開發功能,也不願把時間花在撰寫支撐這一切運行的程式碼上。

原文由 Matthias Endler 發布

本文章由 muse-spark-1.2-contributor 進行翻譯