我开始学习使用Knative

这篇文章是 OpenSaaS Studio Advent Calendar 2019 第22天的文章。

初次见面。我是CyberAgent OpenSaaS Studio的@satoshiyamamoto。我负责在内容调控系统Orion中进行数据流处理的开发以及引入到自家服务中。Orion的数据流由Apache Kafka和Spring Cloud Stream组成,但是由于迁移到Kubernetes,我对Cloud Native架构产生了兴趣。因此,我研究了Google Cloud的Cloud Run和Redhat的OpenShift Serverless所采用的Knative,重点关注了版本v0.11。

Knative 是什么

Knative是一个由Google、Pivotal和其他行业领导者的工程师共同启动的新的开源项目。它是一个基于Kubernetes的平台,用于部署和管理现代无服务器工作负载,其中包括Serving和Eventing组件。通过简化基于容器的管理,使开发人员能够更专注于业务逻辑等重要事项。

在介绍Knative项目的概览后,我想在查看官方文档的同时,分别尝试使用Serving和Eventing的各个组件来开始入门。

服务

Knative Serving 是搭建在 Kubernetes 和 Istio 上的,支持部署容器应用程序和函数。Serving 可以轻松启动,并可以扩展以支持高级场景。

Knative Serving项目使以下事项成为可能。

    • サーバーレスコンテナのすばやい展開

 

    • オートスケーリング

 

    • Istio コンポーネントのルーティングとプログラム可能なネットワーキング

 

    デプロイされたコードと構成のバージョン管理

在Kubernetes上能够实现类似于AWS的API Gateway + Lambda的功能,您有这样的印象吗?

提供的资源

Knative Serving 将对象集合定义为 Kubernetes 自定义资源定义(CRD)。使用这些对象来定义和控制集群上的无服务器工作负载的行为。

    • Service:

Route + Configuration = Service

Route:

ネットワークのエンドポイントを 1 つ以上の Revision にマップ

Configuration:

デプロイメントの望ましい状態を維持

Revision:

コードの変更と Coniguration のスナップショット
Configuration の変更で Revision が作成される

object_model.png

开始进入

按照 Installing Knative 的步骤,在 Kubernetes 集群上安装 Knative。参考 Configuring DNS 部分,在 xip.io 的 DNS 上配置 istio-ingressgateway 的外部 IP 可以更方便地进行确认。

那么,我们将创建Serving的清单文件并部署一个示例应用程序。

# service.yaml
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: helloworld-go
  namespace: default
spec:
  template:
    spec:
      containers:
        - image: gcr.io/knative-samples/helloworld-go
          env:
            - name: TARGET
              value: OpenSaaS Studio!
---
kubectl apply --filename service.yaml

检查示例应用程序是否成功部署。

kubectl get ksvc helloworld-go

NAME            URL                                                LATESTCREATED         LATESTREADY           READY   REASON
helloworld-go   http://helloworld-go.default.1.2.3.4.xip.io        helloworld-go-96dtk   helloworld-go-96dtk   True

发送HTTP请求到由Route创建的端点

curl http://helloworld-go.default.1.2.3.4.xip.io
Hello OpenSaaS Studio!

如果我们使用Kubernetes的标准资源来部署应用程序,我们需要Deployment、Service和Ingress的清单文件,但与此相比,通过Knative Service只需很少的描述即可部署应用程序。

接下来,我们来看一下Eventing组件。

事件化

Knative Eventing 是一个旨在满足云原生开发的常见需求的系统,它提供了有效启用事件源和事件消费者之间的延迟绑定的基本组件。

目前,还有各种各样的官方提供的事件源。

    • KubernetesEventSource

 

    • GitHubSource

 

    • GcpPubSubSource

 

    • AwsSqsSource

 

    • ContainerSource

 

    • CronJobSource

 

    • KafkaSource

 

    CamelSource

设计概要

Knative事件驱动基于以下目标进行设计。

    Knative事件服务是松耦合的。事件生产者和事件消费者是相互独立的。其他服务可以连接到Knative的事件系统,以确保服务之间的互操作性。Knative Eventing符合由CNCF无服务器工作组开发的CloudEvents标准。

消费者

为了实现向多种类型的服务传递,Knative Eventing 定义了可以通过多个 Kubernetes 资源来实现的两个通用接口。

    1. 可寻址的对象可以通过status.address.url字段接收和确认通过HTTP传输的事件。

 

    可调用的对象可以接收通过HTTP传输的事件,并将事件转化为0个或1个新事件,以HTTP响应返回。这些返回的事件将以与外部事件源相同的方式进一步处理。

经纪人与触发器

broker-trigger-overview.png

事件注册

v0.6 版本时,Knative Eventing 定义了 EventType 对象,使得 Consumer 能够轻松发现并消费来自不同 Broker 的不同类型的事件。Registry 是由 EventType 集合组成的。存储在 Registry 中的 EventType 包含了 Consumer 创建 Trigger 所需的信息,无需依赖其他外部机制。

事件频道和订阅

