History of Kubernets
K8s的前身google Borg
最近看了Large-scale cluster management at Google with Borg,Borg是谷歌公司自己内部开发的一套集群管理系统,后来开源社区也就慢慢开始孵化和推出了Kubernets集群管理系统,k8s和Borg有着很深的渊源关系。
先从Kubernets入手,我们来看下K8s的组件:
-
Kubelet
在每一台节点上都会运行Kubelet,管理节点上的容器。
-
kube-apiServer
验证和配置关于Pods,Services,ReplicationControllers等数据,其他kube都需要通过调用api来获取所需的数据。
-
kube-Controller-Manager
用于控制系统的状态,通过API接口来同步系统数据,比如将系统从A状态同步更新到B状态。
-
kube-proxy
运行在每一台的节点上,主要作用充当网络代理,能够简单的处理TCP UDP 转发和轮询。
-
kube-scheduler
用来调度Pods,当有新的Pods需要创建时,调度器会给每一台工作节点打分,然后将Pods分配到满足要求的节点上。
然后再看关于Borg系统的描述,每一个集群都称之为一个Cell,每一个Cell都运行在一个独立的数据中心里。
Borg 架构
Borg 设计准则主要是以下三点:
-
隐藏对系统底层资源的管理以及容错处理,方便开发者将注意力集中在开发上,而不过多注意底层细节。
-
系统中运行的应用都是高可用的,某台节点的宕机,不会引起服务的不可用。
-
必须非常高效的在数万台节点上完成对应用的扩展。
Borg的用户基本上是google内部开发人员和SRE,正是因为google注重对SRE的发展,才慢慢在google内部形成了上述三点的认知体系,提到google SRE不得不提GOOGLE SRE How Google Runs Production Systems这本书,我只看过中文版,看完英文版之后会写一篇博客专门详细讨论这本书里的内容。
集群设计架构
Borg系统有一套自己的配置文件语法体系BCL,然后通过borgcfg加载到基于Paxos的分布式数据库存储中,在k8s中我们是通过编写Yaml状态配置文件,然后使用kubectl将状态配置通过kube-api接口提交到后端分布式ETCD存储中,在边缘设施的k8s场景中,像k3s,microk8s会默认使用基于SQL的数据库引擎Dqlite,dqlite也采用了Raft一致性存储协议,这样就可以做到边缘设备上的k8s高可用。
在Borg系统中,SRE工程师大部分通过Borgctl命令行工具对系统进行访问和修改。
Borg的核心组成部分有Scheduler,BorgMaster,Paxos Datastore,LinkShard,Borglet。
BorgMaster: master分为两部分,第一部分是对外处理请求,比如RPC调用获取集群状态,任务分配信息,获取borglet的状态,第二部分是用于处理集群内部的任务调度模块,master 支持高可用,所有的数据都是通过基于Paxos的数据库进行存储,当前master leader宕机之后,master 副本中会选举出新的master节点继续对外提供服务。最引人注目的是,master节点的数据存储使用了CheckPoint技术,每隔一段时间,系统都会自动保存当前的状态到checkpoint中,这样在集群发生问题的时候,我们就能将集群回退到前一个正常的checkpoint的状态,并且在线下使用这些point来做debug。
BorgScheduler: 当任务通过master提交之后,master会将数据存储到paxos中,并且将任务放到待处理队列中,Scheduler会根据这些任务的优先级进行先后处理,Scheduler拿到任务之后,计算哪些机器是符合要求的,根据一定的打分算法给这些机器打分,再根据分数将任务分配到对应的节点上。
Borglet: 运行在每一台节点上,用于管理任务的生命周期,比如停止运行重启,维护操作系统的内核配置,borgmaster会定期去拉取let上的状态信息,如果let没有应答那么master就认为这个对应节点已经下线或者宕机。
Jobs&&Tasks: Borgs运行的对象以Jobs Tasks来进行管理,一个Job可以由多个Tasks组成。以下是任务的生命周期。
当任务被提交到Borg Master时,如果任务被master接受则会进入Pending状态,等待Scheduler进行调度,调度完成之后,组成job’s Tasks就开始运行,如果超出系统的资源限制,Tasks就会被Evicted,然后进入Pending状态,继续等待被调度到新的节点上,当Tasks运行结束,任务的状态会被更新为Dead,有点像进程的生命周期,整个主干过程就是 Create -> Pending -> Scheduling -> Runing -> Dead。
然后我们看下k8s整个架构以及任务的调度是如何完成的,可以与Borg对比,发现两者的优缺点。
K8s 架构
Control Plane:
-
kube-apiServer -> 所有对数据的请求处理存储都需要通过api来完成
-
kube-scheduler -> 通过一定的算法找出符合当前要求的机器,并为这些节点,最后将任务分配到其中一台满足要求的节点上,将数据通过api记录到etcd中
-
kube-controller-manager -> 从API接口获取到任务信息,监控任务状态能够接近预期的定义,如果发现不符合预期,则会通知API接口对任务进行创建或者销毁
-
etcd -> 分布式存储系统
Node:
-
Pods -> k8s中最小的任务运行单元
-
Container runtime engine -> 后端容器引擎,比如Docker,Containerd
-
kubelet -> 会和API接口通信,管理自身节点上任务的生命周期
-
kube-proxy -> 设置节点路由信息,使得任务应用能够对外被访问
从中我们不难发现kubernets有点像Borg的升级版本,比如Borg使用了Linux choot jail来隔离任务运行环境,k8s则采用了更先进的容器技术来隔离运行环境。k8s的组建拆分的更加细致,把主服务master拆分成了多个微服务,这样能够更有效的提高master节点的请求处理能力。
总结
google内部使用Borg已经有很长的时间,Borg开发者意识到Borg本身会有很多缺陷,于是他们在设计k8s的时候,都会试图去改进这套集群管理体系的架构,使得这套系统更加好用,这就是目前k8s越来越流行的原因,我个人认为k8s还是面向开发者的,运维可能会去处理集群的部署和管理,但是要高效的在运维和开发之间将k8s投入到生产环境,我们需要做很多自定义的开发,使得集群自身的运作方式符合公司内部的业务架构,比如自动的处理应用故障,自动扩展节点,这些都是和业务逻辑相关,只考虑运维层面是没办法发挥k8s的强大集群管理能力的,所以阅读Borg这篇论文,我们可以更好的将论文中说到的优缺点考虑到自己的生产系统中,然后按照自身的逻辑,量身打造这套系统。
备注
部署集群
kubeadm.yaml
/etcd-server.crt
keyFile: /etc/etcd/etcd-server.key
imageRepository: 192.168.1.114:5000
kind: ClusterConfiguration
kubernetesVersion: v1.19.1
networking:
dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12
podSubnet: 10.244.0.0/16
scheduler: {}~
升级集群
upgrade master node
kubeadm upgrade apply v1.20.0
upgrade worker node
sudo apt-mark unhold kubeadm kubelet kubectl
sudo apt-get update
sudo apt-get install kubeadm kubelet kubectl
kubectl drain <node-to-drain> --ignore-daemonsets
sudo kubeadm upgrade node
sudo systemctl daemon-reload
sudo systemctl restart kubelet
kubectl uncordon <node-to-drain>
sudo apt-mark hold kubeadm kubelet kubectl