简介
在Kubernetes中,工作负载调度的基本单位是Pod。创建工作负载时,调度器会自动对工作负载中的Pod进行合理分配,例如将Pod分散到资源充足的节点上。
虽然调度器的默认行为已经能够满足许多基本需求,但在一些特定场景下,用户可能需要更精细地控制Pod的部署位置。为了实现这一点,Kubernetes允许用户在工作负载定义中配置调度策略。例如:
- 将前端应用和后端应用部署在一起,有助于减少延迟,因为这两种类型的Pod可以共享相同的物理资源。
- 某类应用部署到某些特定的节点,确保关键应用总是运行在最优的硬件或配置上。
- 不同应用部署到不同的节点,有助于隔离应用,防止一个应用的问题影响到其他应用。
2.调度策略
Kubernetes对Pod的调度策略方式如下表:
| 调度策略 | Yaml定义 | 说明 |
|---|---|---|
| 节点名称 | nodeName | 指定节点的主机名来实现pod调度到指定节点上 |
| 节点选择 | nodeSelector | 通过选择节点的标签来希望调度到目标节点,然后Pod被调度到拥有指定标签的节点上。 |
| 节点亲和 | nodeAffinity | 节点亲和可以实现nodeSelector的能力,但其表达能力更强,您可以根据节点上的标签,使用标签选择器来筛选需要亲和的节点,支持必须满足和尽量满足的亲和性规则 注:如果同时指定nodeSelector和nodeAffinity,则两者必须都要满足,才能将Pod调度到候选节点上 |
| pod亲和/反亲和 | podAffinity/podAntiAffinity | 根据工作负载标签,使用标签选择器来筛选需要 亲和/反亲和 的Pod,并将新建的pod 调度/不调度 至目标Pod所在的节点(或节点组),也支持必须满足和尽量满足的亲和性规则 注:pod的亲和性和反亲和性需要一定的计算时间,因此在大规模集群中会显著降低调度的速度。在包含数百个节点的集群中,不建议使用这类设置。 |
3.亲和性规则
基于节点亲和或pod亲和/反亲和的调度策略还可以设置必须满足的硬约束和尽量满足的软约束,以满足更复杂的调度情况。
在下表亲和规则中,YAML字段前半段requiredDuringScheduling或preferredDuringScheduling表示在调度时需要强制满足(require)或尽量满足(prefer)定义的标签规则。而后半段IgnoredDuringExecution表示如果节点标签在Kubernetes调度Pod后发生了变更,Pod仍将继续运行不会重新调度。但是如果该节点上的kubelet重启,kubelet会重新对节点亲和性规则进行校验,Pod仍会被调度至其他节点。
| 规则类型 | YAML字段定义 | 说明 |
|---|---|---|
| 必须满足 | requiredDuringSchedulingIgnoredDuringExecution | 硬约束,即调度器只有在规则被满足的时候才能执行调度。 |
| 尽量满足 | preferredDuringSchedulingIgnoredDuringExecution | 软约束,即调度器会尝试寻找满足对应规则的目标对象。即使找不到匹配的目标,调度器仍然会调度该Pod。 在使用尽量满足的亲和性类型时,您可以为每个实例设置weight字段,其取值范围是1到100。 权重越高,调度的优先级越高。 |
4.标签选择器
在创建调度策略时,您需要使用标签选择器的逻辑运算符来筛选标签值,最终确定需要亲和/反亲和的对象。
| 参数 | 说明 |
|---|---|
| key | 标签键名,满足筛选条件的对象需包含该键名的标签,且标签的取值满足标签值列表(values字段)和逻辑运算符的运算关系。 |
| operator | 您可以使用逻辑运算符来确定标签值的筛选规则,所有逻辑运算符如下: In:亲和/反亲和对象的标签包含在标签值列表(values字段)中。 NotIn:亲和/反亲和对象的标签不包含在标签值列表(values字段)中。 Exists:亲和/反亲和对象存在指定标签名,此时无需填写values字段。 DoesNotExist:亲和/反亲和对象不存在指定标签名,此时无需填写values字段。 Gt:仅在节点亲和性中设置,调度节点的标签值大于列表值 (字符串比较)。 Lt:仅在节点亲和性中设置,调度节点的标签值小于列表值 (字符串比较)。 |
| values | 标签值的数组 |
5.yaml配置示例
5.1.nodeName yaml示例
本示例中会将pod调度到通过nodeName指定的节点:gt5-node-test-vm10-188 上
spec:
containers:
- image: crpi-44hgz4440mgo9lnt.cn-guangzhou.xxx.com/eeenet/nginx:stable-alpine3.17-slim
imagePullPolicy: Always
name: test-one
ports:
- containerPort: 80
protocol: TCP
dnsPolicy: ClusterFirst
nodeName: gt5-node-test-vm10-188
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
5.2.nodeSelector yaml示例
本示例表明该Pod将被调度到有deploy_qa=true标签的节点。示例如下
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
nodeSelector:
deploy_qa: "true"
containers:
- image: nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
imagePullSecrets:
- name: default-secret
5.3.nodeAffinity yaml示例
本示例中,必须满足的规则表示调度的节点必须包含一个键名为gpu的标签,且标签值为true。而尽量满足规则表示根据节点可用区的标签topology.kubernetes.io/zone进行优先级排序,尽量将Pod调度至可用区az1的节点上:
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu
labels:
app: gpu
spec:
selector:
matchLabels:
app: gpu
replicas: 3
template:
metadata:
labels:
app: gpu
spec:
containers:
- image: nginx:alpine
name: gpu
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
affinity: # 设置调度策略
nodeAffinity: # 表示节点亲和性调度
requiredDuringSchedulingIgnoredDuringExecution: # 表示必须满足的调度策略
nodeSelectorTerms: # 根据节点标签选择满足条件的节点
- matchExpressions: # 节点标签匹配规则
- key: gpu # 节点标签的键为gpu
operator: In # 表示存在values列表中的值即满足规则
values: # 节点标签的值为true
- "true"
preferredDuringSchedulingIgnoredDuringExecution: # 表示尽量满足的调度策略
- weight: 100 # 使用尽量满足策略时可设置优先级,取值为1-100,数值越大优先级越高
preference: # 使用尽量满足策略时,设置优先选择的节点标签匹配规则
matchExpressions: # 节点标签匹配规则
- key: topology.kubernetes.io/zone # 节点可用区的标签
operator: In # 表示存在values列表中的值即满足规则
values: # 节点标签的值为az1
- "az1"
5.4.podAffinity亲和性yaml示例
Kubernetes支持Pod和Pod之间的亲和,例如将应用的前端和后端部署在一起,从而减少访问延迟。
假设有个应用的后端已经创建,且带有app=backend的标签。您可以使用.spec.affinity.podAffinity字段来设置工作负载亲和性,将前端Pod(标签为app=frontend)和后端Pod(标签为app=backend)部署在一起。
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app: frontend
spec:
selector:
matchLabels:
app: frontend
replicas: 3
template:
metadata:
labels:
app: frontend
spec:
containers:
- image: nginx:alpine
name: frontend
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
affinity: # 设置调度策略
podAffinity: # 工作负载亲和性调度规则
requiredDuringSchedulingIgnoredDuringExecution: # 表示必须满足的调度策略
- topologyKey: prefer # 根据节点标签划分拓扑域,示例中prefer为自定义标签
labelSelector: # 根据工作负载标签选择满足条件的工作负载
matchExpressions: # 工作负载标签匹配规则
- key: app # 工作负载标签的键为app
operator: In # 表示存在values列表中的值即满足规则
values: # 工作负载标签的值列表
- backend
preferredDuringSchedulingIgnoredDuringExecution: # 表示尽量满足的调度策略
- weight: 100 # 使用尽量满足策略时可设置优先级,取值为1-100,数值越大优先级越高
podAffinityTerm: # 使用尽量满足策略时的亲和项
topologyKey: topology.kubernetes.io/zone # 根据节点标签划分拓扑域,以节点的可用区为粒度
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- backend
上述示例中的pod调度时,必须满足规则会根据prefer标签划分节点拓扑域,如果当拓扑域中某个节点运行着后端Pod(标签为app=backend),即使该拓扑域中并非所有节点均运行了后端Pod,前端Pod(标签为app=frontend)同样会部署在此拓扑域中。而尽量满足规则根据topology.kubernetes.io/zone划分拓扑域,以节点的可用区为粒度进行调度,表示尽量将前后端部署至同一可用区的节点。
注意:
对于工作负载亲和来说,使用requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution规则时, topologyKey字段不允许为空。
topologyKey字段用于划分拓扑域,当节点上存在topologyKey字段指定的标签,且键、值均相同时,这些节点会被认为属于同一拓扑域,然后调度器会根据工作负载的标签选择需要调度的拓扑域。一个拓扑域中可能包含多个节点,当拓扑域中的一个节点上运行了满足标签选择规则的工作负载时,则该拓扑域中的节点均可以被调度。
例如,当topologyKey的标签为topology.kubernetes.io/zone时,表示以节点的可用区作为拓扑域,工作负载在部署时会以可用区为粒度进行调度。
5.5.podAntiAffinity yaml示例
工作负载反亲和性
在某些情况下,需要将Pod分开部署,例如Pod之间部署在一起会影响性能的情况。
假设有个应用的前端已经创建,且带有app=frontend的标签。您可以使用.spec.affinity.podAntiAffinity字段来设置工作负载反亲和性,将各个Pod部署在不同的节点,且优先多可用区。
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app: frontend
spec:
selector:
matchLabels:
app: frontend
replicas: 5
template:
metadata:
labels:
app: frontend
spec:
containers:
- image: nginx:alpine
name: frontend
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
affinity:
podAntiAffinity: # 工作负载反亲和性调度规则
requiredDuringSchedulingIgnoredDuringExecution: # 表示必须满足的调度策略
- topologyKey: kubernetes.io/hostname # 根据节点标签划分拓扑域
labelSelector: # Pod标签匹配规则
matchExpressions: # 工作负载标签的键为app
- key: app # 工作负载标签的键为app
operator: In # 表示存在values列表中的值即满足规则
values: # 工作负载标签的值列表
- frontend
preferredDuringSchedulingIgnoredDuringExecution: # 表示尽量满足的调度策略
- weight: 100 # 使用尽量满足策略时可设置优先级,取值为1-100,数值越大优先级越高
podAffinityTerm: # 使用尽量满足策略时的亲和项
topologyKey: topology.kubernetes.io/zone # 根据节点标签划分拓扑域,以节点的可用区为粒度
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- frontend
以上示例中定义了反亲和规则,必须满足的规则表示根据kubernetes.io/hostname标签划分节点拓扑域。由于拥有kubernetes.io/hostname标签的节点中,每个节点的标签值均不同,因此一个拓扑域中只有一个节点。当一个拓扑域中(此处为一个节点)已经存在frontend标签的Pod时,该拓扑域不会被继续调度具有相同标签的Pod。而尽量满足规则根据topology.kubernetes.io/zone划分拓扑域,以节点的可用区为粒度进行调度,表示尽量将Pod分布至不同可用区的节点。
注意:
对于工作负载反亲和来说,使用requiredDuringSchedulingIgnoredDuringExecution规则时, Kubernetes默认的准入控制器 LimitPodHardAntiAffinityTopology要求topologyKey字段只能是kubernetes.io/hostname。如果您希望使用其他定制拓扑逻辑,可以更改或者禁用该准入控制器。
通过xkube进行设置
通过多集群k8s管理平台xkube的功能进行亲和性设置,xkube安装部署参考:https://developer.aliyun.com/article/1696994
功能参考下图:
