Knative詳解

serverless

無服務器架構(serverless),則表示代碼可以只在需要時運行,在不需要時就停止,從而讓你的基礎設施能在其他方面自由使用計算資源。

Kaniko

Kaniko 是 Google 造的輪子之一,用於在 Kubernetes 上無需特權的構建 docker image,在 github(https://github.com/GoogleContainerTools/kaniko) 中,是這樣介紹的。

工作原理

傳統的 Docker build 是 Docker daemon 根據 Dockerfile,使用特權用戶(root)在宿主機依次執行,並生成鏡像的每一層:

而 Kaniko 工作原理和此類似,也是按順序執行每條命令,每條命令執行完畢後爲文件系統做快照(snapshot)。並與上一個快照進行對比,如果發現任何不一致,變回創建一個新的層級,並將任何修改都寫入鏡像的元數據中。

在這裏插入圖片描述
參考:https://blog.ihypo.net/15487483292659.html

什麼是 Knative

Knative 的目標是在基於 Kubernetes 之上爲整個開發生命週期提供幫助。它的具體實現方式是:首先使你作爲開發人員能夠以你想要的語言和以你想要的方式來編寫代碼,其次幫助你構建和打包應用程序,最後幫助你運行和伸縮應用程序。

Knative 主要由 Build、Serving 和 Eventing 三大核心組件構成。Knative 正是依靠這三個核心組件,驅動着 Knative 這艘 Serverless 巨輪前行。下面讓我們來分別介紹一下這三個核心組件。

build

通過靈活的插件化的構建系統將用戶源代碼構建成容器。目前已經支持多個構建系統,比如 Google 的 Kaniko,它無需運行 Docker
daemon 就可以在 Kubernetes 集羣上構建容器鏡像。

Knative Build 是基於現有的 Kubernetes 能力之上,提供的一套標準化、可移植、可複用的容器鏡像構建方式。通過在 Kubernetes 上運行復雜的構建任務,Knative Build 使你不必再單獨開發和重複這些鏡像構建過程, 從而通過系統化、工程化的方式,減少了鏡像構建時間及成本。

Build 通過 Kubernetes 自定義資源定義(CRD)實現。 通過 Build 你可以自定義一個從運行到結束的構建流程。例如,可以使用 Knative Build 來獲取、構建和打包代碼。Build 具備以下功能:

  • 支持 Source 源掛載,目前支持的 Source 源包括:

    – git 代碼倉庫
    – 任意容器鏡像

  • 支持通過 BuildTemplate 創建可重複執行構建的模板

  • 支持 K8s ServiceAccount 身份驗證

在這裏插入圖片描述
雖然目前 Knative Build 並不提供完整的獨立 CI/CD 解決方案,但它卻提供了一個底層的構建模塊,用戶可單獨使用該構建模塊在大型系統中實現集成和利用。

Serving

基於負載自動伸縮,包括在沒有負載時縮減到零。允許你爲多個修訂版本(revision)應用創建流量策略,從而能夠通過 URL輕鬆路由到目標應用程序。

Knative 作爲 Severless 框架最終是用來提供服務的, 那麼 Knative Serving 應運而生。

Knative Serving 構建於 Kubernetes 和 Istio 之上,爲 Serverless 應用提供部署和服務支持。其特性如下:

  • 快速部署 Serverless 容器
  • 支持自動擴縮容和縮到 0 實例
  • 基於 Istio 組件,提供路由和網絡編程
  • 支持部署快照

Knative Serving 中定義了以下 CRD 資源:

  • Service: 自動管理工作負載整個生命週期。負責創建 Route、Configuration 以及 Revision 資源。通過 Service 可以指定路由流量使用最新的 Revision 還是固定的 Revision
  • Route:負責映射網絡端點到一個或多個 Revision。可以通過多種方式管理流量。包括灰度流量和重命名路由
  • Configuration: 負責保持 Deployment 的期望狀態,提供了代碼和配置之間清晰的分離,並遵循應用開發的 12 要素。修改一次 Configuration 產生一個 Revision
  • Revision:Revision 資源是對工作負載進行的每個修改的代碼和配置的時間點快照。Revision 是不可變對象,可以長期保留

在這裏插入圖片描述

Configuration(配置)和 Revision(修訂版本)

Knative Serving 始於 Configuration。您可以在 Configuration 中爲部署定義所需的狀態。最小化 Configuration 至少包括一個配置名稱和一個要部署容器鏡像的引用。在 Knative 中,定義的引用爲 Revision。Revision 代表一個不變的,某一時刻的代碼和 Configuration 的快照。每個 Revision 引用一個特定的容器鏡像和運行它所需要的任何特定對象(例如環境變量和卷)。然而,您不必顯式創建 Revision。由於 Revision 是不變的,它們從不會被改變和刪除,相反,當您修改 Configuration 的時候,Knative 會創建一個 Revision。這允許一個 Configuration 既反映工作負載的當前狀態,同時也維護一個它自己的歷史 Revision 列表。

例如:

apiVersion: serving.knative.dev/v1alpha1
kind: Configuration
metadata:
  name: knative-helloworld
  namespace: default
spec:
  revisionTemplate:
    spec:
      container:
        image: docker.io/gswk/knative-helloworld:latest
        env:
          - name: MESSAGE
            value: "Knative!"
        ports:
          - containerPort: 8081

其中默認情況下,Knative 將假定您的應用程序監聽 8080 端口。

Configuration 可以指定一個已有的容器鏡像,如示例 2-1 中所示。或者,它也可以選擇指向一個 Build 資源以從源代碼創建一個容器鏡像。

Knative 轉換 Configuration 定義爲一些 Kubernetes 對象並在集羣中創建它們。在啓用 Configuration 後,可以看到相應的 Deployment、ReplicaSet 和 Pod。

現在我們有了用於運行我們應用的 Pod,但是我們怎麼知道該向哪裏發送請求?這正是 Route 用武之地。

Route(路由)

Knative 中的 Route 提供了一種將流量路由到正在運行的代碼的機制。它將一個命名的,HTTP 可尋址端點映射到一個或者多個 Revision。Configuration 本身並不定義 Route

apiVersion: serving.knative.dev/v1alpha1
kind: Route
metadata:
  name: knative-helloworld
  namespace: default
spec:
  traffic:
  - configurationName: knative-helloworld
percent: 100

這裏展示了一個將流量發送到指定 Configuration 最新 Revision 的最基本路由定義。

這個定義中,Route 發送 100% 流量到由 configurationName 屬性指定 Configuration 的最新就緒 Revision,該 Revision 由 Configuration YAML 中 latestReadyRevisionName 屬性定義。您可以通過發送如下 curl 命令來測試這些 Route 和 Configuration :

curl -H "Host: knative-routing-demo.default.example.com"
http://$KNATIVE_INGRESS

你可以通過指定流量版本名稱進行abtest

apiVersion: serving.knative.dev/v1alpha1
kind: Route
metadata:
  name: knative-routing-demo
  namespace: default
spec:
  traffic:
  - revisionName: knative-routing-demo-00001
    name: v1
    percent: 100

指定的 Revision 可以使用 v1 子域名訪問,如下 curl 命令所示:

curl -H "Host: v1.knative-routing-demo.default.example.com"
http://$KNATIVE_INGRESS

Knative 也允許以百分比的方式跨 Revision 進行流量分配。支持諸如增量發佈、藍綠部署或者其他複雜的路由場景

注意:Knative 默認使用 example.com 域名,但不適合生產使用。您會注意到在 curl 命令 (v1.knative-routing-demo.default.example.com) 中作爲一個主機頭傳遞的 URL 包含該默認值作爲域名後綴。URL 格式遵循模式 {REVISION_NAME}.{SERVICE_NAME}.{NAMESPACE}.{DOMAIN} 。

在這個案例中,子域名中 default 部分指的是命名空間。

下面我們來看看如何改變這些值以及如何使用自定義域名。

Autoscaler(自動伸縮器)和 Activator(激活器)

Serverless 的一個關鍵原則是可以按需擴容以滿足需要和縮容以節省資源。Serverless 負載應當可以一直縮容至零。這意味着如果沒有請求進入,則不會運行容器實例。Knative 使用兩個關鍵組件以實現該功能。它將 Autoscaler 和 Activator 實現爲集羣中的 Pod。您可以看到它們伴隨其他 Serving 組件一起運行在 knative-serving 命名空間中.
在這裏插入圖片描述
Autoscaler 收集打到 Revision 併發請求數量的有關信息。爲了做到這一點,它在 Revision Pod 內運行一個稱之爲 queue-proxy 的容器,該 Pod 中也運行用戶提供的 (user-provided) 鏡像。可以在相應 Revision Pod 上,通過運行 kubectl describe 命令可以看到這些容器

...
Containers:
  user-container:
    Container ID:  docker://f02dc...
    Image:         index.docker.io/gswk/knative-helloworld...
...
  queue-proxy: 
    Container ID:  docker://1afcb...
    Image:         gcr.io/knative-releases/github.com/knative...
...

queue-proxy 檢測該 Revision 上觀察到的併發量,然後它每隔一秒將此數據發送到 Autoscaler。Autoscaler 每兩秒對這些指標進行評估。基於評估的結果,它增加或者減少 Revision 部署的規模。

默認情況下,Autoscaler 嘗試維持每 Pod 每秒平均 100 個併發請求。這些併發目標和平均併發窗口均可以變化。Autoscaler 也能夠被配置爲利用 Kubernets HPA (Horizontal Pod Autoscaler) 來替代該默認配置。

這將基於 CPU 使用率來自動伸縮但不支持縮容至零。這些設定都能夠通過 Revision 元數據註解 (annotations) 定製。有關這些註解的詳情,請參閱 Knative 文檔。https://knative.dev/blog/

例如,一個 Revision 每秒收到 350 個請求並且每次請求大約需要處理 0.5 秒。使用默認設置 (每 Pod 100 個併發請求),這個 Revision 將擴展至兩個 Pod:

350 * .5 = 175
175 / 100 = 1.75
ceil(1.75) = 2 pods

Autoscaler 也負責縮容至零。Revision 處於 Active (激活) 狀態才接受請求。當一個 Revision 停止接受請求時,Autoscaler 將其置爲 Reserve (待命) 狀態,條件是每 Pod 平均併發必須持續 30 秒保持爲 0 (這是默認設置,但可以配置)。

處於 Reserve 狀態下,一個 Revision 底層部署縮容至零並且所有到它的流量均路由至 Activator。Activator 是一個共享組件,其捕獲所有到待命 Revisios 的流量。當它收到一個到某一待命 Revision 的請求後,它轉變 Revision 狀態至 Active。然後代理請求至合適的 Pods。

Autoscaler 如何伸縮

Autoscaler 採用的伸縮算法針對兩個獨立的時間間隔計算所有數據點的平均值。它維護兩個時間窗,分別是 60 秒和 6 秒。Autoscaler 使用這些數據以兩種模式運作:Stable Mode (穩定模式) 和 Panic Mode (忙亂模式)。在 Stable 模式下,它使用 60 秒時間窗平均值決定如何伸縮部署以滿足期望的併發量。

如果 6 秒窗口的平均併發量兩次到達期望目標,Autoscaler 轉換爲 Panic Mode 並使用 6 秒時間窗。這讓它更加快捷的響應瞬間流量的增長。它也僅僅在 Panic Mode 期間擴容以防止 Pod 數量快速波動。如果超過 60 秒沒有擴容發生,Autoscaler 會轉換回 Stable Mode。

serving

在 Knative 中,Service 管理負責的整個生命週期。包括部署、路由和回滾。(不要將 Knative Service 和 Kubernetes Service 混淆。它們是不同的資源。) Knative Service 控制一系列組成軟件的 Route 和 Configuration。Knative Service 可以被看作是一段代碼 —— 您正在部署的應用或者函數。

一個 Service 注意確保一個應用有一個 Route、一個 Configuation,以及爲每次 Service 更新產生的一個新 Revision。當創建一個 Service 時,您沒有特別定義一個 Route,Knative 創建一個發送流量到最新 Revision 的路由。您可以選擇一個特定的 Revision 以路由流量到該 Revision。

不要求您明確創建一個 Service。Route 和 Configuration 可以被分開在不同的 YAML 文件(如示例 2-1 和 示例 2-4)。在這種情形下,您可以應用每個單獨的對象到集羣。然而,推薦的方式使用一個 Service 來編排 Route 和 Configuration。

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: knative-helloworld
  namespace: default
spec:
  runLatest:
    configuration:
      revisionTemplate:
        spec:
          container:
            image: docker.io/gswk/knative-helloworld:latest

注意這個 service.yml 文件和 configuration.yml 非常相似。這個文件定義 Configuration 並且是最小化 Service 定義。由於這裏沒有 Route 定義,一個默認 Route 指向最新 Revision。Service 控制器整體追蹤它所有的 configuration 和 Route 的狀態。然後反映這些狀態在它的 ConfigurationsReady 和 RoutesReady conditions 屬性裏。當通過 CLI 使用 kubectl get ksvc 命令請求 Knative Service 信息的時候,這些狀態可以被看到。

kubectl get ksvc xxx -o yaml

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
...
  name: knative-helloworld
  namespace: default
...
spec:
...
status:
conditions:
  - lastTransitionTime: YYYY-MM-DDTHH:MM:SSZ
    status: "True"
    type: ConfigurationsReady
  - lastTransitionTime: YYYY-MM-DDTHH:MM:SSZ
    status: "True"
    type: Ready
  - lastTransitionTime: YYYY-MM-DDTHH:MM:SSZ
    status: "True"
    type: RoutesReady
  domain: knative-helloworld.default.example.com
  domainInternal: knative-helloworld.default.svc.cluster.local
  latestCreatedRevisionName: knative-helloworld-00001
  latestReadyRevisionName: knative-helloworld-00001
  observedGeneration: 1
  targetable:
    domainInternal: knative-helloworld.default.svc.cluster.local
  traffic:
  - percent: 100
    revisionName: knative-helloworld-00001

總結

至此已經向您介紹了 Service、Route、Configuration 和 Revision。Revision 是不變的並且只能經由 Configuration 改變而被創建。您可以分別單獨創建 Configuration 和 Route,或者把它們組合在一起並定義爲一個 Service。理解 Serving 組件的這些構建塊是使用 Knative 的基礎。您部署的應用均需要一個 Service 或者 Configuration 以在 Knative 中作爲容器運行。

參考:https://www.servicemesher.com/blog/knative-serving/#example-2-8

Eventing

使得生產和消費事件變得容易。抽象出事件源,並允許操作人員使用自己選擇的消息傳遞層。

Knative Eventing 旨在滿足雲原生開發中通用需求, 以提供可組合的方式綁定事件源和事件消費者。其設計目標:

  • Knative Eventing 提供的服務是鬆散耦合,可獨立開發和部署。服務可跨平臺使用(如 Kubernetes, VMs, SaaS 或者 FaaS)
  • 事件的生產者和事件的消費者是相互獨立的。任何事件的生產者(事件源)可以先於事件的消費者監聽之前產生事件,同樣事件的消費者可以先於事件產生之前監聽事件
  • 支持第三方的服務對接該 Eventing 系統
  • 確保跨服務的互操作性

在這裏插入圖片描述

如上圖所示,Eventing 主要由事件源(Event Source)、事件處理(Flow)以及事件消費者(Event Consumer)三部分構成。

事件源(Event Source)

當前支持以下幾種類型的事件源:

  • ApiserverSource:每次創建或更新 Kubernetes 資源時,ApiserverSource 都會觸發一個新事件
  • GitHubSource:GitHub 操作時,GitHubSource 會觸發一個新事件
  • GcpPubSubSource: GCP 雲平臺 Pub/Sub 服務會觸發一個新事件
  • AwsSqsSource:Aws 雲平臺 SQS 服務會觸發一個新事件
  • ContainerSource: ContainerSource 將實例化一個容器,通過該容器產生事件
  • CronJobSource: 通過 CronJob 產生事件
  • KafkaSource: 接收 Kafka 事件並觸發一個新事件
  • CamelSource: 接收 Camel 相關組件事件並觸發一個新事件

事件接收/轉發(Flow)

當前 Knative 支持如下事件接收處理:

  • 直接事件接收
    通過事件源直接轉發到單一事件消費者。支持直接調用 Knative Service 或者 Kubernetes Service 進行消費處理。這樣的場景下,如果調用的服務不可用,事件源負責重試機制處理。

  • 通過事件通道(Channel)以及事件訂閱(Subscriptions)轉發事件處理
    這樣的情況下,可以通過 Channel 保證事件不丟失並進行緩衝處理,通過 Subscriptions 訂閱事件以滿足多個消費端處理。

  • 通過 brokers 和 triggers 支持事件消費及過濾機制

從 v0.5 開始,Knative Eventing 定義 Broker 和 Trigger 對象,實現了對事件進行過濾(亦如通過 ingress 和 ingress controller 對網絡流量的過濾一樣)。

通過定義 Broker 創建 Channel,通過 Trigger 創建 Channel 的訂閱(subscription),併產生事件過濾規則。

事件消費者(Event Consumer)

爲了滿足將事件發送到不同類型的服務進行消費, Knative Eventing 通過多個 k8s 資源定義了兩個通用的接口:

  • Addressable 接口提供可用於事件接收和發送的 HTTP 請求地址,並通過status.address.hostname字段定義。作爲一種特殊情況,Kubernetes Service 對象也可以實現 Addressable 接口
  • Callable 接口接收通過 HTTP 傳遞的事件並轉換事件。可以按照處理來自外部事件源事件的相同方式,對這些返回的事件做進一步處理

當前 Knative 支持通過 Knative Service 或者 Kubernetes Service 進行消費事件。

另外針對事件消費者,如何事先知道哪些事件可以被消費? Knative Eventing 在最新的 0.6 版本中提供 Registry 事件註冊機制, 這樣事件消費者就可以事先通過 Registry 獲得哪些 Broker 中的事件類型可以被消費。

總結

Knative 使用 Build 提供雲原生“從源代碼到容器”的鏡像構建能力,通過 Serving 部署容器並提供通用的服務模型,同時以 Eventing 提供事件全局訂閱、傳遞和管理能力,實現事件驅動。這就是 Knative 呈現給我們的標準 Serverless 編排框架。

參考:https://segmentfault.com/a/1190000019349457

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章