白话Kubernetes核心组件及原理

Kubernetes是什么?

Kubernetes其实就是一个集群,从我们此前运维的角度来理解,它就是一个集群,组合多台主机的资源(内存、CPU、磁盘等)整合成一个大的资源池并统一对外提供计算存储等能力的集群。我们找很多台主机,每台主机上面安装Kubernetes的相关程序,而不同的主机程序之间相互通信,从而完成彼此之间的协调,并且通过这些应用程序之间的协同工作,把多个主机当成一个主机来使用,形成一个集群,仅此而已,但是在kubernetes集群当中主机是分角色的,即所谓的有中心结点架构的集群系统,master/nodes模型, 由一组节点用于master不需要太多,一般高可用的话需要三个(根据集群规模来判断),nodes节点(worker节点)就是干活的,Kubernetes上提供的各种资源服务,运行在node节点上面。

用户如何在kubernetes运行容器,逻辑过程是什么?

用户把创建启动容器的请求首先发给master,具体来说是发给了master节点上面的API Server组件, API Server通过调度器,按照预设的调度算法及策略,去分析各node节点上面现有的可用资源状态,然后找一个最佳适配,来运行用户所请求容器的结点,并把它调度上去(这里需要与node节点上面的kubelet交互),并由这个node节点上面本地的docker或其它容器引擎负责把这个容器启动起来,要启动容器,需要有镜像,镜像在哪里呢?在仓库上面,node上面启动容器是会先检查本地是否有镜像(根据镜像拉取策略),如果没有会docker pull下来,然后再启动,kubernetes自身并没有托管自动所依赖的每一个容器镜像,而是需要到仓库中去下载的,仓库可以是私有的,也可以是公有的。

集群在master节点上面提供一个API Server组件,它负责接受请求,解析请求,处理请求的,至于当用户请求的是创建一个容器,最好不要运行在master节点上面,而应该运行在node节点上面,确定哪个node更合适,这个时间就需要scheder调度器,它负责监控每个node节点上面总可用的计算、内存、存储等资源,并根据用户所请求创建的这个容器所需要的资源,docker容器可以做资源限制 ,但在kubernetes上面不但可以设定容器使用资源的上限(阀值),还可以设定资源使用的下限(资源请求量),调度器就是根据容器的最低需求来进行评估,哪一个节点最合适;当然了,资源的评估不只是一个维度,而是从多个维度考虑,这都是调度器scheduler根据调度策略和算法需要考虑的,如果在一个node上面把容器启动起来了,我们还需要对容器中的应用程序的健康状态做监测,我们不但能根据容器中应用程序是否运行判断它的健康状况,还可以根据额外的健康状态探测方式来探测,我们叫做可用性探测机制来探测服务的可用性。如果一旦容器中的应用挂了,我们又需要确保容器中的一个容器要运行,此时怎么办?node节点之上有一个应用程序,这个应用程序就是kebulet,这个应用程序确保容器始终处于健康状态,如果出现问题,它就会通知API Server,然后重新调度;但是有一点,很不幸,这个node节点如果宕机了,那么此前拖管在此node上面的所有容器就挂了,我们知道kubernetes具有自愈的能力,无论是单个容器,还是node节点上面的所有容器,一旦容器不见了,是不需要人工参与的,kubernetes会使用新的个体来取代它,它会在其它node上面创建出来一模一样的容器出来。如何确保这个容器是健康的呢?以及一旦出问题就可以及时被发现呢? 其实kubernetes是通过控制器组件来负责监控它所管理的每一个容器的健康状态,一旦发现不健康了,控制器向master上面 API server发请求,容器挂了一个,你帮我重新调度再启动一个,这里控制器需要在本地不停的loop循环,周期性,持续性的探测所管理的容器的健康状况,一旦不健康,或者不符合用户所定义(期望)的目标,此是调度器就会向用户期待的状态向前移,确保符合用户期望的状态;其实在kubernetes集群中我们有很多很多的控制器,假设我们有一个控制器挂了呢,用于监控容器健康的控制器不健康了,容器的健康状态就无法保证,怎么办?我们在master节点上面有一个控制器管理器,控制器管理器负责控制监控每个控制器的健康状况,控制器管理器如果出现问题怎么办,因此在这里,我们需要对控制器管理器做冗余。

