K8S 亲和性与反亲和性 深度好文
今天我们来实验 pod 亲和性。官网描述如下:
假设有如下三个节点的 K8S 集群:
k8s31master 是控制节点
k8s31node1、k8s31node2 是工作节点
容器运行时是 containerd
一、镜像准备
1.1、镜像拉取
docker pull tomcat:8.5-jre8-alpine
docker pull nginx:1.14.2
1.2、镜像导出
docker save -o tomcat-8.5-jre8-alpine.tar.gz docker.io/library/tomcat:8.5-jre8-alpine
docker save -o nginx-1.14.2.tar.gz docker.io/library/nginx:1.14.2
1.3、镜像导入工作节点 containerd
# k8s31node1 执行
[root@k8s31node1 ~]# ctr -n=k8s.io images import tomcat-8.5-jre8-alpine.tar.gz
[root@k8s31node1 ~]# ctr -n=k8s.io images import nginx-1.14.2.tar.gz
[root@k8s31node1 ~]# ctr -n=k8s.io images ls|grep tomcat
[root@k8s31node1 ~]# ctr -n=k8s.io images ls|grep nginx# k8s31node2 执行
[root@k8s31node2 ~]# ctr -n=k8s.io images import tomcat-8.5-jre8-alpine.tar.gz
[root@k8s31node2 ~]# ctr -n=k8s.io images import nginx-1.14.2.tar.gz
[root@k8s31node2 ~]# ctr -n=k8s.io images ls|grep tomcat
[root@k8s31node2 ~]# ctr -n=k8s.io images ls|grep nginx
说明:
- ctr 是 containerd 命令
- ctr images import:导入镜像
- -n=k8s.io:K8S 镜像存储命名空间
1.4、亲和性介绍
- 亲和性(affinity)属性,位于 pod.spec.affinity,它有三种亲和性:
kubectl explain pod.spec.affinity
分别是 nodeAffinity(节点亲和性)、podAffinity(pod间亲和性)、podAntiAffinity(pod间反亲和性),它们可以分为两类:
- 节点亲和性功能类似于 nodeSelector 字段,但它的表达能力更强,并且允许你指定软规则。
- Pod 间亲和性/反亲和性允许你根据其他 Pod 的标签来约束 Pod。
简单来说:
nodeAffinity 定义了 pod 倾向于(亲和)被调度到哪些节点上。
podAffinity 定义了 pod 倾向于(亲和)跟哪些 pod 调度在一起。
podAntiAffinity 定义 pod 倾向于不(反亲和)跟哪些 pod 调度在一起。
二、nodeAffinity(节点亲和性)
- 查看帮助文档
kubectl explain pod.spec.affinity.nodeAffinity
节点亲和性概念上类似于
nodeSelector
, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。 节点亲和性有两种:
requiredDuringSchedulingIgnoredDuringExecution
: 调度器只有在规则被满足的时候才能执行调度。此功能类似于nodeSelector
, 但其语法表达能力更强。preferredDuringSchedulingIgnoredDuringExecution
: 调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。在上述类型中,
IgnoredDuringExecution
意味着如果节点标签在 Kubernetes 调度 Pod 后发生了变更,Pod 仍将继续运行。简单来说:
required 表示必须有节点满足这个位置定义的亲和性,这是个硬性条件,硬亲和性。
preferred 表示有节点尽量满足这个位置定义的亲和性,这不是一个必须的条件,软亲和性。
2.1、required 硬亲和性
- 查看 requiredDuringSchedulingIgnoredDuringExecution
kubectl explain pod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution
它有一个必填字段 nodeSelectorTerms。
- 查看 nodeSelectorTerms
kubectl explain pod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms
它是 NodeSelectorTerm 数组
NodeSelectorTerm 定义了两种匹配模式:
- matchExpressions 数组
- matchFields 数组
2.1.1、matchExpressions
matchExpressions:它允许你使用表达式来匹配节点的标签。例如,你可以使用 In、NotIn、Exists、DoesNotExist、Gt、Lt 等操作符来创建复杂的标签匹配规则。
kubectl explain pod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchExpressions
key:标签名称。
operator:匹配操作。
values:值列表。[] 或 - 形式都可以。
- 编写资源文件
pod-node-affinity-required-match-expressions-demo.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-node-affinity-required-match-expressions
spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: zoneoperator: Invalues:- east- southcontainers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
matchExpressions 表达式的意思是,寻找具有 label key 为 zone,值为 east 或 south 的节点,把 pod 绑定上去。
- 运行并查看
kubectl apply -f pod-node-affinity-required-match-expressions-demo.yaml
kubectl get pod -owide
会发现 pod 并没有被正确调度。
因为我现在工作节点上,并没有一个节点有 zone 标签,值为 east 或 south。
required 是硬亲和性,必须满足表达式,pod 才能被正确调度。
- 查看 pod 日志
kubectl describe pod pod-node-affinity-required-match-expressions
也能发现报 node affinity 错误。
- 给 k8s31node1 打上标签 zone=east
kubectl label node k8s31node1 zone=east
kubectl get node --show-labels
- 观察 pod 现在可以正常调度了
- 如果这个时候,我们变更 k8s31node1 的标签
kubectl label node k8s31node1 zone-
# zone- 表示删除标签 zone
kubectl get pod -owide
会发现 pod 并没有被驱逐。
2.1.2、matchFields
matchFields:它允许你根据资源的非标签字段进行匹配,例如资源的名称、状态等。
kubectl explain pod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchFields
matchFields 跟 matchExpressions 匹配模式一样。
key:标签名称。
operator:匹配操作。
values:值列表。[] 或 - 形式都可以。
- 编写资源文件
pod-node-affinity-required-match-fields-demo.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-node-affinity-required-match-fields
spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchFields:- key: metadata.nameoperator: Invalues: ['k8s31node2']containers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
matchFields 表达式的意思是,寻找节点具有 metadata.name 属性,且值是 k8s31node2 的节点,把 pod 绑定上去。
怎么看 node 节点具有哪些属性?
# 以 json 格式或 yaml 格式输出 节点信息 kubectl get node k8s31node2 -o json kubectl get node k8s31node2 -o yaml
- 运行并查看
kubectl apply -f pod-node-affinity-required-match-fields-demo.yaml
kubectl get pod -owide
可以发现,它被正确调度到 k8s31node2 上。
2.2、preferred 软亲和性
- 查看 preferredDuringSchedulingIgnoredDuringExecution
kubectl explain pod.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution
有两个必填字段:
preference:偏好。偏好 也是一个 NodeSelectorTerm,所以也会有 matchExpressions 和 matchFields。
weight:权重。1-100 的数,weight 是相对权重,权重越高,pod 调度的几率越大。
2.2.1、matchExpressions
- 编写资源文件
pod-node-affinity-preferred-match-expressions-demo.yaml
apiVersion: v1
kind: Pod
metadata: name: pod-node-affinity-preferred-match-expressions
spec:affinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution:- preference:matchExpressions:- key: diskoperator: Invalues: ['SSD']weight: 20- preference:matchExpressions:- key: diskoperator: Invalues: ['HDD']weight: 10containers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
preferred 匹配的意思是:
将 pod 节点优先调度到有标签 disk=SSD 的节点上。因为 disk=SSD 的 weight 数值更大,优先级更高。
业务含义是,将 pod 节点优先调度到拥有固态硬盘的节点上,没有固态硬盘的话,调度到拥有机械硬盘的节点上。
- 运行并查看
kubectl apply -f pod-node-affinity-preferred-match-expressions-demo.yaml
kubectl get pod -owide
虽然我们现在系统上并没有 disk=SSD 与 disk=HDD 的节点,但是 pod 依然可以正常调度。这是因为 preferred 是一种软亲和性,即使找不到符合条件的节点,调度器 scheduler 依然会调度该 pod。
- 给节点打标签
# 给 k8s31node1 节点打上 disk=HDD
kubectl label node k8s31node1 disk=HDD
# 给 k8s31node2 节点打上 disk=SSD
kubectl label node k8s31node2 disk=SSD
# 查看节点信息
kubectl get node --show-labels
- 删除原来的 pod 并运行
kubectl delete -f pod-node-affinity-preferred-match-expressions-demo.yaml
kubectl apply -f pod-node-affinity-preferred-match-expressions-demo.yaml
kubectl get pod -owide
可以看到 pod 被优先调度到 k8s31node2,因为它标签是 disk=SSD,权重最高。
- 如果这个时候,我们变更 k8s31node2 的标签
kubectl label node k8s31node2 disk-
kubectl get pod -owide
会发现 pod 并没有被驱逐。
matchFields 的情况与 required 类似,就不举例了。
2.3、节点亲和性总结
- 节点亲和性 nodeAffinity 包括 required 和 preferred
- required 是硬亲和性,只有条件满足,pod 才会被调度。
- preferred 是软亲和性,条件匹配,优先按条件调度,条件不匹配,按默认算法调度。
- matchExpressions 是按节点标签表达式来进行匹配。
- matchFields 是按节点属性来进行匹配。
- 无论 required 还是 preferred,在 pod 运行期,标签变更,pod 不会被驱逐。
2.4、还原实验环境
删除 default 命名空间下所有 pod,
删除节点所有标签,为下一个实验做准备。
三、podAffinity(pod间亲和性)
podAffinity 定义了 pod 倾向于(亲和)跟哪些 pod 调度在同一个位置。
- 查看帮助文档
kubectl explain pod.spec.affinity.podAffinity
与节点亲和性类似,Pod 的亲和性与反亲和性也有两种类型:
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
例如,你可以使用
requiredDuringSchedulingIgnoredDuringExecution
亲和性来告诉调度器,将两个服务的 Pod 放到同一个云提供商可用区内,因为它们彼此之间通信非常频繁。类似地,你可以使用
preferredDuringSchedulingIgnoredDuringExecution
反亲和性来将同一服务的多个 Pod 分布到多个云提供商可用区中。
3.1、required 硬亲和性
- 查看 requiredDuringSchedulingIgnoredDuringExecution
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution
它是 PodAffinityTerm 数组。
PodAffinityTerm 它有三个比较重要的字段:
toplogyKey:拓扑键,必填。我们在定义 pod 间亲和性时有一个前提,就是 B pod 想调度到跟 A pod 同一个位置,那么怎么定义这个位置?就是以这个字段来定义的。其取值是系统用来标示域的节点标签键。也就是说,不同节点具有相同标签 key,且 key 所对应的 value 也相同,则它们被定义为同一个位置。
假设有如下服务器集群,在可用区A中有节点 node1、node2,它们拥有相同的节点标签(zone=A)则 node1 与 node2 被定义为同一个位置。node3 因为拥有不同的节点标签(zone=B)所以 node3 被视为不同位置。
toplogyKey 它是一个拓扑的概念,同一个机架、可用区、地域里面所有节点,都可以被 K8S 视为同一个位置而被统一调度。
labelSelector:标签选择器。通过 labelSelector 选取一组能作为亲和对象的已存在的 pod 资源。它定义了两种匹配模式:
- matchExpressions <[]LabelSelectorRequirement>
- matchLabels <map[string]string>
namespaces:名称空间。pod 在 Kubernetes 中是名称空间作用域的对象,因此 pod 的标签也隐式地具有名称空间属性。 针对 pod 标签的所有 标签选择器 都要指定名称空间,Kubernetes 会在指定的名称空间内寻找标签。
如果不指定 namespaces,那么 标签选择器 就是在当前要创建的 pod 的名称空间里查找符合条件的一组 pod。
3.1.1、matchExpressions
matchExpressions:它允许你使用表达式来匹配 Pod 的标签。例如,你可以使用 In、NotIn、Exists、DoesNotExist 等操作符来创建复杂的标签匹配规则。
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.labelSelector.matchExpressions
key:标签名称。
operator:匹配操作。
values:值列表。[] 或 - 形式都可以。
- 实验准备
使用 kubeadm join 往集群中加入一个新的工作节点 k8s31node3:
- 镜像准备
# 将 tomcat、nginx 镜像也导入到 k8s31node3
# k8s31node3 执行
[root@k8s31node3 ~]# ctr -n=k8s.io images import tomcat-8.5-jre8-alpine.tar.gz
[root@k8s31node3 ~]# ctr -n=k8s.io images import nginx-1.14.2.tar.gz
[root@k8s31node3 ~]# ctr -n=k8s.io images ls|grep tomcat
[root@k8s31node3 ~]# ctr -n=k8s.io images ls|grep nginx
- 资源文件编写
假设我们现在有两个 pod,nginx 跟 tomcat,nginx 反向代理 tomcat,它们之间要频繁通信,所以我们希望 pod-nginx 跟 pod-tomcat 能调度到同一个可用区内。
pod-pod-affinity-required-match-expressions-tomcat.yaml
apiVersion: v1
kind: Pod
metadata:name: tomcatlabels:app: tomcat
spec:containers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
- 运行并查看
kubectl apply -f pod-pod-affinity-required-match-expressions-tomcat.yaml
kubectl get pod -owide
tomcat 被调度到 k8s31node3 节点上。
- 资源文件编写
pod-pod-affinity-required-match-expressions-nginx.yaml
apiVersion: v1
kind: Pod
metadata:name: nginx
spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- topologyKey: zonelabelSelector:matchExpressions:- {key: app, operator: In, values: ["tomcat"]}containers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentports:- containerPort: 80
- 运行并查看
kubectl apply -f pod-pod-affinity-required-match-expressions-nginx.yaml
kubectl get pod -owide
发现 nginx 无法被调度。
查看 pod 日志
kubectl describe pod nginx
报 pod 亲和性不符合。
原因是我们现在所有节点上,并没有定义 topologyKey=zone 这个键,而 required 是属于硬亲和性,在节点调度期,找不到符合调度规则的节点,系统不会对 pod 进行调度。
- 给节点打标签
kubectl label node k8s31node1 zone=A
kubectl label node k8s31node2 zone=B
kubectl label node k8s31node3 zone=B
kubectl get pod -owide
可以看到 nginx 被调度到 tomcat 所在的节点 k8s31node3 上了。
倘若这个时候,我们起一个 nginx:
pod-pod-affinity-required-match-expressions-nginx2.yaml
就只是把上一个 nginx.yaml 改了一下 metadata.name 为 nginx2 而已。
apiVersion: v1
kind: Pod
metadata:name: nginx2
spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- topologyKey: zonelabelSelector:matchExpressions:- {key: app, operator: In, values: ["tomcat"]}containers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentports:- containerPort: 80
kubectl apply -f pod-pod-affinity-required-match-expressions-nginx2.yaml
kubectl get pod -owide
会发现 nginx2 被调度到 k8s31node2 上了。
- 分析
整个的部署图如下:
- tomcat 首先被调度到 node3,这个过程是随机的,scheduler 调度器根据自己内部的调度算法来决定的。
- nginx 被调度时,因为 nginx 跟 tomcat podAffinity,所以它要被调度到跟 tomcat 具有相同 topologyKey 的节点上,这个时候 node3 跟 node2 都满足要求(node1 因为 topologyKey 的值是 A,所以不满足要求),scheduler 调度器觉得 node3 这个时候的负载不高,所以也把 nginx 调度到 node3 上。
- nginx2 被调度时,走 nginx 一样的逻辑,所以 node3 跟 node2 都满足要求,但此时 node3 负载已经很高了(跑着两个 pod),所以 scheduler 调度器决定将 nginx2 调度到 node2。
- 还原实验环境
删除 nginx 跟 nginx2 以便进行下面的实验。
kubectl delete pod nginx
kubectl delete pod nginx2
3.1.2、matchLabels
matchLabels 的匹配方式,相对于 matchExpressions 更简单,它是以 键值对 的方式进行匹配的。
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.labelSelector.matchLabels
- 编写资源文件
pod-pod-affinity-required-match-labels-nginx.yaml
apiVersion: v1
kind: Pod
metadata:name: nginx
spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- topologyKey: zonelabelSelector:matchLabels:app: tomcatcontainers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentports:- containerPort: 80
pod-pod-affinity-required-match-labels-nginx2.yaml
俩个文件之间的差异,只在 metadata.name
apiVersion: v1
kind: Pod
metadata:name: nginx2
spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- topologyKey: zonelabelSelector:matchLabels:app: tomcatcontainers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentports:- containerPort: 80
- 运行并查看
kubectl apply -f pod-pod-affinity-required-match-labels-nginx.yaml
kubectl apply -f pod-pod-affinity-required-match-labels-nginx2.yaml
kubectl get pod -owide
可以看到跟 matchExpressions 是一样的效果。
- 节点标签变更
倘如我们这个时候,将所有节点的 zone 标签删除,看看会有什么效果。
kubectl label node k8s31node1 zone-
kubectl label node k8s31node2 zone-
kubectl label node k8s31node3 zone-
kubectl get pod -owide
可以看到 pod 并不会被驱逐。
- 还原实验环境
删除 tomcat、nginx 跟 nginx2 以便进行下面的实验。
kubectl delete pod tomcat
kubectl delete pod nginx
kubectl delete pod nginx2
3.2、preferred 软亲和性
- 查看 preferredDuringSchedulingIgnoredDuringExecution
kubectl explain pod.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution
它是 WeightedPodAffinityTerm 数组。
WeightedPodAffinityTerm 它有两个必填字段:
podAffinityTerm:PodAffinityTerm 对象。它跟上面 required 是一模一样的。所以也必然有 toplogyKey、labelSelector、namespaces。
weight:权重。1-100 的数,weight 是相对权重,权重越高,pod 调度的几率越大。
假设现在有如下部署图:
node1、node2、node3 分别有标签 zone=A、zone=B、zone=C,表示它们分别位于可用区 A B C。
node1、node2、node3 上分别运行着 pod tomcat1、tomcat2、tomcat3。
tomcat1、tomcat2、tomcat3 分别有标签 app=tomcat1、app=tomcat2、app=tomcat3,它们的权重,分别是 10、30、20。
现在有一个新的 pod-nginx,加入进来,我们看看 K8S 是如何调度的-->
- 编写 tomcat 配置文件
pod-pod-affinity-preferred-match-expressions-tomcat1.yaml
apiVersion: v1
kind: Pod
metadata:name: tomcat1labels:app: tomcat1
spec:nodeName: k8s31node1containers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
nodeName 指定它运行在 node1 节点上。
pod-pod-affinity-preferred-match-expressions-tomcat2.yaml
apiVersion: v1
kind: Pod
metadata:name: tomcat2labels:app: tomcat2
spec:nodeName: k8s31node2containers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
nodeName 指定它运行在 node2 节点上。
pod-pod-affinity-preferred-match-expressions-tomcat3.yaml
apiVersion: v1
kind: Pod
metadata:name: tomcat3labels:app: tomcat3
spec:nodeName: k8s31node3containers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
nodeName 指定它运行在 node3 节点上。
- 启动 tomcat
kubectl apply -f pod-pod-affinity-preferred-match-expressions-tomcat1.yaml
kubectl apply -f pod-pod-affinity-preferred-match-expressions-tomcat2.yaml
kubectl apply -f pod-pod-affinity-preferred-match-expressions-tomcat3.yaml
kubectl get pod -owide --show-labels
- 给节点打标签
kubectl label node k8s31node1 zone=A
kubectl label node k8s31node2 zone=B
kubectl label node k8s31node3 zone=C
kubectl get node --show-labels
- 编写 nginx 配置文件
pod-pod-affinity-preferred-match-expressions-nginx.yaml
apiVersion: v1
kind: Pod
metadata:name: nginx
spec:affinity:podAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 10podAffinityTerm: topologyKey: zonelabelSelector:matchExpressions:- {key: app, operator: In, values: ["tomcat1"]}- weight: 30podAffinityTerm:topologyKey: zonelabelSelector:matchExpressions:- {key: app, operator: In, values: ["tomcat2"]}- weight: 20podAffinityTerm:topologyKey: zonelabelSelector:matchExpressions:- {key: app, operator: In, values: ["tomcat3"]}containers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentports:- containerPort: 80
app=tomcat2 的权重最高,所以 nginx 需要被调度到跟 tomcat2 相同 zone 下的服务器上,因为该 zone 只有一个 node2,所以 nginx 被调度到 node2 上。
- 删掉 nginx,并修改所有 pod label
kubectl delete pod nginx
kubectl label pod tomcat1 app=tomcat --overwrite=true
kubectl label pod tomcat2 app=tomcat --overwrite=true
kubectl label pod tomcat3 app=tomcat --overwrite=true
# --overwrite=true 表示覆盖原来 label 的值
kubectl get pod -owide --show-labels
- 重新运行 nginx,我们看看会发生什么
kubectl apply -f pod-pod-affinity-preferred-match-expressions-nginx.yaml
kubectl get pod -owide --show-labels
现在没有一个 pod 的标签符合 标签选择器 的规则,但是 nginx 还是能被正常的调度,因为 preferred 是一种软亲和性。标签选择器的规则不匹配,scheduler 调度器会根据内部的算法选择合适的节点来绑定pod。
3.3、pod 间亲和性总结
- pod 间亲和性 podAffinity 包括 required 和 preferred
- required 是硬亲和性,只有条件满足,pod 才会被调度。
- preferred 是软亲和性,条件匹配,优先按条件调度,条件不匹配,按默认算法调度。
- matchExpressions 是按 pod 标签表达式来进行匹配。
- matchLabels 也是按 pod 标签来进行匹配,不过它是以键值对的方式来表示匹配规则。
- 无论 required 还是 preferred,在 pod 运行期,不管是 节点 标签变更,还是被亲和的 pod 标签变更,pod 都不会被驱逐。
- podAffinity 中有一个很重要的概念是 toplogyKey,理解它对于理解 pod 调度非常重要。
3.4、还原实验环境
删除 nginx tomcat1 tomcat2 tomcat3
kubectl delete pod nginx tomcat1 tomcat2 tomcat3 --force --grace-period=0
保留 node1、node2、node3 的 zone label
四、podAntiAffinity(pod间反亲和性)
podAntiAffinity 定义了 pod 倾向于不跟哪些 pod 调度在同一个位置。
- 查看帮助文档
kubectl explain pod.spec.affinity.podAntiAffinity
可以看到,它跟 podAffinity 的定义,几乎是一摸一样的。
可以猜到,K8S 内部在进行调度的时候,应该是采用一种取反的操作。
筛选出不想亲和的 pod 所具有的 toplogyKey,然后在剩下的 toplogyKey 里选择节点进行绑定。
下面是 podAffinity 的定义。
4.1、required 硬亲和性
这一节,我们只演示 required,其他大同小异。
假设现在有如下部署图:
node1、node2、node3 分别有标签 zone=A、zone=B、zone=C,表示它们分别位于可用区 A B C。
node3 运行着 tomcat1,它有标签 app=tomcat。
现在再来一个 tomcat2,我们不希望它跟 tomcat1 在同一个可用区下。
在实际业务中,相同的可用区往往意味着同一个机房,而部署同一个应用,往往不希望它们在同一个节点、或者同一个可用区下,因为这样容易导致 单点故障。从而让整个服务不可用。
我们看看这在 K8S 中要怎么实现-->
- 编写 tomcat1 资源文件
pod-pod-antiaffinity-required-match-expressions-tomcat1.yaml
apiVersion: v1
kind: Pod
metadata:name: tomcat1labels:app: tomcat
spec:nodeName: k8s31node3containers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
nodeName 指定它运行在 node3 节点上。
- 编写 tomcat2 资源文件
pod-pod-antiaffinity-required-match-expressions-tomcat2.yaml
apiVersion: v1
kind: Pod
metadata:name: tomcat2labels:app: tomcat
spec:affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- topologyKey: zonelabelSelector:matchExpressions:- {key: app, operator: In, values: ["tomcat"]}containers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
- 运行并查看
kubectl apply -f pod-pod-antiaffinity-required-match-expressions-tomcat1.yaml
kubectl apply -f pod-pod-antiaffinity-required-match-expressions-tomcat2.yaml
kubectl get pod -owide --show-labels
可以看到 tomcat2 被调度到跟 tomcat1 不同的 zone 的服务器上(node1、node2 都可以)。
相关文章:

