K8s环境中部署各种开发应用服务(一)有状态服务的准备工作——科普
写在前面
首先,这是一篇在接下来的工作开展之前值得一看的科普类文章。纯文字,寓教于乐,长本领,学知识。
开始
正式进入到K8s的环境中,我们要为后面的开发工作提前部署一系列的服务,例如 Redis和MQ服务。
这里就涉及到一个概念,无状态服务和有状态服务
两种服务之间的差异,最直白来讲,就是实例(跑起来的一个服务)需不需要在本地做持久化存储数据。
这里举一个例子用白话来说明一下K8s容器大工厂的运作方式
首先,整个K8s集群就好比是一个大工厂,也就是我们总说到的一个云。当然,在这个世界观下也存在着同样的其他工厂,如果涉及到多个工厂的协作,这就是跨云。我们目前仅关注自家工厂里面的事情。
接下来,当前K8s集群下的每一台主机节点(node),就比作是一个车间。
车间中运作的Deployment,Statefulset,可以看做是生产任务
生产任务下在干活的每一个节点(pod),就是工人了
在整个工厂中,不同的车间内可以混杂着不同生产任务中的工人在干活,反正工人只是使用车间的设备(性能资源)就可以,哪里有资源就去那里(其实是哪里有资源,克隆工人就出生在哪里),也因此引出了节点(容器)的漂移这个概念。
再回到无服务和有服务这里
最典型的无状态服务,就是我们开发部署时,拉代码,打成jar包(各种包)或镜像发布到服务器或容器内去跑的程序。这些服务内部的配置文件都一致,它们所需的各种持久化数据都来源于外部。
这样的实例就满足容器这个大工厂的设计理念:
一个无状态服务的生产任务下的工人都是一模一样的克隆人(pod),谁倒下了立刻拖出去,同时生出来一个新的克隆人顶上去继续干活就是了。工厂在运转的时候会关注每个车间的运转情况(负载压力),以此对生产任务进行扩招(扩容)和裁员(缩容)。
在K8s工厂中,为这种类型的生产任务提供了 ReplicaSet,ReplicationController,Deployment等几种资源对工人们(pods)进行控制管理,对pod发放随机的工牌(序号),同时对pod的扩容缩容处理也是随机的。
也就是说,无状态服务的应用控制起来很简单,直接扔给K8s集群就完事儿了。
而有状态服务就高级了一点点,例如MySQL,Redis,各种MQ一类的。这些服务的生产任务上,每一个pod在工作中都需要有自己独立的持久化存储。这些存储内容,不仅仅是数据,还包括这条生产任务上的工人们之间的合作经验:例如redis集群中多个节点之间的交互方式。
这样一来这些克隆人也就具备了自己的唯一性和不可替代性。毕竟每个人身上都带着独立的数据内容和生产任务中的工作经验,随便撤下一个人整个生产任务就停滞了,随便顶上一个不会干活的新人也无济于事。这样的话,这条生产任务上的扩容缩容就不能随便应对了。
于是,K8s大工厂为这种有状态服务提供了专门的控制资源Statefulset,以及一系列的解决方案。首先,Statefulset控制的每一名工人(pod)都具备唯一的工牌(固定序号),并且给每一名指定工牌(序号)的工人分配了PVC(持久卷声明)指向了指定的PV(持久卷)。从此,这条生产任务中的工人(pod)身上的所有持久化存储内容就被抽离出来统一保存了。这就好比是把工人身上的数据和脑子都交给工厂保管。最后,Statefulset的生产任务还会指定一个干活专用数据和脑子的领取方案,也就是VCT(volumeClaimTemplates持久卷声明模板)。
最终,这条生产任务上工作的pods,就又变成了无状态化的“无垢”克隆人了。生产任务 部署和扩容缩容的时候,这些克隆人先去被分配一个指定的工牌(固定序号),然后去这个工牌(序号)指定(PVC持久卷声明)的地方(PV持久卷)领脑子领数据,开始干活。如果这个pod挂掉了,也是直接被拖走,再来一个新人拿着他的工牌(序号)继续去PV持久卷领脑子领数据,继续干活。
前面说了工厂中有不同的车间,也就是K8s集群内的不同的节点主机。同时提到了工人们会在不同的车间中到处乱蹿(旧节点的死去和新节点的出生不一定在同一个主机中),也就是节点漂移。
那么如果在某个车间中,一个正在有状态服务的生产任务下干活的工人挂掉了,而新出生来顶替他的工人却出生在了另一个车间里,那么他究竟应该去哪里领数据领脑子呢?
这里就回到Statefulset继续说明一下它机制。
首先,我们知道了要想管理持久化数据,需要PV,PVC。
PV,PVC是可以手动部署的,K8s管理员手动部署Deployment这种生产任务并且指定专属的PV,PVC,同时这些PV,PVC也是独立唯一针对每一个节点(pod)的。
那么问题就来了,由于PV,PVC的部署是非自动的,那么Deployment中的节点(pod)的扩容缩容也就失去了灵活性。
在某些有状态服务的场景下,我们对节点(pod)的扩容缩容没有要求,一个典型的例子就是,部署了6个Deployment服务启动6个pod来做3主3从的redis集群,这种情况看似手动搞一下也并不麻烦。
然而问题又出现了,6个节点(pod)分布在了不同的主机节点(node)上,当节点漂移出现时,如果先前节点使用的PV,PVC资源仅存在于之前那台主机上的时候,就取不到数据了。
Statefulset之所以是用来专门应对有状态服务,自然有它的一套办法。
在Statefulset控制的生产任务中,我们前面知道了它的节点(pod)序号是固定的,这里要说一下,这些序号同时也是自动生成的。
随之而来的,节点使用的PV,PVC也是在Statefulset任务部署是自动生成的。Statefulset控制下的节点(pod)死去的时候,它对应的PVC以及该PVC所绑定的PV并不会被直接删除(具体在什么时候删除PV,这里涉及到持久卷的回收机制的配置,后面会提到)。这样一来,无论节点(pod)如何死去和重生(重新生一个),他们拿着固定的序号,依旧会顺着原来的PVC从原先的PV持久卷里面去使用对应的持久化内容。
最后需要说的是,为Statefulset任务提供自动部署PV,PVC这一功能的服务是provisioner。这部分会根据具体采用的持久化存储方案而有所不同,一些云厂商会提供存储配套的provisioner以及相应的StorageClass(存储类)服务。
然而对于一路刀耕火种的我们来说,当然是要自己来搭建这么一套网络存储服务了。
下一章节将正式开始搭建自己的存储服务——NFS