You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

267 lines
18 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 09 | 搭建私有Serverless基于K8s的Serverless
你好我是秦粤。上节课我向你介绍了云原生基金会CNCF的重要成员K8s它是用于自动部署、扩展和管理容器化应用程序的开源系统。通过实践我们在本地搭建K8s并将“待办任务”Web服务案例部署到了本地K8s上。K8s这门技术我推荐你一定要学习下不管是前端还是后端因为从目前的发展趋势来看这门技术必定会越来越重要。
今天这节课我们就继续学习如何搭建私有的Serverless环境。具体来说我们会在上节课部署本地K8s的基础上搭建Serverless底座然后继续安装组件扩展K8s的集群能力最终让本地K8s集群支持Serverless。
## 搭建Serverless的前提
在开始之前我们先得清楚一个问题之前在基础篇讲Serverless Computing也就是FaaS的时候也有同学提问到“微服务、Service Mesh和Serverless到底是什么关系
这些概念确实很高频地出现在我们的视野不过你不要有压力我在学习Serverless的时候也被这些概念所困扰。我们可以先回顾下微服务我们在用微服务做BaaS化时相信你也发现了微服务中有很多概念和Serverless的概念很接近。
Service Mesh简单来说就是让微服务应用无感知的微服务网络通讯方案。我们可以将Serverless架构的网络通讯也托管给ServiceMesh。通过Service MeshServerless组件可以和K8s集群复杂且精密地配合在一起最终支撑我们部署的应用Serverless化。我们看下下面这张架构图
![](https://static001.geekbang.org/resource/image/9d/31/9d7da97db12f285d62b317c3ff894731.png)
通过图示我们可以清楚地看到Serverless的底层可以基于Service Mesh来搭建。但Service Mesh其实只是实现Serverless网络通讯的其中一种方案除此之外还有RSocket、gRPC、Dubbo等方案而我选择Service Mesh的原因是这套方案是可以基于K8s组件的而且还提供了可视化工具帮助我们理解Serverless的运行机制比如如何做到零启动如何控制灰度流量等等。如果要实践Service Mesh无疑是首选。
## Serverless底座Service Mesh
有人把Kubernetes、 Service Mesh和Serverless技术称为云原生应用开发的三驾马车到现在我估摸着你也理解这其中的缘由了吧。这里我还是要说明下我们后面几节课里引入了很多新名词这些名词我基本都是走马观花大致给你过了下让你有个宏观的了解。有时间的话你还是应该再深入进去看看。
那么言归正传我们现在具体看下Service Mesh的应用原理。
我们在讲微服务的时候只是讲了拆解与合并的理论指导并没有涉及到具体实现。那如果切换到实现的话业界其实就有很多的微服务框架了但大多数都是限定语言的SDK尤其是Java的微服务框架特别多。
而SDK版本的微服务框架其实很重的一块都在于微服务之间网络通讯的实现。例如服务请求失败重试调用多个服务实例的负载均衡服务请求量过大时的限流降级等等。这些逻辑往往还需要微服务的开发者关心而且在每种SDK语言中都需要重复实现一遍。那有没有可能将微服务的网络通信逻辑从SDK中抽离出来呢让我们的微服务变得更加轻量甚至不用去关心网络如何通讯呢
答案就是Service Mesh。我们举例来说明还是用咱们的“待办任务”Web服务来举例。
我们如果把拆解后的应用部署到K8s集群Pod中过程就如下图所示MyApp应用会通过HTTP直接去调用集群内的用户微服务和待办任务微服务。
![](https://static001.geekbang.org/resource/image/a0/00/a0a9b05966e8761be2e72ba4532ec700.png)
但HTTP直接访问带来的安全性问题又怎么办呢如果有人在我们的集群中启动一个BusyBox容器那不就直接可以对我们的用户微服务和待办任务微服务进行攻击了吗还有当我们有多个用户微服务实例时我们又该如何分配流量呢
所以通常我们使用传统微服务架构SDK它里面会有大量的这种逻辑而且有很多策略需要我们自己在调用SDK时去判断开启我们的代码也将会和微服务架构的SDK大量地耦合在一起。
服务网格Service Mesh则是换了一种思路它将微服务中的网络通信逻辑抽离了出来通过无侵入的方式接管我们的网络流量让我们不用再去关心那么重的微服务SDK了。下面我们看看Service Mesh是怎么解决这个问题的。
![](https://static001.geekbang.org/resource/image/d1/75/d11343ddee25c4c28d6df0f822558975.png)
通过图示可知Service Mesh可以分为数据面板和控制面板数据面板负责接管我们的网络通讯控制面板则控制和反馈网络通讯的状态。Service Mesh将我们网络的通讯通过注入一个边车Sidecar全部承接了过去。
数据面板中我们的应用和微服务看上去直接跟Sidecar通信但实际上Sidecar可以通过流量劫持来实现所以通常我们的应用和微服务是无感的不用有任何的改动只是使用HTTP请求数据。
控制面板则复杂一些它也是Service Mesh的运作核心。pilot是整个Control Plane的驾驶员它负责服务发现、流量管理和扩缩容citadel则是控制面板的守护堡垒它负责安全证书和鉴权Mixer则是通讯官将我们控制面板的策略下发并且收集每个服务的运行状况。
现在你应该清楚Service Mesh是怎么回事了它是怎么把微服务的网络通信逻辑从SDK中抽离出来的我们又为什么说Service Mesh就是Serverless的网络通讯底座。
## Serverless底座搭建K8s组件Istio
那接下来我们就要动手搭建Service Mesh了。
首先我们得扩大一下Docker Desktop操作系统的占用资源这是因为我们后面在搭建的过程中需要创建一堆容器。我推荐你将CPUs配置到4核内存至少8GB这是从我的经验出发一个较为合适的参数。
![](https://static001.geekbang.org/resource/image/50/ec/50d7e965a9734aacf39bb7f77e55a1ec.png)
设置好后我们需要用到CNCF的另外一位重要成员Istio\[1\] 了它是在K8s上Service Mesh的实现方式用于连接、保护、控制和观测服务。有关它的详细介绍我们在这就不展开了不清楚的话可以查看我们的参考资料。
Istio的安装脚本我已经放在我们这节课的[Github仓库代码](https://github.com/pusongyang/todolist-backend/tree/lesson09)中了。下面我们要进入根目录下的install-istio这里Istio官方其实提供了Istio 1.4.3 K8s安装包但为了简化大家的操作我直接放到项目代码中了。
然后kubectl apply安装Istio。
```
kubectl apply -f .
```
安装完毕后我们就可以通过命名空间istio-system来查看Istio是否安装成功。
```
kubectl get all -n istio-system
```
相信你也发现了吧这跟我们上节课中安装metrics组件一样Istio也是采用K8s组件Component的方式来安装的。
![](https://static001.geekbang.org/resource/image/a3/b5/a3cadbeebf92e839ac0b4a76c35d26b5.png)
不过使用了Istio以后还有一个细节需要我们注意默认Istio不会开启Sidecar注入这个需要我们手动开启。
我们可以通过给命名空间default打上label让我们部署的Pod自动打开Sidecar。
```
kubectl label ns default istio-injection=enabled
```
Istio会根据我们的标签去判断当前部署的应用是否需要开启Sidecar。
```
kubectl describe ns default
```
![](https://static001.geekbang.org/resource/image/19/42/19d917ec41aa365693d4830d88c0ad42.png)
好了我们现在可以部署我们的应用了。我们将项目目录下的Dockerfile、Dockerfile-rule、Dockerfile-user都用Docker构建一遍并且上传到我们的Registry。
```
docker build --force-rm -t registry.cn-shanghai.aliyuncs.com/jike-serverless/todolist:lesson09 -f Dockerfile .
docker build --force-rm -t registry.cn-shanghai.aliyuncs.com/jike-serverless/rule:lesson09 -f Dockerfile-rule .
docker build --force-rm -t registry.cn-shanghai.aliyuncs.com/jike-serverless/user:lesson09 -f Dockerfile-user .
docker push registry.cn-shanghai.aliyuncs.com/jike-serverless/todolist:lesson09
docker push registry.cn-shanghai.aliyuncs.com/jike-serverless/rule:lesson09
docker push registry.cn-shanghai.aliyuncs.com/jike-serverless/user:lesson09
```
然后修改项目istio-myapp目录中的YAML文件改成你自己仓库中的URI。接着在istio-myapp目录下执行kubectl apply“点”部署所有YAML文件。
```
kubectl apply -f .
```
部署完成后我们通过kubectl describe查看一下MyApp服务暴露的端口号
```
kubectl describe service/myapp
```
![](https://static001.geekbang.org/resource/image/03/97/0331db708282a70543c4c4ef3bcab997.png)
接下来我们用浏览器访问[http://localhost:31947](http://localhost:31947%EF%BC%8C)就可以看到我们新部署的MyApp应用了。
你也许会好奇这跟我们之前的有什么不同Istio都做了什么
我们将Istio的控制面板服务kiali也通过Service暴露到端口上访问一下看看。
```
kubectl expose deployment.apps/kiali --type=NodePort --port=20001 --name='kiali-local' -n istio-system
```
然后查看一下kiali的本地端口号。
```
kubectl describe svc kiali-local -n istio-system
```
接着用浏览器打开kiali进入后你就可以看到我们应用调用微服务的网络拓扑图了。怎么样是不是很惊艳
![](https://static001.geekbang.org/resource/image/c8/7c/c86557779fd349ee8032a62d2bcfc57c.png)
Service Mesh可以协助我们处理应用和微服务网络的连接、保护、控制和观测问题。综上再次总结下Serverless的底层我们完全可以依赖Service Mesh来处理复杂的网络问题。
最后演示完我们就可以通过kubectl delete清除刚刚部署的应用注意要在istio-myapp目录下执行。
```
kubectl delete -f .
```
到这儿部署好Istio Service MeshServerless的底层部署我们就已经实现了一大半。
## Serverless完整实现K8s组件Knative
现在就还剩最后一步安装组件了。如果你能想到在Service Mesh的底座上还需要加上哪些组件才能满足我们Serverless的需求说明你已经真正地理解了K8s的组件Component的威力了。
接下来我们就基于Istio看看Serverless在K8s的架构方案上还需要添加哪些内容。
Knative是通过整合工作负载管理和动态扩缩以及事件模型来实现的Serverless标准也叫容器化Serverless。
Knative社区的主要贡献者有Google、Pivotal、IBM、Red Hat可见其阵容强大。还有CloudFoundry、OpenShift这些PaaS提供商都在积极地参与Knative的建设。参考资料中我还放了“由阿里云提供Knative”的[电子书](https://github.com/cloudnativeapp/meetup/blob/master/Knative%20%E4%BA%91%E5%8E%9F%E7%94%9F%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97.pdf),这里作为福利送给你。
接下来我们看看Knative是怎么做到Serverless的吧。
Knative在Istio的基础上加上了流量管控和灰度发布能力、路由Route控制、版本Revision快照和自动扩缩容就组成了Server集合它还将触发器、发布管道Pipeline结合起来组成了Event集合。
![](https://static001.geekbang.org/resource/image/f9/3d/f963da41d4c978542e685e74c9c5903d.png)
我们近几节课都有操作K8s其实你应该能感觉到它还是有些复杂的。Knative提供的应用托管服务可以大大降低直接操作K8s资源的复杂度和风险提升应用的迭代和服务交付效率。
我们还是以“待办任务”Web服务为例看看Knative是怎么做到的吧。
首先进入项目install-knative目录执行kubectl apply安装Knative。
```
kubectl apply -f .
```
安装完毕你就可以通过命名空间knative-eventing和knative-serving看到我们都安装了哪些组件。
```
kubectl get all -n knative-serving
kubectl get all -n knative-eventing
```
Knative帮我们部署了一套自动运维托管应用的服务因此我们要部署应用才能看到效果。我们再进入项目knative-myapp目录执行kubectl apply。
```
kubectl apply -f .
```
到这我们的应用就部署起来了。要访问应用也比Istio要简单通过get kservice我们就可以查看部署的应用域名。
```
kubectl get ksvc
```
这个域名其实是通过Istio-ingress部署的我们再看看Istio-ingressgateway的信息。
```
kubectl get svc istio-ingressgateway -n istio-system
```
![](https://static001.geekbang.org/resource/image/84/90/84bacc4deff3166b7a444dc5bec12590.png)
我们可以看到它绑定到我们本地IP:localhost上了所以我们只需要添加一下Host绑定我们MyApp的域名到127.0.0.1就可以了。
```
// Mac上host文件地址是/etc/hosts
127.0.0.1 myapp.default.example.com
```
然后我们就可以用浏览器直接访问我们的域名myapp.default.example.com进入“待办任务”Web服务了。如果你多刷新几次页面就可以看到右上角我的名字有时是“秦粤”有时是“粤秦D”。这其实是因为我的user服务开启了2个版本而且我设置了50%的流量进入版本v150%的流量进入版本v2。另外我们还需要在页面上多点击几下这是为了让我们查看Kiali的网络拓扑图时有数据。
目前Knative还没有官方的控制台所以最后我们再看看Istio中Kiali的网络拓扑图如果忘记了如何访问Kiali请看上一节介绍Istio中的内容。
![](https://static001.geekbang.org/resource/image/7f/26/7f2b922b186b38f4eb324967b094de26.png)
你看网络拓扑图是不是变得更复杂了当然因为Knative也是隐藏了一堆的内容来简化应用和微服务的部署。好了实践完我们就再来看看原理请看下图
![](https://static001.geekbang.org/resource/image/f4/1f/f4a79ce34e82d0f72cf5a0ed3153e81f.png)
我来解释下这张图。为了让你更好地识别我用红色表示Knative添加的组件橙色表示Istio的组件。首先我们看到Knative又给每个Pod里面添加了一个伴生容器queue它是专门用来实时采集反馈我们部署应用容器的监控指标metrics的收集到的信息会反馈到autoscale由autoscale决定是否需要扩缩容。
然后我们看看请求从上面的操作我们知道浏览器的HTTP请求是访问Istio-ingressGateway的我们还需要绑定域名。这是因为Istio-ingressGateway是域名网关它会验证请求的域名。我们的MyApp应用接收到请求会调用用户微服务和待办任务微服务这时就会访问activator了这个是Knative比较重要的组件它会hold住我们的请求并查看一下目前有没有活跃的服务如果没有它会负责拉起一个服务等服务启动后再将请求转接过去。这也就是为什么Serverless可以缩容到0的原因。
另外我再啰嗦一下当我们的autoscale配置了可以缩容到0如果一段时间没有请求那么我们每个应用都会处于很低的水位这时autoscale就会缩容到0了。当然我们的MyApp应用不可以缩容到0因为它需要接收入口请求。
当MyApp有流量进来请求用户服务时此时activator就会临时接收请求让请求等待然后通知autoscale扩容到1等待autoscale扩容到1完毕后activator再让用户容器处理等待的请求。
而我们在Knative中每次发布服务都会被Revision组件拍一个快照这个快照可以用于我们管理版本号和灰度流量分配。我们“待办任务”Web服务中的用户微服务的2个版本流量切换其实就是利用Revision和Route实现的。
## 总结
这节课我首先介绍了Service Mesh。Service Mesh通过Pod给我们的应用容器注入了一个伴生容器Sidecar配合控制面板来接管我们应用和微服务的网络流量从而实现网络流量的连接、保护、控制和观测。
接着我介绍了CNCF的另一位重要成员Istio它是基于K8s组件实现的Service Mesh。我们在Istio的基础上又搭建了Knative它也是基于K8s组件实现的一套Serverless方案。
总结来说就是Service Mesh是一种微服务网络通讯方案它通过Sidecar和控制面板接管了上层的网络通讯可以让上层开发者无感知地使用网络通讯。我们可以复用Service Mesh的网络通讯能力部署Serverless架构。通过Service Mesh、Serverless组件和K8s集群复杂且精密地配合支撑我们部署的应用Serverless化。
另外我需要提醒你一下Knative是容器Serverless方案所以你在容器里面运行函数、微服务或者应用都可以这完全取决于你的Dockerfile里面“编排”的是什么内容。我们这节课介绍的Knative可以让你既部署FaaS也部署BaaS。还记得我们[\[第1课\]](https://time.geekbang.org/column/article/224559) 讲过FaaS和BaaS都是基于CaaS实现的Knative正是一种容器服务Serverless化的产物。我们将\[第 1 课\]和这节课的架构图映射一下相信你对Serverless的实现会有新的体会。
![](https://static001.geekbang.org/resource/image/77/64/7771e2621ac14fddad00657a1909c564.jpg)
## 作业
这节课是实战课所以作业就是我们今天课程中的内容。请在上节课安装的K8s集群的基础上先安装Istio组件部署Istio版本的“待办任务”Web服务再安装Knative组件部署Knative版本的“待办任务”Web服务。实践结束后你可以在Docker Desktop中关闭K8s集群这样就能清掉我们这两节课创建的所有资源了。
大胆尝试一下吧,期待你能与大家分享成果。如果今天的内容让你有所收获,也欢迎你把文章分享给更多的朋友。
## 参考资料
\[1\] [https://istio.io/](https://istio.io/)