gitbook/Serverless入门课/docs/232303.md
2022-09-03 22:05:03 +08:00

267 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 08 | 搭建私有Serverless(一):K8s和云原生CNCF
你好,我是秦粤。上节课我们只是用Docker部署了index.js,如果我们将所有拆解出来的微服务都用Docker独立部署,我们就要同时管理多个Docker容器,也就是Docker集群。如果是更复杂一些的业务,可能需要同时管理几十甚至上百个微服务,显然我们手动维护Docker集群的效率就太低了。而容器即服务CaaS,恰好也需要集群的管理工具。我们也知道FaaS的底层就是CaaS,那CaaS又是如何管理这么多函数实例的呢?怎么做才能提升效率?
我想你应该听过Kubernetes,它也叫K8s(后面统一简称K8s),用于自动部署、扩展和管理容器化应用程序的开源系统,是Docker集群的管理工具。为了解决上述问题,其实我们就可以考虑使用它。K8s的好处就在于,它具备跨环境统一部署的能力。
这节课,我们就试着在本地环境中搭建K8s来管理我们的Docker集群。但正常情况下,这个场景需要几台机器才能完成,而通过Docker,我们还是可以用一台机器就可以在本地搭建一个低配版的K8s。
下节课,我们还会在今天内容的基础上,用K8s的CaaS方式实现一套Serverless环境。通过这两节课的内容,你就可以完整地搭建出属于自己的Serverless了。
话不多说,我们现在就开始,希望你能跟着我一起多动手。
## PC上的K8s
那在开始之前,我们先得把安装问题解决了,这部分可能会有点小困难,所以我也给你详细讲下。
首先我们需要安装[kubectl](https://kubernetes.io/zh/docs/tasks/tools/install-kubectl/),它是K8s的命令行工具。
你需要在你的PC上安装K8s,如果你的操作系统是MacOS或者Windows,那么就比较简单了,桌面版的Docker已经自带了[K8s](https://www.docker.com/products/kubernetes);其它操作系统的同学需要安装[minikube](https://kubernetes.io/zh/docs/tasks/tools/install-minikube)。
不过,要顺利启动桌面版Docker自带的K8s,你还得解决国内Docker镜像下载不了的问题,这里请你先下载[第8课的代码](https://github.com/pusongyang/todolist-backend/tree/lesson08)。接着,请你跟着我的步骤进行操作:
1. 开通阿里云的[容器镜像仓库](https://cr.console.aliyun.com/);
2. 在阿里云的容器镜像服务里,找到镜像加速器,复制你的镜像加速器地址;
3. 打开桌面版Docker的控制台,找到Docker Engine。
```
{
"registry-mirrors" : [
"https://你的加速地址.mirror.aliyuncs.com"
],
"debug" : true,
"experimental" : true
}
```
4. 预下载K8s所需要的所有镜像,执行我目录下的docker-k8s-prefetch.sh,如果你是Windows操作系统,建议使用gitBash\[1\];
```
chmode +x docker-k8s-prefetch.sh
./docker-k8s-prefetch.sh
```
5. 上面拉取完运行K8s所需的Docker镜像,你就可以在桌面版Docker的K8s项中,勾选启动K8s了。
**现在,K8s就能顺利启动了,启动成功后,请你继续执行下面的命令。**
查看安装好的K8s系统运行状况。
```
kubectl get all -n kube-system
```
查看K8s有哪些配置环境,对应~/.kube/config。
```
kubectl config get-contexts
```
查看当前K8s环境的整体情况。
```
kubectl get all
```
按照我的流程走,在大部分的机器上本地运行K8s都是没有问题的,如果你卡住了,请在留言区告知我,我帮你解决问题。
## K8s介绍
安装完K8s之后,我们其实还是要简单地了解下K8s,这对于你后面应用它有重要的意义。
我想你应该知道,K8s是云原生Cloud Native\[2\] 的重要组成部分(目前,K8s的文档\[3\]已经全面中文化,建议你多翻阅),而云原生其实就是一套通用的云服务商解决方案。在我们的前几节课中,就有同学问:“如果我使用了某个运营商的Serverless,就和这个云服务商强制绑定了怎么办?我是不是就没办法使用其他运营商的服务了?”这就是服务商锁定vendor-lock,而云原生的诞生就是为了解决这个问题,通过云原生基金会CNCF(Cloud Native Computing Foundation)\[4\],我们可以得到一整套解锁云服务商的开源解决方案。
那K8s作为云原生Cloud Native的重要组成部分之一,它的作用是什么呢?这里我先留一个悬念,通过理解K8s的原理,你就能清楚这个问题,并充分利用好K8s了。
我们先来看看K8s的原理图:
![](https://static001.geekbang.org/resource/image/70/f8/7084735b118636816d1284a80e67d0f8.png)
通过图示我们可以知道,PC本地安装kubectl是K8s的命令行操作工具,通过它,我们就可以控制K8s集群了。又因为kubectl是通过加密通信的,所以我们可以在一台电脑上同时控制多个K8s集群,不过需要指定当前操作的上下文context。这个也就是云原生的重要理念,我们的架构可以部署在多套云服务环境上。
在K8s集群中,Master节点很重要,它是我们整个集群的中枢。没错,Master节点就是Stateful的。Master节点由API Server、etcd、kube-controller-manager等几个核心成员组成,它只负责维持整个K8s集群的状态,为了保证职责单一,Master节点不会运行我们的容器实例。
Worker节点,也就是K8s Node节点,才是我们部署的容器真正运行的地方,但在K8s中,运行容器的最小单位是Pod。一个Pod具备一个集群IP且端口共享,Pod里可以运行一个或多个容器,但最佳的做法还是一个Pod只运行一个容器。这是因为一个Pod里面运行多个容器,容器会竞争Pod的资源,也会影响Pod的启动速度;而一个Pod里只运行一个容器,可以方便我们快速定位问题,监控指标也比较明确。
在K8s集群中,它会构建自己的私有网络,每个容器都有自己的集群IP,容器在集群内部可以互相访问,集群外却无法直接访问。因此我们如果要从外部访问K8s集群提供的服务,则需要通过K8s service将服务暴露出来才行。
### 案例:“待办任务”K8s版本
现在原理我是讲出来了,但可能还是有点不好理解,接下来我们就还是套进案例去看,依然是我们的“待办任务”Web服务,我们现在把它部署到K8s集群中运行一下,你可以切身体验。相信这样,你就非常清楚这其中的原理了。不过我们本地搭建的例子中,为了节省资源只有一个Master节点,所有的内容都部署在这个Master节点中。
还记得我们上节课构建的Docker镜像吗?我们就用它来部署本地的K8s集群。
我们通常在实际项目中会使用YAML文件来控制我们的应用部署。YAML你可以理解为,就是将我们在K8s部署的所有要做的事情,都写成一个文件,这样就避免了我们要记录大量的kubectl命令执行。不过,K8s也细心地帮我们准备了K8s对象和YAML文件互相转换的能力。这种能力可以让我们快速地将一个K8s集群中部署的结构导出YAML文件,然后再在另一个K8s集群中用这个YAML文件还原出同样的部署结构。
我们需要先确认一下,我们当前的操作是在正确的K8s集群上下文中。对应我们的例子里,也就是看当前选中的集群是不是docker-desktop。
```
kubectl config get-contexts
```
![](https://static001.geekbang.org/resource/image/91/bc/910eae7773766239c81400991d4568bc.png)
如果不对,则需要执行切换集群:
```
kubectl config use-context docker-desktop
```
然后需要我们添加一下拉取镜像的证书服务:
```
kubectl create secret docker-registry regcred --docker-server=registry.cn-shanghai.aliyuncs.com --docker-username=你的容器镜像仓库用户名 --docker-password=你的容器镜像仓库密码
```
这里我需要解释一下,通常我们在镜像仓库中可以设置这个仓库:公开或者私有。如果是操作系统的镜像,设置为公开是完全没有问题的,所有人都可以下载我们的公开镜像;但如果是我们自己的应用镜像,还是需要设置成私有,下载私有镜像需要验证用户身份,也就是Docker Login的操作。因为我们应用的镜像仓库中,包含我们的最终运行代码,往往会有我们数据库的登录用户名和密码,或者我们云服务的ak/sk,这些重要信息如果泄露,很容易让我们的应用受到攻击。
当你添加完secret后,就可以通过下面的命令来查看secret服务了:
```
kubectl get secret regcred
```
另外我还需要再啰嗦一下,secret也不建议你用YAML文件设置,毕竟放着你用户名和密码的文件还是越少越好。
做完准备工作,对我们这次部署的项目而言就很简单了,只需要再执行一句话:
```
kubectl apply -f myapp.yaml
```
这句话的意思就是,告诉K8s集群,请按照我的部署文件myapp.yaml,部署我的应用。具体如下图所示:
![](https://static001.geekbang.org/resource/image/f9/31/f92f4b5abd24b1e25ea269d1c2528b31.png)
通过获取容器的运行状态,对照上图我粗略地讲解一下我们的myapp.yaml文件吧。
* 首先我们指定要创建一个service/myapp,它选中打了"app:myapp"标签的Pod,集群内访问端口号3001,并且暴露service的端口号30512。
* 然后我们创建了部署服务deployment.apps/myapp,它负责保持我们的Pod数量恒久为1,并且给Pod打上"app:myapp"的标签,也就是负责我们的Pod持久化,一旦Pod挂了,部署服务就会重新拉起一个。
* 最后我们的容器服务,申明了自己的Docker镜像是什么,拉取镜像的secret,以及需要什么资源。
现在我们再回看K8s的原理图,不过这张是实现“待办任务”Web服务版本的:
![](https://static001.geekbang.org/resource/image/a1/4c/a11f760ad9e3d8ba42da0d878a61a04c.png)
首先我们可以看出,使用K8s仍然需要我们上节课的Docker发布流程:build、ship、run。不过现在有很多云服务商也会提供Docker镜像构建服务,你只需要上传你的Dockerfile,就会帮你构建镜像并且push到镜像仓库。云服务商提供的镜像构建服务的速度,比你本地构建要快很多倍。
而且相信你也发现了,K8s其实就是一套Docker容器实例的运行保障机制。我们自己Run一个Docker镜像,会有许多因素要考虑,例如安全性、网络隔离、日志、性能监控等等。这些K8s都帮我们考虑到了,它提供了一个Docker运行的最佳环境架构,而且还是开源的。
还有,既然我们本地都可以运行K8s的YAML文件,那么我们在云上是不是也能运行?你还记得前面我们留的悬念吧,现在就解决了。
通过K8s,我们要解开云服务商锁定vendor-lock就很简单了。我们只需要将云服务商提供的K8s环境添加到我们kubectl的配置文件中,就可以让我们的应用运行在云服务商的K8s环境中了。目前所有的较大的云服务商都已经加入CNCF,所以当你掌握K8s后,就可以根据云服务商的价格和自己喜好,自由地选择将你的K8s集群部署在CNCF成员的云服务商上,甚至你也可以在自己的机房搭建K8s环境,部署你的应用。
到这儿,我们就部署好了一个K8s集群,那之后我们该如何实时监控容器的运行状态呢?K8s既然能管理容器集群,控制容器运行实例的个数,那应该也能实时监测容器,帮我们解决扩缩容的问题吧?是的,其实上节课我们已经介绍到了容器扩缩容的原理,但并没有给你讲如何实现,那接下来我们就重点看看K8s如何实现实时监控和自动扩缩容。
## K8s如何实现扩缩容?
首先,我们要知道的一点就是,K8s其实还向我们隐藏了一部分内容,就是它自身的服务状态。而我们不指定命名空间,默认的命名空间其实是default空间。要查看K8s集群系统的运行状态,我们可以通过指定namespace为kube-system来查看。K8s集群通过namespace隔离,一定程度上,隐藏了系统配置,这可以避免我们误操作。另外它也提供给我们一种逻辑隔离手段,将不同用途的服务和节点划分开来。
![](https://static001.geekbang.org/resource/image/fb/13/fb15835cb57a3e7d587aa2d401123213.png)
没错,K8s自己的服务也是运行在自己的集群中的,不过是通过命名空间,将自己做了隔离。这里需要你注意的是,这些服务我不建议你尝试去修改,因为它们涉及到了K8s集群的稳定性;但同时,K8s集群本身也具备扩展性:我们可以通过给K8s安装组件Component,扩展K8s的能力。接下来我先向你介绍K8s中的性能指标metrics组件\[5\]。
我的代码根目录下已经准备好了metric组件的YAML文件,你只需要执行安装就可以了:
```
kubectl apply -f metrics-components.yaml
```
安装完后,我们再看K8s的系统命名空间:
![](https://static001.geekbang.org/resource/image/34/ff/34fe0c9268d5913607fd25464227efff.png)
对比你就能发现,我们已经安装并启动了metrics-server。那么metrics组件有什么用呢?我们执行下面的命令看看:
```
kubectl top node
```
![](https://static001.geekbang.org/resource/image/f0/08/f0e5f436d48c47899ebcdf5045ed2a08.png)
安装metrics组件后,它就可以将我们应用的监控指标metrics显示出来了。没错,这里我们又可以用到上一讲的内容了。既然我们有了实时的监控指标,那么我们就可以依赖这个指标,来做我们的自动扩缩容了:
```
kubectl autoscale deployment myapp --cpu-percent=30 --min=1 --max=3
```
上面这句话的意思就是,添加一个自动扩缩容部署服务,cpu水位是30%,最小维持1个Pod,最大维持3个Pod。执行完后,我们就发现会多了一个部署服务。
```
kubectl get hpa
```
![](https://static001.geekbang.org/resource/image/56/2d/56a7a23302c7feabf3df14ae341bf52d.png)
接下来,我们就可以模拟压测了:
```
kubectl run -i --tty load-generator --image=busybox /bin/sh
$ while true; do wget -q -O- http://10.1.0.16:3001/api/rule; done
```
这里我们用一个K8s的Pod,启动busybox镜像,执行死循环,压测我们的MyApp服务。不过我们目前用Node.js实现的应用可以扛住的流量比较大,单机模拟的压测,轻易还压测不到扩容的水位。
![](https://static001.geekbang.org/resource/image/1e/76/1eaa1c830583326cb0bd5fa919270c76.png)
## 总结
这节课我向你介绍了云原生基金会CNCF的重要成员:Kubernetes。**K8s是用于自动部署、扩展和管理容器化应用程序的开源系统。**云原生其实就是一套通用的云服务商解决方案。
然后我们一起体验了在本地PC上,通过Docker desktop搭建K8s。搭建完后,我还向你介绍了K8s的运行原理:K8s Master节点和Worker节点。其中,Master节点,负责我们整个K8s集群的运作状态;Worker节点则是具体运行我们容器的地方。
之后,我们就开始把“待办任务”Web服务,通过一个K8s的YAML文件来部署,并且暴露NodePort,让我们用浏览器访问。
为了展示K8s如何通过组件Component扩展能力,接着我们介绍了K8s中如何安装使用组件metrics:我们通过一个YAML文件将metrics组件安装到了K8s集群的kube-system命名空间中后,就可以监控到应用的运行指标metrics了。给K8s集群添加上监控指标metrics的能力,我们就可以通过autoscale命令,让应用根据metrics指标和水位来扩缩容了。
最后我们启动了一个BusyBox的Docker容器,模拟压测了我们的“待办任务”Web服务。
总的来说,这节课我们的最终目的就是在本地部署一套K8s集群,通过我们“待办任务”Web服务的K8s版本,让你了解K8s的工作原理。我们已经把下节课的准备工作做好了,下节课我们将在K8s的基础上部署Serverless,可以说,实现属于你自己的Serverless,你已经完成了一半。
## 作业
这节课是实战课,所以作业就是我们今天要练习的内容。请在你自己的电脑上安装K8s集群,部署我们的“待办任务”Web服务到自己的K8s集群,并从浏览器中访问到K8s集群提供的服务。
另外,你可以尝试一下,手动删除我们部署的MyApp Pod。
```
kubectl delete pod/你的pod名字
```
但你很快就会发现,这个Pod会被K8s重新拉起,而我们要清除部署的MyApp的所有内容其实也很简单,只需要告诉K8s删除我们的myapp.yaml文件创建的资源就可以了。
```
kubectl delete -f myapp.yaml
```
快来动手尝试一下吧,期待你也能分享下今天的成果以及感受。另外,如果今天的内容让你有所收获,也欢迎你把它分享给身边的朋友,邀请他加入学习。
## 参考资料
\[1\] [https://gitforwindows.org/](https://gitforwindows.org/)
\[2\] [https://www.cncf.io/](https://www.cncf.io/)
\[3\] [https://kubernetes.io/zh/](https://kubernetes.io/zh/)
\[4\] [https://github.com/cncf/landscape](https://github.com/cncf/landscape)
\[5\] [https://github.com/kubernetes-incubator/metrics-server/](https://github.com/kubernetes-incubator/metrics-server/)