Categories
程式開發

跟踪AWS Lambda函數的正確方法


本文要點

  • AWS Lambda是許多雲原生應用程序和用例的關鍵要素。

  • AWS Lambda的本質要求特別注意可觀察性。

  • 分佈式跟踪對於成功運行複雜的、基於Lambda的應用程序非常有必要。

  • Lambda的分佈式跟踪需求強調了對全面的、即時的、低維護成本的分佈式跟踪的需要。

AWS Lambda可能是過去幾年軟件開發中云原生轉換最典型的技術之一。 根據其官方網站的公告:“使用AWS Lambda,用戶無需提供或管理服務器即可運行代碼,用戶只需為所消耗的計算時間付費。”從2014年最開始只支持Node.js,到現在AWS Lambda已經可以支持在各種編程語言中開發和部署函數,包括Go、Java、Python、Ruby和C#等。

AWS Lambda的發布向我們推出了作為主流雲原生範例的無服務器計算(以函數即服務作為“計算”基本面)。 其他主流的雲平台也提供過類似的功能,如谷歌云函數微軟Azure函數,以及像基尼特語Apache OpenWhisk這樣的開源項目也提供過。

在推出五年多之後,AWS Lambda可以說仍然是最知名的以及採用度最高的的無服務器平台,儘管沒有其被採用的精確數據,而且相對公平來講,它已經超越了“早期採用者”階段(即僅在開發初期選擇使用)。

然而,AWS Lambda仍然與過去幾年雲原生的另一個典型主題-可觀察性有些矛盾。 儘管Lambda集成了CloudWatch的metrics和logs,以及X-Ray的分佈式跟踪,但調試出錯時要找出生產中某個函數的問題仍然是一個相當大的挑戰。

本文重點關注分佈式跟踪,並基於AWS Lambdas在當今計算環境中所使用的用例,討論了在AWS Lambda函數中獲取和利用可觀察性的最佳實踐。

為什麼Lambda用戶會有這麼大的增長?

使用AWS Lambda,用戶可以定義同步執行的函數,例如為HTTP請求提供服務,或者異步地響應其他AWS服務生成的事件。 能觸發AWS Lambda函數的事件非常豐富,並且還在不斷增長,其中被採用最多的事件類型有:

  • CloudWatch事件:可以使用描述AWS資源變化的簡單規則來定義。

  • S3事件:在S3 buckets中創建或刪除對象時發出。

  • SQS事件:它將隊列在SQS中的消息傳遞給Lambda函數進行處理。

但是除了AWS Lambda的基本功能之外,採用者也不需要對它的典型特性多加處理:

  • 無基礎設施管理:AWS Lambda自動管理分配給運行函數的基礎設施,彈性增配和釋放計算資源。 隨著每週一早上,工作日伊始人們回到辦公桌時,應用程序需要提供的負載增加,AWS Lambda會在幕後自動增加函數的實例數量。 在工作日結束之時,所需負載下降,未充分利用的實例將自動釋放。 AWS Lambda的承諾是為開發人員減負,這些本來也不是開發人員真正需要關注的,開發人員只需專注開發。

  • 無固定成本:當提供函數時,用戶只需根據提供工作負載期間分配的CPU時間和內存付費即可,而當沒有工作負載時,不會產生任何成本。 成本隨需變化,不固定。

AWS Lambda達到了上面的要求嗎? 這裡面最主要的是,用戶可以只在需要的時候將代碼放到AWS Lambda運行,並且只支付函數為工作負載服務的時間(雖然會四捨五入到最接近的100ms)。 然而,這是以性能上的不可預測性為代價的。 當AWS Lambda啟動一個實例來處理負載時,因為初始化需要運行時間,通過該實例的第一個請求將遭受相當高的延遲。

這種現像被稱為“冷啟動”,為此Amazon提出來一個創造性的解決方案來保持函數“溫暖”,允許用戶通過付費的方式來為一定數量的實例”保暖“,通過這種方式開發人員可以專心於基礎設施,這可以算是固定成本,但是這對於“對Lambda工作負載延遲峰值”非常敏感的用戶來說非常有用。

