K8S之list-watch机制+节点以及亲和性调度
list-watch机制
Kubernetes 是通过 List-Watch 的机制进行每个组件的协作,保持数据同步的,每个组件之间的设计实现了解耦。
用户是通过 kubectl 根据配置文件,向 APIServer 发送命令,在 Node 节点上面建立 Pod 和 Container。APIServer 经过 API 调用,权限控制,调用资源和存储资源的过程,实际上还没有真正开始部署应用。这里需要 Controller Manager、Scheduler 和 kubelet 的协助才能完成整个部署过程。在 Kubernetes 中,所有部署的信息都会写到 etcd 中保存。实际上 etcd 在存储部署信息的时候,会发送 Create 事件给 APIServer,而 APIServer 会通过监听(Watch)etcd 发过来的事件。其他组件也会监听(Watch)APIServer 发出来的事件。
list-watch工作流程
Pod是Kubernetes的基础单元,Pod 启动典型创建过程如下:
注意:在创建 Pod 的工作就已经完成了后,为什么 kubelet 还要一直监听呢?原因很简单,假设这个时候 kubectl 发命令,要扩充 Pod 副本数量,那么上面的流程又会触发一遍,kubelet 会根据最新的 Pod 的部署情况调整 Node 的资源。又或者 Pod 副本数量没有发生变化,但是其中的镜像文件升级了,kubelet 也会自动获取最新的镜像文件并且加载。
节点调度
调度过程
Scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod分配到集群的节点上。其主要考虑的问题如下:
公平:如何保证每个节点都能被分配资源
资源高效利用:集群所有资源最大化被使用
效率:调度的性能要好,能够尽快地对大批量的pod完成调度工作
灵活:允许用户根据自己的需求控制调度的逻辑
- Sheduler是作为单独的程序运行的,启动之后会一直监听APIServer,获取spec.nodeName为空的pod,对每个pod都会创建一个binding,表明该pod应该放到哪个节点上。
调度分为几个部分:
预算策略
==Predicate有一系列的常见的算法可以使用==:
- PodFitsResources:节点上剩余的资源是否大于 pod 请求的资源。
- PodFitsHost:如果 pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配。
- PodFitsHostPorts:节点上已经使用的 port 是否和 pod 申请的 port 冲突。
- PodSelectorMatches:过滤掉和 pod 指定的 label 不匹配的节点。
- NoDiskConflict:已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读。
如果在 predicate 过程中没有合适的节点,pod 会一直在 pending状态,不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程:按照优先级大小对节点排序。
优选策略
优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。有一系列的常见的优先级选项包括:
- LeastRequestedPriority:通过计算CPU和Memory的使用率来决定权重,使用率越低权重越高。也就是说,这个优先级指标倾向于资源使用比例更低的节点。
- BalancedResourceAllocation:节点上CPU和Memory使用率越接近,权重越高。这个一般和上面的一起使用,不单独使用。比如node01的CPU和Memory使用率20:60,node02的CPU和Memory使用率50:50,虽然node01的总使用率比node02低,但node02的CPU和Memory使用率更接近,从而调度时会优选node02。
- ImageLocalityPriority:倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高。
通过算法对所有的优先级项目和权重进行计算,得出最终的结果。
指定调度节点
方法一:使用pod.spec.nodeName参数,将Pod直接调度到指定的Node节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配
vim myapp.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: myapp spec: replicas: 3 template: metadata: labels: app: myapp spec: nodeName: node02 containers: - name: myapp image: niginx ports: - containerPort: 80 kubectl apply -f myapp.yaml kubectl get pods -o wide方法二:使用pod.spec.nodeSelector参数,通过kubernetes的label-selector机制选择节点,由调度器调度策略匹配label,然后调度Pod到目标节点,该匹配规则属于强制约束
kubectl label --help #获取标签帮助 需要获取 node 上的 NAME 名称 kubectl get node 给对应的 node 设置标签分别为 abc=a 和 abc=b kubectl label nodes node01 abc=aaa kubectl label nodes node02 abc=bbb 查看标签 kubectl get nodes --show-labels 修改成 nodeSelector 调度方式 vim myapp1.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: myapp1 spec: replicas: 3 template: metadata: labels: app: myapp1 spec: nodeSelector: kgc: a containers: - name: myapp1 image: soscscs/myapp:v1 ports: - containerPort: 80 kubectl apply -f myapp1.yaml kubectl get pods -o wide修改一个label的值,需要加上--overwrite参数
kubectl label nodes node02 abc=ccc --overwrite删除一个label,只需在命令行最后指定label的key 名并与一个减号相连即可:
kubectl label nodes node02 abc-指定标签查询 node 节点
kubectl get node -l abc=aaa亲和性
官方文档:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/assign-pod-node/
(1)节点亲和性
pod.spec.nodeAffinity
- preferredDuringSchedulingIgnoredDuringExecution:软策略
- requiredDuringSchedulingIgnoredDuringExecution:硬策略
(2)Pod 亲和性
pod.spec.affinity.podAffinity/podAntiAffinity
- preferredDuringSchedulingIgnoredDuringExecution:软策略
- requiredDuringSchedulingIgnoredDuringExecution:硬策略
可以把自己理解成一个Pod,当你报名来学云计算,如果你更倾向去zhangsan老师带的班级,把不同老师带的班级当作一个node的话,这个就是节点亲和性。如果你是必须要去zhangsan老师带的班级,这就是硬策略;而你说你想去并且最好能去zhangsan老师带的班级,这就是软策略。
如果你有一个很好的朋友交lisi,你倾向和lisi同学在同一个班级,这个就是Pod亲和性。如果你一定要去lisi同学在的班级,这就是硬策略;而你说你想去并且最好能去lisi同学在的班级,这就是软策略。软策略是不去也可以,硬策略则是不去就不行。
键值运算关系
-
In:label 的值在某个列表中
-
NotIn:label 的值不在某个列表中
-
Gt:label 的值大于某个值
-
Lt:label 的值小于某个值
-
Exists:某个 label 存在
- DoesNotExist:某个 label 不存在
节点亲和性(硬策略)
requiredDuringSchedulingIgnoredDuringExecution:硬策略 vim pod1.yaml apiVersion: v1 kind: Pod metadata: name: nginx001 labels: app: node-affinity-pod spec: containers: - name: with-node-affinity image: soscscs/myapp:v1 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname #指定node的标签 operator: NotIn #设置Pod安装到kubernetes.io/hostname的标签值不在values列表中的node上 values: - node02 kubectl apply -f pod1.yaml kubectl get pods -o wide kubectl delete pod --all && kubectl apply -f pod1.yaml && kubectl get pods -o wide #如果硬策略不满足条件,Pod状态一直会处于Pending状态。节点亲和性(软策略)
preferredDuringSchedulingIgnoredDuringExecution:软策略 vim pod2.yaml apiVersion: v1 kind: Pod metadata: name: affinity labels: app: node-affinity-pod spec: containers: - name: with-node-affinity image: soscscs/myapp:v1 affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 #如果有多个软策略选项的话,权重越大,优先级越高 preference: matchExpressions: - key: kubernetes.io/hostname operator: In values: - node03 kubectl apply -f pod2.yaml kubectl get pods -o wide #把values:的值改成node01,则会优先在node01上创建Pod kubectl delete pod --all && kubectl apply -f pod2.yaml && kubectl get pods -o wide ············································································································ 如果把硬策略和软策略合在一起使用,则要先满足硬策略之后才会满足软策略 示例: apiVersion: v1 kind: Pod metadata: name: affinity labels: app: node-affinity-pod spec: containers: - name: with-node-affinity image: soscscs/myapp:v1 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: #先满足硬策略,排除有kubernetes.io/hostname=node02标签的节点 nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: NotIn values: - node02 preferredDuringSchedulingIgnoredDuringExecution: #再满足软策略,优先选择有abc=aaa标签的节点 - weight: 1 preference: matchExpressions: - key: abc operator: In values: - aaa总结:
硬策略:必须满足条件。in节点A就会用 A节点,若没有A节点,会pendingnotin 节点A,就是用除A节点以为的其他节点,若硬策略不满足,也会pending
软策略:尽量满足条件,满足不了也没关系 若有多个软策略选项的话,权重越大,优先级越高