Kubernetes在Serverless 時代的工作負載:架構、平臺和趨勢

本文要點

  • 微服務架構已經演化爲雲原生架構,其中由Kubernetes提供了許多基礎設施關注點,並結合了服務網格和serverless 框架提供的額外抽象。
  • Kubernetes的傑出之處在於它通過Pod抽象爲新的工作負載提供了可擴展性,同時,這也支持出現了新的雲本地應用程序模式
  • Kubernetes不僅支持無狀態應用程序,還支持有狀態工作負載、單例、批處理作業、cron作業,甚至通過CRDs和operator支持serverless 和自定義工作負載。
  • 今天的serverless 平臺有明顯的限制,阻礙了具有很強的互操作性和可移植性約束的企業公司採用它們。
  • 通過標準的探索和開放的打包、運行時和事件格式,serverless 生態系統正在不斷髮展。這些領域的進步正在模糊雲原生工作負載和serverless 工作負載之間的差異,並且已經將serverless 的產品推向開放、可移植和可互操作的框架。

我在2019年紅帽峯會上發表的“serverless 世界的集成模式”演講的靈感來自於客戶提出的問題,他們問,在當前由Kubernetes主導的企業雲原生環境中,serverless 工作負載適用於何處。

本文總結了上述討論,但未太多關注集成,而是更深入地研究了不同的Kubernetes工作負載和serverless 產品的限制。如果您想參加我的另一個相關主題的網絡研討會,請查看本文末尾的詳細信息。

分佈式架構的發展

在預測serverless 的發展方向和它適用於何處之前,我們首先需要分析一下我們是如何以及爲什麼走到這個位置的。

大型獨體架構

當時,面向服務的架構(SOA)是最流行的架構,而企業服務總線(ESB)是其最常見的實現,我在大型集成項目中的職業生涯正是始於此時。

SOA基於良好的原則,其中大多數原則仍然有效:契約優先開發、鬆散耦合、可組合和無狀態的服務,這些服務也是自治的和可重用的。

ESB框架提供了一組很好的能力,比如協議轉換、技術連接器、路由和編排機制、錯誤處理和高可用性原語。

分佈式架構的發展

從架構和組織的角度來看,SOA和ESB的主要問題是集中化。SOA的一個重要原則是服務和組件的重用,從而創建了分層的服務架構,該架構支持重用,但會導致緊密的架構服務耦合。在組織級,ESB由一個團隊擁有,這使得中間件成爲技術和組織上可伸縮性的瓶頸,甚至,成爲快速發展的瓶頸。複雜的行業規範由一小羣人定義,發展緩慢。

微服務架構1.0

雖然SOA爲處理企業級複雜性提供了良好的基礎,但是技術發展很快。開源模式帶來了更快的創新,成爲技術分發和標準化的新的機制。

與此同時,敏捷開發實踐(如極限編程、Scrum和看板)引領了迭代的軟件開發,但是這種方法與現有的大型獨體架構相沖突,現有架構無法處理快速部署的增量設計。因此,特性是在爲期兩週的迭代中開發的,但每年只部署到生產環境中一兩次。

於是微服務架構應運而生,有望解決這些挑戰。鑑於其指導原則,微服務架構使我們可以更快地變更。服務圍繞業務領域建模,這有助於比可重用SOA服務更有效地控制服務邊界內的變更。自治和獨立部署的服務允許每個服務以自己的速度發展和擴展。

雖然這些新原則通過將每個應用程序轉換爲分佈式系統,針對快速迭代和試驗的時代對SOA進行了優化,但它們也帶來了挑戰。因此,微服務還必須具有高度自動化、高度可觀察性、容錯性以及分佈式系統組件必須具備的任何其他特性。這是一個並非所有人一開始都意識得到,並且也不一定願意付出的代價。

微服務架構作爲快速、迭代開發和實驗時代的技術解決方案應運而生,但是我們是在陳舊工具的基礎上構建的第一代微服務,它們很難管理。在ESB時代,業務邏輯泄漏到平臺中,導致了各種耦合,而在早期的微服務時代,我們發現情況反了過來:太多的基礎設施問題泄漏到每個微服務中。早期的微服務也有自己的挑戰;它們必須進行自己的服務發現、配置管理、網絡彈性、日誌分發等。能夠創建數十或數百個新服務並不一定意味着組織已經準備好使用現有工具管理和部署它們到生產環境中。必須改進用於向運維團隊發佈、管理和處理服務的流程和支持工具。所有這些都把我們推到了Kubernetes時代

原生雲架構(也就是微服務2.0)

爲適應快速的變化,微服務架構對SOA進行了優化,但它是通過將代碼複雜性轉換爲運維複雜性來實現這一點的。它的挑戰帶來了對容器的採用,並在一夜之間接管了整個行業。容器作爲一種技術治癒了統一部署大量微服務的痛苦,並有效地使現代DevOps實踐成爲了可能。使用容器,我們可以打包應用程序並以開發和運維團隊都能理解和使用的格式運行它們。很明顯,即使是在早期,管理上百個容器也需要自動化,而Kubernetes從天而降,橫掃所有競爭對手。Kubernetes解決了技術挑戰,DevOps實踐解決了微服務的文化方面。這種雲原生工具是如此的基礎,以至於導致了第二代微服務平臺的出現。