最常見的Lambda用例

與所有通用計算平台一樣,用戶可以使用AWS Lambda做很多不同的事情。 在實踐中,最經常出現的用例如下:

  • 原型設計和早期開發:由於沒有前期的基礎設施成本,AWS Lambda對於新產品和新功能來說是一個非常有吸引力的原型設計平台,特別是對於那些不希望或不能投入人員和資金來維護虛擬機和持久化容器部署的初創企業和小型企業。 然後,隨著產品逐漸成熟,其工作負載變得更加可預測,開始出現遷移趨勢,從AWS Lambda轉向更自管理的計算平台,如EC2或Fargate,原因如下:
  • 成本:如果用戶的工作負載不需要Lambda的“調整為0”功能(即不需要工作負載時完全釋放計算資源),而且願意自己處理容器或虛擬機的擴容或釋放,這種既定工作負載的情況用戶還要為AWSLambda的靈活性和隨需應變性支付一定規模的漲價成本時,成本就變得相當大。
  • 複雜性:儘管沒有什麼能真正阻止用戶將大型代碼庫部署到AWS Lambda(用戶有250MB的可用空間用於部署包,這相當於大量代碼),但函數最好應該相對小和簡單,因為它們在生產環境中很難觀察和調試。
  • 業務流程&系統集成:許多AWS服務中事件觸發器的存在使得Lambda函數自然成為系統間(業務)流程集成的候選對象。 例如,在Instana中我們使用Lambda函數對許多不同類型的自動化,從Quality Assurance任務(像自動供應基礎設施)到測試最新的構建,到將我們的支持門戶整合為項目管理系統,再到自動為我們的工程師創建工作項(以響應客戶即工程師打開Support Tickets的操作)。

人們似乎對將AWS Lambda用於機器學習用例越來越感興趣,尤其是與AWS Sagemaker結合使用。

對於Lambda是一個優秀的業務流程和系統集成工具這一事實,有一個有趣的推論,(幾乎)沒有Lambda函數是孤島。 Lambda函數,往往召集其他Lambda函數,以及沒運行在Lambda上的系統。 這些從Lambda函數中調用的系統,要么是AWS管理的服務,要么是部署在其他AWS計算平台(如EC2、ECS或Fagate,甚至是本地平台)上的其他客戶系統。 相關地,後面將討論關於分佈式跟踪AWS Lambda函數的需求。

Lambda的模棱兩可

每一個計算範例都伴隨著權衡,Lambda也不例外:

  • 難以調試:“無服務器”這一事實意味著,在不可避免地出現錯誤時,用戶基本上無法訪問生產基礎設施進行調試。 (“服務器”當然存在,但是用戶無法控制它,所以對用戶來說它看起來像是“無服務器”。)當然,AWS提供了在本地運行Lambda代碼的方法。 無服務器框架也有本地測試。 在將遠程調試工具附加到Lambda函數(例如對於蟒蛇)方面,還有一些有趣的概念驗證。 然而現實是,當用戶在生產中出問題時,所能調試的開箱即用功能是非常有限的,它往往成為”Cloud Printf“的遊戲(也就是添加更多的日誌到CloudWatch,推出一個新的Lambda版本,然後祈禱它受歡迎),如果你正忙著修復故障的話,這並不是一個有趣的遊戲。 更糟糕的是,由於一次AWS Lambda調用的成本取決於函數運行的時間,讓AWS Lambda函數卡住的Bug(比如處理意外的大數據庫結果集)既難以調試,也會給用戶的雲預算帶來巨大開銷。 這常常使用戶陷入兩難。
  • “無狀態”只是意味著用戶從其他地方提取狀態:就運行期間而言,Lambda需要函數是無狀態的。 用戶不能依賴任何一個Lambda函數來保留狀態,而不處理之前的請求。 然而,需要狀態來處理的業務邏輯是非常罕見的。 因此,大多數Lambda函數需要從其他服務加載一些狀態信息,這可能會導致不可預測的執行時間,而且通常Lambda函數也需要存儲一些狀態修改。 公平地說,AWS基礎設施內部的輸入輸出問題似乎很少,像“調用出一半RDS數據庫就失敗”這樣的編程疏忽也很少。
  • 分佈式複雜性:複雜的場景通常涉及大量Lambda函數,這些函數通過事件彼此鬆散耦合。 來看看作者所描述的“AWS中典型的100%無服務器架構”,這其中有很多移動的部分需要跟踪。 考慮到許多Lambda函數都是異步操作的,那麼要找出哪些函數涉及到哪個請求以及哪裡出錯了,就像嘗試完成一個百萬塊的拼圖遊戲一樣。

