Knative 簡介

螞蟻金服分散式架構發表於2018-10-29

本文作者來自螞蟻金服系統部之芥

什麼是 Knative?

knative 是谷歌開源的 serverless 架構方案,旨在提供一套簡單易用的 serverless 方案,把 serverless 標準化。目前參與的公司主要是 Google、Pivotal、IBM、Red Hat,2018年7月24日才剛剛對外發布,當前還處於快速發展的階段。

這是 Google Cloud Platform 宣佈 knative 時給出的介紹:

Developed in close partnership with Pivotal, IBM, Red Hat, and SAP, Knative pushes Kubernetes-based computing forward by providing the building blocks you need to build and deploy modern, container-based serverless applications.

可以看出,knative 是為了解決容器為核心的 serverless 應用的構建、部署和執行的問題。

serverless 的概念已經出現蠻久了,為了理解 serverless 可以從應用開發者的角度來看,使用 serverless 框架之後,應用開發者的整個操作流程就變成了:

~ # 編寫 code 和 configuration 檔案

~ # faascli build
~ # faascli deploy
~ # curl http://myapp.com/hello
hello, world from Awesome FaaS App!複製程式碼

可以看到使用者只需要編寫程式碼(或者函式),以及配置檔案(如何 build、執行以及訪問等宣告式資訊),然後執行 build 和 deploy 就能把應用自動部署到叢集(可以是公有云,也可以是私有的叢集)。

其他事情都是 serverless 平臺(比如這裡的 knative)自動處理的,這些事情包括:

  • 自動完成程式碼到容器的構建
  • 把應用(或者函式)和特定的事件進行繫結:當事件發生時,自動觸發應用(或者函式)
  • 網路的路由和流量控制
  • 應用的自動伸縮

和標準化的 FaaS 不同,knative 期望能夠執行所有的 workload : traditional application、function、container。

knative 是建立在 kubernetes 和 istio 平臺之上的,使用 kubernetes 提供的容器管理能力(deployment、replicaset、和 pods等),以及 istio 提供的網路管理功能(ingress、LB、dynamic route等)。


Knative 簡介


knative 核心概念和原理

為了實現 serverless 應用的管理,knative 把整個系統分成了三個部分:

  • Build:構建系統,把使用者定義的函式和應用 build 成容器映象
  • Serving:服務系統,用來配置應用的路由、升級策略、自動擴縮容等功能
  • Eventing:事件系統,用來自動完成事件的繫結和觸發

Build 構建系統

build 的功能是把使用者的程式碼自動化構建成容器映象,初次聽起來很奇怪,有了 docker 之後有一個 Dockerfile 不就能構建容器了嗎?為什麼還需要一個新的 Build 系統?

Knative 的特別之處在於兩點:一是它的構建完成是在 kubernetes 中進行的,和整個 kubernetes 生態結合更緊密;另外,它旨在提供一個通用的標準化的構建元件,可以作為其他更大系統中的一部分。

正如官方文件中的說的那樣,更多是為了定義標準化、可移植、可重用、效能高效的構建方法:

The goal of a Knative build is to provide a standard, portable, reusable, and performance optimized method for defining and running on-cluster container image builds.

Knative 提供了 Build CRD 物件,讓使用者可以通過 yaml 檔案定義構建過程。一個典型的 Build 配置檔案如下:

apiVersion: build.knative.dev/v1alpha1
kind: Build
metadata:
  name: example-build
spec:
  serviceAccountName: build-auth-example
  source:
    git:
      url: https://github.com/example/build-example.git
      revision: master
  steps:
  - name: ubuntu-example
    image: ubuntu
    args: ["ubuntu-build-example", "SECRETS-example.md"]
  steps:
  - image: gcr.io/example-builders/build-example
    args: ['echo', 'hello-example', 'build']
複製程式碼


其中,serviceAccountName 是構建過程中需要用到的密碼和認證資訊(比如連線到 git repo 的 SSH keys、push 映象到 registry 的使用者名稱和密碼等);source 是程式碼資訊,比如這裡的 git 地址和分支;steps 是真正執行過程中的各個步驟,這個示例中的步驟只是作為 demo,真正的構建過程一般是 pull 程式碼、 build 映象和 push映象到 registry 等邏輯。

