返回文章列表

無伺服器運算核心概念與實踐

本文探討無伺服器運算的核心概念,包含工作單元、請求處理、事件驅動架構以及藍綠佈署等關鍵技術。同時,分析了無伺服器運算的優勢與挑戰,並以 CockroachDB 和 Knative 為例,闡述其實際應用和運作原理。最後,本文也探討了無伺服器架構如何簡化開發流程、提升效率、降低成本,並強化系統的安全性與彈性。

雲端運算 軟體架構

無伺服器運算的核心在於將應用程式分解成獨立的工作單元,並依據需求動態調整資源。每個工作單元都具備無狀態、獨立性以及請求驅動的特性,系統能根據待處理的工作單元數量,自動調整所需的例項數。此架構能有效降低成本、提升佈署速度,並實作自動擴充套件,讓開發者專注於程式碼,無需管理底層基礎設施。然而,無伺服器運算也面臨冷啟動、除錯困難以及供應商鎖定等挑戰。透過理解應用程式請求並將其與例項生命週期解耦,無伺服器系統能提升效率和降低延遲。同時,事件驅動架構雖能統一同步和非同步通訊,但也需注意超時管理、錯誤回報以及基數和相關性管理等議題。

無伺服器運算的理論基礎

何謂無伺服器運算?

無伺服器運算(Serverless)被視為一項具有變革性的技術,不僅被各大開發平台作為品牌宣傳,也被主要雲端供應商標籤於數十種服務之上。它承諾能夠實作每日多次佈署程式碼,以幾十行程式碼快速原型新應用,並擴充套件至最大規模的應用問題。從Snap(主要的Snapchat應用程式)到iRobot(處理數十萬台家電的通訊)以及微軟(各種網站和入口網站)的生產應用中,無伺服器運算都得到了成功應用。

如同前言所提,「無伺服器」一詞不僅適用於計算基礎設施服務,也涵蓋儲存、訊息傳遞及其他系統。定義有助於闡明無伺服器運算的涵義及其界限。由於本文著重於如何使用無伺服器基礎設施構建應用程式,許多章節將集中討論如何設計和程式設計計算層。不過,我們的定義也將為思考構建非計算無伺服器抽象化的基礎設施團隊提供指引。為此,我將採用以下對無伺服器運算的定義:

無伺服器運算是設計和執行分散式應用程式的一種模式,它將負載分解為獨立的工作單元,然後在自動擴充套件的例項數量上自動排程和執行這些工作。

一般來說,無伺服器模式是根據可用工作量自動擴充套件計算程式的數量,如圖1-1所示。

圖表翻譯:

此圖示展示了無伺服器架構如何根據工作負載自動調整計算資源。當工作量增加時,系統會自動擴充套件以處理更多請求;當工作量減少時,系統則會縮減資源以節省成本。這種彈性擴充套件的能力是無伺服器運算的一個關鍵優勢。

無伺服器運算的核心概念

無伺服器運算的核心在於將應用程式分解為可獨立執行的任務或事件,並根據實際需求動態調整資源。這種架構允許開發者專注於撰寫程式碼,而無需擔心底層基礎設施的管理和維護。

def process_event(event):
    # 事件處理邏輯
    result = perform_computation(event.data)
    return result

def perform_computation(data):
    # 具體的計算邏輯
    processed_data = data * 2  # 簡單示例
    return processed_data

內容解密:

上述程式碼展示了一個簡單的事件處理函式process_event,它接收一個事件物件作為輸入,並呼叫perform_computation函式進行具體的計算處理。這裡的perform_computation函式僅為示例,實際應用中可根據業務需求實作複雜的邏輯運算。

  • process_event函式是無伺服器環境中的一個典型入口點,用於接收和處理事件。
  • perform_computation函式代表了具體的業務邏輯,可以根據需要進行擴充套件或修改。
  • 這種模組化的設計使得程式碼易於維護和擴充套件,並且能夠根據工作負載自動調整執行的例項數量。