許多AWS Lambda架構中固有的分佈式複雜性,以及在AWS“生產”中運行的AWS Lambdas的有限調試能力,都要求用戶集中Lambda函數的所有可觀察性。 這意味著,除了CloudWatch中明顯的logs和metrics之外,還要對Lambda函數採用分佈式跟踪。

分佈式跟踪有助於防丟失

自本世紀初以來,分佈式跟踪已成為應用程序性能監測方法中不可分割的一部分。 而且由於開放追踪API和implementations通過開源項目實現,比如齊普金積家,以及像一些無關的項目開放式人口普查HTrace,分佈式跟踪最近已經在監測和可觀察性的前沿擁有了很大話語權。 雖然OpenTracing和OpenCensus項目已經停止了,但是他們的繼承者開放遙測仍在積極地工作著。

分佈式跟踪概述

微服務和雲原生架構的出現無疑使我們的分佈式系統比以往任何時候都更加分佈式。 與此同時,信號軟件的組件變得越來越小。 如果用戶越希望有更多的活動部件協同工作以服務於工作負載,那麼他的集體和個體行為就越需要可見,特別是與“如何為最終用戶服務”有關的行為。 有人說,開發人員的工作越來越像水管工的工作,專注於連接各種微服務的“管道”。

這種增加的分佈性和相互依賴性正是分佈式跟踪變得如此重要和有價值的原因。 分佈式跟踪是一種監測實踐,它涉及到服務,以集體和協作的方式記錄那些描述它們在服務一個請求時所採取的操作的spans。 與相同請求相關的spans被分組在一個跟踪中。 為了明了哪個跟踪被記錄了,每個服務都必須在它自己對其他上游服務的請求中包含跟踪上下文。 簡而言之,你可以把分佈式追踪想像成一場接力賽,一種田徑運動,運動員輪流跑,互相傳遞接力棒。

將分佈式跟踪類比為接力賽,每個服務都是運動員,跟踪上下文是接力棒。 如果其中一個服務丟失了它,或者服務之間的切換不成功(例如由於實現了不同的分佈式跟踪協議而導致的不成功),那麼跟踪就會中斷。 分佈式跟踪和接力賽之間的另一個相似之處是,比賽的每個環節都很重要,都有可能會讓你輸掉比賽,但除了在每個環節不掉鍊子之外,同時你必須在每個環節都跑得快才能出類拔萃。

AWS Lambda函數的分佈式跟踪

在討論跟踪AWS Lambda有什麼可用之處之前,讓我們討論一下分佈式跟踪解決方案應該滿足的功能性和非功能性需求::

  • 多軌並行的運行時間:AWS Lambdas可以用多種語言編寫,最常用的是Node.js和Python,但還有更多的語言,比如Java,Ruby,.Net Core和Powershell。 與微服務發生的情況類似(因為Lambda函數實際上是一種非常微小的微服務),開發團隊選擇他們認為最適合這項任務的語言,涉及到他們想採用的libraries和SDKs,以及團隊對該語言的熟悉程度等。 我從與我交互過的幾乎每一個採用Lambda的人那裡聽到的都是,他們至少使用兩個不同的AWS Lambda運行時間。
  • 多平台:如前所述,沒有一個AWS Lambda函數是孤島。 有些情況下,架構是僅作為AWS Lambda函數實現的,但根據我們的經驗,這種情況是(相當特殊的)例外,而不是規則。 通過分佈式跟踪實現的洞察力的價值隨著相互連接的系統數量的增加而增加,這意味著無論用戶希望在AWS Lambda函數中使用什麼分佈式跟踪框架,都應該更好地用於基礎設施的其他部分。 對於Lambda使用的基礎設施來說,這種做法尤其正確,但網絡效應毫無疑問適用於分佈式跟踪。 順便說一句,這正是W3C跟踪上下文規範的目的所在,該規範旨在提供分佈式跟踪implementations之間互操作性的措施。 順便提一下,用戶在Lambda函數中使用的語言可能與用戶在數據中心的“遺留”應用程序中使用的語言不同,這就增加了Lambda跟踪的多軌並行的運行時間需求。
  • 間接成本低:分佈式跟踪實現帶來的間接成本不僅為最終用戶帶來了延遲,還直接影響了AWS賬單(Lambda函數按CPU時間和最大內存分配來收費)。 凡是考慮成本的分佈式跟踪implementation,都不會給Lambda函數的內存增加幾十或幾百兆字節的佔用(不過,我在其他平台上看到過這種情況)。 但是,CPU時間可能會受到影響,特別的,由於Lambda是無狀態的,跟踪數據必須在Lambda函數完成之前發送到APM解決方案,這通常會阻塞函數的完成,直到跟踪數據上傳完成,不這樣做就可能會丟失這些跟踪數據。