因為大部分的構建過程都是一致的,因此 knative 還提供了 Build template 的概念,Build template 封裝了預先定義好的構建過程(就是封裝了上面的 steps 過程),並提供了非常簡單的配置引數來使用。

使用 build template 構建容器映象就更簡單了,只需要提供程式碼的地址和映象名字即可,比如下面是使用 Google kaniko 模板構建 github 原始碼的 yaml 檔案(需要在程式碼根目錄存在 Dockerfile 檔案):

apiVersion: build.knative.dev/v1alpha1
kind: Build
metadata:
  name: kaniko-build
spec:
  serviceAccountName: build-bot
  source:
    git:
      url: https://github.com/my-user/my-repo
      revision: master
  template:
    name: kaniko
    arguments:
    - name: IMAGE
      value: us.gcr.io/my-project/my-app複製程式碼

Serving:服務系統

serving 的核心功能是讓應用執行起來提供服務。雖然聽起來很簡單,但這裡包括了很多的事情:

  • 自動化啟動和銷燬容器
  • 根據名字生成網路訪問相關的 service、ingress 等物件
  • 監控應用的請求,並自動擴縮容
  • 支援藍綠髮布、回滾功能,方便應用方法流程

knative serving 功能是基於 kubernetes 和 istio 開發的,它使用 kubernetes 來管理容器(deployment、pod),istio 來管理網路路由(VirtualService、DestinationRule)。

因為 kubernetes 和 istio 本身的概念非常多,理解和管理起來比較困難,knative 在此之上提供了更高一層的抽象(這些對應是基於 kubernetes 的 CRD 實現的)。這些抽象出來的概念對應的關係如下圖:

Knative 簡介
  • Configuration:應用的最新配置,也就是應用目前期望的狀態,對應了 kubernetes 的容器管理(deployment)。每次應用升級都會更新 configuration,而 knative 也會保留歷史版本的記錄(圖中的 revision),結合流量管理,knative 可以讓多個不同的版本共同提供服務,方便藍綠髮布和滾動升級
  • Route:應用的路由規則,也就是進來的流量如何訪問應用,對應了 istio 的流量管理(VirtualService)
  • Service:注意這裡不是 kubernetes 中提供服務發現的那個 service,而是 knative 自定義的 CRD,它的全稱目前是 services.serving.knative.dev 。單獨控制 route 和 configuration 就能實現 serving 的所有功能,但knative 更推薦使用 Service 來管理,因為它會自動幫你管理 route 和 configuration

一個 hello world 的 serving 配置如下所示:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: helloworld-go
  namespace: default
spec:
  runLatest:
    configuration:
      revisionTemplate:
        spec:
          container:
            image: docker.io/{username}/helloworld-go
            env:
            - name: TARGET
              value: "Go Sample v1"
複製程式碼


看起來和 kubernetes 的 pod 定義非常類似,但是它會幫你管理 deployment、ingress、service discovery、auto scaling……從這個角度來看,可以認為 knative 提供了更高的抽象,自動幫你封裝掉了 kubernetes 和 istio 的實現細節。

下面這張圖介紹了 knative serving 各元件之間的關係:


Knative 簡介



  • 可以看到,每個 revision 對應了一組 deployment 管理的 pod
  • pod 會自動彙報 metrics 資料到 autoscaler,autoscaler 會根據請求量和資源使用情況修改 deployment 的 replicas 數量,從而實現自動擴縮容。serverless 一個重要的特定是它會 scale to 0 的,也就是當應用沒有流量訪問時,它會自動銷燬所有的 pod
  • activator 比較有趣,它是為了處理 scale to 0 而出現的。當某個 revision 後面的 pod 縮容到 0 時,route 的流量會指向 activator,activator 接收到請求之後會自動拉起 pod,然後把流量轉發過去
  • route 物件對應了 istio 的 DestinationRoute 和 VirtualService,決定了訪問應用的流量如何路由

Eventing:事件系統

serving 系統實現的功能是讓應用/函式能夠執行起來,並且自動伸縮,那什麼時候才會呼叫應用呢?除了我們熟悉的正常應用呼叫之外,serverless 最重要的是基於事件的觸發機制,也就是說當某件事發生時,就觸發某個特定的函式。