這是一個新的轉變的開始,一個基礎設施職責從應用層轉移到平臺層的轉變。雲原生平臺(最初有很多,但最終主要是Kubernetes)提供了諸如資源隔離和管理、自動放置、聲明式部署、健康探測和自修復、配置管理、服務發現、自動伸縮等能力。這些特性允許應用程序開發人員專注於業務邏輯,並使用開箱即用的平臺特性統一解決基礎設施問題。

當時,我將流行的微服務1.0工具(Spring Cloud和Netflix OSS)與微服務2.0工具(Kubernetes作爲事實上的標準)進行了比較,收到了讀者的不同反應。如今,這是一個被更廣泛理解和接受的轉變,證實了Kubernetes作爲微服務管理平臺的完全主導地位,以及許多來自於上一世代的Netflix OSS庫被棄用。

但這一切都已成爲歷史。讓我們來探索一下Kubernetes接下來要做什麼。

Kubernetes的閃光點

Kubernetes架構有許多迷人的元素:容器在其基礎上提供公共打包、運行時和資源隔離模型;簡單的控制迴路機制,其監控組件的實際狀態,並將其與所需狀態進行協調;自定義資源定義。但是擴展Kubernetes以支持不同工作負載的真正推動者是pod這一概念。

pod提供兩組保證。部署保證確保pod的容器總是放置在同一個節點上。這會有一些有益之處,比如允許容器通過本地主機、進程間通信(IPC)或使用本地文件系統進行同步或異步通信。

pod的生命週期保證確保pod中的容器實際上被管理爲兩組:初始化容器和應用程序容器。初始化容器先運行;它們一個接一個地運行,並且只有在前一個容器成功完成時才運行。初始化容器支持有順序的流水線行爲,單獨的容器執行每個步驟。另一方面,應用程序容器並行運行,沒有任何順序保證。這組容器支持流行的邊車模式,用於擴展和增強具有正交功能的現有容器的功能。

Pod的部署和生命週期保證

可擴展的控制循環機制,加上通用的pod特性,使Kubernetes能夠處理各種工作負載,包括serverless 的工作負載。讓我們檢查一下這些不同的工作負載,並看看適用於每種工作負載的用例是什麼。

雲原生工作負載

要證明Kubernetes是一個能夠支持不同工作負載和用例的通用平臺,需要考察不同的工作負載類型及其需求。

有狀態的服務

有一種工作負載讓人一點兒也提不起興致,但在企業環境中幾乎總是出現它,讓我們從此類工作負載開始吧:它就是有狀態服務。通過將具有業務邏輯的有狀態服務移動到外部數據存儲中,可以將其轉換爲可伸縮的無狀態服務。這樣的設計將約束從業務服務轉移到數據源上,數據源成爲架構中的有狀態組件。這些數據存儲通常是現成的關係數據庫、分佈式緩存、鍵值存儲、搜索索引等。在動態雲基礎設施上管理分佈式有狀態組件需要一些保證,比如:

  • 持久存儲——狀態通常位於磁盤上,分佈式有狀態應用程序需要爲每個實例提供專用的持久存儲來保存其狀態。
  • 固定不變的網絡ID——與存儲需求類似,分佈式有狀態應用程序需要固定不變的網絡標識。除了在存儲空間中存儲特定於應用程序的數據外,有狀態應用程序還存儲配置細節,如主機名和它們的對等點的連接細節。這意味着每個實例都應該通過一個不應該動態更改的可預測地址來訪問,就像ReplicaSet中的pod IP地址一樣。
  • 固定不變的標識——正如我們從之前的需求中所看到的,集羣的有狀態應用程序嚴重依賴於掌控其長生命週期存儲的每個實例和網絡標識。這是因爲在有狀態的應用程序中,每個實例都是惟一的,並且知道自己的標識,而標識的主要組成部分是長生命週期的存儲和網絡座標。在這個列表中,我們還可以添加實例的標識/名稱(一些有狀態的應用程序也需要惟一的持久性名稱),即爲Kubernetes中的pod名稱。
  • 序數性——除了一個獨特的和長期存在的標識之外,集羣的有狀態應用程序的每個實例相對於其他相關應用程序都有一個固定的位置。這種順序通常會影響實例伸縮的順序,但它也可以用作一致哈希算法、數據分發和訪問以及集羣內行爲(如鎖、單例或主機)放置的基礎。

Kubernetes上的分佈式有狀態應用程序

這些正是Kubernetes StatefulSet所提供的保證。StatefulSet 爲管理具有狀態特徵的pod提供了通用原語。除了帶有StatefulSet 的典型ZooKeeper、Redis和Hazelcast部署外,其他用例還包括消息代理,甚至事務管理器。

例如,Narayana事務管理器使用StatefulSet 來確保在使用分佈式事務縮減服務的過程中不會遺漏任何JTA日誌。在縮小集羣消息代理的規模時,Apache Artemis 消息代理依賴於StatefulSet來消費光消息。StatefulSet 是一個強大的通用抽象,對於複雜的有狀態用例非常有用。

