导读:经过前面的一系列文章你已经知道如何基于 kubectl 来操做 Knative 的各类资源。可是若是想要在项目中集成 Knative 仅仅使用 kubectl 这种命令的方式是不够的,还须要在代码中基于 Knative Serving SDK 进行集成开发。本篇文章中,阿里云智能事业群技术专家冬岛将从 Knative Serving SDK 入手,介绍如何基于 Knative SDK 进行 serverless 开发。html
在正式开始介绍 Knative Serving SDK 以前,咱们先简单介绍一下 Golang Context 的机理。由于在 Knative Serving 中 client、Informer 的初始化和信息传递彻底是基于 Golang Context 实现的。git
Golang 是从 1.7 版本开始引入的 Context ,Golang 的 Context 能够很好的简化多个 goroutine 之间以及请求域间的数据传递、取消信号和截至时间等相关操做。Context 主要有两个做用:github
Context 自己是一个接口。golang
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
这个接口中定义了四个方法,下面分别介绍:编程
关于 Context 主要记住一点:能够经过 Value 传递数据,Value 是一个键值对结构。更多详细的介绍能够参见下面这些文章:api
在 Context 的这些特性中,Knative Serving 中重度依赖的是 Value 功能。以 Service 的 Informer 初始化为例进行说明,这里能够看到源码。安全
Informer “构造函数”是在 init 函数中自动注册到 injection.Default 中的。当 Informer “构造函数”被调用以后会自动把生成的 Informer 注入到 Context 中 context.WithValue(ctx, Key{}, inf), inf.Informer()
。app
从上图中能够看到:Informer 初始化的时候须要调用 factory,而 factory 自己是从 Context 中获取的。下面咱们再看看 factory 是怎么初始化的。factory 的初始化。less
上图能够发现,factory 也是把“构造函数”注册到 injection.Default 中,并会将生成的 SharedInformerFactory 注入到 Context 中。并且 factory 中使用的 client (连接 kube-apiserver 使用的对象)也是从 Context 获取到的。函数
能够说 Knative Serving SDK 初始化的过程是面向 Context 编程的。关键对象是自动注入到 Context,在使用的时候从 Context 中取出。
顺带提一点,Knative Serving 的日志对象也是在 Context 保存的,当须要打印日志的时候先经过 logger := logging.FromContext(ctx)
从 Context 中拿到 logger,而后就可使用了。这样作的好处是能够经过管理 logger 对象,好比作 trace 功能。
以下所示是基于 logger 打印出来的日志,能够看到对于同一个请求的处理是能够经过 traceID 进行追踪的。下面这段日志都是对 577f8de5-cec9-4c17-84f7-f08d39f40127
这个 trace 的处理。
{"level":"info","ts":"2019-08-28T20:24:39.871+0800","caller":"controller/service.go:67","msg":"Reconcile: default/helloworld-go","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"} {"level":"info","ts":"2019-08-28T20:24:39.871+0800","caller":"controller/controller.go:339","msg":"Reconcile succeeded. Time taken: 487.347µs.","knative.dev/traceid":"90653eda-644b-4b1e-8bdb-4a1a7a7ff0d8","knative.dev/key":"eci-test/helloworld-go"} {"level":"info","ts":"2019-08-28T20:24:39.871+0800","caller":"controller/service.go:106","msg":"service: default/helloworld-go route: default/helloworld-go ","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"} {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:67","msg":"Reconcile: eci-test/helloworld-go","knative.dev/traceid":"22f6c77d-8365-4773-bd78-e011ccb2fa3d","knative.dev/key":"eci-test/helloworld-go"} {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:116","msg":"service: default/helloworld-go revisions: 1 ","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"} {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:118","msg":"service: default/helloworld-go revision: default/helloworld-go-cgt65 ","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"} {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/controller.go:339","msg":"Reconcile succeeded. Time taken: 416.527µs.","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"} {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:106","msg
介绍完 Knative Serving client 的初始化过程,下面咱们看一下应该如何在代码中用 Knative Serving SDK 进行编码。
示例参见:https://github.com/knative-sample/serving-controller/blob/v0.1/cmd/app/app.go
这个示例中首先使用配置初始化 *zap.SugaredLogger
对象,而后基于 ctx := signals.NewContext()
生成一个 Context。signals.NewContext()
做用是监听 SIGINT 信号,也就是处理 CTRL+c 指令。这里用到了 Context 接口的 Done 函数机制。
接着使用 ctx, informers := injection.Default.SetupInformers(ctx, cfg)
构造出全部的 informer,而后调用下面这段代码执行注入,把 informer 注入到 Context 中。
// Start all of the informers and wait for them to sync. logger.Info("Starting informers.") if err := controller.StartInformers(ctx.Done(), informers...); err != nil { logger.Fatalw("Failed to start informers", err) }
实例代码: https://github.com/knative-sample/serving-controller/blob/v0.1/pkg/controller/controller.go
如上所示,全部的 informer 都是从 Context 中获取的。
最后 Controller 初始化一个 Reconciler 接口,接口的定义以下, 里面只有一个 Reconcile 函数。这个使用方式和 sigs.k8s.io/controller-runtime
使用的逻辑是同样的。若是你以前写过 Operator 之类的功能,对这个操做应该不会陌生。
// Reconciler is the interface that controller implementations are expected // to implement, so that the shared controller.Impl can drive work through it. type Reconciler interface { Reconcile(ctx context.Context, key string) error }
代码示例: https://github.com/knative-sample/serving-controller/blob/v0.1/pkg/controller/service.go
如今就能够在 Reconcile 中经过 c.serviceLister.Services(namespace).Get(name)
这种方式直接操做 Seving 资源了。
至此,咱们已经把基于 Knative Seving 开发 Serverless 应用的关键脉梳理了一遍。更详细的代码示例请参见:https://github.com/knative-sample/serving-controller ,这里面有完整能够运行的代码。
本文从 Knative Serving client 的初始化过程开始展开,介绍了 Knative informer 的设计以及使用方法。经过本文你能够了解到:
本文做者:牛秋霖(冬岛)
本文为云栖社区原创内容,未经容许不得转载。