Knative 概述

Knative 概述

Knative 是以 Kubernetes 的一组自定义资源类型(CRD)的方式来安装的,Knative 的目标是在基于 Kubernetes 之上为整个开发生命周期提供帮助。Knative 将重点放在两个组件上:为应用提供流量的 serving(服务),以及确保应用程序能够轻松地生产和消费 event(事件)。

Serving(服务)

基于负载自动伸缩,包括在没有负载时缩减到零。允许你为多个修订版本(revision)应用创建流量策略,从而能够通过 URL 轻松路由到目标应用程序。

Event(事件)

抽象出事件源,并允许操作人员使用自己选择的消息传递层。

serverless

开发模式从瀑布式开发到敏捷再到精益甚至DevOps,应用框架从单体模型到分层模型再到微服务,部署及打包方式从面向物理机到虚拟机再到容器,应用程序的技术框架从自建机房到托管再到云计算。

无服务器架构(serverless)仍然在寻找一个整个行业都能认同的定义。许多人都同意这个理念的影响最大的是代码量,比如以前需要编写大型、单一的应用程序,现在你只需编写通过事件来调用的小型、单一用途的函数即可,这表示着你的代码只有在处理请求时才用到计算资源。

这意味着你只需要为活跃期间的计算服务付费,而不是一台 7x24 小时运行并可能在大部分时间内无所事事的虚拟机。在本地或非托管的无服务器架构(serverless)平台上,则表示代码可以只在需要时运行,在不需要时就停止,从而让你的基础设施能在其他方面*使用计算资源。

也基于此定义,有些人坚持无服务器架构(serverless)只适合在托管的云环境中运行,在本地运行这样的平台完全是不对的。其他人则认为它更像是一种哲学理论上的设计。也许这些定义最后会合并,也许不会。就目前来说,随着无服务器架构(serverless)普及率的持续增长,Knative 最有可能成为其标准。

Knative Serving 组件

本次将会围绕 Knative Serving 如何管理部署以及为应用和函数提供服务、如何通过 Serving 轻松地将一个预先构建好的镜像部署到底层 Kubernetes 集群中。
Knative Serving 维护某一时刻的快照,提供自动化伸缩功能(既支持扩容,也支持缩容直至为零),以及处理必要的路由和网络编排。

Serving 模块定义一组特定的对象以控制所有功能:修订版本、配置、路由和服务。Knative 使用 Kubernetes CRD的方式实现这些 Kubernetes 对象。

Knative 概述

Service

在 Knative 中,Service 管理工作负载的整个生命周期。包括部署、路由和回滚。(不要将 Knative Service 和 Kubernetes Service 混淆。它们是不同的资源。) Knative Service 控制一系列组成软件的 Route 和 Configuration。

一个 Service 小心确保一个应用有一个 Route、一个 Configuation,以及为每次 Service 更新产生的一个新 Revision。当创建一个 Service 时,您没有特别定义一个 Route,Knative 创建一个发送流量到最新 Revision 的路由。您可以选择一个特定的 Revision 以路由流量到该 Revision。

虽然 Route 和 Configuration 可以被分开在不同的 YAML 文件,在这种情形下,可以应用每个单独的对象到集群。然而,推荐的方式使用一个 Service 来编排 Route 和 Configuration。

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: helloworld-go
  namespace: default
spec:
  template:
    spec:
      containers:
        - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/helloworld-go:73fbdd56

这个文件定义 Configuration 并且是最小化 Service 定义。其中 spec 就是 pod 的 spec 由于这里没有 Route 定义,一个默认 Route 指向最新 Revision。Service 控制器整体追踪它所有的 configuration 和 Route 的状态。然后反映这些状态在它的 ConfigurationsReady 和 RoutesReady conditions (工作状况)属性里。

该镜像的源码发布在 Knative 托管 github : https://github.com/knative-sample/helloworld-go

Demo

  1. 引用该 yaml 文件后作如下操作。

    [root@iZuf6f5bhxcme4quh2ammpZ helloworld]# kubectl get ksvc
    NAME            URL                                                                  LATESTCREATED         LATESTREADY           READY   REASON
    helloworld-go   http://helloworld-go.default.b0da4945a7228ad2.app.alicontainer.com   helloworld-go-bpq9b   helloworld-go-bpq9b   True
    
  2. 其中 http://helloworld-go.default.b0da4945a7228ad2.app.alicontainer.com 则是默认 Route 指向的 Revision,并且 ksvc 还会调用 K8s 自身的 svc 和 deploy,为应用创建访问链路。

    [root@iZuf6f5bhxcme4quh2ammpZ helloworld]# kubectl get svc -A|grep hello
    default            helloworld-go                        ExternalName   <none>           kourier-internal.knative-serving.svc.cluster.local   80/TCP                              32m
    default            helloworld-go-bpq9b                  ClusterIP      172.16.238.106   <none>                                               80/TCP                              32m
    default            helloworld-go-bpq9b-private          ClusterIP      172.16.135.10    <none>                                               80/TCP,9090/TCP,9091/TCP,8022/TCP   32m
    [root@iZuf6f5bhxcme4quh2ammpZ helloworld]# kubectl get deploy -A|grep hello
    default            helloworld-go-bpq9b-deployment   0/0     0            0           34m
    

    此时因为该应用没有人访问,它的的 deploy 数量会自动缩减为 0,当有访问时 pod 会自动被激活。

Configuration

