learn-tech/专栏/Serverless进阶实战课/23实战进阶(四):如何从0到1进阶一个开源引擎?.md
2024-10-16 06:37:41 +08:00

20 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        23 实战进阶如何从0到1进阶一个开源引擎
                        你好,我是静远。

从这节课开始我们就要完整体验一个Serverless平台的构建学习其中的注意事项了。

今天的前半节课我们会从环境准备、组件安装以及组件验证几个角度先重点演练一遍Knative核心组件的部署和验证后半节课我们再去探讨一下在面临一个开源Serverless框架的时候应该怎么去学习。

在实战开始之前你可以先复习一下扩缩容和流量转发这两节课中Knative的知识点再来开始动手实操。

话不多说,我们现在就开始实操准备吧。

环境准备

由于Knative本身完全依赖于Kubernetes因此我们需要提前准备一个Kubernetes集群对于集群的规格官网也有建议你可以参考最新版本来进行。

原型环境方面如果只是搭建一个试用的原型Knative函数环境那么至少需要一个单节点K8s集群并且这个节点的规格至少应该是2核4GB。

如果想达到生产级别的Knative函数环境单节点集群的规格至少应该是6核6G以及30G的磁盘空间多节点环境的规格应该保证每个节点都是2核4G以及20GB的磁盘空间。另外还有几个要点需要注意

必须保证K8s集群版本应该至少是1.22以上; 必须安装K8s的命令行访问工具kubectl 必须保证K8s能够正常访问到镜像仓库。

这节课我会按照生产级别环境搭建其中K8s集群节点数量为2每个节点的规格为4核8G同时配备50GB磁盘空间。我选择了百度智能云CCE来构建K8s集群。

组件部署与验证

准备好K8s集群环境以后我们就可以开始在集群上部署Knative Serving组件了。不要着急我们一步步来。

部署Serving组件

首先我们来部署Serving组件按照官网命令即可

kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.4.0/serving-crds.yaml kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.4.0/serving-core.yaml

部署之后在knative-serving这个namespace下所有的Pod都应该是Running状态

$ kubectl get pod -n knative-serving NAME READY STATUS RESTARTS AGE activator-847d5df4bc-psd7k 1/1 Running 0 47h autoscaler-65898c7fcf-wkkmc 1/1 Running 0 46h controller-775b774844-fwm2k 1/1 Running 0 46h domain-mapping-555ff9b9c8-8dtcw 1/1 Running 0 46h domainmapping-webhook-57456fcb8c-x6frp 1/1 Running 0 46h net-istio-controller-59b6b7765-z8w2g 1/1 Running 0 46h net-istio-webhook-6b98fd5bd4-4pcdf 1/1 Running 0 46h webhook-7f987868-5f8bg 1/1 Running 0 46h

如果出现异常那么可以通过kubectl describe 查看Pod部署卡在哪一步了你可以参考如下的命令查看

kubectl describe pod controller-744577dddc-7jf5r -nknative-serving

注意如果是网络问题可能会出现下载镜像卡住的情况这个时候通过Google Cloud虚机下载镜像上传到可访问的镜像仓库上就可以或者直接找到可供访问的镜像仓库手工替换掉gcr.io的镜像包。

部署网络组件

完成Serving 组件的部署我们就需要部署Knative所依赖的网络组件了。官网推荐了三种我们以Isito来部署。

kubectl apply -l knative.dev/crd-install=true -f https://github.com/knative/net-istio/releases/download/knative-v1.4.0/istio.yaml kubectl apply -f https://github.com/knative/net-istio/releases/download/knative-v1.4.0/istio.yaml kubectl apply -f https://github.com/knative/net-istio/releases/download/knative-v1.4.0/net-istio.yaml

完成之后我们需要检查一下istio-system的namespace下pod是否正常

$ k get pod -nistio-system
NAME READY STATUS RESTARTS AGE istio-ingressgateway-666588bf64-jpwgl 1/1 Running 0 46h istiod-56967d8fcc-d6vmr 1/1 Running 0 46h istiod-56967d8fcc-f46kr 1/1 Running 0 2d2h istiod-56967d8fcc-mmfkd 1/1 Running 0 46h

同时还需要检查一下是否能够获得gateway的external-ip用于后续的函数访问

$ kubectl --namespace istio-system get service istio-ingressgateway NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istio-ingressgateway LoadBalancer 10.0.142.236 182.61.144.199,192.168.0.2 15021:31462/TCP,80:30977/TCP,443:32684/TCP 2d2h

这里需要说明一点百度智能云CCE里如果用户创建类型是LoadBalancer的Service默认情况下CCE会联动的创建BLB并为此BLB绑定EIP。所以你可以看到表格中显示的EIP地址是182.61.144.199。