全局單例

來自“四人幫”的單例模式是一個古老且易於理解的概念。在分佈式的雲原生世界中,與之相當的是一個單例組件(一個完整的服務或它的一部分)的概念,它是一個全局單例組件(在所有分佈式服務中),但仍然是高度可用的。這種工作負載類型的用例通常來自於我們必須與之交互的其他系統的技術約束,例如,API、數據源和每次只允許一個客戶機(單例)訪問的文件系統。另一個用例是當消息排序必須由消費服務保持時,將其限制爲單例。Kubernetes有幾個選項來支持這類用例。

最簡單的選擇是依賴Kubernetes來運行服務的單個實例。我們可以通過使用replicas=1的ReplicaSet 或StatefulSet 輕鬆實現這一點。這兩種選擇之間的區別在於,您是需要具有“最多一個”保證的強一致單例,還是需要具有“至少一個”保證的弱單例。ReplicaSet 支持可用性並優先保持單個實例(“至少一個”語義)。這有時會導致同時運行多個實例,例如,當一個節點與集羣斷開連接時,ReplicaSet在另一個節點上啓動另一個pod,而沒有確認第一個pod是否已停止。StatefulSet 傾向於一致性而不是可用性,並且提供“最多一個”語義。在節點與集羣斷開連接的情況下,它不會在健康節點上啓動pod。這種情況只有在運維人員確認斷開連接的節點或pod確實關閉之後纔會發生。這有時可能導致服務停機,但絕不會導致多個實例同時運行。

還可以在應用程序中實現自己管理的單例。雖然在前面的用例中,應用程序並不知道作爲單例進行管理,但是自己管理的單例確保只激活一個組件,而不管啓動了多少服務實例(pod)。這種單例方法需要特定於運行時的實現來獲取鎖並充當單例,但是它有一些優點。首先,沒有意外錯誤配置的危險,並且副本的數量的增加仍然會保持在任何給定時間只有一個組件處於活動狀態。其次,它允許服務的擴展,同時能夠確保只針對服務的一部分(比如端點)進行單例行爲。當由於特定操作或端點的外部技術限制,一個微服務僅部分而不是整體必須是單例時,這是非常有用的。這個用例的一個示例實現是Apache Camel的Kubernetes連接器的單例特性,它能夠使用Kubernetes ConfigMap作爲分佈式密鑰,並且只激活部署到Kubernetes中的多個Camel服務中的單個Camel消費者

Kubernetes上的單例工作負載

單例是另一種工作負載類型,它的數量很少,但是非常常見,很值得一提。單例和高可用性是兩個相互衝突的需求,但是Kubernetes足夠靈活,可以爲兩者提供可接受的折衷方案。

批處理作業

批處理作業的用例適合於管理處理獨立原子工作單元的工作負載。在Kubernetes原語中,它被實現爲作業抽象,在分佈式環境中可靠地運行短期的pod以完成任務。

Kubernetes上的批處理和重複工作負載

從生命週期的角度來看,批處理工作負載很少有類似於異步serverless 工作負載的特性,因爲它們集中於單個操作,並且它們生命週期短暫,最多持續到任務完成。但是,儘管基於作業的工作負載本質上是異步的,但它們不接受來自消費者的直接輸入,也不直接啓動以響應消費者的請求。他們通常知道從哪裏檢索輸入數據,以及在哪裏輸出結果。如果作業具有時間維度,即它是已安排好的,那麼它的執行將定期由時間事件觸發。

無狀態的工作負載(也就是12-Factor-Apps)

無狀態工作負載是Kubernetes上使用最廣泛的工作負載類型。這是典型的基於12因素的應用程序,或者是在Kubernetes之上使用ReplicaSet管理的基於微服務的系統。通常,一個ReplicaSet 將管理這樣一個服務的多個實例,並使用不同的自動伸縮策略來橫向和縱向伸縮這樣的工作負載。

在Kubernetes上具有服務發現的無狀態工作負載

對由ReplicaSet管理的服務的一個常見需求是服務發現和負載平衡。針對於此,Kubernetes提供了多種開箱即用的選項。

Kubernetes上的服務發現機制

這裏的重點是,即使有各種服務發現機制可以動態地檢測健康和不健康的pod實例,本質上各種服務類型也是相對靜態的。Kubernetes服務原語不提供動態流量監控和轉移能力。這正是服務網格的用武之地。

服務網格

實現基於微服務的系統時面臨的挑戰之一是構建不屬於業務邏輯的產品特性,例如彈性通信、跟蹤、監控等。該邏輯過去常常位於中央的ESB層,現在必須在微服務的智能客戶端之間進行隔離和重複。服務網格技術旨在通過提供額外的增強網絡能力來解決這個問題,例如:

  • 流量路由——A/B測試、階段性發布。
  • 恢復能力——重試、斷路器、連接限制、健康檢查。
  • 安全性——身份驗證、授權、加密(mTLS)。
  • 可觀察性——度量、跟蹤。
  • 測試——故障注入、流量鏡像。
  • 平臺獨立性——多語言支持,允許運行時配置。

