什么是Knative,它能做什么?

什么是Knative

最近有许多关于无服务器、 Kubernetes  和  Knative  的讨论。让我们先来解释一下 Knative 在这生态系统中适用位置及其独特之处。

首先,Knative 非常适合 Kubernetes 应用程序的开发者,它能让他们减少对基础架构的关注,而将更多精力集中在代码上。这点与其他无服务器平台没什么太大差别,但是大多数无服务器平台都采用自上而下的方法来提供以代码为中心的新界面,而 Knative 则专注于构建这些工具和服务来提升现有的 Kubernetes 体验。

其次 ,Knative 使 用与 Kubern etes 本身相同的模式(控制器)、API (kube-api) 和 Kube rn etes 基础架构(Kubernetes 资源 )构建而成

Knative 还提供”缩容至零”功能,支持真正零成本使用空闲应用程序,并支持采用蓝/绿部署来测试无服务器应用的新版本。

Knaive核心组件

为了实现 serverless 应用的管理,knative 把整个系统分成了三个部分:

  • Build:构建系统,把用户定义的函数和应用 build 成容器镜像, 提供显式”运行至完成”功能,这对创建 CI/CD 工作流程很有用。Serving 使用它将源存储库转换为包含应用程序的容器镜像。

  • Serving:服务系统,用来配置应用的路由、升级策略、自动扩缩容等功能 ,提供缩容至零、请求驱动的计算功能。它本质上是无服务器平台的执行和扩展组件。

  • Eventing:事件系统,用来自动完成事件的绑定和触发 提供抽象的交付和订阅机制,允许构建松散耦合和事件驱动的无服务器应用程序。

Build 构建系统

build 的功能是把用户的代码自动化构建成容器镜像,初次听起来很奇怪,有了 docker 之后有一个 Dockerfile 不就能构建容器了吗?为什么还需要一个新的 Build 系统?

Knative 的特别之处在于两点:一是它的构建完成是在 kubernetes 中进行的,和整个 kubernetes 生态结合更紧密;另外,它旨在提供一个通用的标准化的构建组件,可以作为其他更大系统中的一部分。

Serving:服务系统

serving 的核心功能是让应用运行起来提供服务。虽然听起来很简单,但这里包括了很多的事情:

  • Revision :这代表一个应用程序的单个实例。它以不可变形式包含一个特定的镜像和一组特定的配置选项(如环境变量)。它负责管理资源来实现包括运行应用程序的 Kubernetes 部署。

  • Configuration :用于创建修订的界面,因此大多数应用程序生命周期管理都通过此资源进行。将此视为部署脚本:配置负责定义应用程序镜像及其配置,类似于修订,但这些值是可变的。这允许更新配置中的环境变量或镜像标记,以便部署新版本。每当更改这些值时,都会创建一个应用程序的新实例(修订)。因此,给定配置始终至少有一个修订。

  • Route :通过此资源将流量引到一个特定修订。将一个服务用于简单部署时,通常不需要注意此资源,因为它在默认方式下将所有流量发送到最新修订。用户还可以使用此资源按百分比指定流量拆分(例如,a/b 部署,其中 10% 的流量将流向修订 2,90% 的流量将流向修订 1)。

  • (Knative) Service :不要与 Kubernetes 服务资源混淆,此 Knative 服务是将完整的无服务器应用程序连接在一起的最高级别资源。对于简单的用法,这是用户在部署其应用程序时需要与之交互的唯一资源。创建服务时,还会创建路由和配置。

knative serving 功能是基于 kubernetes 和 istio 开发的,它使用 kubernetes 来管理容器(deployment、pod),istio 来管理网络路由(VirtualService、DestinationRule)。

因为 kubernetes 和 istio 本身的概念非常多,理解和管理起来比较困难,knative 在此之上提供了更高一层的抽象(这些对应是基于 kubernetes 的 CRD 实现的)。这些抽象出来的概念对应的关系如下图:

Knaive部署与配置

