FST Network 如何以 Kubernetes 及 Deno Runtime 打造敏捷、自動化的容器佈署及 FaaS 服務
FST Network Tradition Chinese Blog #2
敬啟讀者: 本文描述的技術細節乃是針對 FST Network 的舊版 LOC 而寫。LOC 仍然在快速演進當中,好滿足客戶的資料需求並迎合技術潮流,因此最新消息和技術分享請參閱我們的官方部落格以及官方產品文件。
在前一篇文中,我們認識了 FST Network 的資料治理方案 Logic Operating Centre (LOC) 是什麼,以及各個主要部分採用了哪些技術。本篇我們則要來更深入檢視 LOC 如何運用 Kubernetes 的威力實現自動容器佈署,並搭配 Deno runtime 來更快速地執行使用者建置的程式 (即資料程序:data processes)。
什麼是 Kubernetes (K8S)?
從單體應用程式到微服務
一二十年前,主流的程式開發是所謂的單體式應用程式 (monolithic application) — — 其前後端包在同一個程式庫中。這對小型程式來說容易管理得多,但當專案的規模開始提高時,開發就會變成惡夢:前端與後端的任何更動都會相互影響。此外,假如要擴大應用程式的服務容量,你就得重複佈署這些大型、複雜的應用程式 (用一個負載均衡伺服器把它們串起來),其實也不是那麼方便。
服務導向架構 (Service-oriented Architecture, SOA) 首先嘗試將後端切割成單元化的服務,但這些後端服務本身仍然是單一一個應用程式。微服務 (microservice) 則是將後端服務個別抽離出來運作,每個服務本身就是一個微型應用程式,這麼一來它們可以個別修改而不影響彼此,而且更容易重複佈署 — — 換言之,更容易提高規模 (more scalable)。
從虛擬機到容器
如果要重複佈署微服務,每台伺服器只裝一個應用程式未免太浪費了。然而,你可能會有用不同語言撰寫和發佈的微服務,就連前端程式也會需要不同的環境。這一切要安裝在不同類型的機器跟系統上,就增加了佈署上的難度。
虛擬機 (virtual machine, VM) 使用所謂的機器層級虛擬化 (machine-level virtualization),在機器上「模擬」一個獨立電腦的作業系統環境,使得你能在任何機器上執行多重虛擬機,並在其中運行不同的應用程式。只是虛擬機既然會模擬整個獨立作業系統,它們對主機處理器及記憶體的佔用也就十分可觀。
作業系統層級虛擬化 (OS-level virtualization) 解決了這種問題:與其在每個虛擬環境模擬作業系統,容器 (container) 引擎會與主機共用 OS,但容器內的應用程式環境是相同的。既然不需要消耗那麼多資源,你就能在同樣的機器上運行更多容器了。2013 年問世的 Docker 並不是第一個容器引擎,卻大大推動了容器的運用。
事實上,IBM 在 2021 年就做過一次測試 — — 在一組由四台伺服器組成的環境中,它可以執行 8 個虛擬機或者 33 個容器!也就是說,每個容器需要的資源大概只有虛擬機的四分之一而已。此外測試也證實,在同樣的服務容量之下,容器的營運成本僅有虛擬機的 25%。
容器是由映像檔 (image) 來建立的,而假如應用程式有更新,只要產生新的映象檔和重新佈署即可,任何人也能在任何地方產生同樣的容器。換言之,你能在伺服器或雲端環境建立大量的微服務容器來應付任何流量。這便可以理解,為何 Docker 這類容器會開始變得如此受歡迎了。
容器管理:Container Orchestration
不過,要執行並管理大量的容器仍然有維運上的問題。倘若你得啟用數十個、乃至數百個微服務容器,更別提用來執行前端網站、負載平衡伺服器等等的容器,單靠人工想必會是一大挑戰。你怎麼知道哪些容器掛掉了,又要怎麼把服務容量補救回來?
Google 開源專案 Kubernetes (希臘文「舵手」之意) 起源於 Google 的兩個內部程序管控系統,Borg (2003,《星際迷航記》系列的博格人) 與 Omega (2013)。據了解,Borg 能在數以萬計的伺服器上管理數十萬個程序,其威力便可見一班。而 Kubernetes 於 2016 年正式推出後,很快就被微軟、亞馬遜等企業採用。
簡單來說,Kubernetes 是一套容器管控系統 (正式的說法是它會做 container orchestration):你只需告訴它,你需要讓多少容器同時運作,Kubernetes 就會自行啟動對應數量的容器,並在任何容器掛掉時重啟它。你甚至可以在不撤回應用程式的前提下,讓 Kubernetes 逐次更新容器版本。
也就是說,Kubernetes 能確保容器化應用程式的穩定運行,能在不中斷服務下更新軟體,也能輕易擴大服務規模。
一個著名的例子是 2016 年,手機遊戲 Pokémon GO 在澳洲與紐西蘭上線 15 分鐘後,爆量的玩家流量達到最初預估上限的 50 倍。剛好 Pokémon GO 是佈署在 Google 自家的 GKE (Google Kubernete Engine) 平台上,因此 Google 透過緊急調度伺服器而穩住了服務。
遊戲同樣順利撐過隔天在美國上線、以及在該月月底於日本上線 (其流量是美國的 3 倍)。這展示了 Kubernetes 如何能在極端的例子下急速擴張服務規模。
根據 CNCF (Cloud Native Computing Foundation) 在 2021 年 Q1 調查,全球有 6800 萬名雲端工程師,其中 5600 萬使用過 Kubernetes。CNCF 同時也估計全球有 2 億 6800 萬名開發人員,所以大約每 5 名開發者中就有 1 人在用 Kubernetes!
LOC 如何使用 Kubernetes 來自動佈署資料程序
資料程序是自動打包的自定義容器
前面提到,我們可以告訴 Kubernetes 我們希望啟動哪些容器。也就是說,我們只需要撰寫一份定義檔,而無須實際手動啟動容器。
Kubernetes 支援數種原生容器規格,但我們也可以定義自己的容器,並告訴 Kubernetes 要如何操作它們。這個模式稱為 Operator pattern,由兩部分組成:
- Custom Resource Definition (CRD) — — 自訂容器的定義檔
- Controller 或 control loop — — 一段自訂程式,讓 Kubernetes 知道由 CRD 產生的容器要如何使用
你可以試想 Operator pattern 是在模擬一個人類操作員 (operator):CRD 定義了他能使用的資源,Controller 則是其操作手冊。自訂容器本質上和 Kubernetes 原生容器並無不同,但開發者可以用更有彈性的方式在其上建置功能。
使用者在 LOC 中佈署的每個資料程序,本身就是一個以 CRD 產生的自定義容器。最特別的是,這些 CRD 定義檔與 Controller 並不是事先由開發者撰寫,而是在佈署時由 LOC 動態產生,並交由 Kubernetes 來佈署。
簡單來說,LOC 的容器「打包」過程如下:
- 使用者按下資料程序佈署,其設定檔被上傳到 LOC 的內部服務 Orchestrator,而程式碼則傳送到一個雲端資料庫保存。
- 使用者同時佈署 API route,它會在 LOC 的 API Gateway 容器中產生 API 端點,並和資料程序容器對應起來。
- Orchestrator 服務自行產生 CRD 定義檔以及 Controller。
- Kubernetes 根據新的 CRD 定義檔產生資料程序容器 (並注入使用者的程式 — — 參見下一節),這個容器會連接像是 API gateway、session storage/event store 等 LOC 內部服務,以便被使用者呼叫及使用。
- 現在使用者即可使用任何 HTTP client 工具來透過 API route 觸發 (執行) 資料程序。
如果你想知道的話,LOC 目前使用的容器引擎是 CRI-O。資料程序容器的基底映像檔則會儲存在 Amazon ECR 或是本地 Harbor 伺服器上 (FST 團隊可協助客戶建置自己的 Harbor 服務)。
在網路上的大部分教學材料中,人們常用 Go 語言來撰寫自訂容器的 Controller。不過,LOC 產生 CRD 定義檔及 Controller 的功能,都是 FST 開發團隊以 Rust 語言自行撰寫的。
Deno Runtime 與「邏輯注入」
自動產生容器映像檔並佈署並非新玩意;你可以用像是 Jenkins 之類的工具來達成目的。問題在於,這麼做得花時間 (數分鐘到數十分鐘),對於想要快速看到結果的客戶來說,恐怕只會把耐心磨光。
因此,與其在每次佈署資料程序時都產生一個映像檔,LOC 會直接沿用同一個映像檔。
LOC 的資料程序映像檔中會包含一個 Deno runtime — — 這是一個可執行 JavaScript 或 TypeScript 程式的引擎,有點像一個無介面的瀏覽器環境 — — 並在佈署後將使用者的程式「注入」到該 runtime 中。換言之,所有資料程序都是同樣的容器,只有裡面的資料邏輯程式不一樣而已。
Deno 是由 Node.js 的開發者所設計,如今被視為 Node.js 之外的另一個選擇。Deno 的好處是它是一個可攜式的二進位檔,而且直接支援 JavaScript 與 TypeScript。
當然,LOC 資料程序容器裡面不只是有 Deno runtime 而已,也會有一些以 Rust 撰寫的後端服務,用來銜接 runtime 內的 agents (用來連接內外部服務的驅動介面)。但這裡就不多討論了。
於是,LOC 資料程序容器僅需數秒時間就能產生完畢,而且在幾分鐘內即可被 Kubernetes 偵測到並執行。
事實上,在像是 Amazon Lambda、Google Functions 這類 FaaS (Function as a Service) 平台服務中,也存在所謂的 cold start (冷啟動) 問題 — — 當使用者閒置一段時間後重新呼叫函式,平台就得花時間重新佈署它,而這可能導致呼叫後得等待數十秒甚至更長的時間,也有可能導致呼叫端逾時。
相對的,LOC 資料程序是在客戶的 Kubernetes 環境運作,所以隨時可以回應呼叫。Kubernetes 會確保容器掛掉時重新啟動,進一步確保了服務的穩定性。
資料程序服務是一種更強大的 FaaS
現在我們回頭來再次看資料程序。LOC data process 與一般 FaaS 服務的最大不同,便是一個資料程序可以由多個函式組成:
- 如前篇文章提過,資料程序可由 API 端點、排程器或訊息佇列來觸發 (而且可以依序觸發多個資料程序)。
- 首先會照順序執行 generic logic,每一個可以處理特定的作業、存取內外部服務。
- 最後由 aggregator logic 傳回一個結果或處理狀態。
- 當其中一個 logic 發生錯誤時,資料程序仍會完成執行,但從錯誤發生處之後的所有 generic/aggregator logic 都會進入錯誤處理模式。
既然能由多重函式構成,一個資料程序本身就能涵蓋複雜的商業邏輯,無須拆成零散的功能。而使用者想要如何組織資料邏輯函式,是可以非常自由的。例如,你能將從讀取呼叫者 payload 的功能放在第一塊,第二塊查詢資料庫,第三塊檢查事件,第四塊拿來做轉換,第五塊將結果傳給別處的 API…
因此,從這種將資料程序視為服務的角度來看,我們或許可使用一個詞 LFaaS (Logic Functions as a Service) 來區別化 — — 畢竟 LOC 所在做的,正是提供一個能自動佈署資料程序的平台,使用者操作 LOC 就和使用其他 FaaS 服務並無不同。話說回來,資料程序能做得更多和更有彈性,更適合現代企業用於整合自己的資料流。
產生的結果同樣可以用多種方式回傳給使用者,例如從 API 端點或訊息佇列,不過在此先不多討論。
Local Simple Runtime
前一篇文章我們也提過,開發者可以使用 LOC CLI 命令列工具在本機撰寫並上傳資料程序,或者透過稱為 simple runtime 的本機環境來測試。
事實上,這個本機環境會使用 Docker Compose 來建置 (本質像是簡易版的 Kubernetes),它會包含 LOC 的最基本服務。除此之外,使用者也要下載資料程序的映像檔。
當開發者執行資料程序時,你可以看到一個新的資料程序容器被產生出來,而且被執行一次。它將能使用這個本機環境的一系列服務,所以運作起來就和在雲端幾乎沒有兩樣。
結語
本篇我們看了 LOC 的一些幕後運作,了解它如何利用 Kubernetes 及 Deno runtime 來實現快速且自動化的容器佈署,讓使用者撰寫的第三方程式能夠在雲端運作,扮演串接既有企業資料或服務的「水管」角色。
拜 Kubernetes 之賜,LOC 可以佈署在任何地方,而不管是 LOC 本身的服務或資料程序都能保持出色的可靠性。而這是因為這些設計,LOC Studio 的使用者能在完全無需了解維運層面的前提下開發資料程序。
當然,儘管 LOC 的骨幹已經大致開發完成,它的演進仍在持續中。新的技術優勢會被持續整併進來 — — 例如,採用更新版本的 Kubernetes 來改良容器管理,或者像是 FST 開發團隊已經有在考慮將 Deno runtime 換成一個 WebAssembly (WASM) runtime,這麼一來使用者就能用種類更加廣泛的程式語言 (例如 Rust、Go、Java、Python 等) 來撰寫資料程序。但不論是什麼改變,都是為了能讓使用者更便利地建置及佈署資料程序。
本文的材料要感謝 FST Network 開發團隊鼎力協助.
欲進一步了解 Logic Operating Centre (LOC) 和 FST 團隊提供的豐富支援嗎?歡迎來信 support@fstk.io。