如果我們仔細研究這些特性,就會注意到集成框架提供的功能有明顯的重疊。

服務網格和集成框架職責重疊

對於將所有這些職責移出服務是否是一種好方法,存在着不同的意見。雖然在此明顯地將網絡職責從應用層轉移到了公共雲原生平臺,但並不是所有的網絡職責都從應用程序中移出:

  • 服務網格適合於基於連接的流量路由,服務內部的集成框架適合於基於內容的路由。
  • 服務網格可以進行協議轉換,集成框架可以進行內容轉換。
  • 服務網格可以進行“摸黑啓動”,而集成框架可以進行“竊聽”。
  • 服務網格執行基於連接的加密,而集成框架可以執行內容加密。

某些需求可以在服務內部更好地處理,而有些需求則可以使用服務網格從外部處理。有些還必須同時在這兩層處理:連接超時可以從服務網格層配置,但仍然必須從服務內部配置。對於其他行爲,如通過重試和任何其他錯誤處理邏輯進行恢復,也是如此。服務網格、Kubernetes和其他雲服務是當今的工具,但應用程序端到端可靠性和正確性的最終責任在於服務實現及其開發和設計團隊。這一點沒有改變。

服務網格技術進一步強調了第一代和第二代微服務之間的主要區別:將某些運維職責轉移到平臺。Kubernetes將部署職責轉移到平臺,服務網格將網絡職責轉移到平臺。但這不是最終狀態;這些更改只是爲實現serverless 世界鋪平了道路,在這個世界中,部署和基於流量的即時可伸縮性是先決條件。

serverless 的概念

所有問題都關乎視角

爲了討論serverless 的特性,我將使用雲原生計算基金會(CNCF)的serverless 工作組(serverless working group)的定義,因爲在許多不同軟件供應商給出的定義中,它是得到最廣泛認可的其中一個:

serverless 計算的概念是指構建和運行不需要服務器管理的應用程序。它描述了一個更細粒度的部署模型,在這個模型中,捆綁爲一個或多個函數的應用程序被上傳到一個平臺上,並根據所需的確切需求執行、伸縮和計費。

如果我們作爲將從serverless 平臺中獲益的開發人員來看待這個定義,那麼我們可以將serverless 總結爲一種架構,它支持“在無需服務器管理的情況下按需運行更細粒度的函數”。通常都是從開發人員的角度來考慮serverless ,但是還有另一個很少討論的角度。每個serverless 的平臺都有管理平臺和服務器的供應商:它們必須管理粗粒度的計算單元,並且無論需求如何,它們的平臺都要花費24x7的成本。供應商是AWS Lambda、Azure Functions和谷歌雲函數背後的團隊,或者是您公司中使用Knative管理Apache OpenWhisk、Kubernetes或其他東西的團隊。在這兩種情況下,這些供應商都允許開發人員將計算和存儲作爲一種工作負載類型來使用,而這種工作負載類型沒有任何服務器的概念。根據組織和業務因素,供應商可以是同一組織中的另一個團隊/部門(想象一支使用AWS Lambda滿足其需求的Amazon團隊),也可以是另一個組織(如果AWS客戶使用Lambda和其他服務)。無論供應商和使用者之間的業務如何安排,使用者不對服務器負責,負責的是供應商。

serverless 架構

上面的定義僅指“serverless 計算”。但是應用程序的架構是由計算和數據組合而成的。更完整的serverless 架構定義包括serverless 計算和serverless 數據。通常,此類應用程序將合併雲託管服務來管理狀態和通用服務器端邏輯,如身份驗證、API網關、監視、警報、日誌記錄等。我們通常將這些託管服務稱爲“後端即服務”(BaaS)——可以將服務視爲DynamoDB、SQS、SNS、API網關、CloudWatch等。事後看來,術語“serviceful”相比“serverless ”可以更準確地描述得出的架構。但並不是所有的東西都可以被第三方服務取代;如果是這樣,有了針對於您的業務邏輯的服務,那麼您還做什麼業務呢!因此,serverless 架構通常還具有“功能即服務”(functions as a service, FaaS)元素,這些元素允許執行由事件觸發的自定義無狀態計算。這裏最流行的例子是AWS Lambda。

一個完整的serverless 架構由BaaS和FaaS組成,無需從消費者/開發人員的角度考慮服務器的概念。不需要管理或提供服務器還意味着基於消費的定價(不提供容量)、內置的自動伸縮(直到某個上限)、內置的可用性和容錯性、內置的補丁和安全性增強(帶有內置的支持策略約束)、監控和日誌記錄(作爲附加的付費服務)等。所有這些都由serverless 開發人員使用,並由serverless 供應商提供。

純serverless