假设你已经安装了 Knative,并可使用  kubectl  来访问 Knative 集群。 (访问  https://github.com/knative/docs/blob/master/install/README.md ,获取有关安装 Knative 的更多信息。

让我们使用  kubectl ,通过将以下内容写入文件 (service.yaml) 并运行,定义以下 Knative 服务:

$ kubectl -f service.yaml
 
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: helloworld-go
  namespace: default
spec:
  runLatest:
    configuration:
      revisionTemplate:
        spec:
          container:
            image: 
            env:
            - name: TARGET
              value: "Go Sample v1"

这就是我们创建一个 helloworld-go 应用程序的请求驱动实例所需的全部内容。

通过从以下命令中复制 EXTERNAL IP 字段,查找可以访问应用程序的 IP 地址:

$ kubectl get svc knative-ingressgateway --namespace istio-system
 
NAME                     TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                                      AGE
knative-ingressgateway   LoadBalancer   10.23.247.74   35.203.155.229   80:32380/TCP,443:32390/TCP,32400:32400/TCP   2d

然后使用以下命令查找应用程序的域,复制 DOMAIN 字段:

$ kubectl get ksvc helloworld-go
 
NAME            DOMAIN                              LATESTCREATED         LATESTREADY           READY     REASON
helloworld-go   helloworld-go.default.example.com   helloworld-go-00001   helloworld-go-00001   True

接着,可以使用以下命令向应用程序发出请求:

$ curl -H "Host: {DOMAIN}" http://{IP_ADDRESS}
 
Hello World: Go Sample v1!

恭喜! 你已经成功部署了第一个 Knative 无服务器应用程序!

Service(服务)

现在,让我们来看看应用程序的构成资源。 先查看定义时所使用的服务:

$ kubectl get ksvc helloworld-go
 
NAME            DOMAIN                              LATESTCREATED         LATESTREADY           READY     REASON
helloworld-go   helloworld-go.default.example.com   helloworld-go-00001   helloworld-go-00001   True

这向我们展示了应用程序的域,以及应用程序最新创建的修订和已有的修订。 由于这是我们最初定义的确切资源,在此处看到的内容并不多,因此让我们深入了解一下 Knative 为我们创建的一些其他资源。

Configuration(配置)

在我们定义 Knative 服务时,会自动为我们的应用程序创建一个配置。 我们可以使用以下命令查看此配置:

$ kubectl get configuration helloworld-go -o yaml
 
apiVersion: serving.knative.dev/v1alpha1
kind: Configuration
metadata:
  name: helloworld-go
  namespace: default
  
spec:
  generation: 1
  revisionTemplate:
    metadata:
      annotations:
        sidecar.istio.io/inject: "false"
      creationTimestamp: null
    spec:
      container:
        env:
        - name: TARGET
          value: Go Sample v1
        image: /helloworld-go
        name: ""
        resources: {}
      containerConcurrency: 1
      timeoutSeconds: 1m0s
status:
  conditions:
  
  latestCreatedRevisionName: helloworld-go-00001
  latestReadyRevisionName: helloworld-go-00001
  observedGeneration: 1

为便于阅读,已删除了部分输出。 如您所见,根据我们发出的命令,此配置的名称 (helloworld-go) 与我们所定义的服务名称相匹配 – 定义服务时总是如此。 最有趣的组件是  spec.revisionTemplate  部分。

Revision(修订)

运行以下命令来查看由我们的配置所创建的修订:

$ kubectl get revision helloworld-go-00001 -o yaml
 
apiVersion: serving.knative.dev/v1alpha1
kind: Revision
metadata:
  name: helloworld-go-00001
  namespace: default
  
spec:
  container:
    env:
    - name: TARGET
      value: Go Sample v1
    image: 
    name: ""
    resources: {}
  containerConcurrency: 1
  generation: 1
  timeoutSeconds: 1m0s
status:
  conditions:
  
  serviceName: helloworld-go-00001-service

与上文配置一样,为便于阅读,我删除了一些修订输出。 如前所述,每次修改配置时,都会创建一个新的修订,同时也会为任何配置创建初始修订。 由于这是第一个修订,因此它的修订号为 00001。 注意,修订规范与上述配置中的 revisionTemplate 相匹配。

现在让我们尝试更改配置(通过我们的服务),并观察新修订的创建。 为此,我们可以将  service.yaml  文件中的行  value: "Go Sample v1"  更改为  value: "Go Sample v2"

然后使用以下命令来更新服务: kubectl apply -f service.yaml

让我们使用以下命令来查看当前修订:

$ kubectl get revision
 
NAME                  SERVICE NAME                  READY     REASON
helloworld-go-00001   helloworld-go-00001-service   True
helloworld-go-00002   helloworld-go-00002-service   True

可见,我们创建了一个新修订 (helloworld-go-00002)。 重新运行 curl 命令我们还可以看到应用程序的响应已发生更改:

$ curl -H "Host: {DOMAIN}" http://{IP_ADDRESS}
 
Hello World: Go Sample v2!

Route(路由)

现在运行以下命令查看 Knative 为我们的服务创建的路由:

$ kubectl get route helloworld-go -oyaml
 
apiVersion: serving.knative.dev/v1alpha1
kind: Route
metadata:
  name: helloworld-go
  namespace: default
  
spec:
  generation: 1
  traffic:
  - configurationName: helloworld-go
    percent: 100
status:
  address:
    hostname: helloworld-go.default.svc.cluster.local
  

可见,与我们的其他资源相比,此资源的 spec 部分相当简略,因为它只包含一个 generation 和 traffic 部分。 用户可通过 traffic 部分指定配置(就像我们此时的配置一样),这始终会将流量引到最新就绪的修订或一个特定修订。 在定位一个特定修订时,值得注意的是,您可以指定修订列表 - 每个修订都有各自的目标百分比,然后允许用户逐步更改,最初仅将一小部分流量发送到一个新修订。

欢迎关注“Java架构师学习”