我开始学习使用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 が作成される

开始进入
按照 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 资源来实现的两个通用接口。
-
- 可寻址的对象可以通过status.address.url字段接收和确认通过HTTP传输的事件。
- 可调用的对象可以接收通过HTTP传输的事件,并将事件转化为0个或1个新事件,以HTTP响应返回。这些返回的事件将以与外部事件源相同的方式进一步处理。
经纪人与触发器

事件注册
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