部署Eventing组件

接着我们再来安装一下Eventing的相关组件。Eventing的安装和Serving类似包括两个yaml文件

kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.4.1/eventing-crds.yaml kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.4.1/eventing-core.yaml

执行完成后我们来检查一下knative-eventing这个命名空间下的pod是否正常运行

$ kubectl get pod -n knative-eventing NAME READY STATUS RESTARTS AGE eventing-controller-c7bdccd6d-ktnmc 1/1 Running 0 13m eventing-webhook-7c49c58fcb-ng576 1/1 Running 0 13m

如果部署成功的话应该可以看到这个命名空间下有两个running状态的pod如果出现Error那么还是按照我们在小试牛刀这节课中讨论的方法通过kubectl的describe命令查看错误原因对症下药即可。

部署完Serving和Eventing你会发现是不是还缺少Tekton组件我把它作为课后作业留给你来进一步演练你可以按照同样的方式进行部署。需要注意的是Tekton是从Knative的Building组件孵化出来的目前功能已经相当丰富涉及的组件较多因此最好根据官网的安装手册一步一步进行部署避免遗漏。

到这里你的Knative组件已经可以正常运行了接下来我们尝试执行一个简单的Demo验证一下。

验证函数调用

我们先验证一下Knative最基本的函数调用功能确保Serving组件能够正常运行。这里我使用的是官网给的helloworld-go的例子进行演示我将handler中的响应内容改成了如下的打印语句“Hello Serverless!”:

func handler(w http.ResponseWriter, r *http.Request) { log.Print("helloworld: received a request")

fmt.Fprintf(w, "Hello Serverless!\n")

}

改完代码之后我们接着进行镜像打包这里需要注意下如果你电脑是mac 的m1需要加上 platform linux/amd64 这个参数,如下所示:

$ docker --platform linux/amd64 build -t jingyuan/hello:v1 .

然后就可以将镜像推送到一个集群可以访问到的仓库中这里我使用的是CCR具体使用可以参照他的说明文档接着我们替换一下knative demo中service.yaml的image然后执行kubectl apply就可以了。

apiVersion: serving.knative.dev/v1 kind: Service metadata: name: helloworld-go namespace: default spec: template: spec: containers: - image: jingyuan/hello:v1 env: - name: TARGET value: "Go Sample v1"

我这里只使用了最简单的一些字段你还可以在Knative Service中设置不同的revision以及流量比例。- 最后会是什么样的效果呢?我们来一起看下:

curl -H "Host: helloworld-go.default.example.com" http://182.61.144.199

返回: Hello Serverless!

这一步如果你观察仔细的话会发现在default 命名空间下在调用之前是不存在helloworld-go函数的相关pod的而在调用之后产生了这其实就是第一次调用产生的冷启动现象了。

验证事件触发

最后我们再来验证一下Knative的事件触发功能确保Eventing的组件确实能够正常工作。这里我们采用官网给出的PingSource模型进行验证我们首先创建一个用于接收事件的服务名为 event-display。这里采用的是官网的Demo它的deployment和service定义如下

apiVersion: apps/v1 kind: Deployment metadata: name: event-display namespace: default spec: replicas: 1 selector: matchLabels: &labels app: event-display template: metadata: labels: *labels spec: containers: - name: event-display image: gcr.io/knative-releases/knative.dev/eventing/cmd/event_display


kind: Service apiVersion: v1 metadata: name: event-display namespace: default spec: selector: app: event-display ports:

  • protocol: TCP port: 80 targetPort: 8080

接着创建一个 PingSource 对应:

apiVersion: sources.knative.dev/v1 kind: PingSource metadata: name: test-ping namespace: default spec: schedule: "*/1 * * * *" contentType: "application/json" data: '{"message": "Hello geek!"}' sink: ref: apiVersion: v1 kind: Service name: event-display

它的含义是根据Schedule定义的规则每分钟发送一次data指定的数据发送的目的端是Sink.name指定的服务 event-display 即我们前一步定义的Service。

接着我们可以查看一下knative-eventing命名空间下的deployment会发现多了一个pingsource的deployment这就是事件的生产者会根据 PingSource 对象的定义,产生事件并发送给对应的服务。

$ kubectl get deploy -nknative-eventing NAME READY UP-TO-DATE AVAILABLE AGE eventing-controller 1/1 1 1 3h36m eventing-webhook 1/1 1 1 3h36m pingsource-mt-adapter 1/1 1 1 3h36m