無伺服器運算的優勢與挑戰

無伺服器運算提供了多項優勢,包括但不限於:

  1. 成本效益:只需為實際使用的資源付費,避免了資源浪費。
  2. 自動擴充套件:能夠根據工作負載自動調整資源,無需手動干預。
  3. 快速佈署:支援快速迭代和佈署,有助於敏捷開發。

同時,無伺服器運算也面臨一些挑戰,如:

  1. 冷啟動問題:首次呼叫或長時間未被呼叫的函式可能會經歷較長的啟動時間。
  2. 除錯困難:由於無伺服器函式的執行環境是動態且分散的,因此除錯變得更加複雜。
  3. 供應商鎖定:不同的雲端服務提供商有不同的無伺服器實作,可能導致遷移困難。

圖表翻譯:

此圖示描述了無伺服器架構中的自動擴充套件流程。系統會根據工作負載的需求判斷是否需要擴充套件資源。如果需要,則自動增加資源以處理更多的工作;如果不需要,則維持現有的資源組態。這種機制確保了資源的高效利用和應用的平穩執行。

綜上所述,無伺服器運算是一種強大的技術,能夠幫助開發者構建更靈活、更高效的應用程式。儘管存在一些挑戰,但其優勢使其成為現代雲端應用開發的一個重要方向。

無伺服器運算的本質

無伺服器運算(Serverless)的定義相當廣泛,不僅涵蓋瞭如AWS Lambda或Cloudflare Workers等功能即服務(FaaS)平台,也包括了GitHub Actions(建置和持續整合動作)、Chrome中的服務工作執行緒(在瀏覽器和伺服器之間協調的背景執行緒),甚至是像Google BigQuery(分散式SQL倉函式庫)這樣的查詢平台上的使用者自訂函式。無伺服器運算同樣適用於像Amazon Simple Storage Service(S3)這樣的物件儲存平台(一項工作單位是儲存一個二進位大型物件),甚至可以涵蓋自動擴充套件管理角色狀態的實體框架。

本文將主要以開源專案Knative為例,闡述無伺服器運算的原理,但也會在適當的情況下討論其他無伺服器平台。選擇Knative有三個原因:

  • 作為一個開源專案,Knative可在從雲端供應商到像Raspberry Pi這樣的單板電腦等多種平台上使用。
  • 作為開源專案,其原始碼可供實驗。你可以新增偵錯行或將其整合到更大的現有專案或平台中。許多無伺服器平台是商業雲端產品,與特定的供應商繫結,通常不允許你修改或替換其平台。
  • Knative出現在書名中,我預計有些讀者如果不提到它會感到失望。此外,我在Knative社群中有五年的工作經驗,負責軟體開發、社群建設和解答問題。我在Knative方面的專業知識至少與我在其他平台上的經驗一樣深入。

為什麼稱為無伺服器?

喜歡吹毛求疵的批評家喜歡指出“無伺服器”這個名稱中的“陷阱”——雖然名稱是“無伺服器”,但它仍然是在伺服器上執行應用程式碼的一種方式,無論在哪裡。那麼,這個名稱是如何產生的,無伺服器又如何融入過去50年來運算系統的發展軌跡中?

在20世紀60年代和70年代,大型主機非常稀少、昂貴,並且完全是運算的核心。70年代和80年代帶來了越來越小、越來越便宜的電腦,以至於到90年代中期,將多台較小的電腦聯網共同解決任務往往比購買一台更昂貴的電腦更划算。有效地協調這些電腦是分散式運算的基礎。(有關無伺服器的更完整歷史,請參閱第11章。有關無伺服器名稱如何演變的歷史,請參閱第14頁的“雲端託管無伺服器”。)