Knative事件驱动也定义了事件传递和持久化层,称为通道。每个通道都是独立的Kubernetes自定义资源。事件可以通过订阅传递到服务中,也可以转发到其他通道。这样可以根据要求动态改变群集内的消息传递,有些事件可以通过内存实现进行处理,而其他事件则可以使用Apache Kafka或NATS Streaming进行持久化。

由于资源众多,Eventing 变得很长,但是我们依然坚持不放弃,开始着手进行初步工作。

开始

按照 Installing the Knative Eventing component的指示 安装Knative Eventing组件。

在”起步”部分,我们将创建一个名为”event-example”的命名空间,并将Knative资源分组以进行整理。

kubectl create namespace event-example
kubectl label namespace event-example knative-eventing-injection=enabled

确认经纪人是否正在运行。

kubectl --namespace event-example get Broker default
NAME      READY   REASON   URL                                                        AGE
default   True             http://default-broker.event-example.svc.cluster.local      17s

创建两个事件消费者,hello-display和goodbye-display。
首先是hello-display的部署和服务。

kubectl --namespace event-example apply --filename - << END
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-display
spec:
replicas: 1
selector:
    matchLabels: &labels
    app: hello-display
template:
    metadata:
    labels: *labels
    spec:
    containers:
        - name: event-display
        # Source code: https://github.com/knative/eventing-contrib/blob/release-0.6/cmd/event_display/main.go
        image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display@sha256:37ace92b63fc516ad4c8331b6b3b2d84e4ab2d8ba898e387c0b6f68f0e3081c4

---

# Service pointing at the previous Deployment. This will be the target for event
# consumption.
kind: Service
apiVersion: v1
metadata:
    name: hello-display
spec:
    selector:
    app: hello-display
    ports:
    - protocol: TCP
    port: 80
    targetPort: 8080
END

接下来是再见显示。

kubectl --namespace event-example apply --filename - << END
apiVersion: apps/v1
kind: Deployment
metadata:
name: goodbye-display
spec:
replicas: 1
selector:
    matchLabels: &labels
    app: goodbye-display
template:
    metadata:
    labels: *labels
    spec:
    containers:
        - name: event-display
        # Source code: https://github.com/knative/eventing-contrib/blob/release-0.6/cmd/event_display/main.go
        image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display@sha256:37ace92b63fc516ad4c8331b6b3b2d84e4ab2d8ba898e387c0b6f68f0e3081c4

---

# Service pointing at the previous Deployment. This will be the target for event
# consumption.
kind: Service
apiVersion: v1
metadata:
name: goodbye-display
spec:
selector:
    app: goodbye-display
ports:
- protocol: TCP
    port: 80
    targetPort: 8080
END

我将确认是否已经部署。

kubectl --namespace event-example get deployments hello-display goodbye-display
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
hello-display     1/1     1            1           14s
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
goodbye-display   1/1     1            1           7s

为了将事件转发给适当的消费者,我们将创建一个触发器。这个触发器可以根据CloudEvents的上下文属性来指定过滤器。

创建一个触发器,用于将类型为“greeting”的事件发送到hello-display。

kubectl --namespace event-example apply --filename - << END
apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
name: hello-display
spec:
filter:
    attributes:
    type: greeting
subscriber:
    ref:
    apiVersion: v1
    kind: Service
    name: hello-display
END

接下来,我们将创建一个触发器,将源代码“sendoff”的事件发送到“goodbye-display”。

kubectl --namespace event-example apply --filename - << END
apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
name: goodbye-display
spec:
filter:
    attributes:
    source: sendoff
subscriber:
    ref:
    apiVersion: v1
    kind: Service
    name: goodbye-display
END

我将确认触发器是否已创建。

kubectl --namespace event-example get triggers
NAME              READY   REASON   BROKER    SUBSCRIBER_URI                                            AGE
goodbye-display   True             default   http://goodbye-display.event-example.svc.cluster.local/   4s
hello-display     True             default   http://hello-display.event-example.svc.cluster.local/     3m16s

最后,创建一个事件生产者的Pod。使用curl手动发送每个事件,并观察这些事件是如何被正确的消费者接收的。

kubectl --namespace event-example apply --filename - << END
apiVersion: v1
kind: Pod
metadata:
labels:
    run: curl
name: curl
spec:
containers:
    # This could be any image that we can SSH into and has curl.
- image: radial/busyboxplus:curl
    imagePullPolicy: IfNotPresent
    name: curl
    resources: {}
    stdin: true
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    tty: true
END

使用Broker通过HTTP请求创建事件。

kubectl --namespace event-example attach curl -it
Defaulting container name to curl.
Use 'kubectl describe pod/ -n event-example' to see all of the containers in this pod.
If you don't see a command prompt, try pressing enter.
[ root@curl:/ ]$

为了创建不同类型的事件,我们将进行三个请求。
首先请求创建类型为“问候”的事件。

curl -v "http://default-broker.event-example.svc.cluster.local" \
  -X POST \
  -H "Ce-Id: say-hello" \
  -H "Ce-Specversion: 0.3" \
  -H "Ce-Type: greeting" \
  -H "Ce-Source: not-sendoff" \
  -H "Content-Type: application/json" \
  -d '{"msg":"Hello Knative!"}'