如果serverless 架構聽起來這麼好,爲什麼不擁有一個純serverless 架構,所有組件都100% serverless 且完全沒有服務器的概念?原因可以用艾倫·烏爾曼(Ellen Ullman)的這句話來解釋:“我們建造計算機系統的方式就像我們建造城市一樣:隨着時間的推移在一片廢墟之上修修補補,無需一份完整的計劃。”企業制度就像一座老城;通常,它已經存在了十多年,這就是它的價值和臨界點的來源。這就是它“進取”的原因。想象一下倫敦,一個存在了2000多年的城市,有着百年曆史的地鐵系統、狹窄的街道、宮殿、維多利亞時代的社區和供應系統;這樣一個正在使用的複雜系統永遠不會完全被一個新的系統所替代,並且它總是處於恢復和更新的過程中(用IT術語來說,就是重構、升級、遷移和重新構建平臺)。在這樣的體系中,變化是常態,新舊並存是常態。這就是這些系統應該存在的方式。

serverless 1.0

容器技術以各種形式存在了許多年,但是Docker使其得以普及,Kubernetes使其成爲部署的規範。類似地是,serverless 技術已經存在很多年了,但是AWS Lambda使其流行開來,我們還沒有看到誰將把它帶到下一個層次。

serverless 1.0基本上是由AWS定義的,並由用於FaaS組件的AWS Lambda和用於BaaS組件的其他AWS服務(如API網關、SQS、SNS等)表示。隨着AWS定義了這一趨勢,而谷歌和Azure等其他廠商也在努力追趕,下面是當前一代中一些不太理想的serverless 特性,這些特性可能需要改進。

非確定性執行模型具有:

  • 不可預測的容器生命週期和影響冷啓動的重用語義;
  • 對編程模型的約束,它會影響代碼初始化邏輯,導致回調泄漏,產生遞增的遞歸調用成本等;
  • 不可預測的資源生命週期,例如/tmp文件存儲;
  • 對內存、超時、有效負載、包、臨時文件系統和環境變量的任意限制;以及
  • 跨行業的整體非標準化執行模型,帶有影響特定serverless 供應商編程模型的非標準化約束。

有限的運行時支持意味着:

  • serverless 軟件棧是操作系統、語言運行時和庫版本的組合,受限於OS、JDK和應用程序庫(如AWS SDK)的(單個版本)。
  • 平臺支持策略通常規定在哪些條件下可以廢棄和更新任一serverless 棧組件,從而迫使所有serverless 用戶以相同的速度遵循嚴格的時間表。
  • 編程API可能會導致問題。雖然我有着很好的針對其他服務使用AWS SDK的體驗,但我不喜歡所有函數都必須與com.amazonaws.services.lambda.runtime包及其編程模型緊密耦合。
  • 我們使用非標準的打包。使用.zip、uber-JAR或lib目錄,並使用自定義的特定於AWS的分層和依賴關係模型,並不能使我對這種打包的未來抱有信心,或者它能在沒有服務器的平臺上工作抱有信心。
  • 自定義環境變量(以AWS_開頭)不能在其他serverless 的平臺上工作。

專用的數據格式

專用的數據格式是一個障礙。事件是serverless 架構中函數的主要連接機制。它們將每個函數與其他函數和BaaS連接起來。它們實際上是函數的API和數據格式。在所有函數中使用com.amazonaws.services.lambda.runtime.events包中定義的事件保證零互操作性。

不支持Java

雖然AWS Lambda有一個Java運行時,但是Java和Lambda之間的不匹配非常嚴重,甚至AWS都不推薦使用它。在Tim Bray的“AWS內幕:現代應用的技術選擇”演講中,他建議使用Go和Python。我希望serverless 的供應商能做得更好,並改進它們的運行時,而不是試圖對數百萬Java開發人員進行再教育,改變由數百萬Java類庫組成的生態系統。Java已經像Go一樣輕便和快速(即使沒有更好的話),所以用這種語言構建serverless 是無可迴避的。

可能的影響

回顧一下,不確定性和非標準化執行模型、專有運行時、專有數據格式、專有API和缺乏Java支持,它們組合起來意味着所有這些問題都會泄漏到應用程序代碼中,並影響我們實現業務邏輯的方式。這是把控制權從一個組織移交給另一個組織的最終結果。由於今天的serverless 提供了構建時構件(以sdk和打包格式、事件格式和軟件棧的形式),並提供了運行時環境,所以serverless 消費者對支持策略和供應商強加的專有限制做出承諾。這些消費者承諾緊緊跟隨供應商的語言運行時、SDK、升級和棄用。他們通過編寫跨serverless 平臺具有零互操作性的函數來遵守這些術語。如果我們對所有事情都使用BaaS,那麼我們只能將組織的業務邏輯(用函數編寫)與專有的執行模型、運行時、API和數據格式耦合起來,否則我們將別無選擇。雖然我們可能不想有其他選擇,但對某些人來說,擁有這樣的選擇權很重要。

耦合和鎖定本身並不是壞事,但是遷移的高成本是壞事。使用AWS AMI、AWS RDS、流行的開源項目作爲託管服務,甚至選擇SQS都是不介意被鎖定的消費者的例子,因爲遷移到替代服務或供應商是一個可行的替代方案。這與將我們的業務邏輯耦合到不成熟的serverless 技術和特定的serverless 供應商的特性是不一樣的。在這裏,遷移工作是完全重寫和測試業務邏輯及粘合代碼,考慮到serverless 架構的高度分佈式特性,這些事的成本尤其高昂。

