核心组件
- etcd 保存了整个集群的状态;
- API Server 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册 和发现等机制;
- Controller Manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新 等
- Scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上;
- Kubelet 负责维护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的 管理;
- Container Runtime 负责镜像管理以及 Pod 和容器的真正运行(CRI);
- Kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡;
组件通信
Kubernetes 多组件之间的通信原理
- API Server负责etcd存储的所有操作,且只有 API Server才能直接操作etcd集群;
- API Server 对内和对外提供统一的Rest API,其他组件均通过API Server进行通信:
- Controller Manager、Scheduler、Kube-proxy和Kubelet 等均通过API Server waych API监测资源变化情况,并对资源相应的操作;
- 所有需要更新资源状态的操作均通过API Server的Rest API进行;
- API Server也会直接调用Kubelet API;
创建Pod的流程为:
- 用户通过Rest API创建一个Pod;
- API server将其写入etcd;
- Scheduluer检测到未绑定Node的Pod,开始调度并更新Pod的Node绑定;
- Kubelet 检测到有新的Pod调度过来,通过Container Runtime运行该Pod;
- Kubelet通过Container Runtime取到Pod状态,并更新到API Server中;
etcd
etcd是分布式key-value存储,可用于服务发现、共享配置以及一致性保障;
主要功能
- 基本的key-value存储;
- 监听机制;
- key的过期及续约机制,勇于监控和服务发现;
- 原子CAS和CAD,勇于分布式锁和leader选举;
etcd给予RAFT的一致性
选举方法
- 初始启动时,节点处于follower状态并被设定一个election timeout,如果在这一时间周期内没有收到来自leader的heartbeat,节点将发起选举:将自己切换为candidate之后,向集群中其他follower节点发送请求,询问其是否选举自己成为leader;
- 当收到来自集群中过半数节点的接受投票后,节点即成为leader,开始接收保存client的数据并向其他follower节点同步日志,如果没有达成一致,则candidate随机选择一个等待间隔(150-300ms)再次发起投票,得到集群中半数以上的follower接受的candidate将成为leader;
- leader节点依靠定时向follower发送heartbeat来保持其地位;
- 任何时候如果其他follower在election timeout期间都没有收到来自leader的heartbeat,同样会将自己的状态切换为candidate并发起选举,每成功选举一次,新的leader的任期都会比之前的leader的任期大1;
日志复制 当前leader收到客户端的日志后先把该日志追加到本地的Log中,然后通过heartbeat把改Entry同步给其他follower,follower接收到日志后记录日志然后向leader发送ack,当leader收到大多数(n/2+1)follower的ack信息后将该日志设置为已提交并追加到本地磁盘中,通知客户端在下个heartbeat中leader将通知所有的follower将该日志存储在本地磁盘中。
安全性 安全性是用于保证每个节点都执行相同序列的安全机制,如当某个 Follower 在当前 Leader commit Log 时变得不可用了,稍后可能该 Follower 又会被选举为 Leader,这 时新 Leader 可能会用新的 Log 覆盖先前已 committed 的 Log,这就是导致节点执行不 同序列;Safety 就是用于保证选举出来的 Leader 一定包含先前 committed Log 的机 制;
- 选举安全性:每个任期只能选举出一个leader;
- leader完整性:指的是leader日志的完整性,当Log在任期term1被commit后,那么以后任期的leader必须包含该Log,Raft在选举节点使用term的判断用于保证完整性:当请求投票的该candidate的term较大或term相同index更大则投票,否则拒绝该请求。
失效处理
- leader失效:其他没有收到heartbeat的节点会发起新的选举,而当leader回复后由于步进数小会自动成为follower,日志也会被新的leader的日志覆盖;
- follower节点不可用:follower节点不可用的情况相对容易解决,因为集群中的日志内容始终是从leader节点同步的,只要这一节点再次加入集群时重新从leader节点处复制日志即可。
- 多个candidate:冲突后candidate将随机选择一个等待间隔(150-300ms),再次发起投票,得到集群中半数以上follower接受的candidate将成为leader。
API Server
主要功能:
- 提供集群管理的REST API接口,包括认证授权、数据校验以及集群状态变更等;
- 提供其他模块之间的数据交互和通信枢纽,其他模块通过API SERVER查询或修改数据,只有API Server才直接操作etcd;
在实际使用中,通常通过kubectl来访问apiserver,也可以通过Kubernetes各个语言的client库来访问apiserver,在使用kubectl时,打开调试日志也可以看到每个api调用的格式。
访问控制
Kubernetes API的每个请求都会经过多阶段的访问控制之后才会被接受,包括认证、授权以及准入控制等。 认证
开启TLS时,所有的请求都需要首先认证,Kubernetes支持多种认证机制,并支持同时开启多个认证插件,如果认证成功,则用户的username会传入授权模块做进一步授权验证,而对于认证失败的请求则返回HTTP 401;
授权
认证之后的请求就到了授权模块,跟认证类似,Kubernetes也支持多种授权机制,并支持同时开启多个授权插件,如果授权成功,则用户的请求会发送到准入模块做进一步的请求验证,而对于授权失败的请求则返回HTTP 403;
准入控制
准入控制用来对请求做进一步的验证或添加默认参数,不同于授权和认证只关心请求的用户和操作,准入控制还处理请求的内容,并且仅对创建、更新、删除或链接等有效,而对读操作无效,同时也支持同时开启多个插件,依次调用,只有全部插件都通过的请求才可以放过进入系统。
工作原理
kube-apiserver提供了Kubernetes的REST API,实现了认证、授权、准入控制等安全校验功能,同时也负责集群状态的存储操作。
kube-scheduler
kube-Scheduler负责分配调度Pod到集群内的节点上,它坚挺kube-apiserver,查询还未分配Node的Pod,然后根据调度策略为这些Pod分配节点;
调度器需要充分考虑诸多因素:
- 公平调度;
- 资源高效利用;
- QoS;
- affinity和anti-affinity;
- 数据本地化;
- 内部负载干扰;
- deadlines;
指定Node节点调度
有三种方式指定Pod只运行在指定的Node节点上:
- nodeSelector:之调度到匹配制定label的Node上;
- nodeAffinity:功能更丰富的Node选择器,支持集合操作;
- podAffinity:调度到满足条件的Pod所在的Node上;
nodeSelector示例
首先给Node打上标签:
kubectl label nodes node-01 disktype=ssd
然后在daemonset中指定nodeSelector为disktype=ssd :
spec:
nodeSelector:
disktype: ssd
nodeAffinity示例
nodeAffinity目前支持两种:requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,分别代表必须满足条件和优选条件。比如下面的例子代表调度到包含标签 kubernetes.io/e2e-az-name 并且值为 e2e-az1 或 e2e-az2 的 Node 上,并且优选还带有标签 another-node-label- key=another-node-label-value 的 Node。
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: gcr.io/google_containers/pause:2.0
podAffinity示例
podAffinity基于Pod的标签来选择Node,仅调度到满足条件Pod所在的Node上,支持podAffinity和podAntiAffinity。
Taints和tolerations
Taints 和 tolerations用于保证Pod不被调度到不合适的Node上,其中Taint应用于Node上,而toleration则用于Pod上;
目前支持的Taint类型:
- NoSchedule:新的Pod不调度到该Node上,不影响正在运行的Pod;
- PreferNodeSchedule:soft版的NoSchedule,尽量不调度到该Node上;
- NoExecute:新的Pod不调度到该Node上,并且删除(evict)已在运行的Pod,Pod可以增加一个时间。
然而,当Pod的Tolerations匹配Node的所有Taints 的时候可以调度到该Node上;当Pod是运行的时候,也不会被删除(evictd)。另外对于NoExecute,如果Pod增加了一个tolerationSeconds,则会在该时间之后才删除Pod。
假设node1上应用以下几个taint:
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule
下面这个Pod由于没有tolerate key2=value2:NoSchedule 无法调度到node1上
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
而正在运行且带有tolerationSeconds的Pod则会在600s之后删除:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "key2"
operator: "Equal"
value: "value2"
effect: "NoSchedule"
DaemonSet 创建的 Pod 会自动加上对 node.alpha.kubernetes.io/unreachable 和 node.alpha.kubernetes.io/notReady 的 NoExecute Toleration,以避免它们因此 被删除。
优先级调度 从v1.8开始,kube-scheduler支持定义Pod的优先级,从而保证高优先级的Pod优点调度,并从v1.11开始默认开启。
在指定Pod的优先级之前需要先定义一个PriorityClass,如:
apiVersion: v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."
- value 为32位整数的优先级,该值越大,优先级越高;
- globalDefault 用于未配置PriorityClassName的Pod,整个集群中应该只有一个PriorityClass将其设置为true;
然后在Pod Spec中通过PriorityClassName设置Pod的优先级:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority
多调度器
如果默认的调度器不满足要求,还可以部署自定义的调度器,并且,在整个集群中还可以同时运行多个调度器实例,通过podSpec.SchedulerName 来选择使用哪一个调度器。
spec:
my-scheduler
schedulerName: my-scheduler
containers:
- name: nginx
image: nginx:1.10
kube-scheduler工作原理
kube-scheduler 调度分为两个阶段:predictcate 和 priority
- predicate:过滤不符合条件的节点;
- priority:优先级排序,选择优先级最高的节点;
Controller Manager
Controller Manager由kube-controller-manager 和 cloud-controller-manager组成,是Kubernetes的大脑,它通过apiserver监控整个集群状态,并确保集群处于预期的工作状态。
kube-controller-manager由一系列的控制器组成:Replication Controller、Node controller等;cloud-controller-manager在Kubernetes启用cloud provider的时候才需要。
Kubelet
每个Node节点上都运行一个Kubelet服务进程,默认监听端口10250,接收并执行Master发来的指令,管理Pod及Pod中的容器,每个kubelet进程会在API server注册所在Node节点的信息,定期向Master节点汇报该节点的资源使用情况,并通过cAdvise监控节点和容器的资源。
节点管理
节点管理主要是节点自注册和节点状态更新:
- kubelet可以通过设置启动参数–register-node 来确定是否向API server注册自己;
- 如果kubelet 没有选择自注册模式,则需要用户自己配置Node资源信息,同时需要告知kubelet集群上的API server位置;
- kubelet在启动时通过API server注册节点信息,并定时向api server发送节点新消息,api server在接收到新消息后,将信息写入etcd。
Pod管理
获取Pod清单
kubelet以podspec 的方式工作,podspec是描述一个pod的YAML或JSON对象。kubelet采用一组通过各种机制提供的PodSpecs,并确保这些PodSpecs中描述的Pod正常健康运行。
向kubelet提供节点上运行的Pod清单的方法:
- 文件:启动参数 --config 指定的配置目录下的文件,改文件每20秒重新检查一次(可配置);
- HTTP endpoint(URL):启动参数 --manifest-url设置,每20秒检查一次这个端点(可配置);
- API Server:通过API server监听etcd目录,同步Pod清单;
- HTTP server:kubelet侦听HTTP请求,并响应简单的API以提交新的Pod清单。
通过API server 获取Pod清单及创建Pod的过程 kubelet读取监听到的消息,如果是创建和修改Pod任务,则执行如下处理:
- 为该Pod创建一个数据目录;
- 从API server读取该Pod清单;
- 为该Pod挂载外部卷;
- 下载Pod用到的secret;
- 检查已经在节点上运行的Pod,如果该Pod没有容器或Pause容器没有启动,则先停止Pod里所有容器的进程,如果在Pod中有需要删除的容器,则删除这些容器。
- “Kubernetes/pause”镜像为每个Pod创建一个容器,pause容器用于接管Pod中所有其他容器的网络。每创建一个新的Pod,kubelet都会先创建一个Pause容器,然后创建其他容器。
- 为Pod中的每个容器做如下处理:
- 为容器计算一个hash值,然后用容器的名字去docker查询对应容器的hash值,若查找到容器,并且hash值不同,则停止docker中容器的进程,并停止与之关联的pause容器进程,如果两者相同,则不做任何处理。
- 如果容器被终止了,并且容器没有指定的restartPolicy则不做任何处理。
- 调用docker client 下载容器镜像,调用docker client运行容器。
static Pod
所有以非API server方式创建的pod都叫static pod,kubelet将static pod的状态汇报给api server,api server为该static pod创建一个mirror Pod和其相匹配。Mirror Pod的状态将真实反映static pod的状态,放static pod被删除时,与之相对应的Mirror Pod也会被删除。
容器健康检查
Pod通过两类探针检查容器的健康状态:
- LivenessProbe探针:用于判断容器是否健康,告诉kubelet一个容器什么时候处于不健康状态。如果LivenessProbe探针检测到容器不健康,则kubelet将删除该容器,并根据容器的重启策略做响应的处理,如果一个容器不包含LivenessProbe探针,则认为该容器的LivenessProbe探针返回值永远是success。
- ReadinessProbe:用于判断容器是否启动完成并且准备接收请求,如果ReadnessProbe探针检测到失败,则Pod的状态将被修改,Endpoint Controller将从service 的endpoint中删除包含该容器所在Pod的IP地址的endpoint条目。
kubelet定期调用容器中的LivenessProbe探针来诊断容器的健康状况,LivenessProbe包含如下三种方式:
- ExecAction:在容器内部执行一个命令,如果该命令的推出状态吗为0,则表明容器健康;
- TCPSocketAction:通过容器的IP地址和端口号执行TCP检查,如果端口能被访问,则表明容器健康。
- HTTPGetAction:通过容器的IP地址和端口号及路径调用HTTP GET方法,如果响应的状态码大于等于200且小于400,则认为容器状态健康。
Kubelet Eviction(驱逐)
Kubelet会监控资源的使用情况,并使用驱逐机制防止计算和存储资源耗尽,在驱逐时,Kubelet会将Pod的所有容器停止,并将PodPhase设置为Failed。
Kubelet定期检查系统的资源是否达到了预先配置的驱逐阈值。
|