1、亲和性/反亲和性介绍
nodeSelector 提供了一种最简单的方法来将 Pod 约束到具有特定标签的节点上。 亲和性和反亲和性扩展了你可以定义的约束类型。使用亲和性与反亲和性的一些好处有:
- 亲和性、反亲和性语言的表达能力更强。nodeSelector 只能选择拥有所有指定标签的节点。 亲和性、反亲和性为你提供对选择逻辑的更强控制能力。
- 你可以标明某规则是“软需求”或者“偏好”,这样调度器在无法找到匹配节点时仍然调度该 Pod。
- 你可以使用节点上(或其他拓扑域中)运行的其他 Pod 的标签来实施调度约束, 而不是只能使用节点本身的标签。这个能力让你能够定义规则允许哪些 Pod 可以被放置在一起。
亲和性功能由两种类型的亲和性组成:
- 节点亲和性功能类似于 nodeSelector 字段,但它的表达能力更强,并且允许你指定软规则。
- Pod 间亲和性/反亲和性允许你根据其他 Pod 的标签来约束 Pod。
2、Node节点亲和力/反亲和力
节点亲和性概念上类似于 nodeSelector, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。 节点亲和性有两种:
- requiredDuringSchedulingIgnoredDuringExecution: 调度器只有在规则被满足的时候才能执行调度。此功能类似于 nodeSelector, 但其语法表达能力更强。
- preferredDuringSchedulingIgnoredDuringExecution: 调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。
2.1 配置文件
spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬匹配
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os # 匹配的key
operator: In # 匹配的操作,In代表在
values:
- linux # 匹配的值
preferredDuringSchedulingIgnoredDuringExecution: # 软匹配
- weight: 1 # 匹配的权重,数值越大,权重越高
preference:
matchExpressions:
- key: label-1 # 匹配的key
operator: In
values:
- key-1 # 匹配的值
- weight: 50
preference:
matchExpressions:
- key: label-2
operator: In
values:
- key-2
2.2 上文的软匹配中,给node1和node2分别添加一个标签
[root@k8s-master ~]# kubectl label nodes k8s-node-01 label-1=key-1
node/k8s-node-01 labeled
[root@k8s-master ~]# kubectl label nodes k8s-node-02 label-2=key-2
node/k8s-node-02 labeled
2.3 将上面节点亲和力的配置添加到deployment中
2.4 查看pod情况
由于requiredDuringSchedulingIgnoredDuringExecution 硬匹配都可以匹配到,但是 在 preferredDuringSchedulingIgnoredDuringExecution 软匹配的时候,node2的亲和力比node1的高,所以pod都跑到了node2上。
2.5 修改一个软亲和力的操作
2.6 查看pod情况
2.7 NodeAffinity 亲和力的匹配类型
- In: 满足一个就满足,就把pod部署过去
- NotIn: 一个都不能满足,就是你满足了这个条件,pod不可以部署过来,也就是反亲和力
- Exist: 只要存在就满足
- DoesNotExist: 只要不存在就满足
- Gt: 必须要打大于节点上的数值
- Lt: 必须要打小于节点上的数值
3、Pod资源亲和力/反亲和力
Pod 间亲和性与反亲和性使你可以基于已经在节点上运行的 Pod 的标签来约束 Pod 可以调度到的节点,而不是基于节点上的标签。
- Pod亲和力:将与指定pod亲和力相匹配的pod部在同一节点。
- Pod反亲和力,将与指定的pod亲和力想匹配的pod部署在不同的同一节点
3.1 分别给node打标签
[root@k8s-master ~]# kubectl label nodes k8s-node-01 k8s-node-02 topology.kubernetes.io/zone=V
node/k8s-node-01 labeled
node/k8s-node-02 labeled
[root@k8s-master ~]# kubectl label nodes k8s-master topology.kubernetes.io/zone=R
node/k8s-master labeled
[root@k8s-master ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready control-plane 9d v1.25.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=,topology.kubernetes.io/zone=R
k8s-node-01 Ready <none> 9d v1.25.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node-01,kubernetes.io/os=linux,label-1=key-1,topology.kubernetes.io/zone=V,type=microsvc
k8s-node-02 Ready <none> 7d1h v1.25.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node-02,kubernetes.io/os=linux,label-2=key-2,topology.kubernetes.io/zone=V,type=microsvc
3.2 创建第一个deploy资源,pod标签是 security: S1
3.2.1 配置文件
apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息
labels: # 标签
app: nginx-deploy # 具体的 key: value 配置形式
name: nginx-deploy-s1 # deployment 的名字
namespace: default # 所在的命名空间
spec:
replicas: 2 # 期望副本数
revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
selector: # 选择器,用于找到匹配的 RS
matchLabels: # 按照标签匹配
app: nginx-deploy # 匹配的标签key/value
strategy: # 更新策略
rollingUpdate: # 滚动更新配置
maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
type: RollingUpdate # 更新类型,采用滚动更新
template: # pod 模板
metadata: # pod 的元信息
labels: # pod 的标签
app: nginx-deploy
# topology.kubernetes.io/zone: V
security: S1
spec: # pod 期望信息
containers: # pod 的容器
- image: nginx:1.20 # 镜像
imagePullPolicy: IfNotPresent # 拉取策略
name: nginx # 容器名称
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
restartPolicy: Always # 重启策略
terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间
3.2.2 创建这个deploy
[root@k8s-master affinity]# kubectl create -f s1-nginx-deploy-affinity.yaml
deployment.apps/nginx-deploy-s1 created
3.2.3 查看这个pod信息
- 有一个pod运行在了master、有一个运行在了node-02
[root@k8s-master affinity]# kubectl get po -owide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
dns-test 1/1 Running 2 (2d12h ago) 4d21h 10.2.1.58 k8s-node-02 <none> <none> run=dns-test
fluentd-59k8k 1/1 Running 1 (2d12h ago) 4d3h 10.2.2.34 k8s-node-01 <none> <none> app=logging,controller-revision-hash=7555d95dc,id=fluentd,pod-template-generation=2
fluentd-hhtls 1/1 Running 1 (2d12h ago) 4d3h 10.2.1.59 k8s-node-02 <none> <none> app=logging,controller-revision-hash=7555d95dc,id=fluentd,pod-template-generation=2
nginx-deploy-579987f57f-ccgd8 1/1 Running 0 51m 10.2.2.113 k8s-node-01 <none> <none> app=nginx-deploy,pod-template-hash=579987f57f
nginx-deploy-579987f57f-hwpjk 1/1 Running 0 51m 10.2.2.112 k8s-node-01 <none> <none> app=nginx-deploy,pod-template-hash=579987f57f
nginx-deploy-s1-5cc987b754-82xt5 1/1 Running 0 26s 10.2.1.92 k8s-node-02 <none> <none> app=nginx-deploy,pod-template-hash=5cc987b754,security=S1
nginx-deploy-s1-5cc987b754-kwsk5 1/1 Running 0 26s 10.2.0.7 k8s-master <none> <none> app=nginx-deploy,pod-template-hash=5cc987b754,security=S1
3.3 创建第二个deploy资源,pod标签是 security: S2
3.3.1 配置文件
apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息
labels: # 标签
app: nginx-deploy # 具体的 key: value 配置形式
name: nginx-deploy-s2 # deployment 的名字
namespace: default # 所在的命名空间
spec:
replicas: 1 # 期望副本数
revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
selector: # 选择器,用于找到匹配的 RS
matchLabels: # 按照标签匹配
app: nginx-deploy # 匹配的标签key/value
strategy: # 更新策略
rollingUpdate: # 滚动更新配置
maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
type: RollingUpdate # 更新类型,采用滚动更新
template: # pod 模板
metadata: # pod 的元信息
labels: # pod 的标签
app: nginx-deploy
# topology.kubernetes.io/zone: V
security: S2
spec: # pod 期望信息
nodeSelector:
kubernetes.io/hostname: k8s-master
containers: # pod 的容器
- image: nginx:1.20 # 镜像
imagePullPolicy: IfNotPresent # 拉取策略
name: nginx # 容器名称
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
restartPolicy: Always # 重启策略
terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间
3.3.2 创建这个deploy
[root@k8s-master affinity]# kubectl create -f s2-nginx-deploy-affinity.yaml
deployment.apps/nginx-deploy-s2 created
3.3.3 查看这个pod信息
- 这个pod运行在了master上
[root@k8s-master affinity]# kubectl get po -owide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
dns-test 1/1 Running 2 (2d12h ago) 4d21h 10.2.1.58 k8s-node-02 <none> <none> run=dns-test
fluentd-59k8k 1/1 Running 1 (2d12h ago) 4d3h 10.2.2.34 k8s-node-01 <none> <none> app=logging,controller-revision-hash=7555d95dc,id=fluentd,pod-template-generation=2
fluentd-hhtls 1/1 Running 1 (2d12h ago) 4d3h 10.2.1.59 k8s-node-02 <none> <none> app=logging,controller-revision-hash=7555d95dc,id=fluentd,pod-template-generation=2
nginx-deploy-579987f57f-ccgd8 1/1 Running 0 55m 10.2.2.113 k8s-node-01 <none> <none> app=nginx-deploy,pod-template-hash=579987f57f
nginx-deploy-579987f57f-hwpjk 1/1 Running 0 56m 10.2.2.112 k8s-node-01 <none> <none> app=nginx-deploy,pod-template-hash=579987f57f
nginx-deploy-s1-5cc987b754-82xt5 1/1 Running 0 5m19s 10.2.1.92 k8s-node-02 <none> <none> app=nginx-deploy,pod-template-hash=5cc987b754,security=S1
nginx-deploy-s1-5cc987b754-kwsk5 1/1 Running 0 5m19s 10.2.0.7 k8s-master <none> <none> app=nginx-deploy,pod-template-hash=5cc987b754,security=S1
nginx-deploy-s2-656bfc8d9c-qpzl4 1/1 Running 0 3s 10.2.0.8 k8s-master <none> <none> app=nginx-deploy,pod-template-hash=656bfc8d9c,security=S2
3.4 创建这个Pod亲和力的deploy资源
3.4.1 配置文件
apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息
labels: # 标签
app: nginx-deploy # 具体的 key: value 配置形式
name: nginx-deploy-test # deployment 的名字
namespace: default # 所在的命名空间
spec:
replicas: 2 # 期望副本数
revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
selector: # 选择器,用于找到匹配的 RS
matchLabels: # 按照标签匹配
app: nginx-deploy # 匹配的标签key/value
strategy: # 更新策略
rollingUpdate: # 滚动更新配置
maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
type: RollingUpdate # 更新类型,采用滚动更新
template: # pod 模板
metadata: # pod 的元信息
labels: # pod 的标签
app: nginx-deploy
spec: # pod 期望信息
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: topology.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: topology.kubernetes.io/zone
containers: # pod 的容器
- image: nginx:1.20 # 镜像
imagePullPolicy: IfNotPresent # 拉取策略
name: nginx # 容器名称
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
restartPolicy: Always # 重启策略
terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间
3.4.2 创建这个deploy
[root@k8s-master affinity]# kubectl create -f nginx-affinity-deploy.yaml
deployment.apps/nginx-deploy-test created
3.4.3 查看这个pod信息
- 正常按照调度规则是s1所在的node上,新的这个亲和力的pod应该被创建到和s1相同的节点上,但是目前这个创建是不一样的。
- 可能原因:
3.5 删除目前不使用的资源后重复以上3.2-3.4的操作过程后
- 复现了我们设置的亲和力规则,新创建的资源匹配到 security=S1,两个资源所在的节点都是同一个节点。