K8S 亲和性与反亲和性 深度好文
今天我们来实验 pod 亲和性。官网描述如下: 假设有如下三个节点的 K8S 集群: k8s31master 是控制节点 k8s31node1、k8s31node2 是工作节点 容器运行时是 containerd 一、镜像准备 1.1、镜像拉取 docker pull tomcat:8.5-jre8-alpine docker pull nginx…...

关于php语言api接口开发的流程
确定接口需求:首先明确接口的功能和需求,包括输入参数、输出结果以及接口的业务逻辑。 设计接口路由:根据接口需求,设计具体的接口路由,即URL路径,用于访问接口。 搭建PHP环境:确保你的服务器上…...

医疗集群系统中基于超融合数据库架构的应用与前景探析
一、引言 1.1 研究背景与意义 随着医疗信息化的飞速发展,医疗数据呈爆炸式增长。从日常诊疗记录、患者病历,到各类医疗影像、检查检验数据等,海量信息不断涌现。据统计,医疗数据的年增长率高达 30% 以上 ,2025 年,全球医疗数据量将达到 2314 艾字节(EB)。如此庞大的数…...

浅谈云计算15 | 存储可靠性技术(RAID)
存储可靠性技术 一、存储可靠性需求1.1 数据完整性1.2 数据可用性1.3 故障容错性 二、传统RAID技术剖析2.1 RAID 02.2 RAID 12.3 RAID 52.4 RAID 62.5 RAID 10 三、RAID 2.0技术3.1 RAID 2.0技术原理3.1.1 两层虚拟化管理模式3.1.2 数据分布与重构 3.2 RAID 2.0技术优势3.2.1 自…...

