好的,各位亲爱的 Kubernetes 爱好者们,大家好!我是你们的老朋友,今天咱们来聊聊 K8s 里那些“爱恨情仇”的故事——高级亲和性与反亲和性规则。
想象一下,你的 Kubernetes 集群就像一个繁忙的都市,里面住着各种各样的 Pod,它们就像都市里的居民,有的喜欢热闹,扎堆住在市中心;有的喜欢安静,偏爱郊外的别墅。而亲和性与反亲和性,就像是城市规划局制定的居住政策,决定了这些 Pod 们该如何选择自己的“邻居”。
一、 初识亲和性与反亲和性:牵线搭桥与划清界限
简单来说,亲和性(Affinity)就是“我喜欢你,我想和你住在一起”,而反亲和性(Anti-affinity)则是“我讨厌你,我不想见到你”。
- 亲和性(Affinity): 鼓励 Pod 被调度到满足特定条件的节点上,或者与某些 Pod 尽可能地部署在一起。这就像是给 Pod 们“牵线搭桥”,让它们找到志同道合的伙伴。
- 反亲和性(Anti-affinity): 避免 Pod 被调度到满足特定条件的节点上,或者避免与某些 Pod 部署在一起。这就像是给 Pod 们“划清界限”,避免“冤家路窄”。
二、 为什么我们需要高级亲和性与反亲和性?
你可能会说:“K8s 不是已经有节点选择器(Node Selector)了吗?也能实现一些简单的调度策略啊!”
没错,节点选择器就像是给 Pod 指定了一个简单的“门牌号”,让它们住在特定的节点上。但是,节点选择器的功能比较简单粗暴,只能根据节点的标签进行匹配。而高级亲和性与反亲和性则更加灵活、强大,可以实现更复杂的调度策略。
举个例子:
- 场景一:高可用性
- 假设你有一个重要的 Web 应用,为了保证高可用性,你希望将多个 Pod 分散到不同的节点上,避免单点故障。这时,反亲和性就派上用场了!你可以设置一个反亲和性规则,让这些 Pod 尽量不要部署在同一个节点上。
- 场景二:性能优化
- 假设你有一个计算密集型的任务,需要大量的 CPU 资源。你希望将相关的 Pod 部署在同一个节点上,利用节点上的所有 CPU 核心,提高计算效率。这时,亲和性就派上用场了!你可以设置一个亲和性规则,让这些 Pod 尽量部署在同一个节点上。
- 场景三:资源隔离
- 假设你有一些安全敏感的应用,不希望与其他应用共享节点资源,避免潜在的安全风险。这时,反亲和性也可以发挥作用!你可以设置一个反亲和性规则,让这些敏感应用与其他应用隔离部署。
三、 高级亲和性与反亲和性的语法结构
高级亲和性与反亲和性主要通过 Pod 的 spec.affinity
字段进行配置。这个字段包含以下几个部分:
nodeAffinity
:节点亲和性,用于控制 Pod 被调度到哪些节点上。podAffinity
:Pod 亲和性,用于控制 Pod 与哪些 Pod 部署在一起。podAntiAffinity
:Pod 反亲和性,用于控制 Pod 避免与哪些 Pod 部署在一起。
咱们先来啃一啃 nodeAffinity
这块硬骨头。
3.1 节点亲和性(nodeAffinity
)
节点亲和性允许你基于节点的标签来控制 Pod 的调度。它有两种类型:
requiredDuringSchedulingIgnoredDuringExecution
:必须满足条件才能调度,如果调度后节点标签发生变化,Pod 不会被驱逐。可以理解为“硬性要求”,不满足条件,Pod 就寸步难行。preferredDuringSchedulingIgnoredDuringExecution
:尽量满足条件进行调度,但是如果找不到满足条件的节点,Pod 也可以被调度到其他节点上。如果调度后节点标签发生变化,Pod 不会被驱逐。可以理解为“软性要求”,能满足最好,不能满足也没关系。
3.1.1 requiredDuringSchedulingIgnoredDuringExecution
详解
这种类型的节点亲和性定义了一组必须满足的条件,只有当节点满足这些条件时,Pod 才能被调度到该节点上。它的语法结构如下:
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-required
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: <label-key>
operator: <operator>
values:
- <value1>
- <value2>
...
containers:
- name: nginx
image: nginx
nodeSelectorTerms
:一个或多个nodeSelectorTerm
的列表,每个nodeSelectorTerm
代表一组条件。matchExpressions
:一个或多个matchExpression
的列表,每个matchExpression
定义了一个具体的匹配规则。key
:节点的标签键。operator
:操作符,用于指定匹配规则。常用的操作符包括:In
:标签值在给定的列表中。NotIn
:标签值不在给定的列表中。Exists
:节点存在指定的标签。DoesNotExist
:节点不存在指定的标签。Gt
:标签值大于给定的值(仅适用于字符串类型的标签)。Lt
:标签值小于给定的值(仅适用于字符串类型的标签)。
values
:标签值的列表,用于与节点标签进行比较。
举个栗子🌰:
假设你希望将 Pod 调度到拥有标签 disktype=ssd
的节点上,你可以这样配置:
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-required
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx
image: nginx
这意味着,只有拥有 disktype=ssd
标签的节点,才能成为该 Pod 的候选节点。
3.1.2 preferredDuringSchedulingIgnoredDuringExecution
详解
这种类型的节点亲和性定义了一组尽量满足的条件,调度器会尽量将 Pod 调度到满足这些条件的节点上,但是如果找不到满足条件的节点,Pod 也可以被调度到其他节点上。它的语法结构如下:
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-preferred
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: <weight>
preference:
matchExpressions:
- key: <label-key>
operator: <operator>
values:
- <value1>
- <value2>
...
containers:
- name: nginx
image: nginx
weight
:权重值,范围是 1-100。权重越高,表示调度器越倾向于选择满足条件的节点。preference
:一个nodeSelectorTerm
对象,定义了具体的匹配规则,与requiredDuringSchedulingIgnoredDuringExecution
中的nodeSelectorTerm
结构相同。
举个栗子🌰:
假设你希望将 Pod 尽量调度到拥有标签 zone=us-east-1
的节点上,你可以这样配置:
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-preferred
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 50
preference:
matchExpressions:
- key: zone
operator: In
values:
- us-east-1
containers:
- name: nginx
image: nginx
这意味着,调度器会尽量将 Pod 调度到拥有 zone=us-east-1
标签的节点上,但是如果找不到这样的节点,Pod 也可以被调度到其他节点上。权重值 50
表示调度器对这个偏好有一定的倾向性。
3.2 Pod 亲和性与反亲和性(podAffinity
& podAntiAffinity
)
Pod 亲和性与反亲和性允许你基于 Pod 的标签来控制 Pod 的调度。它们可以实现更复杂的调度策略,例如:
- 将相关的 Pod 部署在一起,提高通信效率。
- 将不同的 Pod 分散到不同的节点上,提高可用性。
- 避免将某些 Pod 部署在一起,避免资源竞争。
与节点亲和性类似,Pod 亲和性与反亲和性也有两种类型:
requiredDuringSchedulingIgnoredDuringExecution
:必须满足条件才能调度,如果调度后满足条件的 Pod 标签发生变化,Pod 不会被驱逐。preferredDuringSchedulingIgnoredDuringExecution
:尽量满足条件进行调度,但是如果找不到满足条件的 Pod,Pod 也可以被调度到其他节点上。如果调度后满足条件的 Pod 标签发生变化,Pod 不会被驱逐。
3.2.1 podAffinity
详解
Pod 亲和性用于控制 Pod 与哪些 Pod 部署在一起。它的语法结构如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: <label-key>
operator: <operator>
values:
- <value1>
- <value2>
...
topologyKey: <topology-key>
containers:
- name: nginx
image: nginx
labelSelector
:一个LabelSelector
对象,用于选择满足条件的 Pod。matchExpressions
:一个或多个matchExpression
的列表,每个matchExpression
定义了一个具体的匹配规则,与节点亲和性中的matchExpression
结构相同。
topologyKey
:一个节点标签键,用于指定亲和性的作用范围。例如,如果topologyKey
设置为kubernetes.io/hostname
,则表示亲和性只在同一个节点上生效。
举个栗子🌰:
假设你希望将 Pod 调度到与拥有标签 app=web
的 Pod 部署在同一个节点上,你可以这样配置:
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx
这意味着,只有与拥有 app=web
标签的 Pod 部署在同一个节点上的节点,才能成为该 Pod 的候选节点。
3.2.2 podAntiAffinity
详解
Pod 反亲和性用于控制 Pod 避免与哪些 Pod 部署在一起。它的语法结构与 podAffinity
类似:
apiVersion: v1
kind: Pod
metadata:
name: pod-anti-affinity
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: <label-key>
operator: <operator>
values:
- <value1>
- <value2>
...
topologyKey: <topology-key>
containers:
- name: nginx
image: nginx
labelSelector
:一个LabelSelector
对象,用于选择需要避免的 Pod。topologyKey
:一个节点标签键,用于指定反亲和性的作用范围。
举个栗子🌰:
假设你希望避免将 Pod 调度到与拥有标签 app=db
的 Pod 部署在同一个节点上,你可以这样配置:
apiVersion: v1
kind: Pod
metadata:
name: pod-anti-affinity
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- db
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx
这意味着,拥有 app=db
标签的 Pod 所在的节点,将不会成为该 Pod 的候选节点。
四、 实战演练:打造你的专属调度策略
理论知识讲了一大堆,现在咱们来做一些实战演练,看看如何将这些高级亲和性与反亲和性规则应用到实际场景中。
场景一:高可用 Web 应用
假设你有一个 Web 应用,为了保证高可用性,你希望将多个 Pod 分散到不同的节点上,并且尽量部署在不同的可用区(Zone)中。
你可以这样配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 50
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: Exists
containers:
- name: web-app
image: nginx
podAntiAffinity
:避免将多个app=web
的 Pod 部署在同一个节点上。nodeAffinity
:尽量将 Pod 部署在不同的可用区中。
场景二:高性能计算任务
假设你有一个计算密集型的任务,需要大量的 CPU 资源。你希望将相关的 Pod 部署在同一个节点上,利用节点上的所有 CPU 核心,提高计算效率。
你可以这样配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: compute-task
spec:
replicas: 2
selector:
matchLabels:
app: compute
template:
metadata:
labels:
app: compute
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- compute
topologyKey: kubernetes.io/hostname
containers:
- name: compute-task
image: busybox
command: ["/bin/sh", "-c", "while true; do echo 'Computing...'; sleep 1; done"]
podAffinity
:将多个app=compute
的 Pod 部署在同一个节点上。
五、 注意事项:避开那些“坑”
在使用高级亲和性与反亲和性时,有一些注意事项需要特别关注:
- 循环依赖: 避免出现循环依赖的情况,例如 Pod A 依赖 Pod B,Pod B 又依赖 Pod A,这会导致调度器无法找到合适的节点。
- 资源不足: 确保集群有足够的资源满足 Pod 的调度需求,否则可能会导致 Pod 无法调度。
- 标签管理: 保持节点和 Pod 标签的一致性和准确性,否则可能会导致亲和性规则失效。
- 测试验证: 在生产环境中使用之前,务必进行充分的测试验证,确保调度策略符合预期。
六、 总结:让 K8s 成为你的“调度大师”
高级亲和性与反亲和性是 Kubernetes 中非常强大的调度工具,它们可以帮助你实现更复杂的调度策略,提高应用的可用性、性能和安全性。
希望通过今天的分享,大家能够对 K8s 的高级亲和性与反亲和性有更深入的了解,并能够灵活运用这些规则,打造你的专属调度策略,让 K8s 成为你的“调度大师”!
最后,送给大家一句名言:“代码虐我千百遍,我待代码如初恋!” 让我们一起在 K8s 的世界里,不断探索、学习、进步!🎉