雖然有多種方法可以連線電腦透過分散式運算解決問題,但無伺服器運算專注於讓解決那些符合眾所周知的分散式工作模式的問題變得更容易。其中大部分與大約2010年開始的微服務運動相吻合,該運動旨在將現有的單體應用程式分解為能夠由更小的團隊編寫和管理的獨立元件。無伺服器運算是對微服務架構的補充(但不是必需的),透過簡化個別微服務的實作。

無伺服器的目標

無伺服器運算的目標是讓開發者在構建和擴充套件應用程式時不再需要以伺服器單位思考,而是使用對應用程式有意義的擴充套件單位。

擁抱眾所周知的模式

透過擁抱眾所周知的模式,無伺服器平台可以自動化那些困難但常見的過程,如容錯移轉、複製或請求路由——分散式運算的“無差異化重複勞動”(由Jeff Bezos在2006年描述Amazon雲端運算平台的好處時創造的術語)。2011年的一個流行的無伺服器模型是十二因素應用程式模型,它闡述了大多數無伺服器平台所採用的幾個模式,包括具有由外部服務或平台處理的儲存的無狀態應用程式,以及組態和應用程式碼的明確分離。

Knative 的優勢

Knative特別適合將無伺服器功能整合到現有的運算平台中,因為它建立在Kubernetes的功能之上並原生地整合了這些功能。這樣,Knative彌合了自2014年引入AWS Lambda和Kubernetes以來一直存在的“無伺服器與容器”之爭。

一些術語

在描述無伺服器系統並將其與傳統運算系統進行比較時,我將在整本文中使用一些特定的術語。我將嘗試一致地使用這些詞,因為實作無伺服器系統的方法有很多,並且值得擁有一些與特定產品不同的精確語言,我們可以用它來描述我們的思維模型如何對映到特定的實作:

程式(Process)

我使用“程式”這個術語是在Unix意義上的:一個具有一個或多個執行緒的執行程式,這些執行緒分享一個共同的記憶體空間並與核心介面互動。大多數無伺服器系統根據Linux核心,因為它既流行又免費,但其他無伺服器系統建立在Windows核心或透過WebAssembly(Wasm)公開的JavaScript執行環境。本文寫作時最常見的無伺服器程式機制是透過容器子系統使用Linux核心,Lambda透過Firecracker VM函式庫和各種開源專案(如Knative)使用Kubernetes排程器和容器執行環境介面(CRI)。

例項(Instance)

例項是一個無伺服器執行環境,以及管理該環境中根程式所需的任何外部系統基礎設施。例項是無伺服器系統中可用的最小排程和計算單位,通常用作日誌記錄、監控和追蹤等系統中的關鍵字,以便啟用相關性。無伺服器系統將例項視為短暫的,並自動處理建立和銷毀它們以回應系統負載。根據無伺服器環境的不同,可能可以在單個例項中執行多個程式,但外部的無伺服器系統將例項視為擴充套件和執行的單位。

圖表翻譯: 此圖示展示了程式、例項、以及無伺服器系統之間的關係。無伺服器系統管理例項,而例項中包含程式。無伺服器系統會根據系統負載自動建立和銷毀例項,以保持系統的高效執行。

無伺服器架構的核心概念

無伺服器(Serverless)技術是一種創新的雲端運算模式,它允許開發者構建和執行應用程式而無需管理底層的基礎設施。在無伺服器架構中,應用程式被分解成多個獨立的單元,每個單元都可以獨立擴充套件和執行。

什麼是「工作單元」?

無伺服器架構的核心在於「工作單元」(Unit of Work)的概念。工作單元是一個獨立的、無狀態的請求,可以由單一例項(Instance)處理完成。工作單元的特點包括:

無狀態(Stateless)

每個請求包含所有需要處理的工作,不依賴於例項之前的處理結果或其他正在處理的請求。這意味著無伺服器程式不能在記憶體中儲存資料供未來使用,也不能假設程式會在工作單元之間持續執行以處理內部計時器等任務。

獨立性(Independent)

