新聞中心
AWS lambda的各種優(yōu)秀實踐
譯文
作者:陳峻編譯 2019-05-07 09:00:40
云計算
服務(wù)器運維 如今,無服務(wù)器已經(jīng)成為了各種云應(yīng)用的最常見部署模式。而在這個領(lǐng)域中,AWS Lambda可謂最為“骨灰級”的工具了。大多數(shù)開發(fā)人員都或多或少地有過,運用Lambda來快速構(gòu)建并運行某個云端函數(shù)代碼的經(jīng)歷。在本文中,我將分享:如何通過了解Lambda的工作原理,來合理并充分地使用Lambda。

【51CTO.com快譯】概述
如今,無服務(wù)器已經(jīng)成為了各種云應(yīng)用的最常見部署模式。而在這個領(lǐng)域中,AWS Lambda可謂最為“骨灰級”的工具了。大多數(shù)開發(fā)人員都或多或少地有過,運用Lambda來快速構(gòu)建并運行某個云端函數(shù)代碼的經(jīng)歷。
然而,AWS在管理和處理可擴(kuò)展性、高可用性(HA)、安全性、以及性能等方面,卻不像AI機(jī)器人那樣,通過自我學(xué)習(xí)和優(yōu)化配置,來改進(jìn)所有的云原生(cloud-native)指標(biāo)。因此,開發(fā)人員需要在設(shè)計時,尤其注意并學(xué)習(xí)如何在成本和性能之間達(dá)到平衡。在本文中,我將分享:如何通過了解Lambda的工作原理,來合理并充分地使用Lambda。
高可用性
當(dāng)我們在運行某個Lambda函數(shù)時,它實際上是默認(rèn)運行在一個可以訪問外網(wǎng)的VPC(Virtual Private Cloud)之上。不過,它卻無法同時訪問到其他任何私有的VPC。此處所謂“訪問外網(wǎng)”是指:它只能訪問S3和DynamoDB的AWS服務(wù);而對于那些運行在其他VPC下的AWS資源(如:RDS,Elasticsearch等),則無法訪問到。
如果某個函數(shù)運行在Lambda所管理的VPC上,那么Lambda將負(fù)責(zé)它在該VPC區(qū)域的多個AZ(Availability Zone)中的可用性。但在大多數(shù)企業(yè)應(yīng)用場景中,我們的確需要同時訪問到RDS和其他的VPC資源。因此,我們需要確保如下兩個方面:
- 通過在不同的AZ中選擇多個子網(wǎng),來設(shè)計Lambda、并實現(xiàn)高可用性。
- 如果某個AZ發(fā)生故障,則其他AZ需要被分配足夠的IP地址,來處理并發(fā)的Lambda請求。(注意,每個Lambda的執(zhí)行都需要有一個私有的IP地址,來處理請求。)因此,我們需要在子網(wǎng)中分配足夠多的IP地址,以達(dá)到HA。
并發(fā)性
雖說AWS Lambda會以自己的方式來實現(xiàn)可伸縮性,但是對于有限的資源而言,Lambda會遵循如下的并發(fā)執(zhí)行限制:
- 帳戶級別 - 默認(rèn)情況下,它會參照每個區(qū)域內(nèi)的所有函數(shù),將該值定為1000。
- 函數(shù)級別 - 默認(rèn)情況下,它會使用“Unreserved Account Concurrency limit”,但是這并不是一種很好的實現(xiàn)方式。為了避免耗盡所有帳戶級別的并發(fā)數(shù),它會限制其他的功能函數(shù)。因此,我們應(yīng)該為每一個函數(shù)保留單獨的并發(fā)數(shù),以便在事件數(shù)量因為某種原因出現(xiàn)激增時,僅影響并隔離在該函數(shù)之中。
注意 - AWS始終保留一個具有至少100個并發(fā)執(zhí)行量的無保留并發(fā)池(unreserved concurrency pool),以處理那些未做特殊設(shè)置的函數(shù)請求。也就是說,您最多只能分配900個。
如果我們是在一個專有的VPC上運行Lambda呢?
在這種情況下,我們需要根據(jù)函數(shù)的ENI(Elastic Network Interfaces,彈性網(wǎng)絡(luò)接口)擴(kuò)展性,去請求足夠多的IP地址。您可以使用如下公式來估算ENI的近似容量:
- Concurrent executions * (Memory in GB / 3 GB)
其中:
- 發(fā)執(zhí)行 - 是工作負(fù)載的預(yù)計并發(fā)數(shù)(用每秒調(diào)用次數(shù)*平均執(zhí)行的時長,以秒為單位)。
- 內(nèi)存大小 - 是為Lambda函數(shù)配置的內(nèi)存數(shù)量(以GB為單位)。
在設(shè)計Lambda的并發(fā)性時,我們還應(yīng)該始終考慮,諸如DynamoDB、RDS等其他集成服務(wù)的限制。我們需要根據(jù)這些服務(wù)能夠處理的***連接,來調(diào)整函數(shù)的并發(fā)限制。
節(jié)流
正如前文在“并發(fā)性”中提到的,一旦函數(shù)事件出現(xiàn)激增,并超過了并發(fā)數(shù)的限制,那么Lambda將無法再處理任何新的請求。如果我們不及時予以處理的話,業(yè)務(wù)系統(tǒng)就會受到影響。
- 如果Lambda的調(diào)用是同步模式的話,它會馬上接收到429類型的錯誤代碼。同時,如果節(jié)流被設(shè)定為函數(shù)級別或是帳戶級別,那么它還能接收其他一些信息。因此,諸如API網(wǎng)關(guān)之類的調(diào)用服務(wù),則需要處理此類重試問題。
- 如果Lambda的調(diào)用是異步模式的話,Lambda只會在丟棄事件之前嘗試兩次。因此,如果函數(shù)無法處理該事件,我們就應(yīng)該使用SQS或SNS所定義的DLQ(Dead Letter Queue),來做稍后調(diào)試與處理。而如果我們忘記了定義DLQ,那些消息則會被直接丟棄掉。
- 如果Lambda調(diào)用是基于輪詢模式的話,我們進(jìn)一步細(xì)分兩種情況:
- 如果是流式(Kinesis),它將繼續(xù)重試,直到超時(最多為7天)。
- 如果是非流式(SQS),它將把消息放回到隊列之中,并僅在Visibility時限到期后才開始重試,并持續(xù)執(zhí)行下去,直到它能夠成功地完成處理、或是超過保留期。
內(nèi)存與成本之間的平衡
在Lambda里,內(nèi)存和CPU息息相關(guān),也就是說,如果您增加了內(nèi)存,那么CPU分配也應(yīng)該有所增加。因此,如果需要減少Lambda的執(zhí)行時間,那么我們就應(yīng)當(dāng)增加內(nèi)存和CPU。但是,如果您進(jìn)行過詳細(xì)的實驗就會發(fā)現(xiàn):在一定的限制情況下,單憑增加內(nèi)存只會增加購置成本,而并不會大幅減少執(zhí)行的時間。
目前市面上很少有開源的工具,能夠幫助我們找到***的資源配置。我個人傾向使用CloudWatch的各種日志,來監(jiān)控內(nèi)存的使用情況和執(zhí)行時間,進(jìn)而調(diào)整相應(yīng)的配置。對內(nèi)存進(jìn)行參數(shù)微調(diào),便可對AWS的整體成本產(chǎn)生較大的影響,我籍此來找到***平衡點。
性能 - 冷啟動與熱啟動
當(dāng)我們***次調(diào)用Lambda時,它會從S3那里下載代碼和所有依賴項,以創(chuàng)建容器,并在執(zhí)行代碼之前先啟動對應(yīng)的應(yīng)用程序。整個過程的耗時(代碼的執(zhí)行除外)被稱為冷啟動時間。而一旦容器被啟動并運行起來后,Lambda就已經(jīng)為后續(xù)的調(diào)用完成了初始化,它只需要執(zhí)行應(yīng)用程序的邏輯便可。因此,這段時長就被稱為熱啟動時間。
那么問題來了,我們應(yīng)該縮短冷啟動時間、還是熱啟動時間呢?原則上說,作為完整執(zhí)行時長的一部分,冷啟動占據(jù)了大部分時間,因此需要想辦法予以減少。但是,在實踐中,我們卻可以通過優(yōu)質(zhì)的代碼,來減少熱啟動的時間。
下面,讓我們討論如何才能提高Lambda的整體性能:
- 選擇諸如Nodejs和Python之類的解釋性語言,而不是Java或C ++,來減少冷啟動時間。
- 如果出于某種原因不得不選用Java的話,請使用Spring Cloud Functions,而不是Spring Boot Web框架。
- 由于我們設(shè)置ENI的耗時較長、并且會增加冷啟動時間,因此除非您需要帶有專有IP地址的VPC資源,否則請使用默認(rèn)的網(wǎng)絡(luò)環(huán)境。我個人判讀:隨著新版AWS Lambda的即將發(fā)布,此方面應(yīng)該有所改進(jìn)。
- 刪除所有與運行該函數(shù)無關(guān)的依賴項。僅保留那些必需的。
- 使用各種全局/靜態(tài)變量、以及Singleton對象,這些變量能夠在容器發(fā)生故障之前,一直保持活動狀態(tài)。因此,任何后續(xù)調(diào)用都不必再重新初始化這些變量與對象了。
- 請使用全局定義的數(shù)據(jù)庫連接,以便它們能夠被重用到后續(xù)的調(diào)用中。
- 如果您選用的是Java,那么請使用諸如Dagger和Guice之類的簡單IoC依賴注入,而不是Spring框架。
- 同樣,如果您選用了Java,那么請將依賴項.jar文件與函數(shù)的代碼相分離,以便對解包程序加速。
- 如果您選用的是Nodejs,請控制Function js文件的體積小于600字符,并使用V8的運行環(huán)境(runtime)。V8優(yōu)化器能夠內(nèi)聯(lián)主體小于600字符(包括各種注釋)的函數(shù)。
- 同樣,如果您選用了Nodejs,則可以使用代碼的minification和/或uglification,來減小包的大小,進(jìn)而大幅減少下載包的耗時。在某些情況下,我曾經(jīng)看到有將包的體積從10MB減少到1MB的案例。
- Minification – 會刪除掉所有的空格、換行符、以及注釋。
- Uglification – 會對所有變量進(jìn)行混淆和簡化。
示例,原代碼:
- var organizationname = “xyz”
- var bigArray = [1,2,3,4,5,6]
- //write some code
- for(var index = 0; index < 6; index++){
- console.log(bigArray[index]);
- }
Minification之后:
- var organizationname = “xyz”, bigArray = [1,2,3,4,5,6] for(var index = 0; index < 6; index++) console.log(bigArray[index]);
Uglification之后:
- for(var o=”myname”,a=[1,2,3,4,5,6],e=0;e<6;e++)console.log(a[e])
網(wǎng)上有不少的文章都提到:Lambda的執(zhí)行環(huán)境已經(jīng)具有了適用于Nodejs和Python的AWS SDK。因此,我們不必在依賴項中添加它們。此特性雖然有利于提高性能,但是潛藏著一個問題:該SDK庫將定期使用***的修補(bǔ)程序來進(jìn)行升級,為了不影響Lambda的各種行為,您***采用自己的依賴項管理方式。
安全性
- 為每個函數(shù)分配一個IAM角色。即使有多個函數(shù)需要相同的IAM策略,單個IAM角色也應(yīng)該只映射一個函數(shù)。當(dāng)特定的函數(shù)安全策略需要加固時,這將有助于保持最小權(quán)限的策略。
- 由于Lambda會在共享的VPC上運行,因此將AWS的憑據(jù)保留在代碼中并不可取。
- 在大多數(shù)情況下,IAM的執(zhí)行角色已足以通過使用AWS SDK,去連接到AWS的各種服務(wù)。
- 如果函數(shù)需要調(diào)用跨帳戶的服務(wù),則可能會使用到不同的憑據(jù)。因此我們需要在AWS的Security Token Service(請參見https://docs.aws.amazon.com/STS/latest/APIReference/Welcome.html)中使用Assume Role API,并檢索各種臨時憑據(jù)。
- 如果函數(shù)需要存儲長期憑據(jù)(如DB憑據(jù)、訪問密鑰),請使用帶有加密助手或AWS System Manager的環(huán)境變量。
可測性
由于AWS Lambda讓用戶的代碼運行在云端,那么我們該如何在本地進(jìn)行測試呢?
雖然Lambda并不提供任何直接測試的URL,但是我們可以根據(jù)要啟動的事件源系統(tǒng)來開展測試。
- 我們可以使用AWS SAM(請參見https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html)進(jìn)行Lambda函數(shù)的本地測試。它為CLI提供了類似本地Lambda的執(zhí)行環(huán)境。我們可以獲得API Gateway的localhost URL,它會在本地調(diào)用Lambda函數(shù)。
- 我們可以使用localstack(請參見https://github.com/localstack/localstack)開源項目,來創(chuàng)建具有大量AWS資源/服務(wù)的本地環(huán)境。它可以與其他AWS服務(wù)一起運行Lambda。而且由于它能夠以API的形式提供所有的服務(wù),并能夠在后端作為Docker容器運行,因此您也可以將AWS SAM與localstack相集成。
- 將業(yè)務(wù)邏輯放到Lambda Handler之外。Handler函數(shù)應(yīng)當(dāng)僅用于檢索各項輸入,然后將它們傳遞給其他函數(shù)/方法。這些函數(shù)/方法會將它們解析成為與我們的應(yīng)用程序相關(guān)的變量,然后進(jìn)一步使用。該過程不但實現(xiàn)了將業(yè)務(wù)邏輯與處理程序相分離,而且可以在我們創(chuàng)建的對象和函數(shù)的上下文中進(jìn)行測試。
藍(lán)/綠部署
通過Lambda附帶的Versioning和Alias功能,我們可以發(fā)布一個函數(shù)的多個版本。同時,我們可以在單獨的容器中并行地調(diào)用每個版本。默認(rèn)情況下,版本特征是由$LATEST來表示的。在開發(fā)的過程中,我們可以使用這些版本,來創(chuàng)建諸如dev/UAT等多個環(huán)境。但是,由于我們在每一次上傳新的代碼時,版本都會遞增,而客戶則會被指向***的版本。因此,我們***不要直接將Versioning用于生產(chǎn)環(huán)境,而可以用到Alias。
Alias可以指向函數(shù)的某個特定版本。因此,如果您的代碼發(fā)生了更改,并且發(fā)布了更新的版本時,事件源仍將指向原來相同的Alias。我們只需管理好Alias何時需要被指向新的版本便可。這便實現(xiàn)了藍(lán)/綠部署。我們可以使用一些樣本事件來測試新的版本,確認(rèn)其工作正常之后,再通過修改Alias的指向,來切換訪問的流量。與此同時,如果發(fā)現(xiàn)出現(xiàn)任何問題,我們還可以迅速回滾到原始的版本上。
監(jiān)控
個人以為:CloudWatch能夠很好地與Lambda配合使用,并為用戶提供Lambda執(zhí)行的各種詳細(xì)信息。Lambda能夠自動跟蹤請求數(shù)、每個請求的執(zhí)行時間、導(dǎo)致錯誤的請求數(shù)、以及發(fā)布相關(guān)的CloudWatch指標(biāo)。同時,您也可以利用這些指標(biāo),來自定義各種CloudWatch的警報功能。
另外,我們還可以使用X-Ray來識別Lambda執(zhí)行中的各種潛在瓶頸。它對于我們試圖可視化那些耗費在函數(shù)執(zhí)行上的時間,是非常實用的。而且,X-Ray還有助于跟蹤那些與整個流程相連接的所有下游系統(tǒng)。
其他建議
- 請勿使用AWS Lambda Console開發(fā)那些直接被用在生產(chǎn)環(huán)境中的代碼。
- 由于代碼版本控制并非自動生效的,因此如果您誤點了Save按鈕,那么生產(chǎn)環(huán)境中的工作代碼就會被***覆蓋掉。
- 它并沒有與GitHub、或其他代碼存儲庫相集成。
- 它無法被導(dǎo)入AWS SDK之外的模塊中。因此,如果您需要某種特定的庫,則必須從一開始就在本地開發(fā)自己的函數(shù)、創(chuàng)建.zip文件、然后將其上傳到AWS Lambda中。
- 請使用AWS SAM或無服務(wù)器框架來進(jìn)行開發(fā)。
- 就Lambda部署的CI/CD計劃而言,它其實與其他可交付式的計劃并無不同。
- 可使用各種環(huán)境變量(Environment Variables)和參數(shù)存儲(Parameter Store),來將代碼與配置相分離。
總結(jié)
在本文中,我們討論了在設(shè)計和部署Lambda時,各種值得參考和使用的***實踐。我們可以根據(jù)實際應(yīng)用的編碼語言和用例,來不斷改進(jìn)業(yè)務(wù)系統(tǒng)的性能。當(dāng)然,我們也可以在其他的云平臺,以及Kubernetes的無服務(wù)器平臺中借鑒這些***實踐。希望您能夠?qū)⑦@些實踐總結(jié)運用到自己成熟的生產(chǎn)環(huán)境與應(yīng)用之中。
原文標(biāo)題:AWS Lambda Best Practices,作者:Rajesh Bhojwani
【51CTO譯稿,合作站點轉(zhuǎn)載請注明原文譯者和出處為51CTO.com】
文章標(biāo)題:AWSLambda的各種優(yōu)秀實踐
網(wǎng)頁URL:http://m.fisionsoft.com.cn/article/djejieh.html


咨詢
建站咨詢