43.Textbox的数据绑定 C#例子 WPF例子
固定最简步骤,包括 XAML: 题头里引入命名空间 标题下面引入类 box和block绑定属性 C#: 通知的类,及对应固定的任务 引入字段 引入属性 属性双触发,其中一个更新block的属性 block>指向box的属性 从Textbo…...

LLM大语言模型的分类
从架构和功能的角度来看,LLM(Large Language Model,大语言模型)主要可以分为以下几种类型: **1. 基础语言模型:** * **定义:** 通过在大规模文本数据上进行预训练,学习语言的规律和模式&#…...

【北京迅为】iTOP-4412全能版使用手册-第八十七章 安装Android Studio
iTOP-4412全能版采用四核Cortex-A9,主频为1.4GHz-1.6GHz,配备S5M8767 电源管理,集成USB HUB,选用高品质板对板连接器稳定可靠,大厂生产,做工精良。接口一应俱全,开发更简单,搭载全网通4G、支持WIFI、蓝牙、…...

【深度学习】神经网络之Softmax
Softmax 函数是神经网络中常用的一种激活函数,尤其在分类问题中广泛应用。它将一个实数向量转换为概率分布,使得每个输出值都位于 [0, 1] 之间,并且所有输出值的和为 1。这样,Softmax 可以用来表示各类别的预测概率。 Softmax 函…...

