1、Affity亲和性
在k8s中亲和性有两种:节点亲和性(node affinity)和Pod亲和性(pod affinity)。
1.1. 节点亲和性(Node Affinity)
定义:
节点亲和性用于指定Pod与节点之间的关系,使Pod倾向于在具有特定标签或节点特征的节点上运行。
适用场景:
资源需求
当某个Pod对计算资源(如CPU、内存)或其他特定硬件资源(如GPU)有特定需求时,可以使用节点亲和性将它调度到具有相应资源的节点上。这有助于优化资源利用和性能。逻辑分组
当需要将相关的Pod分组部署到特定节点上时,可以使用节点亲和性。例如,在分布式系统中,某个节点需要承担特定的角色或任务,可以使用节点亲和性将相关的Pod调度到该节点上,以便实现逻辑上的分组和管理。特定硬件或软件要求
当某个Pod需要依赖特定的硬件设备或软件环境时,可以使用节点亲和性将它调度到具备所需条件的节点上。例如,某个Pod需要与具有特定硬件加速器的节点进行通信,可以使用节点亲和性将它调度到具备所需硬件的节点上。
1.1.1 requiredDuringSchedulingIgnoredDuringExecution(硬亲和性)
新创建一个pod-aff.yaml配置,然后填入以下内容
apiVersion: v1 # Kubernetes API 版本 v1 kind: Pod # 资源类型为 Pod metadata: # 元数据部分 name: demo-pod # Pod 名称 namespace: default # 所属命名空间(默认) labels: # 标签集合,用于筛选和分组 app: busybox-tomcat # 应用标签 env: pro # 环境标签(生产环境) spec: # Pod 规格定义 affinity: # 亲和性配置 nodeAffinity: # 节点亲和性 requiredDuringSchedulingIgnoredDuringExecution: # 硬亲和性:调度时必须满足,运行时忽略变化 nodeSelectorTerms: # 节点选择器条件列表(多个 term 之间是 OR 关系) - matchExpressions: # 匹配表达式列表(多个表达式之间是 AND 关系) - key: a # 节点标签的键名 operator: In # 操作符:In 表示标签值必须在给定的列表中 values: # 标签值列表 - b # 允许的标签值(即节点必须包含标签 a=b) containers: # 容器列表,定义 Pod 中运行的容器 - name: tomcat # 第一个容器名称 ports: # 容器端口配置 - containerPort: 8080 # 容器内部监听的端口号 image: tomcat:8.5.34-jre8-alpine # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略:仅当本地不存在时才拉取 - name: busybox # 第二个容器名称 image: busybox:latest # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略同前 command: # 容器启动命令(覆盖镜像默认的 CMD) - "/bin/sh" # 调用 sh 解释器 - "-c" # 执行后续字符串中的命令 - "sleep 36000" # 休眠 36000 秒(即 10 小时),保持容器运行启动这个pod
kubectl apply -f pod-aff.yaml当前节点中有任意一个节点拥有a=b标签,就可以把pod调度到有a=b这个标签的节点,很显然我们的k8s集群中没有这样的一个标签,这次调度将会被pending。
执行查看pod详细信息命令,可以看到没有匹配到亲和节点,所以被挂起了
kubectl describe pod demo-pod现在给k8s-node-1创建这个标签,就可以将这个pod给调度起来
kubectl label nodes k8s-node-1 a=b1.1.2 preferredDuringSchedulingIgnoredDuringExecution(软亲和性)
顾名思义,软亲和性如果没有满足条件不会挂起,而是直接运行这个pod。如果满足条件则按照条件指定的节点调度pod
apiVersion: v1 # Kubernetes API 版本 v1 kind: Pod # 资源类型为 Pod metadata: # 元数据部分 name: demo-pod # Pod 名称 namespace: default # 所属命名空间(默认) labels: # 标签集合,用于筛选和分组 app: busybox-tomcat # 应用标签 env: pro # 环境标签(生产环境) spec: # Pod 规格定义 affinity: # 亲和性配置 nodeAffinity: # 节点亲和性 preferredDuringSchedulingIgnoredDuringExecution: # 软亲和性:调度时尽量满足,但不强制 - preference: # 偏好设置 matchExpressions: # 匹配表达式列表(多个表达式之间是 AND 关系) - key: a1 # 节点标签的键名 operator: In # 操作符:In 表示标签值必须在给定的列表中 values: # 标签值列表 - b1 # 偏好的标签值(节点包含标签 a1=b1 会被优先调度) weight: 80 # 权重值(范围 1-100),数值越高优先级越高 containers: # 容器列表,定义 Pod 中运行的容器 - name: tomcat # 第一个容器名称 ports: # 容器端口配置 - containerPort: 8080 # 容器内部监听的端口号 image: tomcat:8.5.34-jre8-alpine # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略:仅当本地不存在时才拉取 - name: busybox # 第二个容器名称 image: busybox:latest # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略同前 command: # 容器启动命令(覆盖镜像默认的 CMD) - "/bin/sh" # 调用 sh 解释器 - "-c" # 执行后续字符串中的命令 - "sleep 36000" # 休眠 36000 秒(即 10 小时),保持容器运行可以看到我的集群中没有a1=b1的标签,但是k8s还是将pod调度到k8s-node-1节点,不会将这个pod挂起。
1.1.3 硬亲和性 vs 软亲和性对比
| 特性 | 硬亲和性(required) | 软亲和性(preferred) |
|---|---|---|
| 调度行为 | 必须满足条件,否则 Pod 无法调度 | 尽量满足条件,不满足也能调度 |
| 关键字 | requiredDuringSchedulingIgnoredDuringExecution | preferredDuringSchedulingIgnoredDuringExecution |
| 权重支持 | 不支持 | 支持(1-100),用于优先级排序 |
| 适用场景 | 强制约束(如合规性、硬件要求) | 性能优化(如就近部署、资源偏好) |
1.2. Pod亲和性(Pod Affinity)
定义:
Pod亲和性用于指定Pod之间的关系,使它们倾向于在同一节点或具有相似特征的节点上运行。
适用场景:
数据本地性
当两个或多个Pod需要访问相同的本地数据时,可以使用Pod亲和性将它们调度到同一节点上。例如,在分布式数据库中,多个数据库实例需要访问相同的数据卷或存储,可以通过Pod亲和性将它们调度到同一节点上,减少网络传输延迟。互为依赖
当两个或多个Pod之间存在依赖关系,需要相互通信或协同工作时,可以使用Pod亲和性将它们调度到同一节点上。例如,在微服务架构中,某个服务需要与特定的缓存服务进行交互,可以使用Pod亲和性将它们调度到同一节点上,提高性能和减少网络开销。服务发现和负载均衡
在需要实现服务发现和负载均衡的场景中,可以使用Pod亲和性将属于同一服务的多个实例调度到同一节点或相近的节点上。这样可以提高服务的可用性、降低延迟,并简化负载均衡配置。
1.2.1 requiredDuringSchedulingIgnoredDuringExecution(硬亲和性)
pod硬亲和性可以将两个pod同时调度到同一个节点,即tomcat-pod被调度到哪个节点,则busy-box也会被调度到同一个节点,我下面的例子中tomcat-pod被调度到node2节点,然后busy-box根据pod亲和性也会被调度到node2节点。
创建tomcat-pod.yaml
apiVersion: v1 # Kubernetes API 版本 v1 kind: Pod # 资源类型为 Pod metadata: # 元数据部分 name: tomcat-pod # Pod 名称 namespace: default # 所属命名空间(默认) labels: # 标签集合,用于筛选和分组 app: first # 应用标签,busybox 将通过此标签找到 tomcat Pod env: pro # 环境标签(生产环境) spec: # Pod 规格定义 containers: # 容器列表,定义 Pod 中运行的容器 - name: tomcat # 容器名称 ports: # 容器端口配置 - containerPort: 8080 # 容器内部监听的端口号 image: tomcat:8.5.34-jre8-alpine # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略:仅当本地不存在时才拉取创建busybox-pod.yaml
拓扑域键名:hostname 表示节点级别,即要求调度到同一节点
topologyKey: kubernetes.io/hostname
apiVersion: v1 # Kubernetes API 版本 v1 kind: Pod # 资源类型为 Pod metadata: # 元数据部分 name: busybox-pod # Pod 名称 namespace: default # 所属命名空间(默认) labels: # 标签集合,用于筛选和分组 app: busybox # 应用标签 env: pro # 环境标签(生产环境) spec: # Pod 规格定义 affinity: # 亲和性配置 podAffinity: # Pod 亲和性(与 Pod 的调度位置偏好相关) requiredDuringSchedulingIgnoredDuringExecution: # 硬亲和性:调度时必须满足,运行时忽略变化 - labelSelector: # 标签选择器,用于选择目标 Pod matchExpressions: # 匹配表达式列表(多个表达式之间是 AND 关系) - {key: app, operator: In, values: ["first"]} # 数组形式:选择标签 app=first 的 Pod 作为亲和目标 topologyKey: kubernetes.io/hostname # 拓扑域键名:hostname 表示节点级别,即要求调度到同一节点 containers: # 容器列表,定义 Pod 中运行的容器 - name: busybox # 容器名称 image: busybox:latest # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略:仅当本地不存在时才拉取 command: # 容器启动命令(覆盖镜像默认的 CMD) - "/bin/sh" # 调用 sh 解释器 - "-c" # 执行后续字符串中的命令 - "sleep 36000" # 休眠 36000 秒(即 10 小时),保持容器运行部署与验证命令
# 1. 给k8s-node-1和k8s-node-2加上标签 kubectl label nodes k8s-node-1 k8s-node-2 app=first env=pro # 2. 先启动 tomcat Pod(作为亲和性目标) kubectl apply -f tomcat-pod.yaml # 3. 等待 tomcat Pod 处于 Running 状态 kubectl get pod tomcat-pod -w # 4. 再启动 busybox Pod(会自动调度到 tomcat 所在节点) kubectl apply -f busybox-pod.yaml # 5. 验证两个 Pod 是否在同一节点(查看 NODE 列是否相同) kubectl get pods -o wide # 6. 查看 Pod 调度事件(确认亲和性生效) kubectl describe pod busybox-pod | grep -A 5 "Events"1.2.2 preferredDuringSchedulingIgnoredDuringExecution(软亲和性)
tomcat-pod.yaml配置
apiVersion: v1 # Kubernetes API 版本 v1 kind: Pod # 资源类型为 Pod metadata: # 元数据部分 name: tomcat-pod # Pod 名称 namespace: default # 所属命名空间(默认) labels: # 标签集合,用于筛选和分组 app: first # 应用标签,busybox 将通过此标签找到 tomcat Pod env: pro # 环境标签(生产环境) spec: # Pod 规格定义 containers: # 容器列表,定义 Pod 中运行的容器 - name: tomcat # 容器名称 ports: # 容器端口配置 - containerPort: 8080 # 容器内部监听的端口号 image: tomcat:8.5.34-jre8-alpine # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略:仅当本地不存在时才拉取busybox-pod.yaml配置
apiVersion: v1 # Kubernetes API 版本 v1 kind: Pod # 资源类型为 Pod metadata: # 元数据部分 name: busybox-pod # Pod 名称 namespace: default # 所属命名空间(默认) labels: # 标签集合,用于筛选和分组 app: busybox # 应用标签 env: pro # 环境标签(生产环境) spec: # Pod 规格定义 affinity: # 亲和性配置 podAffinity: # Pod 亲和性(与 Pod 的调度位置偏好相关) preferredDuringSchedulingIgnoredDuringExecution: # 软亲和性:调度时尽量满足,但不强制 - weight: 80 # 权重值(范围 1-100),数值越高优先级越高,调度器会优先考虑此偏好 podAffinityTerm: # Pod 亲和性条件项 labelSelector: # 标签选择器,用于选择目标 Pod matchExpressions: # 匹配表达式列表(多个表达式之间是 AND 关系) - {key: app, operator: In, values: ["first"]} # 数组形式:选择标签 app=first 的 Pod 作为亲和目标 topologyKey: kubernetes.io/hostname # 拓扑域键名:hostname 表示节点级别,即尽量调度到同一节点 containers: # 容器列表,定义 Pod 中运行的容器 - name: busybox # 容器名称 image: busybox:latest # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略:仅当本地不存在时才拉取 command: # 容器启动命令(覆盖镜像默认的 CMD) - "/bin/sh" # 调用 sh 解释器 - "-c" # 执行后续字符串中的命令 - "sleep 36000" # 休眠 36000 秒(即 10 小时),保持容器运行1.2.3 硬亲和性 vs 软亲和性对比
| 对比维度 | 硬亲和性(Required) | 软亲和性(Preferred) |
|---|---|---|
| 关键字 | requiredDuringSchedulingIgnoredDuringExecution | preferredDuringSchedulingIgnoredDuringExecution |
| 调度行为 | 必须满足条件,否则 Pod 无法调度 | 尽量满足条件,不满足也能调度 |
| 调度结果 | 找不到符合条件的节点 → Pod 保持 Pending | 找不到符合条件的节点 → Pod 正常调度到其他节点 |
| 权重支持 | ❌ 不支持 | ✅ 支持(1-100) |
| 适用场景 | 强制约束(合规性、硬件要求、数据本地性) | 性能优化(就近部署、资源偏好、减少延迟) |
1.2.4 亲和性选择建议
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 合规性要求(如数据本地性) | 硬亲和性 | 必须满足,否则业务无法运行 |
| 高性能计算(GPU 节点) | 硬亲和性 | 必须调度到有 GPU 的节点 |
| 微服务就近部署(减少延迟) | 软亲和性 | 优先同节点,但非强制 |
| 缓存与业务同节点 | 软亲和性 | 提升性能,但降级可用 |
| 多可用区容灾 | 反亲和性(硬/软) | 分散部署,提高可用性 |
1.2.5反亲和性(硬亲和性)
之前我们创建了tomcat-pod和busybox-pod通过pod的亲和性将这两个pod调度到了同一个节点,现在我们可以使用pod的反亲和性,将这两个pod调度到不同的节点。亲和性调度到同一个节点的好处就是如果这两个服务有依赖关系且互相调用,对延迟敏感就可以使用pod的亲和性调度到同一个节点,避免跨节点访问,反亲和性调度到不同的节点使用场景就是两个服务没有太多的关联关系,且其中一个服务比较消耗资源,必须要分开部署或者具有其它必须分开的场景。
在这个例子中tomcat-pod.yaml配置保持不变,新增一个busybox-pod-ant.yaml即可。
apiVersion: v1 # Kubernetes API 版本 v1 kind: Pod # 资源类型为 Pod metadata: # 元数据部分 name: busybox-pod # Pod 名称 namespace: default # 所属命名空间(默认) labels: # 标签集合,用于筛选和分组 app: busybox # 应用标签 env: pro # 环境标签(生产环境) spec: # Pod 规格定义 affinity: # 亲和性配置 podAntiAffinity: # Pod 反亲和性(与 Pod 的调度位置分散相关) requiredDuringSchedulingIgnoredDuringExecution: # 硬反亲和性:调度时必须满足,运行时忽略变化 - labelSelector: # 标签选择器,用于选择目标 Pod matchExpressions: # 匹配表达式列表(多个表达式之间是 AND 关系) - {key: app, operator: In, values: ["first"]} # 数组形式:避免与标签 app=first 的 Pod 调度到同一节点 topologyKey: kubernetes.io/hostname # 拓扑域键名:hostname 表示节点级别,即要求分散到不同节点 containers: # 容器列表,定义 Pod 中运行的容器 - name: busybox # 容器名称 image: busybox:latest # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略:仅当本地不存在时才拉取 command: # 容器启动命令(覆盖镜像默认的 CMD) - "/bin/sh" # 调用 sh 解释器 - "-c" # 执行后续字符串中的命令 - "sleep 36000" # 休眠 36000 秒(即 10 小时),保持容器运行# 1. 删除 tomcat-pod、busybox-pod kubectl delete pod tomcat-pod busybox-pod --force --grace-period=0 # 2. 先启动 tomcat Pod(作为反亲和性目标) kubectl apply -f tomcat-pod.yaml # 3. 等待 tomcat Pod 处于 Running 状态 kubectl get pod tomcat-pod -w # 4. 再启动 busybox Pod(会自动调度到 tomcat 不同的节点) kubectl apply -f busybox-pod-ant.yaml # 5. 验证两个 Pod 是否在不同节点(查看 NODE 列是否不同) kubectl get pods -o wide # 6. 查看 Pod 调度事件(确认反亲和性生效) kubectl describe pod busybox-pod | grep -A 5 "Events"我这里tomcat-pod调度到node2,busybox-pod调度到node1,但是busybox-pod一直处于创建容器状态,并没有真的启动,这时候执行命令查看busybox-pod详细信息
kubectl describe pod busybox-pod | grep -A 5 "Events"这是 Calico 网络插件出现了认证问题,导致 Pod 无法正常创建网络沙箱。这与 Pod 的亲和性配置无关,而是集群网络组件的问题。
根本原因:Calico 的 Kubernetes API 认证失败,通常是因为:
Calico 的 ServiceAccount 权限不足
Calico 配置的 kubeconfig 证书失效或过期
Calico 的 etcd 或 Kubernetes API 连接配置错误
需要重启Calico pod
# 删除 Calico Pod,它们会自动重建 kubectl delete pods -n kube-system -l k8s-app=calico-node kubectl delete pods -n kube-system -l k8s-app=calico-kube-controllers # 等待 Pod 重新启动 kubectl get pods -n kube-system -w | grep calico然后再查看pod状态就OK了
kubectl get pods -o widebusybox-pod正常启动,且被调度到node1节点
1.2.6反亲和性(软亲和性)
如果您需要软反亲和性(尽量分散,但不强制),可以使用以下配置:
busybox-pod-ant.yaml 配置如下:
apiVersion: v1 # Kubernetes API 版本 v1 kind: Pod # 资源类型为 Pod metadata: # 元数据部分 name: busybox-pod # Pod 名称 namespace: default # 所属命名空间(默认) labels: # 标签集合,用于筛选和分组 app: busybox # 应用标签 env: pro # 环境标签(生产环境) spec: # Pod 规格定义 affinity: # 亲和性配置 podAntiAffinity: # Pod 反亲和性(与 Pod 的调度位置分散相关) preferredDuringSchedulingIgnoredDuringExecution: # 软反亲和性:调度时尽量满足,但不强制 - weight: 80 # 权重值(范围 1-100),数值越高优先级越高 podAffinityTerm: # Pod 亲和性条件项 labelSelector: # 标签选择器,用于选择目标 Pod matchExpressions: # 匹配表达式列表(多个表达式之间是 AND 关系) - {key: app, operator: In, values: ["first"]} # 数组形式:尽量不与标签 app=first 的 Pod 调度到同一节点 topologyKey: kubernetes.io/hostname # 拓扑域键名:hostname 表示节点级别,即尽量分散到不同节点 containers: # 容器列表,定义 Pod 中运行的容器 - name: busybox # 容器名称 image: busybox:latest # 使用的镜像及标签 imagePullPolicy: IfNotPresent # 镜像拉取策略:仅当本地不存在时才拉取 command: # 容器启动命令(覆盖镜像默认的 CMD) - "/bin/sh" # 调用 sh 解释器 - "-c" # 执行后续字符串中的命令 - "sleep 36000" # 休眠 36000 秒(即 10 小时),保持容器运行1.2.3 反亲和性硬性vs 反亲和性软性对比
| 对比维度 | 硬反亲和性(Required) | 软反亲和性(Preferred) |
|---|---|---|
| 关键字 | requiredDuringSchedulingIgnoredDuringExecution | preferredDuringSchedulingIgnoredDuringExecution |
| 调度行为 | 必须分散,否则 Pod 无法调度 | 尽量分散,无法分散也能调度 |
| 调度结果 | 找不到其他节点 → Pod 保持 Pending | 找不到其他节点 → Pod 正常调度到同一节点 |
| 权重支持 | ❌ 不支持 | ✅ 支持(1-100) |
| 适用场景 | 高可用强制要求、容灾合规 | 性能优化、资源均衡、尽力而为的分散 |
1.2.4 使用场景对比
| 场景 | 推荐类型 | 原因 | 示例 |
|---|---|---|---|
| 金融/医疗系统 | 硬反亲和性 | 合规要求,必须跨节点/可用区部署 | 银行核心系统、医疗病历系统 |
| 关键业务高可用 | 硬反亲和性 | 避免单点故障,保证业务连续性 | 数据库、消息队列、注册中心 |
| 微服务多副本 | 软反亲和性 | 优先分散,但资源不足时可以容忍 | Web 应用、API 网关 |
| 资源均衡 | 软反亲和性 | 尽量分散负载,但不强制 | 批处理任务、CI/CD 构建 |
| 跨可用区容灾 | 硬反亲和性 | 必须分散到不同可用区 | 异地多活、灾备系统 |
| 开发测试环境 | 软反亲和性 | 资源有限时优先保证 Pod 运行 | 测试环境、临时任务 |
| StatefulSet 部署 | 硬反亲和性 | 每个副本必须在不同节点 | Kafka、Elasticsearch 集群 |
1.2.5 亲和性 vs 反亲和性对比
| 对比维度 | Pod 亲和性(Affinity) | Pod 反亲和性(Anti-Affinity) |
|---|---|---|
| 关键字 | podAffinity | podAntiAffinity |
| 调度行为 | 吸引:尽量/必须与目标 Pod 在同一节点 | 排斥:尽量/必须不与目标 Pod 在同一节点 |
| 适用场景 | 就近部署、减少延迟、缓存与业务同节点 | 高可用、容灾、避免单点故障 |
| 配置语法 | 与反亲和性对称 | 与亲和性对称 |
1.2.6 常用拓扑域类型
| topologyKey | 说明 | 分散粒度 |
|---|---|---|
kubernetes.io/hostname | 按节点分散 | 最细粒度(节点级别) |
topology.kubernetes.io/zone | 按可用区分散 | 中等粒度(机房级别) |
topology.kubernetes.io/region | 按地域分散 | 最大粒度(地域级别) |
1.2.7 选择决策树
是否需要保证 Pod 必须分散在不同节点?
│
├── 是(合规/高可用要求)
│ └── 使用硬反亲和性(requiredDuringSchedulingIgnoredDuringExecution)
│ ├── 节点数足够 → 正常调度,成功分散
│ └── 节点数不足 → Pod Pending,无法调度
│
└── 否(性能优化/资源均衡)
└── 使用软反亲和性(preferredDuringSchedulingIgnoredDuringExecution)
├── 节点数足够 → 优先分散,效果好
└── 节点数不足 → 退而求其次,允许同节点
1.2.8 最佳实践
| 环境 | 推荐配置 | 理由 |
|---|---|---|
| 生产环境(关键应用) | 硬反亲和性 + 多副本 | 保证高可用,避免单点故障 |
| 生产环境(非关键应用) | 软反亲和性 | 提升可用性,但不过度消耗资源 |
| 开发测试环境 | 软反亲和性或不用 | 资源有限,保证 Pod 能运行 |
| StatefulSet 有状态应用 | 硬反亲和性 | 每个副本独立节点,保证稳定性 |
| Deployment 无状态应用 | 软反亲和性 | 均衡分布,提高资源利用率 |
2、污点和容忍度
污点是节点上定义的键值属性,用于拒绝 Pod 调度;容忍度是 Pod 上定义的键值属性,用于声明能容忍哪些污点。只有 Pod 的容忍度匹配节点的污点时,Pod 才能被调度到该节点。
| 概念 | 定义位置 | 作用 | 关系 |
|---|---|---|---|
| 污点(Taints) | 节点(Node) | 标记节点,拒绝不匹配的 Pod 调度 | 节点主动排斥 Pod |
| 容忍度(Tolerations) | Pod | 声明 Pod 能够容忍哪些污点 | Pod 主动申请豁免 |
节点打污点(Taint) → 拒绝不容忍的 Pod
↓
Pod 设置容忍度(Toleration)→ 匹配污点 → 允许调度
↓
不容忍的 Pod → 被拒绝调度(Pending)
2.1 污点与亲和性的区别
污点是节点"挑 Pod",亲和性是 Pod"挑节点",两者配合使用可实现精细的调度控制。
| 对比维度 | 污点(Taints) | 节点亲和性(NodeAffinity) |
|---|---|---|
| 定义位置 | 节点 | Pod |
| 作用 | 节点拒绝Pod(排斥) | Pod选择节点(吸引) |
| 方向 | 节点 → Pod(反向选择) | Pod → 节点(正向选择) |
| 主要用途 | 隔离节点、专用节点 | 按标签选择节点 |
2.2污点完整字段结构
kubectl taint nodes <节点名称> <键>=<值>:<污点效果>污点由三个字段组成:
| 字段 | 说明 | 是否必填 | 示例 |
|---|---|---|---|
| 键(Key) | 污点的标识符 | ✅ 必填 | node-type,disk,gpu |
| 值(Value) | 污点的具体值 | ✅ 必填(配合键使用) | production,ssd,nvidia |
| 污点效果(Effect) | 定义 Pod 不匹配时的行为 | ✅ 必填 | NoSchedule,PreferNoSchedule,NoExecute |
2.3 污点效果(Effect)详解
| Effect | 说明 | 对已运行 Pod 的影响 | 对未调度 Pod 的影响 |
|---|---|---|---|
| NoSchedule | 硬性排斥 | 不影响已运行的 Pod | 不容忍则无法调度 |
| PreferNoSchedule | 软性排斥 | 不影响已运行的 Pod | 不容忍则尽量不调度 |
| NoExecute | 驱逐型排斥 | 驱逐已运行且不容忍的 Pod | 不容忍则无法调度 |
2.4 示例演示
2.4.1 NoSchedule(硬性排斥)
# 给节点打污点:不容忍则无法调度 kubectl taint nodes k8s-node-1 node-type=production:NoSchedule # 查看节点污点 kubectl describe node k8s-node-1 | grep Taints # 输出:Taints: node-type=production:NoSchedule2.4.2 PreferNoSchedule(软性排斥)
# 给节点打污点:尽量不调度,但资源不足时允许 kubectl taint nodes k8s-node-2 disk=ssd:PreferNoSchedule2.4.3 NoExecute(驱逐型排斥)
# 给节点打污点:不容忍的 Pod 会被驱逐 kubectl taint nodes k8s-node-3 maintenance=true:NoExecute # 查看效果:已运行的 Pod 会被驱逐 kubectl get pods -o wide # 不容忍该污点的 Pod 状态变为 Pending 或重新调度到其他节点2.4.4 污点完整操作命令
# 1. 添加污点 kubectl taint nodes k8s-node-1 gpu=nvidia:NoSchedule # 2. 查看节点所有污点 kubectl describe node k8s-node-1 | grep Taints # 或 kubectl get nodes k8s-node-1 -o json | jq '.spec.taints' # 3. 查看所有节点的污点 kubectl get nodes -o json | jq '.items[].spec.taints' # 4. 删除污点(在键后面加 -) kubectl taint nodes k8s-node-1 gpu:NoSchedule- # 5. 删除指定值的污点 kubectl taint nodes k8s-node-1 gpu=nvidia:NoSchedule- # 6. 更新污点(覆盖) kubectl taint nodes k8s-node-1 gpu=amd:NoSchedule --overwrite2.4.5 污点对应的 Pod 容忍度(Tolerations)
tolerations: - key: "gpu" # 匹配污点的键 operator: "Equal" # 匹配操作符:Equal 或 Exists value: "nvidia" # 匹配污点的值(operator=Equal 时必填) effect: "NoSchedule" # 匹配污点的效果 tolerationSeconds: 3600 # 可选,驱逐前的容忍时间(仅 NoExecute 有效)匹配规则
| 操作符 | 规则 | 示例 |
|---|---|---|
| Equal | 键、值、效果必须完全匹配 | 污点gpu=nvidia:NoSchedule→ 容忍key: gpu, value: nvidia, effect: NoSchedule |
| Exists | 只要键和效果匹配即可,忽略值 | 污点gpu=nvidia:NoSchedule→ 容忍key: gpu, effect: NoSchedule |
2.4.6 特殊容忍度(容忍所有污点)
系统级别的pod基本上都是容忍所有污点
tolerations: - operator: "Exists" # 匹配所有污点,忽略键、值、效果2.4.7 生产环境典型使用场景
| 场景 | 污点配置 | 说明 |
|---|---|---|
| GPU 专用节点 | kubectl taint nodes gpu-node gpu=nvidia:NoSchedule | 只允许需要 GPU 的 Pod 调度 |
| 维护节点 | kubectl taint nodes node-01 maintenance=true:NoExecute | 驱逐所有非维护 Pod |
| 生产环境隔离 | kubectl taint nodes prod-node env=production:NoSchedule | 只允许生产 Pod 调度 |
| SSD 优选节点 | kubectl taint nodes ssd-node disk=ssd:PreferNoSchedule | 优先调度,非必须 |
2.4.8 查看污点对应 Pod 是否容忍
# 1. 查看节点污点 kubectl describe node k8s-node-1 | grep Taints # 2. 查看 Pod 容忍度 kubectl describe pod my-pod | grep -A 5 Tolerations # 3. 查看 Pod 调度结果 kubectl get pods -o wide # 4. 查看调度失败原因 kubectl describe pod my-pod | grep -A 5 Events污点核心字段:Key + Value + Effect,三者组合决定节点的"排斥策略"和 Pod 的"容忍能力"。