本文是笔者在学习kubernetes时的学习笔记,主要是为了理清自己对k8s架构的认识,文章将会注重基础知识的降解!会先介绍几个k8s中重要的概念,然后先从k8s的整体架构出发,再逐一分析各个部件,最后对各个部件之间的具体协作做出分析
目录
k8s中的重要术语概念
·k8s整体架构的组成部分
·k8s中各个部件的分析
第一部分:k8s中的重要概念
一.Pod
Pod是k8s最重要也是最基本的概念,pod是集群中调度和操作的最小单元,每个pod中可以有多个容器(一般来说,一个为主容器,其他为辅助容器)。每个pod中都有一个被称为根容器的“pause”容器,Pause容器对应的镜像属于Kubernetes平台的一部分,除了Pause容器,每个Pod中包含一个或多个紧密相关的用户业务容器
(1)为什么需要Pause这个根容器?
下面是kubernetes官网给出的解释
it's part of the infrastructure. This container is started first in all Pods to setup the network for the Pod.
--------
Pause容器是pod的基础设施,这个容器在所有的pod中首先启动,以用来设置网络用于pod间的通信
首先给出一个pod的重要特点,pod中的所有容器是共享同一个网络栈的(一个Pod一个IP地址),即假设pod中运行了两个容器,假设容器A想要需想要与容器B进行通信,那么他只需要将目的地址设置为本地的loopback接口(127.0.0.1)即可。那么为什么pod可以实现多个容器之间共享网络栈呢?
其实pod实现共享网络栈的基础就是Pause容器和namespace(命名空间)。Pod中所有的容器共享使用Pause容器UTS(主机名称)、IPC(进程间通信)、NET(网络名称空间)、USER(隔离用户所用)这四个命名空间,而剩余的两个命名空间MNT(挂载),PID(进程号)被隔离开来(有关命名空间的问题这里不再讲解,附上一个Pod中几个共享命名空间的讲解Pod中共享的命名空间)另附上详细讲解pause的文章
(2)为什么Kubernetes会设计出pod的概念并且其特殊的结构
原因之一:便于更好的获取一组服务的状态,如果Pause容器挂掉了就说明整个pod完全挂掉了
原因之二:Pod里的业务容器,可以共享Pause容器的IP,共享Pause容器挂接的Volume,这样即简化了密切相关的业务之间的通信问题,同时很好的解决了他们之间文件共享的问题
(3)Pod的类型
在k8s中Pod有两种类型
第一种:静态Pod,它并不存放在etcd存储里,由Controller统一进行管理,而是存放在某个Node中的某个具体文件中,只能在本地Node中创建
第二种:普通的Pod,这种Pod一般由Controller创建并管理,可以由controller实现Pod的上线,下线和转移等。这种Pod存放在etcd存储里。该Pod一旦被创建,就会被放入etcd中,随后被调度器Schedular调度到指定的node中,并进行绑定,随后该Pod被Node节点上的kubelet进程实例化成一组相关的Docker容器并启动起来,在默认的情况下,当Pod里的某个容器停止时,Kubernetes会自动检测这个问题并重新启动这个Pod(重启Pod里的所有容器)如果Pod所在的Node宕机,那么整个Pod被移动到别的Node节点进行部署
(3)Master,pod,docker之间的关系
二.Label(标签)和Selector
Label是k8s中另一个重要的概念,其本质是用来表示一个资源对象,以便可以通过标签选择器对某一类资源兑现进行操作。一个Label是一个key=value的键值对,其中key和value由用户自己指定,Label可以附加到各种资源对象上(例如Node,Pod,Service,RC等),一个资源对象可以定义任意数量的Lable,同一个Lable也可以定义在多个资源对象上,Label通常在资源对象定义时确定,也可以在对象创建后动态添加或者删除
(1)Label和Selector
三.资源清单
1.Replication Controller(简称RC)
简单讲,RC定义了我们期望看到的一个场景,即声明在任何时候,某一个Pod的副本应该存在多少个,如果副本数量高于我们设置的数量,则由RC将多出的部分删除,如果副本数量小于我们所期望的,那么就增添Pod副本
RC需要定义的有以下几部分
·Pod期待的副本数
·用于筛选目标Pod的Label Selector
·当Pod的副本数量小于预期数量时,用于创建新Pod的Pod模板
当我们定了一个RC并提交到了k8s集群中的master中以后,Mater节点上的Controller-Manager组件就得到通知,定期巡检系统中当前存活的目标Pod,并确保目标Pod实例的数量刚好等于期望值。通过RC,相当于实现了k8s集群的高可用
注意:删除RC并不会影响到该RC已经创建号的Pod,为了删除所有Pod,可以设置RC中副本的数量为0,然后更新RC,也可以使用命令删除指定的Pod
2.Replica Set
他与RC存在的唯一区别是,Replica Sets支持基于集合的Label Selector,而RC只支持基于等式的Label Selector,这使得RS的功能比RC要好,一般RS不会单独使用,它主要被下面我们将要介绍的Deployment所调用实现更加强大的功能
3.Deployment
Deploment是为了更好的实现对Pod的编排而引入的概念,为此Deployment在内部使用了RS来实现目的,无论从功能还是实现方式上都可以看作Deployment是RC的一次大升级
Deployment相对于RC一个最大的升级就是我们可以随时知道当前Pod部署的进度,
Deplyment的典型使用场景
·创建一个Deployment对象来生产对应的RS并完成对Pod副本的创建过程
·检查Deployment的状态来看部署动作是否完成(Pod副本的数量是否达到预期的值)
·更新Deployment以创建新的Pod(镜像升级)
·如果当前Deployment不稳定,则回滚到一个早先的版本
·挂起或恢复一个Deployment
四.Service(服务)
Service也是k8s中最核心的资源对象之一,k8s中的每个服务其实就是我们经常提起的微服务架构中的一个“微服务”,可以认为Service作为一组服务的访问入口,即假设我们想要访问nginx服务,我们要先访问代表nginx服务的这个Service,再通过Service找到后端真正的提供服务的Pod
(1)Pod,Service,RC之间的关系
上图想表达的意思是
·RC可以通过Label对Pod进行操作(创建,删除等),以保证Pod的数量始终保持在我们想要看到的数量
·整个k8s集群中,有些服务要调用某些服务,此时就有前端后端的概念,如果前端的服务要访问到后端的服务,首先去找Service,然后Service再将对后端的请求转发给后端的Pod,Service类似于iptables或者ipvs,前端的服务器访问的服务的IP地址是Service的地址。(Service的地址是虚拟地址,真是不存在的,非常明显的一个论证,是无法ping通指定Service的地址的)
(2)k8s中的三种IP
在进行服务发现的分析之前,先介绍一下k8s中的三种IP
·Node IP :Node节点的地址,物理网卡的地址真实存在的网络
·Pod IP:Pod的IP地址,通常是一个虚拟的网络,是Docker Engine根据docker0网桥的IP地址段进行分配的
·Cluster IP:Service的地址,是一个虚拟的地址
这里我们着重介绍一下,Service的Cluster IP
·Cluster IP仅仅作用于Kuberneters Service这个对象,并由Kubernetes管理和分配IP地址(来源于Cluster IP地址池)
·Cluster IP地址无法被Ping通,因为没有一个实体网络对象来响应
·Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备TCP/IP通信的基础,他们属于集群内部的一个概念,如果外部想要进行访问,需要一些特殊的处理
(3)服务发现
发现某个服务,有两种方法一种是手动在本地创建对应的项,另一种是通过DNS服务器动态的来获取服务的地址
第二部分.k8s整体架构的组成部分
一.k8s架构的组成部分
整个k8s的架构可以分为一下三个部分
·master节点:负责管理整个k8s集群
·node节点:服务实际部署的地方
·addon:一些额外的插件,帮助k8s更好的实现某些功能,比如帮助实现网络的Flannel等
master中包含如下的组件
kube-apiserver:apiserver可以说是整个k8s集群的大脑,所有的对集群资源的各种操作,都要经过api-server
kube-controller-manager:控制器管理器,管理调控监管整个集群中的所有资源
kube-scheduler:调度器,根据集群中node节点的状态,将要创建的pod绑定到最合适的node上
etcd:一种key-value的数据库,来存放集群运行的一些非常重要的数据,比如label
node中包含如下组件
kubelet:负责节点中pod的创建等,除此之外,负责和master节点中的api-server进行交互,获得来自api-server的消息,也会主动向api-server发送本地的状态等消息
kube-proxy:负责将访问pod的请求,进行负载均衡,负载均衡到pod中的各个容器中,和service有着密切的联系
docker-engine:容器引擎,负载容器的创建
第三部分:组件的分析
一.Kubernetes API-Server原理分析(信息中枢)
kubernetes API-Server的核心功能是提供了Kubernetes各类资源对象的增,删,改,查及Watch等HTTP Rest接口,成为集群内部各个模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心(即任何对资源的操作都要经过API-Server)
1.API-Server的功能特性
(1)集群管理的API入口
(2)资源配额控制的入口
(3)提供完备的集群安全机制
我们使用kubectl来与API-Server进行交互,他们之间的接口就是REST 调用
2.API-Server在集群功能模块之间的通信的地位
API-Server作为集群的核心,负责集群各功能模块之间的通信,集群内的各个功能模块,通过API Server将信息存入到etcd中,当需要获取和操作这些数据时,则通过API-Server的REST接口来实现,从而实现各模块之间的信息交互
有以下几个常见的场景
场景一.kubelet进程与API-Server的交互
每个Node节点上的kubelet每隔一个时间周期,就会调用一次API-server的REST接口报告自身状态,API Server接收到这些消息后,将节点状态信息更新到etcd中。此外,kubelet也通过API-Server的Watch接口监听Pod信息,如果监听到新的Pod对象被调度绑定到本节点,则执行Pod对应的容器创建和启动逻辑,如果监听到Pod对象被删除,则会删除本节点上的相应的Pod容器,如果监听到修改Pod信息,则kubelet监听到变化后,会相应地修改本节点的Pod容器
场景二.kube-controller-manager进程与API-Server的交互,kube-controller-manager中的Node Controller模块通过API Server提供的Watch接口,实时监控Node的信息,并做相应的处理
场景三.kube-scheduler和API-Server的交互,当Scheduler通过API Server的Watch接口监听到新建Pod副本的消息后,它会检索所有符合该Pod要求的Node列表,开始执行的Pod调度逻辑,调度成功后将Pod绑定到目标节点上
二.Controller Manager原理分析(维持在理想状态,控制)
Controller Manager作为集群内部的管理控制中心,负责集群内的Node,Pod副本,服务端点(Endpoint),命名空间(Namespace),资源定额(Resource Quota)等的管理,当某个Node意外宕机时,Controller Manager会及时发现此故障并执行自动化修复流程,确保集群始终处于预期的工作状态
Controller Manager内部包含 Replication Controller ,Node Controller ,ResourceQuota Controller,Namespace Controller,ServiceAccount Controller,Token Controller,Service Controller 及Endpoint Controller等多个Controller,每种Controller都负责一种具体的控制流程,而Controller Manager正是这些Controller的核心管理者
每个Controller都是一个不断修正系统工作状态的“操纵系统”,他们通过API-Server提供的接口实时监控整个集群里的每个资源的当前状态,当发生各种故障导致系统状态发生变化时,会尝试着将系统从“现有状态”修正到期望状态
1.Replication Controller
Controller Manager中的Replication Controller(副本控制器)和资源对象Replicaiton Controller不是同一个东西,却又有着非常密切的联系大家不要记混了!这里将资源对象Replicaiton Controller简写作RC,以作区分
Replicaiton Controller的核心作用是确保在任何时候集群中一个RC所关联的Pod副本数量保持预设值。如果发现Pod副本数量超过预期值,则Replicaiton Controller会销毁一些Pod副本,反之,Replicaiton Controller会创建新的副本
Replication Controller的职责:
(1)确保当前集群中有且仅有N个Pod实例,N是RC中定义的Pod副本数量
(2)通过调整RC的spec.replicas属性值来实现系统的扩容或缩容
(3)通过改变RC中的Pod模板(主要是镜像版本)来实现系统的滚动升级
Replication Controller使用场景
(1)重新调度
(2)弹性伸缩
(3)滚动更新
2.Node Controller
kubelet进程在启动时通过API Server注册自身的节点信息,并定时向API Server汇报状态信息,API Server接收到这些消息后,将这些信息更新到etcd中,etcd中存储的节点信息包括节点健康状况,节点资源,节点名称,节点地址信息,操作系统版本,Docker版本,kublect版本等。节点健康状况包含“就绪”(TRUE)“未就绪”(Flase)和“未知”(Unknown)三种
Node Controller通过API Server实时获取Node相关信息,实现管理和监控集群中的各个Node节点的相关控制功能
3.ResourceQuota Controller
资源配额管理确保了指定的资源对象在任何时候都不会超量占用系统物理资源,避免了由于某些业务进程的设计或实现的缺陷导致整个系统紊乱甚至意外宕机,对整个集群的平稳运行和稳定性有非常重要的作用
目前Kubernetes支持如下三个层次的资源配额管理
(1)容器级别:可以对CPU和Memory进行限制
(2)Pod级别:可以对一个Pod内所有容器的可用资源进行限制
(3)Namespace级别:为Namespace(多租户)级别的资源限制,包括
Pod数量
Replication Controller数量(资源清单)
Service数量
ResourceQuota数量
Secret数量
可持有的PV(Persistent Volume)数量
4.Namespace Controller
用户通过API Server可以创建新的Namespace并保存在etcd中,Namespace Controller定时通过API Server读取这些Namesapce信息
5.Service Controller与Endpoint Controller
首先我们来看以下Service,Endpoints与Pod的关系。Endpoints表示了一个Service对应的所有Pod副本的访问地址,而Endpoints Controller就是负责生成和维护所有Endpoints对象的控制器
它负责监听Service和对应的Pod副本的变化,如果检测到Service被删除,则删除和该Service同名的Endpoints对象,如果检测到新的Service被创建或者修改,则根据该Service信息获得相关的Pod列表,然后创建或者更新Service对应的Endpoints对象。如果检测到Pod的事件,则更新它所对应的Service的Endpoints,
Endpoints在每个Node上的kube-proxy进程被使用,kube-proxy进程获得每个Service的Endpoints,实现了Service的负载均衡功能
Service Controller其实属于Kubernetes集群与外部的云平台之间的一个接口控制器,Service Controller监听Service 的变化,如果是一个LoadBalancer类型的Service(externlLoadBalancers=true)则Service Controller 确保外部的云平台上该Service对应的LoadBalancer实例被相应地创建,删除及更新路由转发表
三.Scheduler原理分析(Pod调度)
Kubernetes Scheduler的在整个系统中承担了“承上启下”的重要功能,“承上”指的是负责接收Controller Manager创建新的Pod,将其绑定在一个合适的Node上,“启下”是指绑定完成后,目标Node上的kubelet服务进程接管后继工作,负责Pod生命周期的后半部分
具体来说,kubernets的作用是将待调度的Pod(API新创建的Pod,Controller Manager为补足副本而创建的Pod等)按照特定的调度算法和调度策略绑定到集群中的某个Node上,并将绑定信息写入到etcd中,整个调度过程中涉及三个对象,分别是:待调度Pod列表,可用Node列表,以及调度算法和策略。简单地说,就是通过调度算法调度为待调度Pod列表的每个Pod从Node列表中选择一个最合适的Node
随后,目标节点上的kubelet通过API Server监听到Kubernetes Scheduler产生的Pod绑定事件,然后获取对应对应的Pod清单,下载image镜像,并启动容器
四.kubelet运行机制分析(接收来自API的消息,负责本节点Pod的部署)
在kubelet集群中,在每个Node节点上都会运行启动一个kubelet服务进程,该进程用于处理Master节点下发到本节点的任务,管理Pod及Pod中的容器,每个kubelet进程会在API Server上注册节点自身信息,定期向Master节点汇报节点资源的使用情况
1.节点管理
节点通过设置kublet的启动参数来决定是否向API Server注册自己,
几个重要的参数
–api-servers:告诉kublet API Server的位置
–kubeconfig:告诉kubelet在哪可以找到用于访问API Server的证书
2.Pod管理
kubelet通过以下几种方式获取自身Node上所要运行的Pod清单
(1)文件:可以配置本地的pod文件来创建很管理Pod
(2)HTTP端点
(3)API Server:kubelet通过API Server监听etcd目录,同步Pod列表
所有以非API Server模式创建的的Pod都叫做Static Pod。kubelet将Static Pod的状态汇报给API Server,API Server为该Static Pod创建一个Mirror Pod和其相匹配,Mirror的状态将真实反映Static Pod的状态,当Static Pod被删除时,Mirror Pod也将被删除
kubelet读取监听到的信息,如果是创建和修改Pod任务,则做如下处理
(1)为该Pod创建一个数据目录
(2)从API Server读取该Pod清单
(3)为该Pod挂载外部卷
(4)下载Pod用到的Secret
(5)检查已经运行在节点中的Pod,如果该Pod没有容器或Pause容器没有启动,则先停止Pod里所有容器的进程。如果在Pod中有需要删除的容器,则删除这些容器
(6)用“kubernetes/pause”镜像为每个Pod创建一个容器,该Pause容器用于接管Pod中所有其他容器的网络。每创建一个新Pod,kublet都会先创建一个Pause容器,然后创建其他容器
(7)创建Pod中的容器
3.容器监控检查
五.kube-proxy运行机制分析
为了支持集群的水平扩展,高可用性,kubernetes抽象除了Service的概念,Service是对一组Pod的抽象,它会根据访问策略(如负载均衡策略)来访问这组Pod
Kubernets在创建服务时回为服务分配一个虚拟的IP地址,客户端通过访问这个虚拟IP地址来访问服务,而Servie则是将请求转发都后端Pod上,这其实就是一个反向代理。和普通的反向代理不同的是,Service的IP地址是虚拟的,无法从外部直接访问
Service在很多情况下都是一个概念,而真正将Service的作用落实的其实背后的kube-proxy服务进程
在kubernets的每个Node节点上都会运行一个kube-proxy服务进程,这个进程可以看作Service的透明代理兼负载均衡服务器,其核心功能是将到某个Service的访问请求转发到后端多个Pod实例上,对每一个TCP类型的Kubernetes Service。kube-proxy都会在本地Node上建立一个Socket Server来负责接受请求,然后均匀发送到后端某个Pod的端口上,这个过程默认采用RR轮询算法
Service的Cluster IP与NodePort等概念是kube-proxy服务通过Iptables的NAT转换实现的,kube-proxy在运行过程中动态创建与Service相关的iptables规则,这些规则实现了Cluster IP及NodePort的请求流量重定向到kube-proxy进程上对应服务的代理端口的功能。由于iptables机制针对的是本地的kube-proxy端口,所以每个Node上都要运行kube-proxy组件,这样依赖,在kubernetes集群内部,我们可以在任意Node上发起对Service的请求
访问Service的请求,无论是使用Cluster IP + TargetPort的方式,还是用节点机IP+NodePort的方式,都被节点机的iptables规则重定向到kube-proxy监听Service服务代理端口,kube-proxy接受到Service的访问请求后,会如何选择都断的Pod呢?
kube-proxy的实现细节
1.本地转发的实现
kube-proxy通过查询和监听API Server中Service与Endpoints的变化,为每个Service都建立了一个“服务代理对象”,并自动同步。服务代理对象是kupe-proxy程序内部的一种数据结构,它包括一个用于监听此服务请求的Socket Server,Socket Server的端口是随机选择一个本地的空闲端口,此外kube-proxy内部也创建了一个负载均衡器,LoadBalancer,LoadBalancer上保存了Service到对应的后端Endpoint列表的动态路由转发表,而具体的路由选择则取决于RR负载均衡算法及Service的Session会话保持这两个特性****
2.针对发生变化的Service列表
(1)如果该Service没有设置集群IP,则不做任何处理,否则,获取该Service的所有端口定义列表(spec.ports域)
(2)逐个读取服务端口定义列表中的端口信息,根据端口名称,Service名称和Namespace判断本地是否已经存在对应的服务代理对象,如果不在则新创建,如果存在并且Service端口被修改过,则先删除IPtables中和该Service端口相关的规则,关闭代理服务器,然后走新建流程,即为该Service端口分配服务代理对象并未该Service创建相关的Iptables规则
(3)更新负载均衡器组件中对应Service的转发地址列表,对于新建的Service,确定转发时的会话保持策略
(4)对于已经删除的Service则进行清理
而针对Endpoint的变化,kube-proxy会自动更新负载均衡器中对应的Service的转发地址列表
3.kube-proxy针对Iptables所做的一些细节操作
kube-proxy在启动时监听到Service或Endpoint的变化后,会在本机Iptables的NAT表中添加四条规则链
(1)KUBE-PROTALS-CONTAINER:从容器中通过Service Cluster IP和端口号访问Service的请求
(2)KUBE-PORTALS-HOST:从主机中通过Service Cluster IP和端口号访问Service的请求
(3)KUBE-NODEPORT-CONTAINER:从容器中通过Service的NodePort端口号访问Service的请求
(4)KUBE-NODEPORT-HOST:从主机中通过Service的NodePort端口号访问Service的请求
kube-proxy工作原理示意图