serverless在微店node領域的探索應用

背景

目前微店中臺團隊爲了知足公司大部分產品、運營以及部分後端開發人員的嚐鮮和試錯的需求,提供了一套基於圖形化搭建的服務端接口交付方案,利用該方案及提供的系統可生成一副包含運行時環境定義可當即運行的工程代碼,最後,經過 「某種serverless平臺」 實現生成後代碼的部署、CI、運行、反向代理、進程守、日誌上報、進程分組擴容等功能。前端

這樣,產品和運營人員可基於此種方式搭建的接口配合經常使用的cms系統實現簡單查詢需求如活動大促的自主「研發」上線,代碼的可靠性、穩定性由中臺研發側提供的「某種serverless平臺」保障,有效支撐了多個業務快速上線,節省後端開發人員的人力與硬件資源的開銷(大多數需求下,nodejs業務對虛擬機的資源開銷小於java業務)。java

接口搭建系統

此處並不講解接口搭建系統的原理與實現,只說明其與上文提到的 「某種serverless平臺」 的關係。node

![enter image description here](https://user-gold-cdn.xitu.io/2019/8/20/16cacb3d159561c2?w=1798&h=1012&f=png&s=127439)

這是系統的全貌,部分細節因爲敏感信息而省略。平臺使用方可基於每一個功能組件搭建出一套複雜的業務流,在搭建階段,提供在線debug和日誌功能,可用於排錯;在部署CI階段,可集成不一樣的運行時平臺,既能夠是自主實現的運行時,也但是第三方雲平臺;在運行階段,經過使用agentool工具實時監控當前服務的性能,並可經過traceId一覽請求在各系統的全貌。git

serverless方案

本節以資源隔離粒度爲度量,介紹了我對三種serverless方案的取捨以及最終爲什麼選擇了隔離程度更高的kubeless雲平臺。github

基於函數隔離的Parse Server方案

Parse Server提供了基礎功能:基於類與對象的權限控制、基於document的nosql存儲、簡易的用戶身份認證、基於hook的自定義邏輯等,但通過筆者的調查與論證,最終並無採用相似單進程下基於函數隔離的Parse Server及相似方案,這有幾點緣由:sql

  1. Parse Server方案很重,額外引入了很是多不須要的功能,如權限控制、認證、存儲等
  2. 服務隔離級別低,多個服務在一個進程運行,多個服務會在libuv層互相搶佔CPU,互相影響對方的業務處理
  3. 水平擴容難度大,針對單個服務的擴容沒法作到
  4. 底層基於express框架,沒法知足運行時接口調用鏈路的trace追蹤
  5. 當多個服務同時引入不一樣的資源如db、es或者服務建立的對象足夠多時,會存在Parse Server主進程溢出的風險,畢竟64位機的node堆內存是有1.4GB上限的,儘管這個上限是可配置的
  6. Parser Server發佈的接口需經過其client調用,這在公司商用狀況下須要作許多額外的配置才能知足

Parse Server官網docker

基於進程隔離的super-agent方案

爲了解決多個服務搶佔libuv的問題,我選擇了自主研發的 super-agent方案,經過其名稱即可知它是一個超級代理,但它不只是代理,仍是一個具備極簡功能且可靠的發佈系統和運行時容器;它是一個分佈式應用,節點間功能劃分明確;它同時提供實時調試功能。express

super-agent是一個多角色分佈式系統,它便可以看作node容器,也可當作serverless的node實現,它以進程爲粒度管理服務。它分爲「協調者」和「參與者」,協調者實現 應用CI部署、啓動、進程維護與管理和反向代理功能,參與者實現 業務請求代理、接受協調者調度後端

enter image description here
在super-agent架構中,端口是區分服務的惟一標識,端口對客戶端而言是透明的,這層端口資源的隔離由super-agent來作掉,所以多個服務可避免在libuv層的互相競爭,提供水平擴容的可能性。

反向代理

super-agent最核心的功能在於反向代理。因爲每一個服務都被包裝成有單獨端口的獨立HTTP應用,所以當用戶請求流量通過前端轉發層後進入super-agent,由其根據相關規則作二次轉發,目前支持基於 「路徑、端口」規則的轉發。安全

部署

後端應用部署須要進行 「優雅降級、流量摘除、健康檢查、應用初始化完畢檢查、流量導入、全部參與節點的部署狀態查詢」 等步驟,須要妥善處理;同時,協調者負責協調衆多參與節點所有完成部署操做,這是一個分佈式事務,須要作好事務出錯後的相關業務補償。

關於流量

上圖並未畫出節點角色的區別,實際上只有參與者節點才真正接受用戶請求。

協調者流量所有來自於內部系統,包括接受 「接口搭建系統」調用或者其餘系統實現的dashboard服務;同時其會向參與者發送相關信令信息,如部署、擴容、下線、debug等。

參與者流量來自於內部系統和外部流量,其中大部分來自於外部流量。內部流量則承載協調者的信令信息。

水平擴容

服務的水平擴容重要性不言而喻。super-agent方案中,協調者負責管理服務的擴容與邏輯分組。

此處的服務是經過服務搭建平臺經過拖拽生成的nodejs代碼,它是一個包含複雜業務邏輯的函數,能夠是多文件。具體的,super-agent經過將該服務包裝成一個HTTP服務在單獨的進程中執行。所以,若是要對服務進行水平擴容,提供多種策略的選擇:

  1. 默認每臺虛擬機或物理機一個服務進程,整體上N個機器N個服務進程
  2. 擴容時默認每臺機器再fork一個服務進程,N機器2*N個服務進程
  3. 爲了更充分利用資源,可爲每臺機器劃分爲邏輯組,同時選擇在某幾個組的機器單獨擴容

這些策略對下游應用透明,只需選擇相關策略便可。

水平擴容的缺點:每臺虛擬機或物理機資源有上限,一個正常的node進程最多消耗1.4GB內存,計算資源共享,在一臺8C16G的虛擬機上,最多可同時運行16個服務。及時經過分組擴容的方式,每當擴展新的虛擬機或物理機,都須要由super-agent根據分組信息實現進程守護,同時每次服務CI部署也一樣如此。運維管理上須要配合很是清晰明瞭的dashboard後臺才能快速定位問題,這點在多服務的問題上尤爲突出。

在線調試

super-agent提供消息機制,由搭建平臺中組件開發人員使用提供的serverless-toolkit工具debug相關邏輯,最終可在super-agent的協調者後臺查看實時debug結果。

總結

super-agent是符合常規的基於業務進程隔離的解決方案,它有效的支撐了微店的幾個活動及產品,雖然峯值QPS不高(100左右),但它也論證了super-agent的穩定性及可靠性(線上無事故,服務無重啓,平穩升級)。

可是,super-agent仍然存在幾個問題,它讓咱們不得不另覓他法:

  1. 平常運維困難,須要開發一系列後臺系統輔助運維,這須要很多人力成本
  2. 這是一個典型的一機多應用場景,當部署super-agent時會對運行其上的服務有所影響(重啓),儘管這個影響並不影響用戶訪問(此時流量已摘除),但仍然是個風險點
  3. 水平擴容實現繁瑣,當下掉某幾臺參與節點時會帶來很多影響

基於內核namespace隔離的kubeless方案

基於kubeless的方案則是隔離最爲完全的解決方法,kubeless是創建在K8s之上的serverless框架,所以它能夠利用K8s實現一些很是有用的特性:

  1. 敏捷構建 - 可以基於用戶提交的源碼迅速構建可執行的函數,簡化部署流程;
  2. 靈活觸發 - 可以方便地基於各種事件觸發函數的執行,並能方便快捷地集成新的事件源;
  3. 自動伸縮 - 可以根據業務需求,自動完成擴容縮容,無須人工干預。

其中,自動伸縮則解決了 super-agent 的痛點。

kubeless中與咱們緊密相關的有兩個概念:「function和runtime」 ,function是一個統稱,它包括運行時代碼、依賴以及其餘配置文件;runtime則定義運行時依賴的環境,是一個docker鏡像。

若要接入kubeless平臺,咱們須要解決以下幾個問題:

  1. 開發自定義運行時,知足商用需求,如trace、日誌分片、上報採集
  2. 自定義構建鏡像,實現ts編譯
  3. 基於yaml的function建立、更新、刪除流程探索,並自動化
  4. function部署,包括流量摘除、流量導入、業務健康檢查
  5. 中間件日誌、業務日誌、trace日誌隔離與掛載
  6. function運行時用戶權限問題
  7. 水平擴容探索與嘗試
  8. 資源申請規範指定與部署規範約定

所以,前進的道路仍然很曲折,並且不少需求須要本身從源碼上去尋找解決方法。

一些說明

kubeless實現的serverless體系中,function所在pod中的全部容器共享網絡和存儲namespace,可是默認外網是不可訪問k8s集羣的全部pods,所以須要經過一層代理實現請求的轉發,這就是「Service」。Service負責服務發現及轉發(iptables四層),所以在Kubeless或者K8s中不會直接經過pod IP來訪問服務,而是經過Service轉發四層流量完成。Service有K8s分配的cluserIp,clusterIp是集羣內部虛擬IP,沒法被外部尋址,而是經過Kube-Proxy在容器網絡之上又抽象了一層虛擬網絡,Kube-Proxy負責Service的路由與轉發(關於kube-proxy細節,請看參考資料)。

Service後端對應是一個或多個pods,這些pods中的一個容器則運行相同的業務代碼。那麼流量是如何路由至Service上來呢?這就涉及到Service的「發佈」,經常使用的是Ingress。Ingress包括HTTP代理服務器和ingress controller,代理服務器負責將請求按照規則路由至對應Service,這層須要Kube-Proxy實現虛擬網絡的路由;ingress controller負責從K8s API拉取最新的HTTP匹配規則。

![enter image description here](https://user-gold-cdn.xitu.io/2019/8/20/16cacb3d1682ae1c?w=1390&h=1074&f=png&s=132284)

問題解決

  1. 自定義鏡像:這裏的鏡像包括兩部分:構建鏡像和運行時鏡像。運行時鏡像須要解決宿主代碼的魯棒性,以及提供 livenessProbe、readinessProbe、metric接口實現;構建鏡像則負責構建階段的操做,如編譯、依賴安裝、環境變量注入等操做。具體編寫可參考 implement runtime images
  2. 構建鏡像參考1
  3. 關於function的CRUD操做,筆者先經過命令行走通整個流程後,又切換成基於yaml的配置文件啓動,使用yaml啓動好處在於:a,可以使用kubeless自帶的流量導入與摘除策略 b,水平擴容簡單 c,命令簡單 d,配置文件模板化,自動化
  4. 部署策略因爲涉及到業務特色,此處不詳細介紹
  5. 日誌的掛載是必要的,不然pod一旦重啓,容器內的全部日誌所有丟失。儘管會存在日誌收集的操做,但是日誌收集進程大多數都是異步進行,所以會存在丟失日誌的狀況。所以,必須經過掛載volumn的形式在K8s node上映射文件。但在這過程當中會出現權限的問題,這在下一點說明
  6. 權限問題在於kubeless將function的執行權限設置爲非root。這是安全且符合常理的設定,但有時function須要root權限,這須要修改K8s的security context配置,須要謹慎處理
  7. 水平擴容基於K8s的HPA組件完成,目前支持基於CPU和QPS等指標進行擴容,目前筆者並未專門測試這項內容,由於它足夠可靠
  8. 資源申請的指定須要符合每一個公司的實際狀況以及業務特色,以node技術棧爲例,pod中每一個容器設置1C2GB的內存符合實際狀況;而至於部署規範,則要兼顧運行時容器的特色,合理配置K8s的node與pod、function的對應關係

總結

運行在kubeless 中的函數和運行在super-agent的代碼沒有什麼不一樣,但是周邊的環境準備可大大不一樣。爲了讓kubeless中的function能夠接入公司內部中間件服務,筆者費了很多功夫,主要集中在日誌及收集部分。好在事在人爲,解決的辦法老是多於失敗的方法。

進度

目前,super-agent方案已承載了10+個線上應用或活動,穩定運行4個月,資源使用率符合預期;

kubeless方案還未正式接入流量,等待進一步作相關異常測試。

參考

kubeless介紹

security-context

kube-proxy

相關文章
相關標籤/搜索