基于 centos7 搭建的 k8s 集群,搭建方法见 CentOS7配置k8s集群环境。
集群包含一个 master 节点,两个 node 节点,IP 如下:
节点 | IP |
---|---|
master | 192.168.10.21 |
node1 | 192.168.10.23 |
node2 | 192.168.10.22 |
Affinity 翻译成中文是“亲和性”,它对应的是 Anti-Affinity,我们翻译成“互斥”。这两个词比较形象,可以把 Pod 选择 node 的过程类比成磁铁的吸引和互斥,不同的是除了简单的正负极之外,Pod 和 node 或 Pod 和 Pod 的吸引和互斥是可以灵活配置的。
亲和度调度可分为两种:节点亲和度和 Pod 亲和度。
两种不同的亲和性调度都有两种策略:硬亲和性调度和软亲和性调度
污点是 node 的属性,容忍度是 Pod 的属性。如果说亲和性是“吸引”,那么污点就是“排斥”。不能容忍 node 污点的 Pod 不会被调度到该 node 上。只有能够容忍 node 污点的 Pod 才可以被调度到该 node 上。通常搭建好集群后,新建的 Pod 不会被调度到 master 节点上,就是因为 k8s 默认对 master 节点添加了污点。
注意:亲和度是为 node 或 pod 加标签,一个 node 或 pod 可以有多个不同的标签。污点是对 node 添加污点属性,与标签类似。在下面的实验中可以看到如何实现。
节点亲和性规则有两种。一是硬亲和性,实现强执行规则,二是软亲和性,实现柔性调度规则。定义节点亲和规则的关键点有两个,一是为节点配置合乎需求的标签,另一个是为 Pod 对象定义合理的标签选择器。
节点的亲和性可以通过 pod.spec.affinity.nodeAffinity 字段定义,nodeAffinity 字段中支持使用 matchExpressions 属性构建更为复杂的标签选择器。
对 node 标签的操作参考博客 k8s对node添加Label 。
kubectl label nodes =
[root@master ~]# kubectl label nodes node1 type=node1
[root@master ~]# kubectl label nodes node2 type=node2
# 查看节点标签
[root@master ~]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready master 9d v1.19.3 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/master=,type=master
node1 Ready 9d v1.19.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux,type=node1
node2 Ready node2 9d v1.19.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux,node-role.kubernetes.io/node2=,type=node2,zone=foo
可以看到 node 信息最后的 type 标签分别为 node1 和 node2 。
[root@master ~]# vim test01.yaml
# 定义一个 pod 资源清单,将该 pod 调度至有标签为 type=node1 的节点上
apiVersion: v1
kind: Pod
metadata:
name: test01
spec:
# 亲和性调度
affinity:
# 节点亲和性调度
nodeAffinity:
# 硬策略
requiredDuringSchedulingIgnoredDuringExecution:
# 指定亲和性
nodeSelectorTerms:
- matchExpressions:
- {key: type, operator: In, values: ["node1"]}
containers:
- name: test01
image: nginx
# 部署pod并查看其信息
[root@master ~]# kubectl apply -f test01.yaml
pod/test01 created
[root@master ~]# kubectl get pod test01-o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test01 1/1 Running 0 35s 10.244.1.25 node1
可以看到 Pod 被部署到 node1 节点上
[root@master ~]# vim test01.yaml
# 定义一个 pod 资源清单,将该 pod 调度至有标签为 type=node2 的节点上
apiVersion: v1
kind: Pod
metadata:
name: test01
spec:
# 亲和性调度
affinity:
# 节点亲和性调度
nodeAffinity:
# 硬策略
requiredDuringSchedulingIgnoredDuringExecution:
# 指定亲和性
nodeSelectorTerms:
- matchExpressions:
- {key: type, operator: In, values: ["node2"]}
containers:
- name: test01
image: nginx
# 部署pod并查看其信息
[root@master ~]# kubectl apply -f test01.yaml
pod/test01 created
[root@master ~]# kubectl get pod test01 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test01 1/1 Running 0 48s 10.244.2.62 node2
可以看到 Pod 被部署到 node2 节点上
节点软亲和性为节点选择提供了一种柔性的控制器逻辑,当条件不满足时,也能够接受被编排与其他不符合条件的节点之上。同时他还为每种倾向性提供了 weight 属性以便用于自定义优先级,范围是1-100,越大越优。
[root@master ~]# vim test01.yaml
# 定义一个 pod 资源清单,将该 pod 调度至有标签为 type=node2 的节点上
apiVersion: v1
kind: Pod
metadata:
name: test01
spec:
# 亲和性调度
affinity:
# 节点亲和性调度
nodeAffinity:
# 软策略
preferredDuringSchedulingIgnoredDuringExecution:
# 指定亲和性及权重
- weight: 60
preference:
matchExpressions:
- {key: type, operator: In, values: ["node1"]}
- weight: 30
preference:
matchExpressions:
- {key: tpye, operator: In, values: ["node2"]}
containers:
- name: test01
image: nginx
# 部署pod并查看其信息
[root@master ~]# kubectl apply -f test01.yaml
pod/test01 created
[root@master ~]# kubectl get pod test-node3 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test01 1/1 Running 0 35s 10.244.1.25 node1
可以看到,由于 Pod 对 node1 的亲和性权重较高,因此被部署到了 node1 上。
出于某些需求,将一些 Pod 对象组织在相近的位置(同一节点、机架、区域等),此时这些这些pod对象间的关系为亲和性。
Pod 的亲和性调度也存在硬亲和性和软亲和性的区别,他们表示的约束意义同节点亲和性相似。
Pod硬亲和性调度也使用 requiredDuringSchedulingIgnoreDuringExecution 属性进行定义。
# 部署第一个基础 Pod
[root@master ~]# vim test-1.yaml
# 定义一个 pod 资源清单,设置标签为 webweb,部署到 node1 上
apiVersion: v1
kind: Pod
metadata:
name: test-1
labels:
app: webweb
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {key: type, operator: In, values: ["node1"]}
containers:
- name: test-1
image: nginx
# 部署
[root@master ~]# kubectl apply -f test-1.yaml
pod/test-1 created
# 部署第二个基础 Pod
[root@master ~]# vim test00.yaml
# 定义一个 pod 资源清单,设置标签为 web,部署到 node2 上
apiVersion: v1
kind: Pod
metadata:
name: test00
labels:
app: web
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {key: type, operator: In, values: ["node2"]}
containers:
- name: test00
image: nginx
[root@master ~]# kubectl apply -f test00.yaml
pod/test00 created
# 查看基础 Pod 位置
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-1 1/1 Running 0 13s 10.244.1.27 node1
test00 1/1 Running 0 37s 10.244.2.63 node2
可以看到,一个位于 node1,一个位于 node2。
[root@master ~]# vim test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["web"]}
topologyKey: kubernetes.io/hostname
containers:
- name: test-pod
image: nginx
[root@master ~]# kubectl apply -f test-pod.yaml
pod/test-pod1created
[root@master ~]# kubectl get pod test-pod1 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-pod 1/1 Running 0 15s 10.244.2.64 node2
可以看到 test-pod 被调度到了 node2 上。
Pod软亲和调度使用方法与Node软亲和调度方法类似。
[root@master ~]# vim test-pod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod2
spec:
# 亲和性调度
affinity:
# 节点亲和性调度
podAffinity:
# 软策略
preferredDuringSchedulingIgnoredDuringExecution:
# 指定亲和性及权重
- weight: 80
podAffinityTerm:
labelSelector:
matchExpressions
- {key: app, operator: In, values: ["web"]}
topologyKey: kubernetes.io/hostname
- weight: 20
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["webweb"]}
topologyKey: kubernetes.io/hostname
containers:
- name: test-pod2
image: nginx
# 生成 Pod 并查看位置信息
[root@master ~]# kubectl apply -f test01.yaml
pod/test-pod2 created
[root@master ~]# kubectl get pod test-pod2 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-pod2 1/1 Running 0 20s 10.244.2.65 node2
可以看到 test-pod2 被调度到 node2 上。
# 先删除 test-pod2
[root@master ~]# kubectl delete pod test-pod2
pod "test-pod2" deleted
# 修改配置文件
[root@master ~]# vim test-pod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod2
spec:
# 亲和性调度
affinity:
# 节点亲和性调度
podAffinity:
# 软策略
preferredDuringSchedulingIgnoredDuringExecution:
# 指定亲和性及权重
- weight: 20
podAffinityTerm:
labelSelector:
matchExpressions
- {key: app, operator: In, values: ["web"]}
topologyKey: kubernetes.io/hostname
- weight: 80
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["webweb"]}
topologyKey: kubernetes.io/hostname
containers:
- name: test-pod2
image: nginx
# 生成 Pod 并查看位置信息
[root@master ~]# kubectl apply -f test01.yaml
pod/test-pod2 created
[root@master ~]# kubectl get pod test-pod2 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-pod2 1/1 Running 0 3m12s 10.244.1.28 node1
污点:定义在节点之上的键值性属性数据,用于让节点拒绝将Pod调度运行于其上,除非该Pod对象具有接纳节点污点的容忍度
容忍度:定义在Pod对象上的键值型的属性数据,用于配置其可容忍的节点污点,而且调度器仅能将Pod对象调度至其能够容忍该节点污点的节点之上。
污点信息在节点的node.Spec字段中定义,容忍度信息在pod的pod.spec字段中定义,他们都是键值型数据,但都额外支持一个效果(effect)标记,语法格式为“key=value:effect”。Effect主要用于定义对Pod对象的排斥等级,主要的类型如下:
(1)NoSchedule:不能容忍此污点的Pod对象不可调度至当前节点,属于强制约束性的关系,对节点现存Pod对象不受影响。
(2)PreferNoSchedule:NoSchedule的柔性约束版本,不能容忍此污点的Pod对象尽量不要调度至当前节点
(3)NoExecute:不能容忍此污点的新Pod对象不可调度至当前节点,属强制型约束关系,而且节点上现存的Pod对象因节点污点变动或Pod容忍度变动而不再满足匹配规则时Pod对象将被驱逐。
[root@master ~]# kubectl taint nodes node1 node-type=production:NoSchedule
node/node1 tainted
[root@master ~]# kubectl taint nodes node2 node-type=web:NoSchedule
node/node2 tainted
[root@master ~]# vim test-taint1.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-taint1
spec:
containers:
- name: test-taint1
image: nginx
# 部署
[root@master ~]# kubectl apply -f test-taint1.yaml
pod/test-taint1 created
[root@master ~]# kubectl get pod test-taint1 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-taint1 0/1 Pending 0 8m12s
可以看到 test-taint1 一直处于 padding 状态,查看其 describe 信息。
[root@master ~]# kubectl describe pod test-taint1
Name: test-taint1
Namespace: default
Priority: 0
Node:
Labels:
Annotations:
Status: Pending
IP:
......
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
---- ------ ---- ---- -------
Warning FailedScheduling 36s default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 1 node(s) had taint {node-type: production}, that the pod didn't tolerate, 1 node(s) had taint {node-type: web}, that the pod didn't tolerate.
Warning FailedScheduling 36s default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 1 node(s) had taint {node-type: production}, that the pod didn't tolerate, 1 node(s) had taint {node-type: web}, that the pod didn't tolerate.
可看到最后的 Warning 信息显示该 pod 不能容忍两个 node 的污点,部署失败。
[root@master ~]# vim test-taint2.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-taint2
spec:
tolerations:
- key: node-type
operator: "Equal"
value: "production"
effect: "NoSchedule"
containers:
- name: test-taint2
image: nginx
# 部署
[root@master ~]# kubectl apply -f test-taint2.yaml // 容忍node1
pod/test-taint2 created
[root@master ~]# kubectl get pod test-taint2 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-taint2 1/1 Running 0 18s 10.244.1.29 node1
可以看到 test-taint2 被调度到了 node1 上。dashboard 显示信息如下。
[root@master ~]# vim test-taint3.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-taint3
spec:
tolerations:
- key: node-type
operator: "Equal"
value: "web"
effect: "NoSchedule"
containers:
- name: test-taint3
image: nginx
# 部署
[root@master ~]# kubectl apply -f test-taint3.yaml // 容忍node1
pod/test-taint3 created
[root@master ~]# kubectl get pod test-taint3 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-taint3 1/1 Running 0 6s 10.244.2.67 node2