以上我们通过在集群上面创建一个容器的例子,讲解了API Serverscheduler控制器控制管理器等。

什么是Pod?

Pod,英文意思是豆荚。大家都知道这种植物,一个豆荚中有几个豆粒。

Kubernetes上面运行的最小单元是Pod, kubernetes并不直接调度容器的运行,而调度的目标是PodPod可以理解为容器的外壳,给容器做了一层抽象的封装,因此Pod成为了Kubernetes集群之上最小的调度单位(逻辑单元),Pod内部主要是用来放容器的,Pod有一个特点,一个Pod中可以运行多个容器,多个容器共享同一个底层的网络命名空间(底层的net, uts, IPC三个网络命名空间),另外三个命名空间相互隔离(User, mnt, pid),这样一来,同一个Pod上面的多个容器,每个容器上面跑应用程序 ,对外更像是同一台“虚拟机”,这也是kubernetes组织容器的一个非常精巧的办法,基于此我们可以构建较为精细的容器间通信,并且同一个Pod上面的容器,还共享第二种资源,叫做存储卷,存储卷不属于容器,属于PodPod的磁盘,相同Pod的容器共享。

各个node节点主要是用来运行Pod的,一般说来,一个Pod上面只放一个容器,除非有特别紧密的关系,需要放在同一个Pod上面,否则,不要放在同一Pod上面;如果确实有需要,将多个容器需要放在一个Pod中,通常有一个容器是主容器,其它的容器为辅助主容器,辅助容器中的应用程序主要是为了完成更多功能来辅佐主容器工作,这里我们调度器也是调度的Pod, node节点上面也是Pod, Pod是一个原子单元,也就意味着一个Pod中有一个容器,还是有多个容器,一旦被调度之后,相同Pod上面的容器,只能在同一个node节点上面。

创建Pod时,可以直接创建,并且自主管理的,但它仍然要提交给API Server,由API Server接收以后,通过调度器调度到指定的Node节点上,而node节点启动此Pod,此后如果pod上面的容器出现故障,需要重要重启容器,需要kubelet完成,但是node节点故障了,节点就消失了。还有一种Pod的创建方式,是通过控制器来创建的,后面为讲什么是控制器,它的作用是什么?

Node节点是做什么的?

刚才说了node是kubernetes集群中的工作节点,负责运行由master节点上面指派的各种任务,而最核心的任务是以Pod的形式运行容器的,理解上讲node可以是任何形式的资源设备,只要有传统意义上的内存、CPU、存储资源即可,并且可以安装上Kubernetes集群的应用程序 ,都可以做为k8s集群的一个份子来工作,它是承载资源的。

这样一来终端用户不需要关心应用程序(Pod)在哪个Node节点上面,它就这样脱离了终端用户的视线,终端用户也无需关注应用程序部署在哪个node节点上面的pod,从而真正意义上实现了资源池,从而进行统一管理。

什么是标签,标签选择器是做什么的?