容器渗透横向
本质上要获得 1.获得容器IP段 2.获得主机IP段 3.获得本机IP 4.通过CNI或Docker0等扫描本机端口 Flannel 容器信息 rootubuntu-linux-22-04-desktop:/home/parallels/Desktop# k get po -A -o wide NAMESPACE NAME …...

黑马Java面试教程_P1_导学与准备篇
系列博客目录 文章目录 系列博客目录导学Why?举例 准备篇企业是如何筛选简历的(筛选简历的规则)HR如何筛选简历部门负责人筛选简历 简历注意事项简历整体结构个人技能该如何描述项目该如何描述 应届生该如何找到合适的练手项目项目来源找到项目后,如何深入学习项目…...
《自动驾驶与机器人中的SLAM技术》ch4:预积分学
目录 1 预积分的定义 2 预积分的测量模型 ( 预积分的测量值可由 IMU 的测量值积分得到 ) 2.1 旋转部分 2.2 速度部分 2.3 平移部分 2.4 将预积分测量和误差式代回最初的定义式 3 预积分的噪声模型和协方差矩阵 3.1 旋转部分 3.2 速度部分 3.3 平移部分 3.4 噪声项合并 4 零偏的…...

Docker部署MySQL 5.7:持久化数据的实战技巧
在生产环境中使用Docker启动MySQL 5.7时,需要考虑数据持久化、配置文件管理、安全性等多个方面。以下是一个详细的步骤指南。 1. 准备工作 (1)创建挂载目录 在宿主机上创建用于挂载的目录,以便持久化数据和配置文件。 sudo mkdi…...