工作單元之間是獨立的,不直接依賴於其他工作單元的執行結果。這使得無伺服器系統可以透過新增更多節點來水平擴充套件,因為每個工作單元可以被路由到任何可用的例項進行處理。

請求驅動(Requests)

無伺服器系統應該只在接收到請求時才進行工作。這些請求可以是事件、HTTP 請求、待處理的資料列或甚至是到達系統的電子郵件。系統可以測量有多少工作單元待處理,並根據這個數量來決定需要啟動多少例項。

工作單元的範例

以下是一些無伺服器系統可能會根據其擴充套件的工作單元範例:

  • 連線請求(Connections):將每個傳入的 TCP 連線請求視為一個工作單元。當連線建立時,它會被分配給一個例項,該例項將使用特定的協定進行通訊,直到連線被關閉。
  • 應用層請求(Requests):無伺服器系統可以理解特定的應用層協定,如 HTTP、SMTP 或 RPC,並將每個協定交換視為一個獨立的工作單元。這樣可以將工作分解成更小的區塊,從而實作更細粒度的負載平衡。

CockroachDB 的無伺服器架構範例

CockroachDB 使用了一種類別似的模型來管理 PostgreSQL 連線。每個客戶端的資料函式庫連線都會被路由到一個專門的 SQL 處理器,該處理器負責處理 PostgreSQL 協定、解釋 SQL 查詢、將查詢轉換成針對分享後端儲存的查詢計畫、執行查詢計畫,並將結果傳回給客戶端。如果某個客戶端沒有開啟連線,則該客戶端的 SQL 處理器池可以縮減到零。

-- CockroachDB 使用的 SQL 查詢範例
SELECT * FROM customers WHERE id = $1;

內容解密:

這段 SQL 查詢用於從 customers 表中檢索特定 id 的客戶資料。其中,$1 是一個引數佔位符,用於防止 SQL 注入攻擊。在無伺服器架構中,這樣的查詢可以被視為一個工作單元,由專門的 SQL 處理器例項進行處理。

圖表翻譯: 此圖示展示了 CockroachDB 的無伺服器架構中,客戶端連線請求如何被路由到專門的 SQL 處理器,並由其執行查詢並傳回結果的過程。客戶端的請求首先到達連線代理,然後被路由到相應的 SQL 處理器。SQL 處理器執行查詢後,將結果傳回給客戶端。

無伺服器架構的優勢

無伺服器架構允許系統根據實際的工作負載動態擴充套件,從而實作更高的資源利用率和成本效益。透過將應用程式分解成獨立的工作單元,無伺服器系統可以實作更細粒度的擴充套件和更高的可用性。

無伺服器架構的核心:請求處理與事件驅動

在無伺服器系統中,正確解讀應用程式請求是實作高效運作的關鍵。這種做法使得連線生命週期與例項生命週期得以解耦。當TCP連線成為工作單元時,佈署新的程式碼需要啟動新的例項來處理新的連線,同時等待現有的連線完成(即所謂的「清空例項」)。如果清空例項的時間從一分鐘延長到五分鐘,那麼應用程式的推出和組態變更所需的時間將會是原來的五倍。許多設定(如環境變數)只能在程式(例項)啟動時設定,因此這限制了團隊變更程式碼或組態的速度。

請求處理的重要性

根據不同的協定,單一連線可能同時包含零、一或多個請求。透過在排程工作單元時解讀應用程式請求,無伺服器系統可以實作更高的效率和更低的延遲——當請求平行發出時,將請求分派到多個例項,或者避免為沒有請求的連線啟動例項。

程式碼範例:請求處理邏輯

def handle_request(request):
    # 請求驗證
    if not validate_request(request):
        return {"error": "Invalid request"}, 400
    
    # 處理請求
    try:
        response = process_request(request)
        return {"data": response}, 200
    except Exception as e:
        return {"error": str(e)}, 500

def validate_request(request):
    # 驗證邏輯
    return True