事件概念的出現,讓函式和具體的呼叫方能夠解耦。函式部署出來不用關心誰會呼叫它,而事件源觸發也不用關心誰會處理它。

Note:目前 serverless 的產品和平臺很多,每個地方支援的事件來源以及對事件的定義都是不同的(比如 AWS Lambda 支援很多自己產品的事件源)。Knative 自然也會定義自己的事件型別,除此之外,knative 還聯合 CNCF 在做事件標準化的工作,目前的產出是 CloudEvents 這個專案。

為了讓整個事件系統更有擴充套件性和通用性,knative 定義了很多事件相關的概念。我們先來介紹一下:

  • EventSource:事件源,能夠產生事件的外部系統
  • Feed:把某種型別的 EventType 和 EventSource 和對應的 Channel 繫結到一起
  • Channel:對訊息實現的一層抽象,後端可以使用 kafka、RabbitMQ、Google PubSub 作為具體的實現。channel name 類似於訊息叢集中的 topic,可以用來解耦事件源和函式。事件發生後 sink 到某個 channel 中,然後 channel 中的資料會被後端的函式消費
  • Subscription:把 channel 和後端的函式繫結的一起,一個 channel 可以繫結到多個knative service

它們之間的關係流程圖如下:

Knative 簡介

Bus 是 knative 內部的事件儲存層,使用者可以選擇自己感興趣的實現,目前支援的方式有:Stub(在記憶體中實現的簡單訊息系統)、Kafka、Google PubSub。如果想要事件能夠正常執行,必須在 knative 叢集中安裝其中一個 bus 實現方式。

有了 bus 之後,我們就可以監聽外部的事件了。目前支援的事件源有三個:github(比如 merge 事件,push 事件等),kubernetes(events),Google PubSub(訊息系統),後面還會不斷接入更多的事件源。

如果要想監聽對應的事件源,需要在 knative 中部署一個 source adaptor 的 pod,它負責從外部的系統中讀取事件。

讀取後的事件,會根據使用者配置的 Feed 物件(裡面包括了事件源和 channel 的對應關係),找到對應的 channel,然後把訊息傳送到這個 channel 中(channel 的訊息最終是儲存在後端的 bus 系統裡的)。

然後,knative 會根據 subscription 的配置,不斷從 channel 中讀取事件,然後把事件作為引數呼叫對應的函式,從而完成了整個事件的流程。

Knative 目前的狀態

knative 是 2018 年 7月才剛剛對外開放,雖然內部已經開發一段時間,但是目前還處於非常早前的階段(從支援的事件源和 bus就能看出來)。目前程式碼還不穩定,很多實現都是 hard-coded。

knative 也是脫產於 google 和 CNCF,因此整個社群執行方式和目標與之前的 kubernetes 以及 istio 非常相似。社群根據元件分成多個 Working Group,每個 Group 獨立負責自己的功能,所有的開源活動(文件、視訊、程式碼)都是開放的。另外,CloudEvents 作為 knative 依賴的標準,目標也是成為 CRI、CNI、CSI 這種類似的標準。

knative 社群目前非常活躍,已 github.com/knative/serving 專案為例,一個月已經有 600+ star,目前有 60+ contributor,900+ commits,而且入門的文件和教程都已經非常全面。


Knative 簡介




Knative 應用場景和思考

knative 基於 kubernetes 和 istio 的 serverless 開源實現,目標是為了提供更高層次的抽象,讓開發者無需關注基礎設施(虛擬機器或者容器,網路配置,容量規劃),而專注於業務程式碼即可。

目前 serverless 以及 knative 的幾個問題:

1. 效能問題

效能問題一直是 serverless 被人詬病的一點,也是目前它不能廣泛用於應用服務上的決定性原因。網際網路的應用大多數有高併發、高效能的要求,serverless 整個網路鏈路很長,容器啟停需要額外的時間,還無法滿足網際網路應用的要求。

針對這一點,很多 serverless 框架也在不斷地做改進,比如不斷精簡容器的啟動時間、容器啟動之後會做快取等,比如 nuclio 就宣稱自己的平臺比 AWS Lambda 要快 10 倍以上。

相信隨著 serverless 的不斷演進,效能問題會不斷優化,至於能不能達到網際網路應用的要求,還要時間給我們答案。