Knative 始于 Configuration。 在 Configuration 中为部署定义所需的状态。最小化 Configuration 至少包括一个配置名称和一个要部署容器镜像的引用。在 Knative 中,定义的引用为 Revision。 Revision 代表一个不变的,某一时刻的代码和 Configuration 的快照。 每个 Revision 引用一个特定的容器镜像和运行它所需要的任何特定对象。Revision 是不变的,它们从不会被改变和删除。而当修改 Configuration 的时候,Knative 会创建一个 Revision。这允许一个 Configuration 既反映工作负载的当前状态,同时也维护一个它自己的历史 Revision 列表。

如下展示一个完整的 Configuration 定义。它指定一个 Revision,该 Revision 使用一个容器镜像仓库 URI 引用一个特定的镜像并且指定其版本标签。

apiVersion: serving.knative.dev/v1alpha1
kind: Configuration
metadata:
  name: knative-helloworld
  namespace: default
spec:
  revisionTemplate:
    spec:
      container:
        image: registry.cn-hangzhou.aliyuncs.com/knative-sample/helloworld-go:73fbdd56

Configuration 同时也支持自定义端口,默认情况下 Knative 默认程序监听 8080 端口,可以通过 containerPort 自定义一个端口:

apiVersion: serving.knative.dev/v1alpha1
kind: Configuration
metadata:
  name: knative-helloworld
  namespace: default
spec:
  revisionTemplate:
    spec:
      container:
        image: registry.cn-hangzhou.aliyuncs.com/knative-sample/helloworld-go:73fbdd56
        ports:
          - containerPort: 8081

那么此时此刻 Kubernetes 集群内部发生了什么?在 Configuration 中指定的容器镜像是什么样子?Knative 转换 Configuration 定义为一些 Kubernetes 对象并在集群中创建它们。在启用 Configuration 后,可以看到相应的 Deployment、ReplicaSet 和 Pod。

[root@iZuf6f5bhxcme4quh2ammpZ helloworld]# kubectl get deployments -oname
deployment.apps/knative-helloworld-jn46v-deployment
[root@iZuf6f5bhxcme4quh2ammpZ helloworld]# kubectl get replicasets -oname
=replicaset.apps/knative-helloworld-jn46v-deployment-87dcd84db
[root@iZuf6f5bhxcme4quh2ammpZ helloworld]# kubectl get pods -oname
pod/knative-helloworld-jn46v-deployment-87dcd84db-cgnfm

Route

Knative 中的 Route 提供了一种将流量路由到正在运行的代码的机制。它将一个命名的,HTTP 可寻址端点映射到一个或者多个 Revision。Configuration 本身并不定义 Route。

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

这个定义中,Route 发送 100% 流量到由 configurationName 属性指定 Configuration 的最新就绪 Revision,该 Revision 由 Configuration YAML 中 latestReadyRevisionName 属性定义。

自动伸缩器和激活器

Serverless 的一个关键原则是可以按需扩容以满足需要和缩容以节省资源。Serverless 负载应当可以一直缩容至零。这意味着如果没有请求进入,则不会运行容器实例。Knative 使用两个关键组件以实现该功能。它将 Autoscaler 和 Activator 实现为集群中的 Pod。

kubectl get pods -n knative-serving 输出中可以看到这两个组件

Autoscaler 收集打到 Revision 并发请求数量的有关信息。为了做到这一点,它在 Revision Pod 内运行一个称之为 queue-proxy 的容器。

...
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 也能够被配置为利用 HPA 来替代该默认配置。这将基于 CPU 使用率来自动伸缩但不支持缩容至零。这些设定都能够通过 Revision 元数据注解(annotations)定制。

例如,一个 Revision 每秒收到 350 个请求并且每次请求大约需要处理 0.5 秒。使用默认设置(每 Pod 100 个并发请求),这个 Revision 将扩展至两个 Pod:

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

Autoscaler 也负责缩容至零。Revision 处于激活状态才接受请求。当一个 Revision 停止接受请求时,Autoscaler 将其置为 Reserve 状态,条件是每 Pod 平均并发必须持续 30 秒保持为 0(这是默认设置,但可以配置)。

处于 Reserve 状态下,一个 Revision 底层部署缩容至零并且所有到它的流量均路由至 Activator。Activator 是一个共享组件,其捕获所有到待命 Revision 的流量。当它收到一个到某一待命 Revision 的请求后,它转变 Revision 状态至 Active。然后代理请求至合适的 Pod。

Autoscaler 如何缩容
Autoscaler 采用的伸缩算法针对两个独立的时间间隔计算所有数据点的平均值。它维护两个时间窗,分别是 60 秒和 6 秒。Autoscaler 使用这些数据以两种模式运作:Stable Mode(稳定模式)和 Panic Mode(恐慌模式)。在 Stable 模式下,它使用 60 秒时间窗平均值决定如何伸缩部署以满足期望的并发量。
如果 6 秒窗口的平均并发量两次到达期望目标,Autoscaler 转换为 Panic Mode 并使用 6 秒时间窗。这让它更加快捷的响应瞬间流量的增长。它也仅仅在 Panic Mode 期间扩容以防止 Pod 数量快速波动。如果超过 60 秒没有扩容发生,Autoscaler 会转换回 Stable Mode。

Knative 概述

上一篇:Serverless 工程实践 | 零基础上手 Knative 应用


下一篇:春色满园关不住,带你体验阿里云 Knative