如何让一个控制器管理指定的Pod,例如,我们创建了5个Pod,Pod中运行tomcat容器,我们让一个控制器来管理这一组Pod,为了让Pod能够实现被控制器管理识别,我们需要在Pod上面附加一些元数据(标签),用标签来识别Pod,在创建Pod的时候,或者人为的打上一个标签,让控制器能够识别出标签,进而识别出Pod。我们前面创建了5个 Pod,我们在每一个pod上面加一个标签app, 标签的值叫tomcat (标签:值====> app:tomcat),我们想把这一类找出来,怎么找,我们先找拥有key是app,并且值是tomcat的pod分拣出来。标签是Kubernetes大规模集群管理、分类、识别资源使用的,标签是非常非常重要的凭证,我们是如何把我们感兴趣的标签找到的呢,我们有一个标签选择器/挑选器(selector)组件,标签选择器,简单来讲就是根据标签,过滤符合条件的资源对象的机制,其实标签不只是Pod有,很多其它资源都有,因此这种选择器叫做标签选择器,而不叫pod标签选择器,Kubernetes是Restfull 风格的API,通过http或者https对外提供服务,所以所有Restfull对外提供的服务资源都称为对象,所有的对象都可以拥有标签,所有的标签都可以使用标签选择器来选择,只不过pod是其中一种比较重要的。

什么是控制器,控制器是做什么的,有哪些控制器?

我们刚才讲Pod的时候 ,讲到了创建Pod时,一种是直接创建Pod,Pod删除后,不会自动创建,还有一种创建Pod的方式,是通过控制器创建的Pod,这种控制器管理的Pod, 正是控制器管理器机制的使用。在Kubernetes设计中,Pod完全可以叫做有生命周期的对象,而后由调度器将其调度至集群中的某节点,运行以后,任务终止也就被删除停掉了,但是有一些任务,比如nginx,或者运行一个tomcat,他们是做为守护进程来运行的,这种程序,我们要确保这种pod随时运行,一旦出现故障,需要第一时间发现,要么取代它,要么重启它,要么重建一个新的pod,这种靠人的右眼是无法保证的,而Kubernetes提供了具备这种工作能力的组件叫Pod控制器

Pod控制器最早的一种叫ReplicationController (副本控制器,早期版本的控制器,也称为Pod控制器), 当我们启动一个pod时,一个不够了,可以再启动一个副本,控制器就是控制同一类资源对象的副本,一旦副本数量少了,就会自动加一个,能够定义要求的副本数,多了就删除,精确符合人们期望的数量,它还可以实现滚动更新,它允许临时添加副本,然后把旧版本的去掉,实现滚动更新;它也允许回滚操作,后来的版本中新加了ReplicaSetController(副本集控制器),而ReplicaSetController也不直接使用,而是有一个声明式更新的控制器叫Deployment,用它来进行管理控制, 我们使用的最多的也是Deployment控制器,而Deployment控制器只能管理哪些无状态的应用,哪么有状态的应用如何控制管理呢?我们使用新的控制器,叫StatefulSet有状态副本集,另外如果我们需要在每个node上面运行一个Pod,而不是随意运行,我们还需要一个DaemonSet,如果我们运行作业,还需要Job, 周期性作业,Cronjob, 这些是常见的Pod控制器;后面的这些控制器都是实现一种特定的应用管理,比如临时运行一个容器去完成删除日志的功能,这个运行完就删除了,我们就可以使用Job控制器管理Pod, 但是如果Job没有运行完挂了,需要重新启动,如果运行完,就删除了,再比如nginx一直需要处于运行状态,就不能使用Job控制器,所以说这么多的控制器是用于确保不同类型的Pod资源,来符合用户所期望的方式来运行,像Deployment控制器还支持二级控制器,叫HPA,叫水平Pod,自动伸缩控制器,比如我们一个控制器控制两个副本在运行,但在资源利用率高的时候,可以自动的伸缩控制,就是使用HPA进行控制,一旦利用率低了,可以自动减少,但要符合我们预期的最小值。

Serveice是什么,为什么需要Service?