我想了很久,很難在上面的列表中添加一個名為“與其他AWS服務集成”的條目。 畢竟,Lambda函數是從其他服務生成的事件異步調用的,以及從AWS的API網關和應用程序負載均衡器同步調用的。 而擁有來自API網關的相同跟踪spans將有助於回答“這個延遲是從哪裡來的?”,這真的和分佈式系統一樣古老。 但是,我仍然決定沒加進來。 因為延遲的根源是很少使用負載平衡器和Lambda函數之間的AWS網絡,而不是AWS Lambda函數本身,也不是他的dependencies,阻礙或等待長時間運行的同步調用,或其內部運算。

Instrumentation類型

追踪數據的收集是由專門的Instrumentation來執行的。 一般來說可以分為兩大類:

  • ProgrammaticInstrumentation是提供API來進行編碼的Instrumentation,例如開放追踪或AWS的X射線SDK
  • Automatic instrumentation是通過修改代碼和用於提取跟踪數據的框架(不需要額外的代碼)來執行的。 例如,通常在Node.js中通過monkeypatching來實現,在Java中通過bytecode manipulation來實現。

注意,如果在用戶消費的框架、庫,以及供應商管理的服務(如AWS RDS)中內置ProgrammaticInstrumentation,它們實現Instrumentation的方式讓您感覺像是自動的,因為用戶不需要維護這些代碼。 對我來說,這正是問題的關鍵:實現Instrumentation的好代碼是用戶不需要持有和維護的。

Instrumentation的交付

但是,用戶如何將Instrumentation交付到生產系統以便利用它收集需要的數據呢? 要實現也是有梯度的,從手動到自動。

  • 隨代碼內置Instrumentation或者在運行代碼期間運行Instrumentation:無論何時部署函數的新版本,Instrumentation代碼都會隨之嵌入。 Programmatic instrumentation主要是內置的,儘管有方法將API和implementation分割,但這樣做的話就需要額外的可用dependencies。 當然,也可能需要配置內置的Instrumentation。
  • Drop-inInstrumentation包含在程序運行時添加激活Instrumentation的一些dependencies和配置。 例如,由Lambda層提供的Instrumentation,通過配置選項激活,像定制包裝器腳本(也通過Lambda層提供),它將執行委託給實際的Lambda函數處理程序。 (順便說一下,Instana就是這樣做的。)

正如我認為自動化配置優於Programmatic配置一樣,交付Instrumentation所需的工作量越少越好。 從這個角度來看,人們可能認為ProgrammaticInstrumentation有其優點,只要它也是內置的。 但在我的經驗中,情況並非如此。 維護ProgrammaticInstrumentation的工作比交付Drop-inInstrumentation的工作要昂貴得多。 在大多數情況下,自動化的交付可以在CI/CD管道中一次性設置完成,或者只需要很少的維護。

