K8S中的Master是集群控制节点,负责整个集群的管理和控制,其中master节点的基本组件有:
Node是k8s集群中的工作节点,每个Node都会被Master分配一些进程,当某个Node发生异常时,其上的工作负载会被Master自动转移到其他节点上,node节点的基本组件包括:
在默认情况下Kubelet会向Master注册自己,一旦Node被纳入集群管理范围,kubelet进程就会定时向Master汇报自身的信息(例如机器的CPU和内存情况以及有哪些Pod在运行等),这样Master就可以获知每个Node的资源使用情况,并实现高效均衡的资源调度策略。而某个Node在超过指定时间不上报信息时,会被Master判定为失败,Node的状态被标记为不可用,随后Master会触发工作负载转移的自动流程
Scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。听起来非常简单,但有很多要考虑的问题:
Sheduler 是作为单独的程序运行的,启动之后会一直监听 API Server,获取Pod中Spec.NodeName为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上。
调度分为几个部分:
predicate
;如果中间任何一步骤有错误,就直接返回错误。
Predicate 有一系列的算法可以使用:
PodFitsResources
:节点上剩余的资源是否大于 pod 请求的资源如果在 predicate 过程中没有合适的节点,pod 会一直在pending状态,不断重试调度,直到有节点满足条件。
经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程:
按照优先级大小对节点排序优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。这些优先级选项包括:
节点亲和性一般存放在pod的pod.spec.nodeAffinity中
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: doggymo/myapp:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: source
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: doggymo/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn # 键值运算关系
values:
- k8s-node02
键值运算关系还包括:
pod亲和性定义在:pod.spec.affinity.podAffinity(pod亲和性)/podAntiAffinity(pod反亲和性)
为什么这里说的是拓扑域,在下图中,node1和node2其实就是属于同拓扑域。
一个node可以设置多个污点,不容忍污点的pod不能调度到这个节点,容忍度设置给pod,这样的pod允许但不是必须调度到有这些污点的node。
使用kubectl taint
命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去每个污点的组成如下:key=value:effect
包括以下三种 effect:
设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。但我们可以在 Pod 上设置容忍 ( Toleration ) ,意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上。
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerationSeconds: 3600
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "key2"
operator: "Exists"
effect: "NoSchedule"
tolerations:
- operator: "Exists"
如果不设置key,表示容忍所有的污点
tolerations:
- key: "key"
operator: "Exists"
如果不设置effect值,表示可以容忍所有的污点作用。
QoS(Quality of Service),大部分译为 “服务质量等级”,又译作 “服务质量保证”,是作用在 Pod 上的一个配置,当 Kubernetes 创建一个 Pod 时,它就会给这个 Pod 分配一个 QoS 等级,可以是以下等级之一:
该配置不是通过一个配置项来配置的,而是通过配置 CPU/MEM的 limits 与 requests 值的大小来确认服务质量等级。
首先我们来看Qos为guaranteed的pod
然后我们来看Qos为brustable的pod
最后我们来看Qos为besteffort的pod
我们可以通过限制limits
和需求requests
的关系来改变Qos的状态
三种 QoS 优先级,从高到低(从左往右)
Guaranteed --> Burstable --> BestEffort
Kubernetes 资源回收策略:当集群监控到 node 节点内存或者CPU资源耗尽时,为了保护node正常工作,就会启动资源回收策略,通过驱逐节点上Pod来减少资源占用。
三种 QoS 策略被驱逐优先级,从高到低(从左往右)
BestEffort --> Burstable --> Guaranteed
首先需要明确的是:优先级和抢占机制,解决的是pod调度失败时该怎么办的问题。
正常情况下,当一个pod调度失败后,就会被暂时 “搁置”pending状态,直到pod被更新,或者集群状态发生变化,调度器才会对这个pod进行重新调度。
场景分析:当一个高优先级的pod调度失败后,该pod并不会被”搁置”,而是会”挤走”某个node上的一些低优先级的Pod。这样就可以保证高优先级pod的调度成功。
apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for high priority service pods only."
kubernetes规定,优先级是一个32bit的整数,最大值不超过1000000000(10亿),并且值越大优先级越高。
上述yaml文件中的globalDefault被设置成true的话,那就意味着这个PriorityClass的值会成为系统的默认值。false表示的是只希望声明使用该PriorityClass的Pod拥有值为1000000的优先级,而对于没有声明Priority的Pod来说,优先级就是0。
创建PriorityClass对象之后,Pod就可以声明使用它,如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority
pod通过priorityClassName字段,声明了要使用high-priority的PriorityClass。当pod提交给kubernetes之后,kubernetes的PriorityAdmissionController就会将这个pod的spec.priority字段设置为100000。
而当一个高优先级的pod调度失败的时候,调度器的抢占能力就会被触发。这时,调度器就会试图从当前集群里寻找一个节点,使得当这个节点上的一个或者多个低优先级pod被删除后,待调度的高优先级pod就可以被调度到这个节点上。这个过程,就是“抢占”在kubernetes中的主要体现。
对于两个需要同时被调度的pod来说,会首先调度优先级大的pod;当一个优先级低的pod01节点被调度成功之后(在node01上),一个优先级高的pod02由于node01节点资源不足出现调度失败的情况,这个时候会删除pod01,并把pod02部署到node01上,这个过程就叫抢占。
子系统(subsystem)实际上是cgroup对进程组进行资源控制的具体体现。子系统具有多种类型,每个类型的子系统都代表一种系统资源,比如CPU、memory等。当创建一个cgroup实例时,必须至少指定一种子系统。也就是说,这个新建的进程组在访问子系统对应的系统资源时就有了一些限制。
Cgroup自身通过文件系统的形式在内核中实现,通过对子系统配置文件的读写即可完成对进程组资源的控制。不过,cgroup对各种资源的实际控制则分布到整个内核代码中。