到这里我们想到一个问题,Pod是由生命周期的,万一Pod所在Node节点宕机了,Pod有可能需要在其它的 Node节点上面重新创建,而重新创建完成后的pod,跟之前的不是一个,只不过是应用程序一样而已,提供相同的服务,由于每个pod中容器的IP地址就不一样,这样一来就有一个问题,我们客户端怎么去访问这些Pod呢?是利用服务发现机制,首先客户端每一次去访问服务时,客户端是不知道后端的服务是谁的(不知道pod的存在),他需要找一个地方问一句,发现一下,有没有这种服务,这些服务是Pod启动的时候注册到一个类似总线地址上,客户端直接去总线位置去问,有没有,有的话,给一个Pod地址,客户端与Pod地址进行通信;因此尽可能降低这种复杂度,Kubernetes为每一组提供相同功能的Pod和客户端之间添加了一个中间层,这个中间层是固定的,这个中间层就叫service,只要service不删除,它就是固定的,名称也是固定的,当客户端需要访问时,只需要在客户端写上service 主机名|服务器|地址即可,也不需要发现,而这个服务service 是一个调度器,不但提供一个固定稳定的访问入口,只要不删除,它就是稳定的,客户端只需要写service名称即可,服务再把请求代理到后面的pod上面,那么Pod宕机了,新创建的pod会被service立即给关联进来;还会把新加的pod作为service后面的可用资源对象之一,怎么实现的呢?我们知道 客户端与服务器通信是通过IP:Port或者域名:Port形式,而service与后面的pod不是依靠IP:Port的形式(因为pod的主机名和IP经常要变),而是通过Pod上面固定的标签来识别,只要是相同标签的pod,不管主机名和IP怎么变,都会被service通过标签识别,service是通过标签选择器来关联pod的;这样一来,只要pod属于这个标签选择器,就能立即被service能选中,并且做为service后端组件存在,关联进来以后,再动态探测这个pod的IP地址是什么,端口是什么,并做为自己后端可调度的服务器,资源对象, 最后,客户端是通过service代理至后端pod进行通信;意味着客户端看到的地址就是service的地址,而在kubernetes集群上service可不是什么应用程序 ,也不是一个实体组件,它只不过是一个iptables DNAT规则;我们创建一个DNAT 规则 ,我们所有到达xxx地址的,都统统被目标地址转换成yyy地址,DNAT规则只是一个规则 ,而service地址,事实上并没有配置到任何一张网卡上,是不存在的,它仅仅出在规则中,可以ping通的,并且可以做请求中转,能ping通是因为有TCP/IP协议栈。这个IP地址,仅出现在规则中,更重要的是service做为Kubernetes中的对象来讲,它有名称,就相当于这个服务的名字,名称可以被解析,就是把service名称解析成IP,名称解析靠DNS,没错,我们安装完k8s后,第一件事,就是让部署一个DNS Pod,以确保service被解析,这个Pod是Kubernetes自身的服务就需要的pod,所以我们称之为基础性的系统架构级的pod或对象,而且称他们叫集群的附件。

集群附件DNS

DNS附件只是Kubernetes集群中纵多附件中的一个,并且这种DNS有一个很有意义的特点,可以动态的创建,动态的改变,动态的更新,动态的变动,比如,你更新了service名称,DNS中的记录即就会被改变,再比如我们手动修改了service IP ,他会自动触发DNS 服务中的解析记录的更改,所以以后客户端访问service时,可以直接访问服务的名称 ,而由集群中专门的DNS服务来负责解析,解析的是service的地址,不是pod地址,再由service代理访问pod,刚才也说了,这种代理是端口代理,由DNAT实现,可不能忘记service后面中两个或者多个pod,这里的DNAT就是多个目标了,多目标调度,对于linux来讲,大家知道对于iptables来讲,已经把负载均衡的功能主要交给了IPVS,因此如果service背后的同一个服务有多个Pod,并且由DNAT来实现,可能在调度效果上并不尽人意,因此在 Kubernetes 1.11版本以后,已经把iptables规则改成了IPVS规则,也就相当于,当你创建一条service规则时,就创建了一条IPVS规则 ,只不过是NAT模型的IPVS规则,因此还支持用户可以指定任意的调度算法,如轮询、加权、最小连接等,所以你就会发现LVS是我们的基础性服务,以上就是我们所讲的service组件。

来源:Linux点滴运维实践

-------------本文结束感谢您的阅读-------------