def process_request(request):
    # 處理邏輯
    return "Request processed successfully"

內容解密:

  1. handle_request 函式是請求處理的入口,負責驗證和處理請求。
  2. validate_request 函式用於驗證請求的有效性。
  3. process_request 函式包含實際的請求處理邏輯。
  4. 傳回值包含HTTP狀態碼,用於表示請求的處理結果。

事件驅動架構的挑戰

事件和請求在本質上是相似的,但在某些層面上,事件是反應式和非同步架構的基本組成部分。事件宣告「在某一時刻,某件事情發生了」。將整個無伺服器系統建模為事件是可行的;例如,可以有一個事件代表「這個HTTP請求發生了」或「這個資料函式庫列被掃描了」。

事件驅動的優缺點

  • 優點:能夠將同步和非同步通訊統一到單一模型中。
  • 缺點
    • 超時管理變得更加困難。
    • 錯誤報告需要額外的機制來將錯誤條件回饋給原始呼叫者。
    • 基數和相關性管理變得複雜。

無伺服器架構的優勢

無伺服器架構不僅僅是關於自動擴充套件。它透過圍繞工作單元構建執行環境,能夠提供更多的價值。一旦基礎設施層瞭解了是什麼觸發了應用程式的執行,就可以注入額外的框架來測量和管理應用程式的效能。工作單元越細粒度,平台能夠提供的幫助就越多,無論是在理解、控制還是安全方面。

無伺服器架構示意圖

圖表翻譯: 此圖示展示了客戶端請求如何透過API閘道器被路由到無伺服器函式進行處理,最終傳回結果給客戶端。

無伺服器架構的運作單元與其優勢

現代無伺服器系統簡化了處理工作單元的常規程式最佳實踐,並以與程式語言無關的方式實作。雲端運算將這些輔助功能稱為「無差異的重複勞動」(undifferentiated heavy lifting),即每個程式都需要它們,但每個程式只要以相同的方式完成就沒問題。

藍綠佈署:發布、回復與日常運作

大多數無伺服器系統包含更新或更改程式碼工件或例項執行組態的概念,並以協調的方式進行。系統還瞭解什麼是工作單元,並具有一些(內部)將請求路由到執行指定程式碼工件的一個或多個例項的方法。一些系統將這些細節暴露給應用程式營運商,並允許營運商明確控制不同版本程式碼工件之間的傳入工作分配。

內容解密:

此段描述了無伺服器系統如何管理程式碼更新和例項組態。它強調了系統對工作單元的理解以及如何將請求路由到正確的例項。這些功能使營運商能夠控制不同版本之間的流量分配。

無伺服器啟動新例項並使用指定版本的程式碼工件,然後在舊例項不再使用時允許其被回收的過程,看起來很像有伺服器的藍綠佈署模式:啟動第二批「綠色」伺服器,同時允許「藍色」伺服器繼續執行,直到綠色伺服器啟動並且流量已從藍色伺服器重新路由到綠色伺服器。藉助無伺服器請求路由和自動擴充套件,這些轉換可以非常快速地完成,小型應用程式可以在不到一秒鐘內完成,而大型應用程式也可以在五分鐘內完成。

藍綠佈署與金絲雀發布

除了藍綠佈署模式外,還可以按百分比(比例)控制將工作分配給不同版本的應用程式。這使得增量發布(工作慢慢從一個版本轉移到另一個版本,例如允許新版本的快取填充)和金絲雀佈署(將小部分工作路由到新應用程式,以檢視應用程式如何對真實使用者請求做出反應)成為可能。

內容解密:

此段描述了無伺服器系統如何支援藍綠佈署和金絲雀發布。這些佈署策略使營運商能夠管理佈署風險,並透過快速回復到已知的工作版本來降低平均還原時間(MTTR)。