当经纪人接收到事件时,”hello-display”会被激活并发送给事件消费者。如果事件被接收,事件生产者将收到”202 Accepted”的回复。

创建一个第二个请求来发送离别活动。

curl -v "http://default-broker.event-example.svc.cluster.local" \
  -X POST \
  -H "Ce-Id: say-goodbye" \
  -H "Ce-Specversion: 0.3" \
  -H "Ce-Type: not-greeting" \
  -H "Ce-Source: sendoff" \
  -H "Content-Type: application/json" \
  -d '{"msg":"Goodbye Knative!"}'

再见显示已激活。

创建一个具有问候和送别功能的活动。

curl -v "http://default-broker.event-example.svc.cluster.local" \
  -X POST \
  -H "Ce-Id: say-hello-goodbye" \
  -H "Ce-Specversion: 0.3" \
  -H "Ce-Type: greeting" \
  -H "Ce-Source: sendoff" \
  -H "Content-Type: application/json" \
  -d '{"msg":"Hello Knative! Goodbye Knative!"}'

验证三个事件是否正确地传递给了Subscriber。
通过指定Pod标签来查看hello-display Consumer的日志。

kubectl --namespace event-example logs -l app=hello-display --tail=100
☁️  cloudevents.Event
Validation: valid
Context Attributes,
  specversion: 0.3
  type: greeting
  source: sendoff
  id: say-hello-goodbye
  time: 2019-12-22T16:29:33.624112047Z
  datacontenttype: application/json
Extensions,
  knativearrivaltime: 2019-12-22T16:29:33Z
  knativehistory: default-kne-trigger-kn-channel.event-example.svc.cluster.local
  traceparent: 00-aa8665dc43c912ba58c8d710575a595a-8d6f71f444b08f8f-00
Data,
  {
    "msg": "Hello Knative! Goodbye Knative!"
  }
☁️  cloudevents.Event
Validation: valid
Context Attributes,
  specversion: 0.3
  type: greeting
  source: sendoff
  id: say-hello-goodbye
  time: 2019-12-22T16:29:33.624112047Z
  datacontenttype: application/json
Extensions,
  knativearrivaltime: 2019-12-22T16:29:33Z
  knativehistory: default-kne-trigger-kn-channel.event-example.svc.cluster.local
  traceparent: 00-aa8665dc43c912ba58c8d710575a595a-8d6f71f444b08f8f-00
Data,
  {
    "msg": "Hello Knative! Goodbye Knative!"
  }

我已在日志中确认了接收到类型为”greeting”的事件。我也将确认”log”中的再见显示。

kubectl --namespace event-example logs -l app=hello-display --tail=100
☁️  cloudevents.Event
Validation: valid
Context Attributes,
  specversion: 0.3
  type: not-greeting
  source: sendoff
  id: say-goodbye
  time: 2019-12-22T16:40:36.143572583Z
  datacontenttype: application/json
Extensions,
  knativearrivaltime: 2019-12-22T16:40:36Z
  knativehistory: default-kne-trigger-kn-channel.event-example.svc.cluster.local
  traceparent: 00-12d3b7c70adcc48bc5961cb8bae7576d-20c7194c25944c0b-00
Data,
  {
    "msg": "Goodbye Knative!"
  }
☁️  cloudevents.Event
Validation: valid
Context Attributes,
  specversion: 0.3
  type: greeting
  source: sendoff
  id: say-hello-goodbye
  time: 2019-12-22T16:40:42.28921708Z
  datacontenttype: application/json
Extensions,
  knativearrivaltime: 2019-12-22T16:40:42Z
  knativehistory: default-kne-trigger-kn-channel.event-example.svc.cluster.local
  traceparent: 00-f0c07fbbd7f1f32810c2bdfec1d12ca2-b31ec2a305780987-00
Data,
  {
    "msg": "Hello Knative! Goodbye Knative!"
  }

总结

Knative 是一個由整個行業參與的開源專案,它在 Kubernetes 上建立了一個供應商中立的無伺服器環境。

Serving组件可以使用简单的清单轻松部署应用程序,并根据流量自动扩展。

通过组合事件源(Event source)和事件消费者(Event consumer),可以在Kubernetes上构建基于事件驱动的系统。消费者可以在各种编程语言中实现可寻址或可调用的对象。此外,通过触发器的过滤器,可以将连续发生的事件传播到适当的消费者。

最后,实际上我本想尝试使用 CronJobSource 和 KafkaSource,但是由于对 Istio 的了解不够,无法解决 cluster-local-gateway 的问题,因此无法运行示例应用程序。结果有点遗憾,所以我想把这些作为寒假作业。

另外,我推荐一些适合对 Knative 感兴趣的人阅读的书籍和幻灯片。

    • Knative の歩き方 Kubernetes から Serverless を訪ねて 第 2 版 #技術書典

 

    Knative Serving のイマ @nak3 – OpenShift Meetup Tokyo #7
广告
将在 10 秒后关闭
bannerAds