微服務以代碼複雜度換取運維複雜度。serverless 爲了速度交出了控制力。選擇一個流程的架構時,要留意閱讀旁邊的小字。每一個架構選擇都是一種權衡。

serverless 1.5

AWS做了一件了不起的工作,將serverless 帶到了目前的位置上,但是如果現在的serverless 就是它的最高點,那就太遺憾了。如果只有AWS能夠在serverless 的環境中進行創新並定義其未來,那將令人很是遺憾。考慮到AWS在開源生態系統中的背景相對有限,期望AWS將serverless 範式標準化,在如此基礎的層面上影響整個行業,這是不現實的。AWS的業務模型和市場地位有助於識別市場趨勢和最初的封閉創新,但是開源模型更適用於泛化、標準化,並不強制行業範圍接受。我希望下一個serverless 的時代將使用開放源碼模型來建設,並在整個行業中進行更廣泛的協作,這將有助於其採用和互操作性。這一過程已經開始,業界正在慢慢探索可互操作和可移植的替代方案,以替代目前專有的serverless 產品。

讓我們來討論一些行業趨勢(排名不分前後),我認爲這些趨勢將推動和影響未來的serverless 技術。

統一的打包和執行模型

容器已確立爲應用程序打包和運行時的行業標準。如前所述,將容器化的應用程序與功能強大的編排引擎相結合,使豐富的工作負載成爲可能。沒有理由讓serverless 的工作負載成爲例外,因爲這將使我們回到混合打包格式和執行模型。Knative是多個供應商共同開發的開放產品,它通過提供Kubernetes上的基於容器的工作負載的serverless 特性(伸縮至零、基於HTTP請求的自動伸縮、訂閱、交付、綁定和事件管理)來挑戰這一現狀。基於容器的打包和基於kubernets的執行將允許一個開放的執行模型,該模型可以跨多個serverless 供應商予以標準化。它將支持一系列更豐富的運行時,更好地支持Java、自定義軟件棧、限制和自定義的可能性。

有些人可能會爭辯說,在函數包中包含語言運行時和事件處理程序並不符合serverless 的原始定義,但這是一個實現細節,這是將來serverless 非常需要的選項。

行業認可的事件格式

按定義來看,serverless 架構是事件驅動的,事件起着中心作用。在serverless 的環境中,事件類型越多,開發人員的體驗就越豐富,可以用現成服務替換的邏輯也就越多。但是,這是有代價的,因爲業務邏輯與事件格式和結構耦合了起來。雖然您可以讀讀AWS的最佳實踐,看它們如何將核心業務邏輯和事件處理邏輯分離爲單獨的方法,但這離解耦還很遠。業務邏輯與serverless 平臺的數據格式之間的這種耦合阻礙了互操作性。CloudEvents致力於創建可以跨所有serverless 平臺運行的標準化事件格式。除了是一個很棒的想法,它還對包括AWS在內的行業抱有巨大的興趣,行業可能是對其重要性和採用潛力的最終驗證。

可移植性和互操作性

一旦有了標準的打包格式和標準事件,下一個級別的自由是能夠在公共或私有云、場所或邊緣上跨服務器提供程序運行serverless 工作負載,並根據需要將所有工作負載混合和匹配到混合服務器中。一個函數應該可以在多雲、混合雲、任何雲、非雲或混合雲上運行,並且應該只需要一些配置和映射。我們以同樣的方式編寫Java應用程序來實現抽象接口並把它們部署到不同的Web容器,我希望能夠寫非專有的API函數、事件和編程模型,並將它們部署到任何serverless 平臺,使它以可預測的、確定性的方式運行。

除了可移植性,我還希望看到互操作性,使函數能夠使用來自任何平臺的事件,而不管函數本身運行在何處。像KEDA這樣的項目允許我們運行如Azure函數之類的自定義函數,以響應AWS、Azure和其他事件觸發器。TriggerMesh等項目允許我們在Kubernetes和OpenShift之上部署AWS lambda兼容的功能。這些跡象表明,未來的功能將在多個級別上具有可移植性和互操作性:打包、執行環境、事件格式、事件源、工具等等。

將Java視爲一類公民

雖然serverless 工作負載適用於許多用例,但是避免使用Java(企業最流行的編程語言)成爲一個主要限制。感謝有Substrate VM Quarkus之類的框架,Java已經是輕量級、快速、雲原生的,並且是serverless 友好的。而且有跡象表明,這種Java運行時很快也將適用於serverless 的環境,包括AWS Lambda,希望如此。

具有serverless 特性的容器工作負載、具有標準化事件的功能可移植性和互操作性,以及爲雲原生環境和serverless 環境創建的超輕型和快速Java運行時,這些都是serverless 即將發生改變的信號。我還不想將這些指示器標記爲“第二代serverless ”,但它肯定不是第一代serverless ,所以大概應該稱爲1.5吧。

我記得當許多人認爲Cloud Foundry贏得了PaaS之戰時,但後來又冒出了Kubernetes。現在許多人聲稱AWS Lambda贏得了FaaS之戰。我希望Kubernetes(或者更好的東西)證明他們錯了。