然后我们查看刚才部署在default 命名空间下的pod ,并查看他的日志,可以看到每分钟打印的消息:

$ kubectl logs event-display-685f8f46f4-k5dbw
2022/10/07 01:06:32 Failed to read tracing config, using the no-op default: empty json tracing config ☁️ cloudevents.Event Context Attributes, specversion: 1.0 type: dev.knative.sources.ping source: /apis/v1/namespaces/default/pingsources/test-ping id: caa8d0f9-f3bb-4c4d-a777-749241a9cb32 time: 2022-10-07T06:52:00.301997865Z datacontenttype: application/json Data, { "message": "Hello geek!" } ☁️ cloudevents.Event Context Attributes, specversion: 1.0 type: dev.knative.sources.ping source: /apis/v1/namespaces/default/pingsources/test-ping id: a029df47-fd4b-4eb4-986d-75d6dd9814d0 time: 2022-10-07T06:53:00.001908595Z datacontenttype: application/json Data, { "message": "Hello geek!" } ☁️ cloudevents.Event Context Attributes, specversion: 1.0 type: dev.knative.sources.ping source: /apis/v1/namespaces/default/pingsources/test-ping id: b4331a71-031f-4b3a-93cb-8764a0c5830e time: 2022-10-07T06:54:00.309563083Z datacontenttype: application/json Data, { "message": "Hello geek!" }

到这里我们就完成了Knative最基本的函数调用以及事件触发功能的验证我们来总结一下。Knative的安装大致分为环境准备、组件部署以及功能验证三个部分这也是安装任意一个Serverless框架的基本思路。

环境准备你需要在安装之前就在官网的安装手册上了解清楚相关内容尤其要注意对资源、操作系统、依赖版本的限制一旦没有注意到可能往往问题到安装的最后一步才会暴露出来费时费力。比如我们这个版本的Knative就明确要求K8s的版本要达到1.22以上并且多节点集群下单节点的规格也至少是2核4G。 组件部署:这里我建议直接参照官方的安装手册进行,因为网上的很多技术博客写的并不全面,并且版本可能和你计划安装的也有差别。同时也要注意一些镜像的访问问题,受限于网络问题,需要你自己去寻找国内的镜像源进行替换才能成功。 功能验证完成组件安装后最重要的还是进行功能验证这一步我建议不要一上来就选用复杂的case测试而是先用最简单的demo去验证看看基本的功能有没有问题保证基本的工作流程以后再验证扩展功能这样不仅可以让你在最短的时间内验证完组件的部署正确性还能够降低问题的排查难度。

如何学习一个开源serverless引擎

结合之前的理论学习和本节课的实操相信你对Knative已经有了一个比较全面的认识了但我相信在你之后搭建Serverless引擎的过程中还会遇到Knative更多的特性。课程中我们不可能面面俱到不过像是Knative中 Triggers、Sinks、CLI等知识点你完全可以通过我们总结的方法举一反三的学习起来。

什么意思呢?

拿到一个开源框架,我的攻坚三步曲是“上手体验”“研究架构”“深入源码”,我们逐一探讨下。

上手体验

在接触到任何一个开源Serverless引擎时我并不建议一开始就通过网上的资料去学习各种概念、架构原理以及源码分析这对于刚接触到引擎框架的你来说几乎没有任何帮助这些繁杂的概念反而会让你一头雾水。

相反,上手体验永远应该放在你学习一个框架的第一步。使用带来的直观感受能够帮助你更快地理解这个框架最基本的产品形态以及工作过程。

这节课我并没有上来就给你长篇介绍Knative的各种概念而是直接手把手带你体验了从搭建Knative到跑起来两个demo的整个过程动手之后你基本就清楚整个Knative运作需要哪些组件同时也能观察到一些现象比如冷启动、事件触发过程了。这样的操作过程会让你后续的学习更加高效。

另外有一个小技巧。很多成熟的框架在官网除了常规的搭建步骤还提供了非生产线部署的Quick Start 部署方式比如我们这节课要学习的knative quick start可以帮你更快地体验。

研究架构

在体验完简单的功能之后,我们再回过头来去学习基本的概念,就很容易结合前面的操作去理解了。在这个阶段,我建议你采用由全貌到细节的一个学习思路,先学习基本概念以及整体的架构,接着再深入到某一个具体的处理流程或者组件中去进行探究。

在体验过Knative 之后我们再去学习Serving中一些组件的交互过程你就很容易理解了。就像这一块核心逻辑我之前已经提前带你学习过了但在这节课实操之后再回过头看一看是不是就更好理解了呢?

