K8S计划任务、污点容忍和亲和性
- Kubernetes
- 2024-09-05
- 270热度
- 0评论
K8S计划任务、污点容忍和亲和性
Job
Kubernetes (K8S) 中的 Job 是一种控制器,它的主要作用是确保一组 Pod 按照设定的次数成功运行完成。Job 通常用于执行一次性任务或批处理任务,这些任务不需要长期运行,但需要在成功完成后自动退出。以下是 Job 的主要功能和使用场景:
- 一次性任务 Job 用于执行一次性任务,例如数据处理、备份、批量计算等。这些任务在完成后自动退出,并且 Kubernetes 会确保任务在设定的次数内成功执行。
- 确保任务成功 如果某个 Pod 因为错误或其他原因失败,Job 控制器会启动一个新的 Pod 来代替它,直到任务成功执行指定的次数。
- 并行处理 Job 支持并行处理,可以配置多个 Pod 同时执行任务。这对于需要并行计算或处理大数据集的场景非常有用。
- 定时任务配合 CronJob Job 通常与 CronJob 结合使用,CronJob 是基于时间调度的任务管理器。通过 CronJob,可以按照设定的时间间隔定期执行 Job。
- 任务成功与失败的处理 定义任务的重试策略,指定最大失败次数等。Job 还可以通过 backoffLimit 参数控制重试的次数。
Job的配置如下所示:
[root@master-01 ~]# vim /k8spod/job/job.yaml
apiVersion: batch/v1
kind: Job
metadata:
labels:
job-name: echo
name: echo
namespace: default
spec:
suspend: false # 1.21+
ttlSecondsAfterFinished: 100
backoffLimit: 4
completions: 1
parallelism: 1
template:
spec:
restartPolicy: Never
containers:
- command:
- echo
- Hello, Job
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/busybox
imagePullPolicy: IfNotPresent
name: echo
参数 | 含义 |
---|---|
backoffLimit | 如果任务执行失败,失败多少次后不再执行 |
completions | 有多少个Pod执行成功,认为任务是成功的,若数值为空,则默认和parallelism数值一样 |
parallelism | 并行执行任务的数量。 如果parallelism数值大于未完成任务数,只会创建未完成的数量;比如completions是4,并发是3,第一次会创建3个Pod执行任务,第二次只会创建1个Pod执行任务 |
ttlSecondsAfterFinished | Job在执行结束之后(状态为completed或Failed)自动清理。设置为0表示执行结束立即删除,不设置则不会清除,需要开启TTLAfterFinished特性 |
创建资源并查看资源状态
[root@master-01 ~]# kubectl create -f /k8spod/job/job.yaml
job.batch/echo created
[root@master-01 ~]# kubectl get jobs.batch
NAME STATUS COMPLETIONS DURATION AGE
echo Running 0/1 5s 5s
[root@master-01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cluster-test-665f554bcc-bcw5v 1/1 Running 279 (39m ago) 77d
echo-v2lfp 0/1 Completed 0 12s
可以查看日志,观察Job是否正常输出内容。等待100秒后,容器和Job都会被清理
[root@master-01 ~]# kubectl logs echo-v2lfp
Hello, Job
读者可以修改suspend字段为true,创建后,可以观察到Job被暂停,等待恢复
[root@master-01 ~]# kubectl get jobs.batch
NAME STATUS COMPLETIONS DURATION AGE
echo Suspended 0/1 3s
CronJob
Kubernetes 的 CronJob 是用于定期执行任务的资源对象,类似于 Linux 的 cron 定时任务。CronJob 可以根据指定的时间表在集群中创建 Job,Job 再启动一个或多个 Pod 来执行任务。这在需要定期运行脚本、批处理任务或维护任务时非常有用。
1.定期执行任务
通过 CronJob,可以按照特定的时间表周期性地运行任务,比如每天、每小时、每分钟等。
2.任务调度
CronJob 使用类 cron 的语法来定义任务的时间表,可以灵活地安排任务的执行时间。
3.任务失败后的重试
配置 Job 的重试策略,如果任务失败,CronJob 会按照设置的策略重试任务。
4.并发策略
CronJob 支持控制并发执行的 Job 数量,允许选择是否允许并发执行,还是需要等上一个任务完成后再执行。
5.历史记录管理
可以配置保留的成功和失败的 Job 的数量,以便管理和监控任务的执行情况。
常见应用场景
- 日志轮转:定期清理日志文件或归档日志。
- 备份:定期备份数据库或其他重要数据。
- 定期报告:每天或每周生成报告,并将其发送到指定位置。
- 健康检查:定期检查系统健康状态,并根据结果执行相应操作。
注意事项
- 时区:CronJob默认使用 UTC 时区。如果你需要使用本地时间,可能需要在容器中手动处理时区转换。
- 性能影响:大量的并发 Job 可能会对集群性能产生影响,因此应合理配置 Job的并发策略和历史记录限制。
- 失败处理:应设置合适的重试策略和错误处理机制,避免因临时性问题导致任务执行失败。
CronJob的配置如下所示:
[root@master-01 ~]# vim /k8spod/cronjob/cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
labels:
run: hello
name: hello
namespace: default
spec:
schedule: '*/1 * * * *'
successfulJobsHistoryLimit: 3
suspend: false
concurrencyPolicy: Allow
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
metadata:
labels:
run: hello
spec:
restartPolicy: OnFailure
containers:
- name: hello
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/busybox
imagePullPolicy: IfNotPresent
参数 | 含义 |
---|---|
schedule | 调度周期,和Linux一致,分别是分时日月周 |
successfulJobsHistoryLimit | 保留多少已完成的任务,按需配置 |
suspend | 如果设置为true,则暂停后续的任务,默认为false |
concurrencyPolicy | 并发调度策略。可选参数如下:Allow:允许同时运行多个任务,Forbid:不允许并发运行,如果之前的任务尚未完成,新的任务不会被创建,Replace:如果之前的任务尚未完成,新的任务会替换的之前的任务 |
restartPolicy | 重启策略,和Pod一致 |
创建资源并查看资源状态,由于执行间隔为1分钟,所有刚开始时无法看到Pod
[root@master-01 ~]# kubectl get cronjobs.batch
NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * <none> False 0 <none> 7s
[root@master-01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cluster-test-665f554bcc-bcw5v 1/1 Running 280 (5m39s ago) 77d
看到Pod内执行结果,并观察到时间间隔为1分钟
[root@master-01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cluster-test-665f554bcc-bcw5v 1/1 Running 280 (7m27s ago) 77d
hello-28747930-gjpv4 0/1 Completed 0 75s
hello-28747931-6vxnb 0/1 Completed 0 15s
[root@master-01 ~]# kubectl logs hello-28747930-gjpv4
Wed Aug 28 20:10:01 UTC 2024
Hello from the Kubernetes cluster
[root@master-01 ~]# kubectl logs hello-28747931-6vxnb
Wed Aug 28 20:11:01 UTC 2024
Hello from the Kubernetes cluster
初始化容器(InitContainer)
K8S初始化容器(init containers)的作用是在主容器启动之前执行一些必要的设置和准备工作。Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码,可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。Init容器可以以root身份运行,执行一些高权限命令。Init容器相关操作执行完成以后即退出,不会给业务容器带来安全隐患。初始化容器通常用于配置环境变量、准备共享卷、等待依赖服务的启动等任务。
初始化容器与postStart生命周期钩子的主要区别在于它们在Pod生命周期中的作用和执行时机。postStart是主容器生命周期的一部分,它在主容器启动后立即执行,但执行是异步的,不保证在主容器的入口(entrypoint)之前运行。postStart钩子主要用于资源部署、环境准备等,如果执行失败,主容器会根据重启策略决定是否重启。
相比之下,初始化容器是在主容器启动之前独立运行的容器,它们的成功执行是主容器启动的前提条件。初始化容器提供了一种确保主容器启动所需环境已经正确设置的机制。一旦初始化容器执行完成并退出,主容器及其后续的生命周期钩子(如postStart)才会开始执行。
总结来说,初始化容器专注于为主容器提供准备工作,而postStart钩子专注于主容器启动后的初始化任务。初始化容器在主容器之前运行,而postStart在主容器之后运行
初始化容器和普通容器大致相同,除以下这几点,它们总是运行到完成,上一个运行完成才会运行下一个,如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,但是Pod对应的 restartPolicy 值为 Never,Kubernetes 不会重新启动 Pod。Init 容器不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe
初始化容器实例如下所示:
[root@master-01 ~]# vim /k8spod/initpod/initpod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: test-init
name: test-init
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: test-init
template:
metadata:
labels:
app: test-init
spec:
volumes:
- name: data
emptyDir: {}
initContainers:
- command:
- sh
- -c
- touch /mnt/test-init.txt
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
imagePullPolicy: IfNotPresent
name: init-touch
volumeMounts:
- name: data
mountPath: /mnt
- command:
- sh
- -c
- for i in `seq 1 100`; do echo $i; sleep 1; done
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
imagePullPolicy: IfNotPresent
name: echo
containers:
- image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
imagePullPolicy: IfNotPresent
name: test-init
volumeMounts:
- name: data
mountPath: /mnt
创建资源,并查看资源创建情况,可以观察到当前正在初始化容器,而在初始化完成之前,主容器是不会启动的
[root@master-01 ~]# kubectl create -f /k8spod/initpod/initpod.yaml
deployment.apps/test-init created
[root@master-01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cluster-test-665f554bcc-bcw5v 1/1 Running 281 (81s ago) 77d
test-init-54ccc78b57-f929f 0/1 Init:0/2 0 5s
test-init-54ccc78b57-smtxn 0/1 Init:1/2 0 5s
test-init-54ccc78b57-zkslp 0/1 Init:1/2 0 5s
查看第二个初始化容器的日志,可以观察到,使用输出1-100来模拟初始化的任务,比如下载某些软件和依赖包或者修改内核参数等
[root@master-01 ~]# kubectl logs test-init-54ccc78b57-zkslp -c echo
1
2
3
...省略部分输出...
临时容器(EphemeralContainer)
Kubernetes中的临时容器(Ephemeral Containers)是一种在Pod中运行临时任务的机制,通常用于调试目的。与初始化容器(init containers)和主容器(main containers)不同,临时容器不在Pod创建时定义,而是可以在Pod运行时动态添加和运行。
临时容器的特点
- 动态添加:可以在Pod运行时添加和删除临时容器,用于调试和诊断。
- 无需重启Pod:添加临时容器不需要重新创建或重启Pod,这使得调试更加方便。
- 短生命周期:临时容器通常用于短暂的调试任务,比如检查Pod的状态、查看文件系统等。
使用场景
- 调试:当你需要在Pod内运行一些临时命令或工具(如
busybox
或debug
工具)来排查问题时,临时容器非常有用。 - 排查问题:可以用来检查容器的网络连接、文件系统或应用的运行状态,而不需要修改Pod的原始定义。
使用刚才的初始化容器,尝试观察IP地址和端口,提示没有此命令
[root@master-01 ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cluster-test-665f554bcc-bcw5v 1/1 Running 281 (30m ago) 77d 172.16.184.126 master-01 <none> <none>
test-init-54ccc78b57-f929f 1/1 Running 0 28m 172.16.222.41 master-02 <none> <none>
[root@master-01 ~]# kubectl exec -it test-init-54ccc78b57-f929f -- bash
Defaulted container "test-init" out of: test-init, init-touch (init), echo (init)
root@test-init-54ccc78b57-f929f:/# ip
bash: ip: command not found
root@test-init-54ccc78b57-f929f:/# ss
bash: ss: command not found
使用临时容器进行调试
[root@master-01 ~]# kubectl debug test-init-54ccc78b57-f929f -it --image=registry.cn-beijing.aliyuncs.com/caijxlinux/debug-tools
Defaulting debug container name to debugger-kpvw5.
If you don't see a command prompt, try pressing enter.
(21:54 test-init-54ccc78b57-f929f:/)
(21:54 test-init-54ccc78b57-f929f:/) ip ad s | grep 172
inet 172.16.222.41/32 scope global eth0
(21:55 test-init-54ccc78b57-f929f:/) ss | tail -1
u_str ESTAB 0 0 * 8542556 * 8542557
输入exit退出临时容器,提示下次可用attach参数再次连接,但是尝试连接会提示报错
(21:50 test-init-54ccc78b57-f929f:/) exit
exit
Session ended, the ephemeral container will not be restarted but may be reattached using 'kubectl attach test-init-54ccc78b57-f929f -c debugger-j9wmw -i -t' if it is still running
[root@master-01 ~]# kubectl attach test-init-54ccc78b57-f929f -c debugger-j9wmw -i -t
If you don't see a command prompt, try pressing enter.
error: Internal error occurred: unable to upgrade connection: container debugger-j9wmw not found in pod test-init-54ccc78b57-f929f_default
通过describe命令查看临时容器的状态,发现是Terminated。所以在使用exit命令退出时,不能再次连接,只能再次创建临时容器。
[root@master-01 ~]# kubectl describe pod test-init-54ccc78b57-f929f
...省略部分输出...
Ephemeral Containers:
debugger-j9wmw:
Container ID: containerd://6b72cb98135aa92d57b04b12de0a37029c9b552fefd92c27dcbc6d8ac6e6fb18
Image: registry.cn-beijing.aliyuncs.com/caijxlinux/debug-tools
Image ID: registry.cn-beijing.aliyuncs.com/caijxlinux/debug-tools@sha256:e5bb1f4eb56039e0e82d7ec3ec788c62e615ec6ba167690f1b675b1dc4a15f36
Port: <none>
Host Port: <none>
State: Terminated
Reason: Completed
Exit Code: 0
Started: Thu, 29 Aug 2024 05:36:17 +0800
Finished: Thu, 29 Aug 2024 05:54:26 +0800
Ready: False
...省略部分输出...
读者也可以在创建临时容器时,使用--attach参数来防止这种情况。 如果会话断开连接,也可以继续使kubectl attach(一次使用)或kubectl exec(多次使用) 重新连接。
[root@master-01 ~]# kubectl debug test-init-54ccc78b57-f929f -it --image=registry.cn-beijing.aliyuncs.com/dotbalo/debug-tools --attach=false
Defaulting debug container name to debugger-xmkxg.
[root@master-01 ~]# kubectl exec -it test-init-54ccc78b57-f929f -c debugger-xmkxg -- bash
(22:14 test-init-54ccc78b57-f929f:/)
K8S污点和容忍(Taint&Toleration)
在Kubernetes中,污点(Taints) 是用于限制或控制Pod调度到特定节点上的一种机制。通过给节点打上污点,Kubernetes可以防止Pod被调度到这些节点上,除非这些Pod具有相应的容忍度(Tolerations)。
污点的作用
污点用于控制Pod的调度行为,确保只有特定的Pod可以被调度到带有污点的节点上。这在以下场景中非常有用:
- 隔离关键工作负载:可以使用污点将关键的、延迟敏感的工作负载隔离到特定节点,防止其他Pod干扰这些节点。
- 维护节点:如果一个节点需要维护,可以打上污点,将Pod从该节点上迁移走,防止新的Pod被调度到该节点上。
- 限制资源使用:在资源有限的节点上打上污点,防止资源密集型的Pod被调度到这些节点上。
节点添加污点的示例如下:(一个节点可以有多个污点)
kubectl taint nodes node1 key=value:NoSchedule
字段 | 含义 |
---|---|
NoSchedule | 禁止调度到该节点,已经在该节点上的Pod不受影响 |
NoExecute | 禁止调度到该节点,如果不符合这个污点,会立马被驱逐(或在一段时间后) |
PreferNoSchedule | 尽量避免将Pod调度到指定的节点上,如果没有更合适的节点,可以部署到该节点 |
在Kubernetes中,容忍(Toleration) 是与污点(Taint)相对应的一种机制。容忍允许Pod调度到带有特定污点的节点上,而不受污点的限制。通过容忍,Pod可以“容忍”节点上的污点,从而允许自己被调度到这些节点上。
使用场景
- 节点维护:当节点需要进行维护时,打上
NoSchedule
或NoExecute
污点,只有具备相应容忍度的Pod才会继续留在节点上或调度到该节点。 - 关键任务隔离:关键任务Pod可以配置容忍度,以确保它们可以调度到带有特定污点的节点上,从而隔离非关键任务的Pod。
- 资源管理:通过配置污点和容忍度,可以更精细地控制Pod的调度行为,以实现资源的合理分配和利用。
假设node-01节点为高性能节点,内置SSD硬盘,只有需要高性能存储的Pod才能调度到该节点上。为节点添加污点和标签
[root@master-01 ~]# kubectl taint nodes node-01 ssd=true:NoExecute
node/node-01 tainted
[root@master-01 ~]# kubectl taint nodes node-01 ssd=true:NoSchedule
node/node-01 tainted
[root@master-01 ~]# kubectl label nodes node-01 ssd=true
node/node-01 labeled
查看node-01节点上添加的污点
[root@master-01 ~]# kubectl describe nodes node-01 | grep Taints -A 2
Taints: ssd=true:NoExecute
ssd=true:NoSchedule
Unschedulable: false
配置Deployment控制器,并添加条件(通过nodeSelector和tolerations字段),模拟需要调度到node-01节点的情况(正常创建DP控制器,不会调度到node-01节点,因为有污点)
[root@master-01 ~]# vim taints_pods.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: taints-pods
name: taints-pods
spec:
replicas: 3
selector:
matchLabels:
app: taints-pods
template:
metadata:
labels:
app: taints-pods
spec:
containers:
- image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
name: nginx
nodeSelector:
ssd: "true"
tolerations:
- key: "ssd"
operator: "Exists"
创建资源并查看资源状态,可以观察容器在node-01节点上运行
[root@master-01 ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cluster-test-665f554bcc-kg54c 1/1 Running 7 (56m ago) 94d 172.16.184.66 master-01 <none> <none>
taints-pods-55956cdf4f-b9ltg 1/1 Running 0 3m1s 172.16.190.7 node-01 <none> <none>
taints-pods-55956cdf4f-ff6wc 1/1 Running 0 3m4s 172.16.190.5 node-01 <none> <none>
taints-pods-55956cdf4f-lc8hb 1/1 Running 0 3m1s 172.16.190.6 node-01 <none> <none>
K8S内内置了一些污点,具体如下表所示
字段 | 含义 |
---|---|
node.kubernetes.io/not-ready | 节点未准备好,相当于节点状态Ready的值为False |
node.kubernetes.io/unreachable | Node Controller访问不到节点,相当于节点状态Ready的值为Unknown |
node.kubernetes.io/out-of-disk | 节点磁盘耗尽 |
node.kubernetes.io/memory-pressure | 节点存在内存压力 |
node.kubernetes.io/disk-pressure | 节点存在磁盘压力 |
node.kubernetes.io/network-unavailable | 节点网络不可达 |
node.kubernetes.io/unschedulable | 节点不可调度 |
node.cloudprovider.kubernetes.io/uninitialized | 如果Kubelet启动时指定了一个外部的cloudprovider,它将给当前节点添加一个Taint将其标记为不可用。在cloud-controller-manager的一个controller初始化这个节点后,Kubelet将删除这个Taint |
节点宕机快速恢复业务示例如下
[root@master-01 ~]# vim fastrecovery.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: tolerations-second
name: tolerations-second
spec:
replicas: 1
selector:
matchLabels:
app: tolerations-second
template:
metadata:
labels:
app: tolerations-second
spec:
containers:
- image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
name: nginx
nodeSelector:
ssd: "true"
tolerations:
- key: ssd
operator: Equal
value: "true"
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 10
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 10
创建资源,并查看资源状态,可以观察到容器在node-01节点被创建
[root@master-01 ~]# kubectl create -f fastrecovery.yaml
deployment.apps/tolerations-second created
[root@master-01 ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tolerations-second-77b86c9947-w8t6j 1/1 Running 0 19s 172.16.190.9 node-01 <none> <none>
node-02节点添加标签和污点
[root@master-01 ~]# kubectl taint node node-02 ssd=true:NoExecute
node/node-02 tainted
[root@master-01 ~]# kubectl taint node node-02 ssd=true:NoSchedule
node/node-02 tainted
[root@master-01 ~]# kubectl label nodes node-02 ssd=true
node/node-02 labeled
将node-01节点关机,大概1min后,K8S检测到节点故障,为节点添加内置污点
[root@node-01 ~]# shutdown -h now
[root@master-01 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-01 Ready <none> 95d v1.30.1
master-02 Ready <none> 95d v1.30.1
master-03 Ready <none> 95d v1.30.1
node-01 NotReady <none> 95d v1.30.1
node-02 Ready <none> 95d v1.30.1
[root@master-01 ~]# kubectl describe nodes node-01
...省略部分输出...
Taints: node.kubernetes.io/unreachable:NoExecute
ssd=true:NoExecute
node.kubernetes.io/unreachable:NoSchedule
ssd=true:NoSchedule
Unschedulable: false
...省略部分输出...
查看容器,可以观察到容器已经在node-02节点重建
[root@master-01 ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tolerations-second-77b86c9947-6zkn9 1/1 Running 0 85s 172.16.184.5 node-02 <none> <none>
tolerations-second-77b86c9947-w8t6j 1/1 Terminating 0 4m53s 172.16.190.9 node-01 <none> <none>
Taint命令常用示例
查看一个节点的污点
[root@master-01 ~]# kubectl get node node-01 -o go-template --template {{.spec.taints}}
[map[effect:NoSchedule key:ssd value:true] map[effect:NoExecute key:ssd value:true]]
删除污点(和label类似)
[root@master-01 ~]# kubectl taint nodes node-01 ssd-
node/node-01 untainted
修改污点
[root@master-01 ~]# kubectl taint nodes node-01 ssd=true:PreferNoSchedule --overwrite
node/node-01 modified
Affinity
在 Kubernetes 中,Affinity(亲和性) 是一种用于控制 Pod 调度的规则,它允许你定义 Pod 如何相互靠近(或远离)调度到集群中的节点上。通过使用 Affinity,你可以更好地控制工作负载在集群中的分布,以实现性能优化、资源利用率最大化、提高容错能力等目标。
Kubernetes 提供了两种主要的 Affinity 规则:
- Node Affinity(节点亲和性)
- Pod Affinity/Anti-Affinity(Pod 亲和性/反亲和性)
1. Node Affinity(节点亲和性)
Node Affinity 允许你指定 Pod 应该调度到哪些节点上。这与旧的 Node Selector 类似,但 Node Affinity 提供了更多的灵活性和条件表达能力。
Node Affinity 通常分为两种类型:
- requiredDuringSchedulingIgnoredDuringExecution: 必须满足的硬性规则。如果条件不满足,Pod 将无法被调度。
- preferredDuringSchedulingIgnoredDuringExecution: 优选的软性规则。调度器会尽量满足这些条件,但即使无法满足,Pod 也会被调度。
2. Pod Affinity/Anti-Affinity(Pod 亲和性/反亲和性)
Pod Affinity 允许你指定 Pod 应该与哪些其他 Pod 一起调度到同一个节点上。Pod Anti-Affinity 则相反,用于指定 Pod 不应该与哪些其他 Pod 调度到同一个节点上。
Affinity 使用场景
- 性能优化:将相关的工作负载调度到相同的节点上,以减少网络延迟。
- 高可用性:将相同类型的 Pod 分散到不同的节点上,以减少单点故障的影响。
- 资源隔离:将对资源需求不同的 Pod 分开调度,避免资源争抢。
- 容错能力:通过 Anti-Affinity 将关键组件分布在不同的节点或区域,以提高容错能力。
某些Pod优先选择有ssd=true标签的节点,如果没有在考虑部署到其它节点;某些Pod需要部署在ssd=true和type=physical的节点上,但是优先部署在ssd=true的节点上(Pod和节点之间的关系)同一个应用的Pod不同的副本或者同一个项目的应用尽量或必须不部署在同一个节点或者符合某个标签的一类节点上或者不同的区域;相互依赖的两个Pod尽量或必须部署在同一个节点上或者同一个域内(Pod和Pod之间的关系)
节点亲和力配置如下所示
[root@master-01 ~]# vim nodeaffinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
- physical
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: network
operator: In
values:
- 100M
containers:
- name: with-node-affinity
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
requiredDuringSchedulingIgnoredDuringExecution(硬亲和力配置)
参数 | 配置 |
---|---|
nodeSelectorTerms | 节点选择器配置,可以配置多个matchExpressions(满足其一),每个matchExpressions下可以配置多个key、value类型的选择器(都需要满足),其中values可以配置多个(满足其一) |
preferredDuringSchedulingIgnoredDuringExecution(软亲和力配置)
参数 | 配置 |
---|---|
weight | 软亲和力的权重,权重越高优先级越大,范围1-100 |
preference | 软亲和力配置项,和weight同级,可以配置多个,matchExpressions和硬亲和力一致 |
operator(标签匹配的方式)
参数 | 配置 |
---|---|
In | 相当于key = value的形式 |
NotIn | 相当于key != value的形式 |
Exists | 节点存在label的key为指定的值即可,不能配置values字段 |
DoesNotExist | 节点不存在label的key为指定的值即可,不能配置values字段 |
Gt | 大于value指定的值 |
Lt | 小于value指定的值 |
Pod亲和力和反亲和力
Pod亲和力和反亲和力配置如下所示:(具体KV需要根据实际环境进行修改)
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
namespaces:
- default
topologyKey: failure-domain.beta.kubernetes.io/zone
containers:
- name: with-pod-affinity
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
参数 | 含义 |
---|---|
labelSelector | Pod选择器配置,可以配置多个 |
matchExpressions | 和节点亲和力配置一致 |
operator | 配置和节点亲和力一致,但是没有Gt和Lt |
topologyKey | 匹配的拓扑域的key,也就是节点上label的key,key和value相同的为同一个域,可以用于标注不同的机房和地区 |
Namespaces | 和哪个命名空间的Pod进行匹配,为空为当前命名空间 |
下面提供几种场景给读者理解
同一个应用部署在不同的宿主机
[root@master-01 ~]# vim mustbediffnode.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: must-be-diff-nodes
name: must-be-diff-nodes
namespace: kube-public
spec:
replicas: 3
selector:
matchLabels:
app: must-be-diff-nodes
template:
metadata:
labels:
app: must-be-diff-nodes
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- must-be-diff-nodes
topologyKey: kubernetes.io/hostname
containers:
- image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
imagePullPolicy: IfNotPresent
name: must-be-diff-nodes
创建资源,并查看创建的Pod是否在不同的节点上
[root@master-01 ~]# kubectl create -f mustbediffnode.yaml
deployment.apps/must-be-diff-nodes created
[root@master-01 ~]# kubectl get pod -owide -n kube-public
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
must-be-diff-nodes-595f4db45-74kb8 1/1 Running 0 43s 172.16.133.134 master-03 <none> <none>
must-be-diff-nodes-595f4db45-grhpp 1/1 Running 0 43s 172.16.184.7 node-02 <none> <none>
must-be-diff-nodes-595f4db45-rn2g7 1/1 Running 0 43s 172.16.190.19 node-01 <none> <none>
应用和缓存尽量部署在同一个域内(读者可以自行添加某些容器作为缓存角色,观察效果)
[root@master-01 ~]# vim appandcache.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
尽量调度到高配置服务器
[root@master-01 ~]# vim highperformance.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: prefer-ssd
name: prefer-ssd
namespace: kube-public
spec:
replicas: 3
selector:
matchLabels:
app: prefer-ssd
template:
metadata:
creationTimestamp: null
labels:
app: prefer-ssd
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: ssd
operator: In
values:
- "true"
- key: master
operator: NotIn
values:
- "true"
weight: 100
- preference:
matchExpressions:
- key: type
operator: In
values:
- physical
weight: 50
containers:
- name: perfer-ssd
image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
拓扑域(TopologyKey)
在 Kubernetes 中,Topology Key 是一个用于定义调度策略的概念,特别是与 Pod Affinity 和 Pod Anti-Affinity 相关的调度策略。它指定了用于组织和分类节点的键,这些键帮助调度器决定 Pod 的调度位置,以满足亲和性或反亲和性的要求。
Topology Key 的作用
- Pod Affinity
- 当你希望将某个 Pod 调度到与已经运行的某些 Pod 同一个拓扑域(如同一个区域、同一个节点等)内时,可以使用 Topology Key 来指定这个要求。
- Pod Anti-Affinity
- 当你希望将某个 Pod 调度到与已经运行的某些 Pod 分离的拓扑域内时,可以使用 Topology Key 来指定这个要求。
常见的 Topology Key
- kubernetes.io/hostname: 指定同一个物理主机或虚拟机。用于要求 Pod 调度到同一台机器上,或者避免在同一台机器上调度。
- failure-domain.beta.kubernetes.io/zone: 指定同一可用区或区域。用于要求 Pod 调度到同一区域,或者避免在同一区域调度。
- failure-domain.beta.kubernetes.io/region: 指定同一地区。用于要求 Pod 调度到同一地区,或者避免在同一地区调度。
- topology.kubernetes.io/zone: 与 failure-domain.beta.kubernetes.io/zone类似,用于指定同一可用区或区域。
配置注意事项
-
一致性: 确保你所使用的 Topology Key 在所有节点上都是一致的。比如,kubernetes.io/hostname 是每个节点的唯一标识,而 failure-domain.beta.kubernetes.io/zone 和 topology.kubernetes.io/zone应该在所有节点上都存在。
-
合适的拓扑域: 根据你的实际需求选择合适的拓扑域。例如,如果你的目标是避免在同一台物理机上运行多个副本,你可以使用 kubernetes.io/hostname;如果希望在不同区域内运行,可以使用 failure-domain.beta.kubernetes.io/zone 或 topology.kubernetes.io/zone。
-
原则上,topologyKey 可以是任何合法的标签键。出于性能和安全原因,topologyKey 有一些限制:
对于 Pod 亲和性而言,在 requiredDuringSchedulingIgnoredDuringExecution 和preferredDuringSchedulingIgnoredDuringExecution 中,topologyKey 不允许为空。对于requiredDuringSchedulingIgnoredDuringExecution 要求的 Pod 反亲和性, 准入控制器 LimitPodHardAntiAffinityTopology 要求 topologyKey 只能是 kubernetes.io/hostname。
举例,此例子并不准确(个人认为),因为会被重复调度
[root@master-01 ~]# vim topologykey.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: must-be-diff-zone
name: must-be-diff-zone
namespace: kube-public
spec:
replicas: 6
selector:
matchLabels:
app: must-be-diff-zone
template:
metadata:
labels:
app: must-be-diff-zone
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- must-be-diff-zone
topologyKey: region
containers:
- image: registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:v1.15.1
imagePullPolicy: IfNotPresent
name: must-be-diff-zone