serverless 工作負載

我們瞭解了微服務架構如何通過圍繞業務域建模服務並將更改封裝在服務中來改進大型獨體應用程序的部署週期。serverless 有一種簡單的說法,是將其表示爲更小的微服務,其中每個操作都是一個函數。雖然這在技術上是可能的,但這將是兩種架構中最糟糕的一種,導致大量函數以同步方式彼此調用,而不會從得到的架構中獲得任何好處。

微服務的價值在於,它可以用基於Web的API(通常是REST風格)封裝一系列請求/響應風格操作背後的複雜業務域邏輯和持久性邏輯。而反之,serverless 和函數主要關注事件和觸發器。雖然函數可以放在API網關的後面,並以請求/響應的方式執行操作,但是API並不是主要接口:事件和觸發器纔是主要接口。當應用程序是異步的(單向的即發即棄(fire-and-forget)樣式,而不是請求/響應樣式)並通過隊列或其他數據和事件源連接時,serverless 的應用程序往往工作得最好。因此,每個函數應只預計執行一個操作,應該避免直接調用其他函數,並將其結果寫入事件存儲。由於執行模型的原因,函數是短生命週期的,並且應該與其他不面向連接的serverless 數據源一起使用(典型的RDBMS就屬於此種情況),並且在部署規模方面比較輕,啓動速度也比較快。以下所有用例都使serverless 更適合,因爲它充當連接各種事件驅動系統的粘合代碼:

  • 按需功能,如批處理、流處理和提取-轉換-加載(ETL);
  • 可分解爲短時間工作的任務調度,如批處理任務;
  • 事件驅動的架構,它執行響應數據源變化的邏輯;
  • 處理不統一的通信,例如不經常發生的不一致的通信,或負載不可預測的通信;
  • 運維中的通用“膠水”代碼;
  • 針對構建作業具有按需資源的持續集成流水線;以及
  • 自動化運維任務,例如在事件發生時觸發操作或通知隨時待命的人員。

我討論了一些serverless 世界中正在發生的主要創新,但沒有說明它們在Kubernetes上會是什麼樣子。大家做了許多努力,試圖將serverless 引入Kubernetes,但是擁有最廣泛行業支持和最佳成功機會的項目是Knative。Knative的主要目標是爲常見的serverless 用例提供具有更高層次抽象的專注的API。它仍然是一個年輕的項目(編寫本文時是0.5版),並且變化很快。我們來研究一下Knative當前支持的serverless 工作負載。

請求伺服

從微服務到serverless 的低阻力的轉換方法是使用單一操作的函數來處理HTTP請求。我們希望serverless 平臺能夠在幾秒鐘內支持無狀態、可伸縮的功能——這正是Knative Serving的目標,它爲serverless 工作負載提供了一個通用的工具包和API框架。在此上下文中,serverless 工作負載是一個單容器、無狀態pod,主要由應用程序級(L7)請求流量驅動。

Knative Serving項目提供了基本的支持:

  • 通過提供高級的、自定義的原語,快速部署serverless 容器;
  • 由請求驅動的激活、上下伸縮到零;
  • 底層原語的自動路由與配置;以及
  • 修訂的不可變快照(已部署的代碼和配置)。

所有這些都可以在特定的限制範圍內實現,比如每個pod只有一個容器、一個端口、無持久化以及其他一些限制

事件

Knative Eventing 處理項目爲創建可靠、可伸縮、異步事件驅動的應用程序提供了構建模塊。它的目標是圍繞使用CloudEvents標準消費和事件的創建制定標準體驗。Knative Eventing 的高級功能包括:

  • 可擴展和可插拔的架構,允許不同的導入實現(如GitHub、Kafka、SQS、Apache Camel等)和通道實現(如Kafka、谷歌Pub/Sub、NATS、在內存中等);
  • 事件註冊表,用於維護事件類型的目錄;
  • 通過綁定事件源、觸發器和服務來實現事件編排的聲明式API;以及
  • 觸發器功能,允許從特定代理訂閱事件,並可以選擇將事件路由到下游Knative Serving之前進行篩選。

這些特性很有趣,但是它們如何幫助雲原生開發人員在Kubernetes上更爲高效呢?

假設我們已經實現了一個函數,將其構建爲一個容器,並對其進行了充分的測試。它基本上是一個只有一個操作的服務,通過HTTP接受CloudEvents。使用Knative,我們可以將容器部署到Kubernetes中,作爲具有serverless 特性的工作負載。例如,使用Knative Serving原語,容器只能在有HTTP請求時激活,並在必要時快速伸縮。此外,還可以通過訂閱通道將相同的pod配置爲接受來自一個代理的CloudEvents。該pod還可以作爲通過Knative序列定義的更復雜的事件編排流程中的一個步驟。所有這些無需修改已經構建的容器,只需要使用聲明性的Knative配置都可能做到。Knative將確保serverless 基礎設施的路由、激活、可伸縮性、可靠性、訂閱、重新交付和代理彈性。這還不是全部,但也接近全部了。

自定義工作負載