增量發布主要用於管理規模,而藍綠和金絲雀發布則主要用於管理佈署風險。執行服務的風險之一是佈署新的應用程式碼或組態——更改已經執行的系統會引入新系統狀態無法運作的風險。透過使啟動具有指定版本的新例項變得容易,無伺服器使回復到現有(工作)版本變得更快、更容易。這降低了平均還原時間(MTTR),這是一個常見的災難還原指標。

無伺服器架構的優勢

透過使個別例項變為臨時例項並根據傳入工作自動擴充套件,無伺服器可以降低「基礎設施雪花」(infrastructure snowflakes)的風險——即那些透過手工特別設定並具有難以複製或修復的手工組態的系統。當例項作為常規基礎設施操作的一部分自動替換和重新佈署時,例項還原和初始化會不斷被測試,這往往能確保任何缺點很快得到修復。

內容解密:

此段描述了無伺服器架構如何透過使例項變為臨時例項來提高基礎設施的彈性和安全性。這種方法簡化了許多操作問題,並降低了基礎設施雪花的風險。

不斷替換或重新整理基礎設施例項也可以帶來安全優勢:被攻擊者破壞的例項將以與被軟體錯誤破壞的例項相同的方式重置。臨時例項可以透過使攻擊者難以保持永續性來改善應用程式的防禦姿態,迫使他們重複破壞相同的基礎設施。需要重複執行破壞會增加檢測攻擊和關閉漏洞的機會。

無差異的重複勞動

正如前面在「為什麼它被稱為無伺服器?」中所描述的那樣,無伺服器平台的一個好處是提供可能難以或重複在每個應用程式中實作的通用功能。

內容解密:

此段描述了無伺服器平台如何提供通用功能,以簡化應用程式開發和運作。

一旦傳入系統的工作被分解並被基礎設施識別,就很容易利用該框架開始協助否則需要應用程式碼的無差異的重複勞動。其中一個例子是可觀察性:傳統應用程式需要檢測每次請求(工作單元)何時發出以及完成該請求需要多長時間。使用管理您的工作單元的無伺服器基礎設施時,當工作交給您的應用程式時,很容易讓基礎設施啟動計時器,並在您的應用程式完成工作時記錄延遲測量結果。

自動監控與可觀察性

雖然仍可能需要從應用程式內部提供更詳細的指標(例如,回應工作請求掃描的記錄數),但對工作單元的吞吐量和延遲進行自動監控、視覺化和甚至警示,可以為應用團隊提供「零努力」的簡單體驗。

內容解密:

此段描述了無伺服器基礎設施如何提供自動監控和可觀察性功能,以簡化應用程式開發和運作。這種方法可以降低應用團隊的工作量,並提高應用程式的整體品質。

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title 無伺服器運算核心概念與實踐

package "安全架構" {
    package "網路安全" {
        component [防火牆] as firewall
        component [WAF] as waf
        component [DDoS 防護] as ddos
    }

    package "身份認證" {
        component [OAuth 2.0] as oauth
        component [JWT Token] as jwt
        component [MFA] as mfa
    }

    package "資料安全" {
        component [加密傳輸 TLS] as tls
        component [資料加密] as encrypt
        component [金鑰管理] as kms
    }

    package "監控審計" {
        component [日誌收集] as log
        component [威脅偵測] as threat
        component [合規審計] as audit
    }
}

firewall --> waf : 過濾流量
waf --> oauth : 驗證身份
oauth --> jwt : 簽發憑證
jwt --> tls : 加密傳輸
tls --> encrypt : 資料保護
log --> threat : 異常分析
threat --> audit : 報告生成

@enduml

圖表翻譯: 此圖表展示了無伺服器系統處理工作單元的流程。它從識別工作單元開始,將其路由到正確的例項,執行程式碼,記錄延遲,然後根據需要自動擴充套件,最後結束流程。這個過程體現了無伺服器架構如何簡化處理工作單元的最佳實踐,並提供了諸如自動監控和可觀察性等功能,以提高應用程式的品質和降低運作風險。