本文是開源 serverless 產品原理剖析系列文章的第二篇,關於 serverless 背景知識的介紹可參考文章開源 serverless 產品原理剖析(一) - Kubeless,這裏再也不贅述。node
Fission 是由私有云服務提供商 Platform9 領導開源的 serverless 產品,它藉助 kubernetes 靈活強大的編排能力完成容器的管理調度工做,而將重心投入到 FaaS 功能的開發上,其發展目標是成爲 AWS lambda 的開源替代品。從 CNCF 視角,fission 屬於 serverless 平臺型產品。python
Fission 包含 Function、Environment 、Trigger 三個核心概念,其關係以下圖所示:git
Fission 包含 Controller、Router、Executor 三個關鍵組件:github
本章節將從如下幾個方面介紹 fission 的基本原理:golang
本文所作的調研基於kubeless 0.12.0
和k8s 1.13
。docker
CNCF 對函數生命週期的定義以下圖所示,它描繪了函數構建、部署、運行的通常流程。apache
要理解 fission,首先須要瞭解它是如何管理函數生命週期的。Fission 的函數執行器是其控制函數生命週期的關鍵組件。Fission 包含 PoolManager 和 NewDeploy 兩類執行器,下面分別對二者進行介紹。設計模式
Poolmgr 使用了池化技術,它經過爲每一個 environment 維持了必定數量的通用 pod 並在函數被觸發時將 pod 特化,大大下降了函數的冷啓動的時間。同時,poolmgr 會自動清理一段時間內未被訪問的函數,減小閒置成本。該執行器的原理以下圖所示。api
此時,函數的生命週期以下:緩存
使用 fission CLI 向 controller 發送請求,建立函數運行時須要的特定語言環境。例如,如下命令將建立一個 python 運行環境。
fission environment create --name python --image fission/python-env
使用 fission CLI 向 controller 發送建立函數的請求。此時,controller 只是將函數源碼等信息持久化存儲,並未真正構建好可執行函數。例如,如下命令將建立一個名爲 hello 的函數,該函數選用已經建立好的 python 運行環境,源碼來自 hello.py,執行器爲 poolmgr。
fission function create --name hello --env python --code hello.py --executortype poolmgr
Poolmgr 很好地平衡了函數的冷啓動時間和閒置成本,但沒法讓函數根據度量指標自動伸縮。NewDeploy 執行器實現了函數 pod 的自動伸縮和負載均衡,該執行器的原理以下圖所示。
此時,函數的生命週期以下:
使用 fission CLI 向 controller 發送建立函數的請求。例如,如下命令將建立一個名爲 hello 的函數,該函數選用已經建立好的 python 運行環境,源碼來自 hello.py,執行器爲 newdeploy,目標副本數在 1 到 3 之間,目標 cpu 使用率是 50%。
fission fn create --name hello --env python --code hello.py --executortype newdeploy --minscale 1 --maxscale 3 --targetcpu 50
Newdeploy 監聽到了函數的 ADD 事件後,會根據 minscale 的取值判斷是否當即爲該函數建立相關資源。
實際使用過程當中,用戶須要從延遲和閒置成本兩個角度考慮選擇何種類型的執行器。不一樣執行器的特色以下表所示。
執行器類型 | 最小副本數 | 延遲 | 閒置成本 |
---|---|---|---|
Newdeploy | 0 | 高 | 很是低 - pods 一段時間未被訪問會被自動清理掉。 |
Newdeploy | >0 | 低 | 中等 - 每一個函數始終會有必定數量的 pod 在運行。 |
Poolmgr | 0 | 低 | 低 - 通用池中的 pod 會一直運行。 |
Fission 將函數執行器的概念暴露給了用戶,增長了產品的使用成本。實際上能夠將 poolmgr 和 newdeploy 技術相結合,經過建立 deployment 將特化後的 pod 管理起來,這樣能夠很天然地利用 HPA 來實現對函數的自動伸縮。
在介紹函數執行器時屢次提到了 pod 特化,它是 fission 將環境容器變成函數容器的奧祕。Pod 特化的本質是經過向容器發送特化請求讓其加載用戶函數,其原理以下圖所示。
一個函數 pod 由下面兩種容器組成:
具體步驟以下:
前面的章節介紹了 fission 函數的構建、加載和執行的邏輯,本章節主要介紹如何基於各類事件源觸發 fission 函數的執行。CNCF 將函數的觸發方式分紅了以下圖所示的幾種類別,關於它們的詳細介紹可參考連接 Function Invocation Types。
對於 fission 函數,最簡單的觸發方式是使用 fission CLI,另外還支持經過各類觸發器。下表展現了 fission 函數目前支持的觸發方式以及它們所屬的類別。
觸發方式 | 類別 |
---|---|
fission CLI | Synchronous Req/Rep |
HTTP Trigger | Synchronous Req/Rep |
Time Trigger | Job (Master/Worker) |
Message Queue Trigger 1. nats-streaming 2. azure-storage-queue 3. kafka |
Async Message Queue |
Kubernetes Watch Trigger | Async Message Queue |
下圖展現了 fission 函數部分觸發方式的原理:
全部發往 fission 函數的請求都會由 router 轉發,fission 經過爲 router 建立 NodePort 或 LoadBalancer 類型的 service 讓其可以被外界訪問。
除了直接訪問 router,還能夠利用 K8s ingress 機制實現 http trigger。如下命令將爲函數 hello 建立一個 http trigger,並指定訪問路徑爲/echo
。
fission httptrigger create --url /echo --method GET --function hello --createingress --host example.com
該命令會建立以下 ingress 對象,能夠參考 createIngress 深刻了解 ingress 的建立邏輯。
apiVersion: extensions/v1beta1 kind: Ingress metadata: # 該 Ingress 的名稱 name: xxx ... spec: rules: - host: example.com http: paths: - backend: # 指向 router service serviceName: router servicePort: 80 # 訪問路徑 path: /echo
Ingress 只是用於描述路由規則,要讓規則生效、實現請求轉發,集羣中須要有一個正在運行的 ingress controller。想要深刻了解 ingress 原理可參考系列文章第一篇中的 HTTP trigger 章節。
若是但願按期觸發函數執行,須要爲函數建立 time trigger。Fission 使用 deployment 部署了組件 timer,該組件負責管理用戶建立的 timer trigger。Timer 每隔一段時間會同步一次 time trigger 列表,並經過 golang 中被普遍使用的 cron 庫 robfig/cron 按期觸發和各 timer trigger 相關聯函數的執行。
如下命令將爲函數 hello 建立一個名爲halfhourly
的 time trigger,該觸發器每半小時會觸發函數 hello 執行一次。這裏使用了標準的 cron 語法定義執行計劃。
fission tt create --name halfhourly --function hello --cron "*/30 * * * *" trigger 'halfhourly' created
爲了支持異步觸發,fission 容許用戶建立消息隊列觸發器。目前可供選擇的消息隊列有 nats-streaming、azure-storage-queue、kafka,下面以 kafka 爲例描述消息隊列觸發器的使用方法和實現原理。
如下命令將爲函數 hello 建立一個基於 kafka 的消息隊列觸發器hellomsg
。該觸發器訂閱了主題 input 中的消息,每當有消息到達它便會觸發函數執行。若是函數執行成功,會將結果寫入主題 output 中,不然將結果寫入主題 error 中。
fission mqt create --name hellomsg --function hello --mqtype kafka --topic input --resptopic output --errortopic error
Fission 使用 deployment 部署了組件 mqtrigger-kafka,該組件負責管理用戶建立的 kafka trigger。它每隔一段時間會同步一次 kafka trigger 列表,併爲每一個 trigger 建立 1 個用於執行觸發邏輯的 go routine,觸發邏輯以下:
Message/Record Streams
觸發方式的支持,該方式要求消息被順序處理;K8s 經過 Horizontal Pod Autoscaler 實現 pod 的自動水平伸縮。對於 fission,只有經過 newdeploy 方式建立的函數才能利用 HPA 實現自動伸縮。
如下命令將建立一個名爲 hello 的函數,運行該函數的 pod 會關聯一個 HPA,該 HPA 會將 pod 數量控制在 1 到 6 之間,並經過增長或減小 pod 個數使得全部 pod 的平均 cpu 使用率維持在 50%。
fission fn create --name hello --env python --code hello.py --executortype newdeploy --minmemory 64 --maxmemory 128 --minscale 1 --maxscale 6 --targetcpu 50
Fission 使用的是autoscaling/v1
版本的 HPA API,該命令將要建立的 HPA 以下:
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: labels: executorInstanceId: xxx executorType: newdeploy functionName: hello ... # 該 HPA 名稱 name: hello-${executorInstanceId} # 該 HPA 所在命名空間 namespace: fission-function ... spec: # 容許的最大副本數 maxReplicas: 6 # 容許的最小副本數 minReplicas: 1 # 該 HPA 關聯的目標 scaleTargetRef: apiVersion: extensions/v1beta1 kind: Deployment name: hello-${executorInstanceId} # 目標 CPU 使用率 targetCPUUtilizationPercentage: 50
想了解 HPA 的原理可參考系列文章第一篇中的自動伸縮章節,那裏詳細介紹了 K8s 如何獲取和使用度量數據以及目前採用的自動伸縮策略。
爲了能更好地洞察函數的運行狀況,每每須要對函數產生的日誌進行採集、處理和分析。Fission 日誌處理的原理以下圖所示。
日誌處理流程以下:
/var/log/
和/var/lib/docker/containers
掛載進來,方便直接採集。fission function logs --name hello
能夠查看到函數 hello 產生的日誌。目前,fission 只作到了函很多天志的集中化存儲,可以提供的查詢分析功能很是有限。另外,influxdb 更適合存儲監控指標類數據,沒法知足日誌處理與分析的多樣性需求。
函數是運行在容器裏的,所以函很多天志處理本質上可歸結爲容器日誌處理。針對容器日誌,阿里雲日誌服務團隊提供了成熟完備的解決方案,欲知詳情可參考文章面向容器日誌的技術實踐。
在介紹完 fission 的基本原理後,不妨從如下幾個方面將其和第一篇介紹的 kubeless 做一個簡單對比。