以上還不是全部。如果您的應用程序具有非常特定的需求,而又沒有標準工作負載原語提供給你,Kubernetes提供了更多的選項。在這種情況下,自定義控制器可以主動監視和維護一組處於所需狀態的Kubernetes資源,從而向集羣的行爲添加定製的功能。

控制器是一個在高層次上執行“觀察->分析->行爲”的主動協調過程。它監視感興趣的對象的預期狀態,並將它們與現實的實際狀態進行比較。然後,該流程發送試圖將當前狀態更改爲更接近所預期的狀態的指令。

處理定製工作負載的更高級方法是使用另一種出色的Kubernetes擴展機制:CustomResourceDefinitions。將Kubernetes Operator 與CustomResourceDefinitions相結合,可以將針對特定應用程序需求的運維性知識封裝在“算法”表單中。Operator 是一個理解Kubernetes和應用程序域的Kubernetes控制器,通過結合這兩個領域的知識,它可以自動執行通常需要人類運維人員執行的任務。

控制器和Operator 正在成爲擴展平臺和在Kubernetes上啓用複雜應用程序生命週期的標準機制。因此,正在形成管理更復雜的雲本地工作負載的完整生命週期的控制器生態系統

Operator 模式使我們可以擴展控制器模式,以獲得更大的靈活性和更強的表達能力。本文中討論的所有工作負載類型,以及其他相關的模式在我最近合著的《Kubernetes 模式》一書中都有所涉及。有關這些主題的更多細節,請閱讀該書。

雲原生趨勢

在Kubernetes生態系統中,正在向將越來越多不屬於業務邏輯的商品特性遷移到平臺層的趨勢演變:

  • 部署、放置、健康檢查、恢復、伸縮、服務發現和配置管理都轉移到了Kubernetes層。
  • 通過將與網絡相關的職責(如彈性通信、跟蹤、監控、傳輸級安全性和流量管理)轉移到平臺上,服務網格延續着這一趨勢。
  • Knative添加了專門的serverless 原語,並將快速伸縮、伸縮到零、路由、事件基礎設施抽象、事件發佈、訂閱機制和工作流組合的職責轉移到平臺上。

這主要是爲讓應用程序開發人員來實現業務邏輯的關注點。剩下的由平臺來處理。

職責轉移到平臺上。更多的抽象使各類工作負載成爲可能

通過向Kubernetes添加更高級別的抽象,我們將越來越多的共性職責從應用程序層轉移到平臺中。例如,Istio提供了依賴於低層Kubernetes原語的高級網絡抽象。Knative添加了更高級別的serverless 抽象,這些抽象依賴於Kubernetes和Istio中的較低級別抽象(請注意,可能很快就不是這樣了,Knative將不再依賴於Istio,儘管它將需要實現類似的功能)。

這些額外的抽象使kuberntete能夠統一地支持各種工作負載,包括具有同樣開放的打包和運行時格式的serverless 。

運行時和應用程序設計

隨着大型獨體架構向微服務和serverless 的過渡,運行時也在不斷髮展;發展如此之快,以至於已有20年曆史的Java運行時正在從“一次編寫,隨處運行”的口號轉向首先實現本機、輕量級、快速和serverless 。

Java運行時和應用程序設計趨勢

多年來,摩爾定律和不斷增長的計算能力帶領Java構建了最先進的運行時之一,包括高級垃圾收集器、JIT編譯器和許多其他東西。摩爾定律的終結使得Java引入了從多處理器和多核系統中獲益的非阻塞原語和庫。本着同樣的精神,隨着雲原生化和serverless 化等平臺趨勢的出現,分佈式系統組件變得更輕、更快,並且針對單個任務進行了優化。

Severless

serverless 範式被認爲是下一個基礎性架構演化。但是,當前這一代的serverless 已經從早期採用者和阻撓者中脫穎而出,他們的首要任務是速度。這不是具有複雜業務和技術限制的企業的首要任務。在這些組織中,行業標準是容器編排。Kubernetes正試圖通過Knative計劃將雲原生和Serverless 結合起來。其他項目,如CloudEvents和Substrate VM也在影響着Serverless 生態系統,並將其推向一個開放、可移植性、互操作性、混合雲和全球採用的時代。

2019年10月10日,我的Red Hat組織的虛擬大會上演講了“用Kubernetes模式設計雲原生應用”。如果你對這個話題和內容感興趣,請查看本次大會的相關內容。

作者簡介

Bilgin Ibryam是Red Hat的首席架構師,也是多個Apache軟件基金會項目的提交者。他經常寫博客、宣傳開源軟件、熱衷於區塊鏈和演講。他寫過《Camel設計模式》和《Kubernetes模式》兩本書。他在設計和構建高度可伸縮和彈性的分佈式系統方面擁有超過十年的經驗。在日常工作中,Ibryam喜歡指導企業中的團隊通過經過驗證和可重複的模式和實踐大規模地構建成功的開源解決方案。他目前的興趣包括企業區塊鏈、雲原生和serverless 範例。@bibryam關注他,定期閱讀他在這些話題上的更新內容。

原文鏈接:

Kubernetes Workloads in the Serverless Era: Architecture, Platforms, and Trends

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章