比如冷启动过程我们通过动手操作发现了第一次请求函数时实例会从无到有并且隔一段时间不请求后实例又会被释放这就很容易让你明白Activator的作用以及AutoScaler的扩缩容流程。

再来Knative中的事件驱动机制。我们部署了一个用于打印事件的服务以及一个定时产生事件的PingSource对象接着我们可以观察到定时事件的任务处理。这个时候我们再去从宏观的角度学习Eventing就可以明白为什么他是一套松耦合的事件驱动体系了。

在Eventing的事件驱动的体系中你可以通过API进行事件的创建而事件将由事件的生产者发起由事件的消费者接收。系统学习过后你也可以知道我们的Demo是多种事件驱动模式中最简单的一种——Source&Sink模式。

其中Source声明的是事件源与事件接受者的一个关联关系而Sink表示一个可以接收事件的对象。结合前面的实践过程你一定可以理解我们部署的PingSource对象就是一个Source类型event-display就是一个事件接收的服务。再去细究PingSource的yaml可以看到Shedule定义的是定时触发的cron表达式而sink.name表示事件将交由event-display处理。

apiVersion: sources.knative.dev/v1 kind: PingSource metadata: name: test-ping namespace: default spec: schedule: "*/1 * * * *" contentType: "application/json" data: '{"message": "Hello geek!"}' sink: ref: apiVersion: v1 kind: Service name: event-display

这样下来理解起来是不是就很迅速了呢学习了这种最基础的事件驱动模型你还可以再进一步去学习Channel&Subscription 以及 Broker&Trigger 这两个较为复杂的模型,通过这样的动手实践,由浅入深,不仅可以让你学得更快,还能记得更牢。

深入源码

对整体的架构以及关健处理流程有了一个清晰的认知以后,就可以深入到源码当中去了,源码能够帮助你更好地理解引擎框架设计者的思路。这其实是一个更高的要求,需要你对框架开发语言以及代码编写风格都有一定的掌握,才能够更好的理解。

源码的阅读途径也比较丰富除了官方文档以外开源的框架代码基本都会放在Github对于一些较难理解的地方也可以通过阅读博客以及社区留言等方式去解决。

以Knative为例Github包含了它全套的代码除了Eventing和Serving这两个核心组件代码库还包含客户端、一些使用文档等对于一些像扩缩容一类的关键流程有些Contributor甚至还会专门给出详细的流程说明可以说是非常全面了。遇到问题的时候Issue也能给你一定程度的参考。除此之外Knative的官方也有一些不错的技术博客这些都是能帮助你深入到源码的有效手段。

最后在学习Serverless框架时提前的知识储备也很重要比如学习Knative之前你至少需要对K8s的基本概念有一些了解而涉及到源码部分还需要掌握Golang的基本语法等等。

这些学习经验其实并不局限于本节课Knative的学习希望你更能够举一反三灵活运用到之后遇到的每一个新框架的学习过程中去。

小结

今天这节课你应该对Knative的核心组件有了更直观的了解Serving提供了管理整个Serverless工作负载的能力Knative Serving 通过自定义的一组特定对象,使用 Kubernetes CRD自定义资源的方式配合上Activator以及AutoScaler等关键组件从而实现了整个流量控制和自动扩缩容能力。

Knative Eventing实际上是一组API能够帮助你使用事件驱动的方式对函数进行调用。Source与Sink模型是Knative中最简单的事件驱动模型除此之外还有Channel&Subscriptions和Broker&Trigger。前者提供了一种事件传输机制能把事件分发给多个目的地或Sink。后者跟前者类似但不同的是后者更方便对事件进行过滤适用于一个 namespace 下的多个事件源,然后消费者根据事件的类型、属性挑选感兴趣的事件进行消费。

Tekton组件主要负责从代码仓库获取源码并编译成镜像推送到镜像仓库所有这些操作都是在Kubernetes Pod中进行的。

整体而言Tekton补充的是Knative快速开发与集成的能力提升了开发与部署的效率Eventing则通过松耦合的构造以及对CloudEvents的支持极大地丰富了Knative 的应用场景Serving组件贯穿到了整个Serverless容器的生命周期。

最后我想跟你说的是Serverless的技术是在不停更迭的我们需要灵活地研究一个新发布的Serverless引擎、框架、平台你可以通过直接上手体验再去研究架构原理最后深入源码细节升华的方式进行学习。

课后作业

好了,这节课到这里也就结束了,最后我给你留了一个课后作业。

你可以继续尝试搭建一下Knative Tekton并且看看能否通过Trigger搭建一个完整的CI/CD系统如果有问题欢迎你在留言区和我交流讨论。

感谢你的阅读,也欢迎你把这节课分享给更多的朋友一起阅读。