- 一、深入理解Pod对象:调度
- 二、创建一个Pod的工作流程
- 三、资源限制对Pod调度的影响
- 四、nodeSelector & nodeAffinity & podAffinity
- 4.1 节点选择器
- 4.2 节点亲和性
- 4.3 节点亲和力
- 五、Tain(污点)& Tolerations(污点容忍)
- 5.1 污点
- 5.11 NoSchedule:不可调度
- 5.12 PreferNoSchedule
- 5.2 Toleration(污点容忍)
- 六、nodename
- 创建一个Pod的工作流程
- Pod中影响调度的主要属性
- 资源限制对Pod调度的英雄
- nodeSelector & nodeAffinity
- Tain(污点)& Tolerations(污点容忍)
- nodeName
Kubernetes Scheduler 是 Kubernetes 控制平面的核心组件之一。它在控制平面上运行,将 Pod 分配给节点,同时平衡节点之间的资源利用率。将 Pod 分配给新节点后,在该节点上运行的 kubelet 会在 Kubernetes API 中检索 Pod 定义,根据节点上的 Pod 规范创建资源和容器。换句话说,Scheduler 在控制平面内运行,并将工作负载分配给 Kubernetes 集群。
参考资料地址:
-
节点调度
-
污点和污点容忍
-
https://coreos.com/fleet/docs/latest/affinity.html
Kubernetes基于list-watch机制的控制器架构,实现组件间交互的解耦。
其他组件监控自己负责的资源,当这些资源发生变化时,kube-apiserver会通知这些组件,这个过程类似于发布与订阅。
容器资源限制:
- resources.limits.cpu
- resources.limits.memory
容器使用的最小资源需求,作为容器调度时资源分配的依据: - resources.requests.cpu
- resources.requests.memory
cpu单位:也可以写m也可以写浮点数,例如0.5=500m,1=1000m
apiVersion: v1
Kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: nginx
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits: // 最少
memory: "128Mi"
cpu: "500m"
K8s会根据 request的值去查找有足够资源的 Node来调度此 Pod
四、nodeSelector & nodeAffinity & podAffinity 4.1 节点选择器nodeSelector:
用于将 Pod调度到匹配 Label的 Node上,如果没有匹配的标签会调度失败。作用:
- 约束 Pod到特点的节点运行
- 完全匹配节点标签
应用场景:
- 专用节点:根据业务将 Node分组管理
- 配置特殊硬件:部分 Node配有 SSD硬盘、GPU
查看 node 的 label:
[root@master ~]# kubectl get nodes --show-labels NAME STATUS ROLES AGE VERSION LABELS master Ready control-plane,master 5d10h v1.23.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers= node1.example.com Ready5d10h v1.23.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1.example.com,kubernetes.io/os=linux node2.example.com Ready 5d10h v1.23.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2.example.com,kubernetes.io/os=linux
现在我们先给节点 node1 增加一个 app=amu的标签,命令如下:
[root@master ~]# kubectl label nodes node1.example.com app=amu node/node1.example.com labeled
我们可以通过上面的 --show-labels参数可以查看上述标签是否生效。当 node 被打上了相关标签后,在调度的时候就可以使用这些标签了,只需要在 POD 的 spec 字段中添加 nodeSelector字段,里面是我们需要被调度的节点的 label。例如,下面是我们之前的一个默认的 busybox POD 的 YAML文件:
// 标签添加成功 [root@master ~]# kubectl get nodes --show-labelsNAME STATUS ROLES AGE VERSION LABELS master Ready control-plane,master 5d10h v1.23.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers= node1.example.com Ready5d10h v1.23.1 app=amu,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1.example.com,kubernetes.io/os=linux node2.example.com Ready 5d10h v1.23.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2.example.com,kubernetes.io/os=linux [root@master ~]# cat test.yaml ```bash apiVersion: v1 kind: Pod metadata: labels: app: busybox-pod name: test-busybox spec: containers: - command: - sleep - "3600" image: busybox imagePullPolicy: Always name: test-busybox
然后我需要让上面的 POD 被调度到 node1的节点上,那么最简单的方法就是去匹配 node1上面的 label,如下:
[root@master ~]# cat test.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
app: busybox-pod
name: test-busybox
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
imagePullPolicy: Always
name: test-busybox
nodeSelector:
source: qikqiak
[root@master ~]# kubectl apply -f test.yaml
pod/test-busybox created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-busybox 1/1 Running 0 12m
然后我们可以通过 describe 命令查看调度结果:
[root@master ~]# kubectl describe pod test-busybox Name: test-busybox Namespace: default Priority: 0 Node: node1.example.com/192.168.91.137 Start Time: Thu, 23 Dec 2021 22:22:07 +0800 Labels: app=busybox-pod // 容器标签 Annotations:Status: Pending IP: IPs: Containers: test-busybox: Container ID: Image: busybox Image ID: Port: Host Port: Command: sleep 3600 State: Waiting Reason: ContainerCreating Ready: False Restart Count: 0 Environment: Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-98lrt (ro) Conditions: Type Status Initialized True Ready False ContainersReady False PodScheduled True Volumes: kube-api-access-98lrt: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt ConfigMapOptional: DownwardAPI: true QoS Class: BestEffort Node-Selectors: app=amu // 节点标签 Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 38m default-scheduler Successfully assigned default/test-busybox to node1.example.com Normal Pulling 38m kubelet Pulling image "busybox" Normal Pulled 38m kubelet Successfully pulled image "busybox" in 16.316909542s Normal Created 38m kubelet Created container test-busybox Normal Started 38m kubelet Started container test-busybox
我们可以看到 Events 下面的信息,上面的 POD 被正确的调度到了 node1节点。通过上面的例子我们可以感受到 nodeSelector的方式比较直观,但是还够灵活,控制粒度偏大,下面我们再看另外一种更加灵活的方式:nodeAffinity。
4.2 节点亲和性nodeAffinity:
节点亲和性,与 nodeSelector作用一样,但相比更灵活,满足更多条件,诸如:
- 匹配右更多的逻辑组合,不只是字符串的完全相等
- 调度分为软策略和硬策略,而不是硬性要求
- 硬(required):必须满足
- 软(preferred):尝试满足,但不保证
操作符:ln、Notln、Exists、DoesNotExist、Gt、Lt
| 操作符 | 作用 |
|---|---|
| In | label 的值在某个列表中 |
| NotIn | label 的值不在某个列表中 |
| Gt | label 的值大于某个值 |
| Lt | label 的值小于某个值 |
| Exists | 某个 label 存在 |
| DoesNotExist | 某个 label 不存在 |
nodeAffinity 相对应的是 Anti-Affinity,就是反亲和性,这种方法比上面的 nodeSelector更加灵活,它可以进行一些简单的逻辑组合了,不只是简单的相等匹配。 调度可以分成软策略和硬策略两种方式。
软策略就是如果你没有满足调度要求的节点的话,Pod 就会忽略这条规则,继续完成调度过程,说白了就是满足条件最好了,没有的话也无所谓了的策。
硬策略就比较强硬了,如果没有满足条件的节点的话,就不断重试直到满足条件为止,简单说就是你必须满足我的要求,不然我就不干的策略。
nodeAffinity就有两上面两种策略:
preferredDuringSchedulingIgnoredDuringExecution:软策略
requiredDuringSchedulingIgnoredDuringExecution:硬策略
例子:
[root@master ~]# cat test2.yaml
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchexpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- node1
- node2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchexpressions:
- key: source
operator: In
values:
- qikqiak
[root@master ~]# kubectl apply -f test2.yaml
pod/with-node-affinity created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-busybox 1/1 Running 0 15m
with-node-affinity 1/1 Running 0 59s
如果 nodeSelectorTerms下面有多个选项的话,满足任何一个条件就可以了
如果 matchexpressions有多个选项的话,则必须同时满足这些条件才能正常调度 Pod。
[root@master ~]# kubectl describe pod with-node-affinity ......以上省略 Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 52s default-scheduler Successfully assigned default/with-node-affinity to node1.example.com Normal Pulling 50s kubelet Pulling image "nginx" Normal Pulled 35s kubelet Successfully pulled image "nginx" in 15.373115161s Normal Created 35s kubelet Created container with-node-affinity Normal Started 35s kubelet Started container with-node-affinity4.3 节点亲和力
上面两种方式都是让 Pod 去选择节点的,有的时候我们也希望能够根据 Pod 之间的关系进行调度,Kubernetes在1.4版本引入的 podAffinity概念就可以实现我们这个需求。
和 nodeAffinity类似,podAffinity也有两种调度策略:
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
唯一不同的是如果要使用互斥性,我们需要使用 podAntiAffinity字段。 如下例子,我们希望 with-pod-affinity和 busybox-pod能够就近部署,而不希望和 node-affinity-pod部署在同一个拓扑域下面:
[root@master ~]# cat test2.yaml
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
labels:
app: pod-affinity-pod
spec:
containers:
- name: with-pod-affinity
image: nginx
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchexpressions:
- key: app
operator: In
values:
- busybox-pod
topologyKey: kubernetes.io/hostname
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchexpressions:
- key: app
operator: In
values:
- node-affinity-pod
topologyKey: kubernetes.io/hostname
上面这个例子中的 Pod 需要调度到某个指定的主机上,至少有一个节点上运行了这样的 Pod:这个 Pod 有一个app=busybox-pod的 label。podAntiAffinity则是希望最好不要调度到这样的节点:这个节点上运行了某个 Pod,而这个 Pod 有app=node-affinity-pod的 label。根据前面两个 Pod 的定义,我们可以预见上面这个 Pod 应该会被调度到 node1的节点上,因为 busybox-pod被调度到了 node1节点,而 node-affinity-pod被调度到了 node1以为的节点,正好满足上面的需求。通过describe查看:
[root@master ~]# kubectl describe pod with-pod-affinity ......以上省略 Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 6m45s default-scheduler Successfully assigned default/with-pod-affinity to node1.example.com Normal Pulling 6m44s kubelet Pulling image "nginx" Normal Pulled 6m28s kubelet Successfully pulled image "nginx" in 15.369524859s Normal Created 6m28s kubelet Created container with-pod-affinity Normal Started 6m28s kubelet Started container with-pod-affinity
上面的事件信息也验证了我们的想法。
在 labelSelector和 topologyKey的同级,还可以定义 namespaces 列表,表示匹配哪些 namespace 里面的 pod,默认情况下,会匹配定义的 pod 所在的 namespace;如果定义了这个字段,但是它的值为空,则匹配所有的 namespaces。
查看上面我们定义的 3个 Pod结果:
[root@master ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-busybox 1/1 Running 0 41m 10.244.1.62 node1.example.comwith-node-affinity 1/1 Running 0 26m 10.244.1.63 node1.example.com with-pod-affinity 1/1 Running 0 11m 10.244.1.64 node1.example.com
亲和性/反亲和性调度策略比较如下:
| 调度策略 | 匹配标签 | 操作符 | 拓扑域支持 | 拓扑域支持 |
|---|---|---|---|---|
| nodeAffinity | 主机 | In, NotIn, Exists, DoesNotExist, Gt, Lt | 否 | 指定主机 |
| podAffinity | Pod | In, NotIn, Exists, DoesNotExist | 是 | POD与指定POD同一拓扑域 |
| podAnitAffinity | Pod | In, NotIn, Exists, DoesNotExist | 是 | POD与指定POD不在同一拓扑域 |
集群中并非所有 Kubernetes 节点都相同。某些节点可能具有特殊的硬件,例如 GPU、磁盘或网络功能。同样,我们可能需要将一些节点专用于测试、数据保护或用户组。我们可以将 Taint 添加到节点以排斥 Pod。
Node Taints:
- 一个Node可以有多个Taints
- Effect(不能为空)
- NoSchedule - 只是禁止新Pods调度上来
- PreferNoSchedule - 尽量不调度到该节点来
- NoExecyte - 会evict(驱逐)没有对应toleration的Pods,并且也不会调度新的来
Pod Tolerations:
- 一个Pod可以有多个Tolerations
- Effect可以为空,匹配所有
- 取值和Taints的Effect一致
- Operator
- Exists/Equal
限制 Pod 调度到节点:
比如说现在有个 node 叫 test-node,这个节点有问题,我想限制一些 Pod 调度上来。这时可以给这个节点打一个 taints,taints 内容包括 key、value、effect:
- key:配置的键值
- value :内容
- effect 是标记了这个 taints 行为是什么
目前 effect 共有三个可选项,可按实际需求进行设置:
- NoSchedule 禁止新的 Pod 调度上来;
- PreferNoSchedul 尽量不调度到这台;
- NoExecute 会 evict 没有对应 toleration 的 Pods,并且也不会调度新的上来。这个策略是非常严格的,大家在使用的时候要小心一点。
查看master节点
[root@master ~]# kubectl describe node master CreationTimestamp: Sat, 18 Dec 2021 11:50:26 +0800 Taints: node-role.kubernetes.io/master:NoSchedule // 默认为不可调度 Unschedulable: false Lease: HolderIdentity: master
注意:这里是kubeadm部署,master自带污点;使用二进制部署的集群,默认是没有污点存在的
去掉 master节点污点
// 删除就是在添加污点的后面加一个减号"-" [root@master ~]# kubectl taint node master node-role.kubernetes.io/master- node/master untainted [root@master ~]# kubectl describe node master CreationTimestamp: Sat, 18 Dec 2021 11:50:26 +0800 Taints:Unschedulable: false Lease: HolderIdentity: master
添加污点
[root@master ~]# kubectl taint node master node-role.kubernetes.io/master="":NoSchedule node/master tainted CreationTimestamp: Sat, 18 Dec 2021 11:50:26 +0800 Taints: node-role.kubernetes.io/master:NoSchedule Unschedulable: false Lease: HolderIdentity: master5.11 NoSchedule:不可调度
实例:
// 查看各节点的污点 [root@master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready control-plane,master 5d11h v1.23.1 node1.example.com Ready5.12 PreferNoSchedule5d11h v1.23.1 node2.example.com Ready 5d11h v1.23.1 [root@master ~]# kubectl describe node master | grep Taints: Taints: node-role.kubernetes.io/master:NoSchedule [root@master ~]# kubectl describe node node1 | grep Taints: Taints: // 添加污点 [root@master ~]# kubectl taint node node1.example.com env_wu=yes:NoSchedule node/node1.example.com tainted [root@master ~]# kubectl describe node node1 | grep Taints:Taints: env_wu=yes:NoSchedule # 格式: # key=value:值 # k/v都可以自定义 添加NoSchedule污点后,节该节点不会调度pod运行;如果该节点已经运行pod,也不会删除 // 测试一下 创建一个test容器,复制成两个 [root@master ~]# kubectl create deployment test --image=nginx deployment.apps/test created [root@master ~]#kubectl create deployment test--replicas=2 deployment.apps/test created // 全部由node2运行 [root@master ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-8499f4f74-nnkn4 1/1 Running 0 38s 10.244.2.72 node2.example.com test-8499f4f74-r2w9l 1/1 Running 0 13s node2.example.com // 删除污点,重新扩展Pod [root@master ~]# kubectl taint node node1.example.com env_wu=yes:NoSchedule- node/node1.example.com untainted [root@master ~]# kubectl describe node node1 | grep Taints:Taints: [root@master ~]# node/node1.example.com untainted CreationTimestamp: Sat, 18 Dec 2021 12:00:45 +0800 Taints: Unschedulable: false Lease: HolderIdentity: node1.example.com
// 添加污点 [root@master ~]# kubectl taint node node1.example.com env_wu=yes:PreferNoSchedule node/node1.example.com tainted [root@master ~]# kubectl create deployment nginx --image=nginx deployment.apps/nginx created // 复制60个 [root@master ~]# kubectl create deployment nginx --replicas=60 deployment.apps/nginx created // 当没有污点的 node2节点资源不足时则会向有污点 node1转移 [root@master ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-76b56fd968-2bb82 0/1 Terminating 0 61s5.2 Toleration(污点容忍)node2.example.con nginx-76b56fd968-2fnbp 0/1 Pending 0 4s node1.example.com nginx-76b56fd968-2l626 0/1 Terminating 0 2m1s node2.example.com nginx-76b56fd968-2z9pv 0/1 Pending 0 4s node1.example.com nginx-76b56fd968-44skb 0/1 Pending 0 4s node1.example.com nginx-76b56fd968-4pphl 0/1 Pending 0 3s node1.example.com nginx-76b56fd968-55jb8 0/1 Terminating 0 61s node1.example.com nginx-76b56fd968-58r4z 0/1 Terminating 0 2m1s node2.example.com nginx-76b56fd968-5c9hn 0/1 Pending 0 4s node1.example.com // 删除污点 [root@master ~]# kubectl taint node node1.example.com env_wu=yes:PreferNoSchedule- //清除污点 node/node01 untainted [root@master ~]# kubectl describe node node1 | grep Taints: Taints: // 查看污点是否删除 [root@master ~]# kubectl describe node node1 CreationTimestamp: Sat, 18 Dec 2021 12:00:45 +0800 Taints: Unschedulable: false Lease: HolderIdentity: node1.example.com
// 添加污点/NoExecute (驱逐)
[root@master ~]# kubectl taint node node01 env_wu=yes:NoExecute
node/node1.example.com tainted
[root@master ~]# vi test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-ton
spec:
selector:
matchLabels:
app: test-ton
replicas: 3
template:
metadata:
labels:
app: test-ton
spec:
containers:
- name: nginx
image: nginx
// 没有node1节点Pod
[root@master ~]# kubectl get pod -o wide //node01无pod
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-ton-6798d496c7-2rfnf 1/1 Running 0 25s node2.example.com
test-ton-6798d496c7-2x6j9 1/1 Running 0 25s node2.example.com
test-ton-6798d496c7-r66wk 1/1 Running 0 25s node2.example.com
// 添加容忍
// 编写yaml文件
[root@master ~]# vi test2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-ton
spec:
selector:
matchLabels:
app: test-ton
replicas: 3
template:
metadata:
labels:
app: test-ton
spec:
containers:
- name: nginx
image: nginx
tolerations: // 容忍
- key: "env_wu" // 设置污点的key
operator: "Equal"
value: "yes" // 设置污点的values
effect: "NoExecute" // 设置污点类型
// 应用yaml文件
[root@master ~]# kubectl apply -f ss.yaml
Warning: resource deployments/test-ton is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically. // 警告可以不用管
deployment.apps/test-taint configured
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-ton-6798d496c7-2x6j9 1/1 Running 0 5m32s 10.244.2.64 node2.example.com
test-ton-749fbcf99f-jlf42 1/1 Running 0 40s 10.244.1.62 node1.example.com
test-ton-749fbcf99f-rtrbr 1/1 Running 0 21s 10.244.2.67 node2.example.com
test-ton-749fbcf99f-v86zc 0/1 ContainerCreating 0 3s node1.example.com
六、nodename
nodeName是最简单的节点选择约束形式,但由于其局限性,通常不使用它。 是 PodSpec 的一个域。如果它是非空的,调度程序将忽略 pod,并且在命名节点上运行的 kubelet 会尝试运行该 pod。因此,如果在 PodSpec 中提供,则它优先于上述节点选择方法。nodeName nodeName
使用来选择节点的一些限制是:nodeName
- 如果命名节点不存在,则不会运行 Pod,并且在某些情况下可能会被自动删除。
- 如果命名节点没有容纳 pod 的资源,则 Pod 将失败,其原因将指示原因,例如 OutOfmemory 或 OutOfcpu。
- 云环境中的节点名称并不总是可预测的或稳定的。
下面是使用该字段的 pod 配置文件的示例:nodeName
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: kube-01