Spring框架 了解
深入浅出Spring框架:为初学者量身定制的入门指南 引言 在现代Java开发中,Spring框架无疑是构建企业级应用的核心技术之一。无论是初学者还是经验丰富的开发者,掌握Spring都能极大地提升你的编程技能和项目开发效率。本文将带你深入了解Spri…...

低代码独特架构带来的编译难点及多线程解决方案
前言 在当今软件开发领域,低代码平台以其快速构建应用的能力,吸引了众多开发者与企业的目光。然而,低代码平台独特的架构在带来便捷的同时,也给编译过程带来了一系列棘手的难点。 一,低代码编译的难点 (1…...

如何使用Ultralytics训练自己的yolo5 yolo8 yolo10 yolo11等目标检测模型
Ultralytics正在以惊人的速度吸收优秀的CV算法,之前Ultralytics定位于YOLOV8,但逐渐地扩展到支持其他版本的YOLO,最新版本的ultralytics全面支持yolo5 yolo7 yolo8 yolo9 yolo10 yolo11,包含模型的训练、验证、预测、部署等。毫无…...

Java技术栈 —— Andorid开发入门
Java技术栈 —— Andorid开发入门 一、搭建开发环境二、HelloWorld三、将Andorid项目打包成APK文件,并安装至手机上四、开发常见问题 一、搭建开发环境 不用Intellij,而是用Andorid Studio(免费),这是专门给Andorid的IDE。 参考文章或视频链…...

Qt天气预报系统获取天气数据
Qt天气预报系统获取天气数据 1、获取天气数据1.1添加天气类头文件1.2定义今天和未来几天天气数据类1.3定义一个解析JSON数据的函数1.4在mainwindow中添加weatherData.h1.5创建今天天气数据和未来几天天气数据对象1.6添加parseJson定义1.7把解析JSON数据添加进去1.8添加错误1.9解…...

力扣 搜索二维矩阵
二分查找,闭区间与开区间的不同解法。 题目 乍一看,不是遍历一下找到元素就可以了。 class Solution {public boolean searchMatrix(int[][] matrix, int target) {for (int[] ints : matrix) {for (int ans : ints) {if (ans target) return true;}}…...

JavaScript 操作符与表达式
Hi, 我是布兰妮甜,编写流畅、愉悦用户体验的程序员。JavaScript 是一种功能强大且灵活的编程语言,广泛应用于前端和后端开发。它提供了一系列丰富的操作符和表达式来处理数据、执行逻辑判断以及控制程序流程。理解这些概念对于编写高效、可读性强的代码至…...

深度学习 Pytorch 张量(Tensor)的创建和常用方法
1 张量的基本创建及其类型 和Numpy中的array一样,张量的本质也是结构化地组织了大量的数据。 并且在实际操作中,张量的创建和基本功能也与其非常类似。 1.1 张量(Tensor)函数创建方法 张量的最基本创建方法和Numpy中创建Array的格式一致。 # Numpy创建…...

在VMwareFusion中使用Ubuntu
在VMwareFusion使用Ubuntu 在VMwareFusion使用Ubuntu背景在VMwareFusion虚拟机里使用Ubuntu1、集成桌面工具2、主机和虚拟机之间共享剪贴板内容3、设置root用户密码4、设置静态ip4.1、静态ip和动态ip的区别4.2、查看当前ip4.2、linux网络配置文件所在位置4.3、基于ubuntu22.04.…...

%.*s——C语言中printf 函数中的一种格式化输出方式
在C语言中,%.*s 是 printf 函数中的一种格式化输出方式,用于控制字符串的输出长度。具体来说,%.*s 中的 * 表示输出宽度(即最多输出的字符数)是一个变量,这个变量的值在运行时通过 printf 函数的参数传递。…...

基于微信小程序的摄影竞赛系统设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...

hydra破解密码
hydra九头蛇是常用的密码破解工具 1、破解centos ssh密码 hydra -l root -P password.txt ssh://192.168.1.107:2222 hydra -l root -P password.txt -s 2222 192.168.1.107 ssh2、破解ftp hydra -l allen -P e:\aa.txt ftp://127.0.0.1 hydra -l allen -P e:\aa.txt ftp:…...

JAVA之外观模式
外观模式,又称门面模式,是一种结构型设计模式,旨在为复杂的子系统提供一个统一且简化的接口。通过这一模式,客户端可以更加便捷地与子系统交互,而无需深入了解其内部结构和实现细节。外观模式不仅简化了客户端的使用&a…...

如何选择合适的服务器?服务器租赁市场趋势分析
服务器租赁市场概览 服务器租赁 market可以分为两种类型:按小时、按月和按年,每种模式都有其特点和适用场景,按小时租赁是最经济实惠的选择,适用于短期需求;按月租赁则适合中长期使用;而按年租赁则是最灵活…...

CentOS 下载软件时报Error: Failed to synchronize cache for repo ‘AppStream‘解决方法
下载软件时出现以下问题 直接把CentOS-AppStream.repo改个名字就行 cd /etc/yum.repos.d/ mv CentOS-AppStream.repo CentOS-AppStream.repo.bak就可以了 解决思路 把AI问遍,无人会,解决法 想要下载软件通通失败了,解决方法当然是问AI&am…...

鲍厚霖:引领AI广告创新,搭建中美合作桥梁
2024年是鲍厚霖和她领导的超能S咨询公司(Triple S AI)收获颇丰的一年。这一年中,她以卓越的战略眼光和创新能力,为中美教育、文化与技术的深度融合注入了新的活力。2025年,Triple S AI计划推出全新2.0版本平台,进一步深化人工智能驱动的营销与文化合作领域,推动产业变革与社会福…...

学习记录1
[SUCTF 2019]EasyWeb 直接给了源代码,分析一下 <?php function get_the_flag(){// webadmin will remove your upload file every 20 min!!!! $userdir "upload/tmp_".md5($_SERVER[REMOTE_ADDR]);if(!file_exists($userdir)){mkdir($userdir);}if…...

【Gossip 协议】Golang的实现库Memberlist 库简介
Gossip 协议简介 Gossip 协议是一种分布式协议,用于在节点之间传播信息,常用于成员管理、故障检测、服务发现等场景。在这个协议中,每个节点定期与其他节点交换信息,最终保证所有节点达到一致的状态。它的工作原理类似于人群中的…...