开源 serverless 产品原理剖析 - Kubeless
背景
Serverless 架構(gòu)的出現(xiàn)讓開發(fā)者不用過多地考慮傳統(tǒng)的服務(wù)器采購、硬件運維、網(wǎng)絡(luò)拓撲、資源擴容等問題,可以將更多的精力放在業(yè)務(wù)的拓展和創(chuàng)新上。
隨著 serverless 概念的深入人心,各大云計算廠商紛紛推出了各自的 serverless 產(chǎn)品,其中比較有代表性的有?AWS lambda、Azure Function、Google Cloud Functions、阿里云函數(shù)計算等。
另外,CNCF 也于 2016 年創(chuàng)立了?Serverless Working Group,它致力于 cloud native 和 serverless 技術(shù)的結(jié)合。下圖是 CNCF serverless 全景圖,它將這些產(chǎn)品分成了工具型、安全型、框架型和平臺型等類別。
同時,容器以及容器編排工具的出現(xiàn),大大降低了 serverless 產(chǎn)品的開發(fā)成本,促進了一大批優(yōu)秀開源 serverless 產(chǎn)品的誕生,它們大多構(gòu)建于?kubernetes?之上,如下圖所示。
Kubeless 簡介
本文將要介紹的 kubeless 便是這些開源 serverless 產(chǎn)品的典型代表。根據(jù)官方的定義,kubeless 是 kubernetes native 的無服務(wù)計算框架,它可以讓用戶在 kubernetes 之上使用 FaaS 構(gòu)建高級應(yīng)用程序。從 CNCF 視角,kubeless 屬于平臺型產(chǎn)品。
Kubless 有三個核心概念:
原理剖析
本章節(jié)將以 kubeless 為例介紹 serverless 產(chǎn)品需要具備的基本能力,以及 kubeless 是如何利用 K8s 現(xiàn)有功能來實現(xiàn)它們的。這些基本能力包括:
本文所做的調(diào)研基于kubeless v1.0.0和k8s 1.13。
敏捷構(gòu)建
CNCF 對函數(shù)生命周期的定義如下圖所示。用戶只需提供源碼和函數(shù)說明,構(gòu)建部署等工作通常由 serverless 平臺完成。 因此,基于用戶提交的源碼迅速構(gòu)建可執(zhí)行函數(shù)是 serverless 產(chǎn)品必須具備的基礎(chǔ)能力。
在 kubeless 里,創(chuàng)建函數(shù)非常簡單:
kubeless function deploy hello --runtime python2.7 \--from-file test.py \--handler test.hello該命令各參數(shù)含義如下:
函數(shù)資源與 K8s Operator
Kubeless 函數(shù)是一個自定義 K8s 對象,本質(zhì)上是?k8s operator。k8s operator 原理如下圖所示:
下面以 kubeless 函數(shù)為例,描述 K8s operator 的一般工作流程:
除了函數(shù)外,下文將要介紹的 trigger 也是一個 k8s operator。
函數(shù)構(gòu)成
Kubeless 的?function-controller監(jiān)聽到針對 function 的 ADD 事件后,會觸發(fā)相應(yīng) handler 創(chuàng)建函數(shù)。一個函數(shù)由若干 K8s 對象組成,包括 ConfigMap、Service、Deployment、Pod 等,其結(jié)構(gòu)如下圖所示:
ConfigMap
函數(shù)中的 ConfigMap 用于描述函數(shù)源碼和依賴。
apiVersion: v1 data:handler: test.hello# 函數(shù)依賴的第三方 python 庫requirements.txt: |kubernetes==2.0.0# 函數(shù)源碼test.py: |def hello(event, context):print eventreturn event['data'] kind: ConfigMap metadata:labels:created-by: kubelessfunction: hello# 該 ConfigMap 名稱name: hellonamespace: default ...Service
函數(shù)中的 Service 用于描述該函數(shù)的訪問方式。該 Service 會與執(zhí)行 function 邏輯的 Pods 相關(guān)聯(lián),類型是 ClusterIP。
apiVersion: v1 kind: Service metadata:labels:created-by: kubelessfunction: hello# 該 Service 名稱name: hellonamespace: default... spec:clusterIP: 10.109.2.217ports:- name: http-function-portport: 8080protocol: TCPtargetPort: 8080selector:created-by: kubelessfunction: hello# Service 類型type: ClusterIP ...Deployment
函數(shù)中的 Deployment 用于編排執(zhí)行函數(shù)邏輯的 Pods,通過它可以描述函數(shù)期望的個數(shù)。
apiVersion: extensions/v1beta1 kind: Deployment metadata:labels:created-by: kubelessfunction: helloname: hellonamespace: default... spec:# 指定函數(shù)期望的個數(shù)replicas: 1 ...Pod
函數(shù)中的 Pod 包含真正執(zhí)行函數(shù)邏輯的容器。
Volumes
Pod 中的 volumes 段指定了該函數(shù)的 ConfigMap。這會將 ConfigMap 中的源碼和依賴添加到 volumeMounts.mountPath 指定的目錄里面。從容器視角來看,文件路徑為/src/test.py和?/src/requirements。
...volumeMounts:- mountPath: /kubelessname: hello- mountPath: /srcname: hello-deps volumes: - emptyDir: {}name: hello - configMap:defaultMode: 420name: hello ...Init Container
Pod 中的 Init Container 主要作用如下:
Func Container
Pod 中的 Func Container 會加載 Init Container 準備好的源碼和依賴并執(zhí)行函數(shù)。不同 runtime 加載代碼的方式大同小異,可參考?kubeless.py,Handler.java。
小結(jié)
靈活觸發(fā)
一款成熟的 serverless 產(chǎn)品需要具備靈活觸發(fā)能力,以滿足事件源的多樣性需求,同時需要能夠方便快捷地接入新事件源。CNCF 將函數(shù)的觸發(fā)方式分成了如下圖所示的幾種類別,關(guān)于它們的詳細介紹可參考鏈接?Function Invocation Types。
對于 kubeless 的函數(shù),最簡單的觸發(fā)方式是使用 kubeless CLI,另外還支持通過各種觸發(fā)器。下表展示了 kubeless 函數(shù)目前支持的觸發(fā)方式以及它們所屬的類別。
| kubeless CLI | Synchronous Req/Rep |
| Http Trigger | Synchronous Req/Rep |
| Cronjob Trigger | Job (Master/Worker) |
| Kafka Trigger | Async Message Queue |
| Nats Trigger | Async Message Queue |
| Kinesis Trigger | Message Stream |
下圖展示了 kubeless 函數(shù)部分觸發(fā)方式的原理:
HTTP trigger
如果希望通過發(fā)送 HTTP 請求觸發(fā)函數(shù)執(zhí)行,需要為函數(shù)創(chuàng)建 HTTP 觸發(fā)器。 Kubeless 利用?K8s ingress?機制實現(xiàn)了 http trigger。Kubeless 創(chuàng)建了一個名為httptriggers.kubeless.io的 CRD 來代表 http trigger 對象。同時,kubeless 包含一個名為http-trigger-controller的 CRD controller,它會持續(xù)監(jiān)聽針對 http trigger 和 function 的 ADD、UPDATE、DELETE 事件,并執(zhí)行對應(yīng)的操作。
以下命令將為函數(shù) hello 創(chuàng)建一個名為http-hello的 http trigger,并指定選用 nginx 作為 gateway。
kubeless trigger http create http-hello --function-name hello --gateway nginx --path echo --hostname example.com該命令會創(chuàng)建如下 ingress 對象,可以參考?CreateIngress?深入了解 ingress 的創(chuàng)建邏輯。
apiVersion: extensions/v1beta1 kind: Ingress metadata:# 該 Ingress 的名字,即創(chuàng)建 http trigger 時指定的 namename: http-hello... spec:rules:- host: example.comhttp:paths:- backend:# 指向 kubeless 為函數(shù) hello 創(chuàng)建的 ClusterIP 類型的 ServiceserviceName: helloservicePort: 8080path: /echoIngress 只是用于描述路由規(guī)則,要讓規(guī)則生效、實現(xiàn)請求轉(zhuǎn)發(fā),集群中需要有一個正在運行的 ingress controller。可供選擇的 ingress controller 有?Contour、F5 BIG-IP Controller for Kubernetes、Kong Ingress Controllerfor Kubernetes、NGINX Ingress Controller for Kubernetes、Traefik?等。這種路由規(guī)則描述和路由功能實現(xiàn)相分離的思想很好地提現(xiàn)了 K8s 始終堅持的需求和供給分離的設(shè)計理念。
上文中的命令在創(chuàng)建 trigger 時指定了 nginx 作為 gateway,因此需要部署一個?nginx-ingress-controller。該 controller 的基本工作原理如下:
想要更深入地了解 nginx-ingress-controller 的工作原理可參考文章?how-it-works。
完成上述工作后,我們便可以通過發(fā)送 HTTP 請求觸發(fā)函數(shù) hello 的執(zhí)行:
樣例如下:
curl --data '{"Another": "Echo"}' \--header "Host: example.com" \--header "Content-Type:application/json" \example.com/echo# 函數(shù)返回 {"Another": "Echo"}Cronjob trigger
如果希望定期觸發(fā)函數(shù)執(zhí)行,需要為函數(shù)創(chuàng)建 cronjob 觸發(fā)器。K8s 支持通過?CronJob?定期運行任務(wù),kubeless 利用這個特性實現(xiàn)了 cronjob trigger。Kubeless 創(chuàng)建了一個名為cronjobtriggers.kubeless.io的 CRD 來代表 cronjob trigger 對象。同時,kubeless 包含一個名為cronjob-trigger-controller的 CRD controller,它會持續(xù)監(jiān)聽針對 cronjob trigger 和 function 的 ADD、UPDATE、DELETE 事件,并執(zhí)行對應(yīng)的操作。
以下命令將為函數(shù) hello 創(chuàng)建一個名為scheduled-invoke-hello的 cronjob trigger,該觸發(fā)器每分鐘會觸發(fā)函數(shù) hello 執(zhí)行一次。
kubeless trigger cronjob create scheduled-invoke-hello --function=hello --schedule="*/1 * * * *"該命令會創(chuàng)建如下 CronJob 對象,可以參考?EnsureCronJob?深入了解 CronJob 的創(chuàng)建邏輯。
apiVersion: batch/v1beta1 kind: CronJob metadata:# 該 CronJob 的名字,即創(chuàng)建 cronjob trigger 時指定的 namename: scheduled-invoke-hello... spec:# 該 CronJob 的執(zhí)行計劃,即創(chuàng)建 cronjob trigger 時指定的 scheduleschedule: */1 * * * *...jobTemplate:spec:activeDeadlineSeconds: 180template:spec:containers:- args:- curl- -Lv# HTTP headers,包含 event-id、event-time、event-type、event-namespace 等信息- ' -H "event-id: xxx" -H "event-time: yyy" -H "event-type: application/json" -H "event-namespace: cronjobtrigger.kubeless.io"'# kubeless 會為 function 創(chuàng)建一個 ClusterIP 類型的 Service# 可以根據(jù) service 的 name、namespace 拼出 endpoint- http://hello.default.svc.cluster.local:8080image: kubeless/unzipname: triggerrestartPolicy: Never...自定義 trigger
如果發(fā)現(xiàn) kubeless 默認提供的觸發(fā)器無法滿足業(yè)務(wù)需求,可以自定義新的觸發(fā)器。新觸發(fā)器的構(gòu)建流程如下:
為該 CRD 創(chuàng)建一個 CRD controller。
我們可以看到,自定義 trigger 的流程遵循了?K8s Operator?設(shè)計模式。
小結(jié)
自動伸縮
K8s 通過?Horizontal Pod Autoscaler?實現(xiàn) pod 的自動水平伸縮。Kubeless 的 function 通過 K8s deployment 部署運行,因此天然可以利用 HPA 實現(xiàn)自動伸縮。
度量數(shù)據(jù)獲取
自動伸縮的第一步是要讓 HPA 能夠獲取度量數(shù)據(jù)。目前,kubeless 中的函數(shù)支持基于 cpu 和 qps 這兩種指標進行自動伸縮。下圖展示了 HPA 獲取這兩種度量數(shù)據(jù)的途徑。
內(nèi)置度量指標 cpu
CPU 使用率屬于內(nèi)置度量指標,對于這類指標 HPA 可以通過 metrics API 從?Metrics Server?中獲取數(shù)據(jù)。Metrics Server 是?Heapster?的繼承者,它可以通過kubernetes.summary_api從 Kubelet、cAdvisor 中獲取度量數(shù)據(jù)。
自定義度量指標 qps
QPS 屬于自定義度量指標,想要獲取這類指標的度量數(shù)據(jù)需要完成下列步驟。
完成上述步驟后,HPA 就可以通過 custom metrics API 從 Prometheus Adapter 中獲取 qps 度量數(shù)據(jù)。詳細配置步驟可參考文章?kubeless-autoscaling。
K8s 度量指標簡介
有時基于 cpu 和 qps 這兩種度量指標對函數(shù)進行自動伸縮還遠遠不夠。如果希望基于其它度量指標,需要了解 K8s 定義的度量指標類型及其獲取方式。
目前,K8s 1.13 版本支持的度量指標類型如下:
準備好相應(yīng)的度量數(shù)據(jù)和獲取數(shù)據(jù)的組件,HPA 就能基于它們對函數(shù)進行自動伸縮。更多關(guān)于 K8s 度量指標的介紹可參考文章?hpa-external-metrics。
度量數(shù)據(jù)使用
知道了 HPA 獲取度量數(shù)據(jù)的途徑后,下面描述 HPA 如何基于這些數(shù)據(jù)對函數(shù)進行自動伸縮。
基于 cpu 使用率
假設(shè)已經(jīng)存在一個名為 hello 的函數(shù),以下命令將為該函數(shù)創(chuàng)建一個基于 cpu 使用率的 HPA,它將運行該函數(shù)的 pod 數(shù)量控制在 1 到 3 之間,并通過增加或減少 pod 個數(shù)使得所有 pod 的平均 cpu 使用率維持在 70%。
kubeless autoscale create hello --metric=cpu --min=1 --max=3 --value=70Kubeless 使用的是?autoscaling/v2alpha1?版本的 HPA API,該命令將要創(chuàng)建的 HPA 如下:
kind: HorizontalPodAutoscaler apiVersion: autoscaling/v2alpha1 metadata:name: hellonamespace: defaultlabels:created-by: kubelessfunction: hello spec:scaleTargetRef:kind: Deploymentname: hellominReplicas: 1maxReplicas: 3metrics:- type: Resourceresource:name: cputargetAverageUtilization: 70該 HPA 計算目標 pod 數(shù)量的公式如下:
TargetNumOfPods = ceil(sum(CurrentPodsCPUUtilization) / Target)基于 qps
以下命令將為函數(shù) hello 創(chuàng)建一個基于 qps 的 HPA,它將運行該函數(shù)的 pod 數(shù)量控制在 1 到 5 之間,并通過增加或減少 pod 個數(shù)確保所有掛在服務(wù) hello 后的 pod 每秒能處理的請求次數(shù)之和達到 2000。
kubeless autoscale create hello --metric=qps --min=1 --max=5 --value=2k該命令將要創(chuàng)建的 HPA 如下:
kind: HorizontalPodAutoscaler apiVersion: autoscaling/v2alpha1 metadata:name: hellonamespace: defaultlabels:created-by: kubelessfunction: hello spec:scaleTargetRef:kind: Deploymentname: hellominReplicas: 1maxReplicas: 5metrics:- type: Objectobject:metricName: function_callstarget:apiVersion: autoscaling/v2beta1kind: Servicename: hellotargetValue: 2k基于多項指標
如果計劃基于多項度量指標對函數(shù)進行自動伸縮,需要直接為運行 function 的 deployment 創(chuàng)建 HPA。
使用如下 yaml 文件可以為函數(shù) hello 創(chuàng)建一個名為hello-cpu-and-memory的 HPA,它將運行該函數(shù)的 pod 數(shù)量控制在 1 到 10 之間,并嘗試讓所有 pod 的平均 cpu 使用率維持在 50%,平均 memory 使用量維持在 200MB。對于多項度量指標,K8s 會計算出每項指標需要的 pod 數(shù)量,取其中的最大值作為最終的目標 pod 數(shù)量。
kind: HorizontalPodAutoscaler apiVersion: autoscaling/v2alpha1 metadata:name: hello-cpu-and-memorynamespace: defaultlabels:created-by: kubelessfunction: hello spec:scaleTargetRef:kind: Deploymentname: hellominReplicas: 1maxReplicas: 10metrics:- type: Resourceresource:name: cputargetAverageUtilization: 50- type: Resourceresource:name: memorytargetAverageValue: 200Mi自動伸縮策略
一個理想的自動伸縮策略應(yīng)當處理好下列場景:
Kubeless 依賴的 HPA 充分考慮了上述情形,不斷改進和完善其使用的自動伸縮策略。下面以 K8s 1.13 版本為例描述該策略。如果想要更加深入地了解策略原理請參考鏈接?horizontal。
HPA 每隔一段時間會根據(jù)獲取的度量數(shù)據(jù)同步一次和該 HPA 關(guān)聯(lián)的 RC / Deployment 中的 pod 個數(shù),時間間隔通過 kube-controller-manager 的參數(shù)--horizontal-pod-autoscaler-sync-period指定,默認為 15s。在每一次同步過程中,HPA 需要經(jīng)歷如下圖所示的計算流程。
計算目標副本數(shù)
分別計算 HPA 列表中每項指標需要的 pod 數(shù)量,記為 replicaCountProposal。選擇其中的最大值作為 metricDesiredReplicas。在計算每項指標的 replicaCountProposal 過程中會考慮下列因素:
平滑目標副本數(shù)
將最近一段時間計算出的 metricDesiredReplicas 記錄下來,取其中的最大值作為 stabilizedRecommendation。這樣做是為了讓縮容過程變得平滑,消除度量數(shù)據(jù)異常波動造成的影響。該時間段可以通過參數(shù)--horizontal-pod-autoscaler-downscale-stabilization-window指定,默認為 5 分鐘。
規(guī)范目標副本數(shù)
執(zhí)行擴容縮容操作
如果通過上述步驟計算出的 desiredReplicas 不等于 currentReplicas,則“執(zhí)行”擴容縮容操作。這里所說的執(zhí)行只是將 desiredReplicas 賦值給 RC / Deployment 中的 replicas,pod 的創(chuàng)建銷毀會由 kube-scheduler 和 worker node 上的 kubelet 異步完成的。
小結(jié)
總結(jié)
Kubeless 基于 K8s 提供了較為完整的 serverless 解決方案,但和一些商業(yè) serverless 產(chǎn)品還存在一定差距:
本文作者:吳波bruce_wu
閱讀原文
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的开源 serverless 产品原理剖析 - Kubeless的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分享云及人工智能的一些学习资源和学习心得
- 下一篇: BCGControlBar教程:Outl