但是ProgrammaticInstrumentation需要隨著代碼不斷變化,而且只要代碼不斷變化,就需要付出一定的代價。 根據Lehman的軟件進化法則,軟件需要不斷變化才能保持有用。 在考慮ProgrammaticInstrumentation時,那些保持軟件有用所需的更改可能需要調整Instrumentation。 在Lambda中,這個變化的可能性會造成的需要調整Instrumentation的頻率高於往常,隨著用戶應用於Lambda的變化越多,集成軟件系統需要關心如何與這些變化進行交互,這通常需要調整跟踪數據的收集,並需要在Lambda碼庫的生命週期持續投入大量ProgrammaticInstrumentation。

最好的Instrumentation類型

總結一下,我之前已經討論過,最好的Instrumentation類型是自動的,並且在沒有太多開銷的情況下交付它是非常重要的。 那麼,這將如何實現呢? 在目前的技術水平下,有兩種方法可以實現這些需求:

  • 定制的Lambda運行時間:AWS Lambda允許用戶為Lambda函數提供定制運行時間,一些分佈式跟踪解決方案的供應商確實提供了定制的Lambda運行時間作為一個總承包解決方案。 然而,這意味著AWS Lambda運行時間提供的安全和維護更新、bug修復和新特性讓分佈式跟踪提供程序充當“守門員”。 用戶的里程可能會有所不同,但我個人會對此感到不舒服:這讓我想起了太多關於Android移動生態系統的問題,移動設備製造商在跟上Android發布和維護舊設備方面的記錄通常都很糟糕。 當然,用戶體驗有多好取決於分佈式跟踪供應商的實際工作質量,我的初衷並不是說這是一個不應該使用的選項。
  • Lambda層上的Instrumentation:用戶可以配置函數來使用AWS Lambda,這些層基本上是Lambda實例文件系統中可用的附加文件。 這可以用非常小的操作開銷來交付跟踪函數所需的Instrumentation。 許多分佈式跟踪供應商確實做到了這一點,用戶也不難發現許多分佈式跟踪供應商,例如,專注監視解決方案的λAWSome Lambda層列表。 在激活Instrumentation的容易程度方面,技術水平存在差異。 對於像Instana這樣的供應商,用戶只需要設置一些環境變量;對於其他類型,用戶則需要對代碼進行小的更改。 我個人的觀點是配置比代碼更改更容易處理,但同樣,具體到每個用戶的情況可能有所不同。

總而言之,在我看來,最好的Instrumentation是自動的,並且以盡可能最簡單的方式交付。

結論

AWS Lambda仍舊是無服務器計算的標準,即使隨著無服務器函數在應用程序開發中的使用達到了空前的高度,Amazon公司仍在探索無服務器計算在生產環境中的有效性能達到一個怎樣的程度。

隨著越來越多的組織將無服務器函數作為其應用程序開發過程和平台的重要組成部分,他們正在考慮如何在生產應用程序中使用更多的無服務器函數(以及是否應該使用)。

還有一個擔心就是可觀察性,對於實現無服務器生產代碼的團隊來說,這仍然是一個主要的挑戰,尤其是因為遺留APM工具仍然努力達到開發團隊所需的可見性水平。

AWS Lambda为开发人员和操作人员带来了显著的差异,强调了专门构建云原生监测工具的需求,这些工具能够应对无服务器监测的挑战,并使用不同的方法来获取应用程序的可观察性。

任何考慮將無服務器作為其生產環境的一部分的人都應該將端到端可觀察性作為一個必要的需求,並專注於能夠跨分佈式系統的監視和跟踪解決方案,這些分佈式系統是基於雲原生技術、像Lambda這樣的無服務器平台、以及通過Lambda集成的可能較老的技術構建的(因為無服務器很少是孤島)。

作者簡介:

Michele Mancioppi擔任Instana高級技術產品經理,負責代理、分佈式跟踪、Cloud Foundry和VMware Tanzu的所有產品開發工作。 在加入Instana之前,他是SAP雲平台性能團隊的開發專家和技術領導。 Michele擁有意大利Trento大學計算機科學學士和碩士學位,以及荷蘭Tilburg大學信息系統博士學位。

查看英文原文:跟踪AWS Lambda函數的正確方法