2. 是否需要 istio 這一層?

基於 kubernetes 的 serverless 元件非常多,比如 kubeless。但是基於同時又基於 istio,目前 knative 還是第一個這麼做的。

有些人的疑問在於,knative 真的有必要基於 istio 來做嗎?對於這個問題,我個人的看法是必要的。

雖然 istio 才剛剛release 1.0 版本,但是它作為叢集基礎設施通用網路層的地位已經開始顯露,相信在未來的發展中接受度會越來越大,並逐漸鞏固自己的地位。雖然現階段來說,很多人並不非常熟悉 istio 的情況,但是從長遠角度來看,這一點將是 knative 的一個優勢所在。

另外,基於 istio 構建自己的 serverless 服務,也符合目前軟體行業不要重複造輪子的工作。istio 在叢集的網路管理方面非常優秀(智慧路由、負載均衡、藍綠髮布等),基於 istio 來做可以讓 knative 不用重複工作就能直接使用 istio 提供的網路通用功能。

3. 系統複雜度

這一點和上面類似,knative 下面已經有兩個非常複雜的平臺:kubernetes 和 istio。這兩個平臺的理解、構建、運維本身就很複雜,如今又加上 knative 整個平臺,需要了解的概念都要幾十個,更不要提落地過程中會遇到的各種問題。

對於公有云來說,kubernetes 和 istio 這些底層平臺可以交給雲供應商來維護(比如 google Function),但是對於內部構建來說,這無疑提高了整個技術門檻,對系統管理人來的要求更高。

如何安裝部署整個叢集?如何對叢集做升級?出現問題怎麼除錯和追蹤?怎麼更好地和內部的系統對接?這些系統的最佳實踐是什麼?怎麼做效能優化?所有這些問題都需要叢集管理人員思考並落實。

4. 函式的可運維性?

相對於編寫微服務來說,單個函式的複雜度已經非常低,但是當非常多的函式需要共同工作的時候,如何管理這些函式就成了一個必須解決的問題。

  • 如何快速找到某個函式?
  • 如何知道一個函式的功能是什麼?接受的引數是什麼?
  • 怎麼保證函式的升級不會破壞原有的功能?升級之後如何回滾?怎麼記錄函式的歷史版本一方面追溯?
  • 當有多個函式需要同時工作的時候,怎麼定義它們之間的關係?
  • 函式出現問題的時候如何除錯?

對於函式的運維,一般的 serverless 平臺(包括 knative)都提供了 logging、metrics、tracing 三個方面的功能。預設情況下,knative 使用 EFK(Elasticsearch、Fluent、Kibana)來收集、查詢和分析日誌;使用 prometheus + grafana 來收集和索引、展示 metrics 資料;使用 jaeger 來進行呼叫關係的 tracing。

針對 serverless 衍生出來的運維工具和平臺還不夠多,如何除錯線上問題還沒有看到非常好的解決方案。

5. knative 成熟度

最後一點是關於 knative 成熟度的,前面已經提到,knative 目前剛出現不久。雖然整個框架和設計都已經搭好了,但是很多實現都比較初級。這裡提幾點來說:

  • 為了實現 autoscaling,knative 在每個 pod 中新增一個叫做 queue proxy 的代理,它會自動把請求的 metrics 傳送給 autoscaler 元件作為參考。這樣一來,整個網路鏈路上又多了一層,對整個效能勢必會有影響,未來的打算是直接使用 envoy sidecar 來替換掉 queue proxy
  • 支援的事件源和訊息系統還還優先,外部事件只支援 github、kubernetes 和 Google PubSub。 這個問題可以慢慢擴充套件,knative 本身會實現很常用的事件型別,自定義的事件源使用者可以自己實現
  • 目前還沒有函式的 pipeline 管理(類似 AWS Lambda Step Functions),多個函式如何協作並沒有自己處理。雖然沒有在官方文件中看到這方面的 roadmap,但是以後一定會有這方面的功能(不管是 knative 本身來做,還是社群作為工具補充來實現)

這方面的問題都不是大事情,隨著 knative 版本的迭代,在很快的時間都能夠解決。

參考資料

Knative 簡介

歡迎關注【金融級分散式架構】微信公眾號,瞭解更多