Pod调度策略
一.污点-Taint
在 Kubernetes 中,污点(Taint)是一种标记,用于标识一个Node节点上的某些资源或条件不可用或不可接受。当一个节点被标记了污点后,只有那些能够容忍该污点的 Pod 才能被调度到该节点上。
污点常用与以下场景:
- 将某些节点标记为"故障",以防止新的 Pod 被调度到这些节点上;
- 将某些节点标记为"高负载",以防止过多的 Pod 被调度到这些节点上,导致节点过载;
- 将某些节点标记为"专用",以保证只有特定的 Pod 能够被调度到这些节点上。
pod亲和性是pod属性;但是污点是节点的属性
,污点定义在k8s集群的节点上的一个字段。
shell
# 查看控住节点定义的污点
[root@master1]# kubectl describe nodes master1 | grep Taints
Taints: node-role.kubernetes.io/control-plane:NoSchedule
# 两个工作节点是没有定义污点
[root@node1]# kubectl describe nodes node1 | grep Taints
Taints: <none>
[root@node2]# kubectl describe nodes node2 | grep Taints
Taints: <none>
1.查看定义taint的信息
shell
# 查看帮助命令
[root@master1]# kubectl explain node.spec
······
taints <[]Object>
If specified, the node's taints.
[root@master1]# kubectl explain node.spec.taints
KIND: Node
VERSION: v1
RESOURCE: taints <[]Object>
DESCRIPTION:
If specified, the node's taints.
The node this Taint is attached to has the "effect" on any pod that does
not tolerate the Taint.
FIELDS:
effect <string> -required-
Required. The effect of the taint on pods that do not tolerate the taint.
Valid effects are NoSchedule, PreferNoSchedule and NoExecute.
Possible enum values:
- `"NoExecute"` Evict any already-running pods that do not tolerate the
taint. Currently enforced by NodeController.
- `"NoSchedule"` Do not allow new pods to schedule onto the node unless
they tolerate the taint, but allow all pods submitted to Kubelet without
going through the scheduler to start, and allow all already-running pods to
continue running. Enforced by the scheduler.
- `"PreferNoSchedule"` Like TaintEffectNoSchedule, but the scheduler tries
not to schedule new pods onto the node, rather than prohibiting new pods
from scheduling onto the node entirely. Enforced by the scheduler.
key <string> -required-
Required. The taint key to be applied to a node.
timeAdded <string>
TimeAdded represents the time at which the taint was added. It is only
written for NoExecute taints.
value <string>
The taint value corresponding to the taint key.
污点排斥等级:
NoSchedule
:表示Pod不会被调度到具有该污点的节点上,不影响已经存在的PodPreferNoSchedule
:表示调度器会尽量避免将Pod调度到具有该污点的节点上。(但是Pod没有定义容忍度,依然会被调度到这两个节点上)NoExecute
:既影响Pod调度过程,又影响现存Pod对象,如果现存Pod不能容忍节点加的污点,那么这个Pod就会被驱逐
2.定义污点
shell
kubectl taint nodes node1 node-type=dev:NoSchedule
3.查看污点
shell
kubectl describe nodes node1 | grep Taint
4.删除污点
shell
kubectl taint nodes node1 node-type=dev:NoSchedule-
二.容忍度-Tolerations
当我们节点定义污点后,如果我们不定义对应的容忍度,那么Pod将不会调度到此Node节点。
方便下面实验,我把所有node节点全部定义上污点
shell
kubectl taint nodes node1 node-type=dev:NoSchedule
kubectl taint nodes node2 node-type=dev:NoSchedule
查看容忍度的帮助:
shell
kubectl explain pod.spec.tolerations
1.定义Pod容忍度,容忍node-type=dev
,且排斥等级等于NoExecute
,使用了operator=Equal
这三点必须同时能满足。
shell
cat pod1.yml
---
apiVersion: v1
kind: Pod
metadata:
name: pod1
namespace: default
labels:
app: nginx
env: dev
spec:
tolerations:
- effect: "NoExecute" # 指定排斥等级
key: "node-type" # 污点key
operator: "Equal" # Equal表示等于
value: "dev" # 污点value
tolerationSeconds: 3600 # 删除Pod前等待时间,默认30s
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
执行文件
shell
kubectl apply -f pod1.yaml
查看状态,因为没有任何节点满足该Pod容忍,所以该Pod处于Pending状态
shell
kubectl get pods pod1
NAME READY STATUS RESTARTS AGE
pod1 0/1 Pending 0 10m
2.定义Pod容忍度,将排斥等级改为 NoSchedule,这样我们污点key,value,排斥等级都满足了,Pod才会调度 Pod资源清单文件如下:
shell
cat pod2.yml
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
namespace: default
labels:
app: nginx
env: dev
spec:
tolerations:
- effect: "NoSchedule" # 指定排斥等级
key: "node-type" # 污点key
operator: "Equal" # Equal表示等于
value: "dev" # 污点value
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
执行文件
shell
kubectl apply -f pod2.yaml
查看状态,Pod成功调度,且状态为 Running
shell
kubectl get pods pod2
NAME READY STATUS RESTARTS AGE
pod2 1/1 Running 0 5m32s
3.定义Pod容忍度,将 operator=Exists表示满足其中一项即可容忍,下面Pod没有定义key,value,表示没有key,value方面限制,容忍排斥等级=NoSchedule的节点。
shell
cat pod3.yml
---
apiVersion: v1
kind: Pod
metadata:
name: pod3
namespace: default
labels:
app: nginx
env: dev
spec:
tolerations:
- effect: "NoSchedule" # 指定排斥等级
operator: "Exists" # Exists表示满足一项即可
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
执行文件
shell
kubectl apply -f pod3.yaml
查看状态,Pod调度成功,且状态为Running
shell
kubectl get pods pod-3
NAME READY STATUS RESTARTS AGE
pod3 1/1 Running 0 5m16s
三.Pod常见状态和重启策略
1.Pod常见状态
第一阶段:
- 挂起(Pending):
- 正在创建Pod,但是Pod中的容器还没有全部被创建完成,处于此状态的Pod应该检查Pod依赖的存储是否有权限挂载、镜像是否可以下载、调度是否正常等;
- 我们在请求创建pod时,条件不满足,调度没有完成,没有任何一个节点能满足调度条件,已经创建了pod但是没有适合它运行的节点叫做挂起,调度没有完成。
- 失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。
- 未知(Unknown):未知状态,所谓pod是什么状态是apiserver和运行在pod节点的kubelet进行通信获取状态信息的,如果节点之上的kubelet本身出故障,那么apiserver就连不上kubelet,得不到信息了,就会看Unknown,通常是由于与pod所在的node节点通信错误。
- Error 状态:Pod 启动过程中发生了错误
- 成功(Succeeded):Pod中的所有容器都被成功终止,即pod里所有的containers均已terminated。
第二阶段:
- Unschedulable:Pod不能被调度, scheduler没有匹配到合适的node节点PodScheduled:pod正处于调度中,在scheduler刚开始调度的时候,还没有将pod分配到指定的node,在筛选出合适的节点后就会更新etcd数据,将pod分配到指定的node
- Initialized:所有pod中的初始化容器已经完成了
- ImagePullBackOff:Pod所在的node节点下载镜像失败
- Running:Pod内部的容器已经被创建并且启动。
扩展:还有其他状态,如下:
- Evicted状态:出现这种情况,多见于系统内存或硬盘资源不足,可df-h查看docker存储所在目录的资源使用情况,如果百分比大于85%,就要及时清理下资源,尤其是一些大文件、docker镜像。
- CrashLoopBackOff:容器曾经启动了,但可能又异常退出了。如pod一直在重启
2.Pod的重启策略
Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,当某个容器异常退出或者健康检查失败时,kubelet将根据 重启策略来进行相应的操作。
Pod 的 spec 中包含一个 restartPolicy 字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。
- Always:只要容器异常退出,kubelet就会自动重启该容器。(这个是默认的重启策略)
- OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器。(生产环境中常用)
- Never:不论容器运行状态如何,kubelet都不会重启该容器。
2.1测试Always重启策略
shell
[root@master1]# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
namespace: default
labels:
app: myapp
spec:
restartPolicy: Always
containers:
- name: tomcat
ports:
- containerPort: 8080
image: tomcat:latest
imagePullPolicy: IfNotPresent
[root@master1]# kubectl apply -f pod.yaml
pod/demo-pod created
[root@master1]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-pod 1/1 Running 0 10s 10.244.169.153 node2 <none> <none>
# 动态显示pod状态信息
[root@master1]# kubectl get pods -o wide -w
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-pod 1/1 Running 0 22s 10.244.169.153 node2 <none> <none>
# 另起一个终端会话,进入pod内部容器,正常停止 tomcat 容器服务。-c 指定容器名称。
[root@master1]# kubectl exec -it demo-pod -c tomcat -- bash
root@demo-pod:/usr/local/tomcat# ls
root@demo-pod:/usr/local/tomcat# bin/shutdown.sh
可以看到容器服务停止后被重启了一次,Pod又恢复正常
shell
# 非正常停止容器里的tomcat服务
[root@master1]# kubectl exec -it demo-pod -c tomcat -- bash
root@demo-pod:/usr/local/tomcat# ps -ef | grep tomcat
root@demo-pod:/usr/local/tomcat# kill 1
容器被终止,再一次重启,重启次数加一
2.2测试Never重启策略
shell
# 修改 pod.yaml,把 Always 改为 Never
[root@master1]# kubectl delete pods demo-pod
pod "demo-pod" deleted
[root@master1]# kubectl apply -f pod.yaml
pod/demo-pod created
[root@master1]# kubectl get pods -o wide -w
# 在另一个终端进入容器,正常停止服务
[root@master1]# kubectl exec -it demo-pod -c tomcat-pod-java -- bash
root@demo-pod:/usr/local/tomcat# bin/shutdown.sh
查看Pod状态,发现正常停止tomcat服务,Pod正常运行,但是容器没有重启
shell
# 非正常停止容器里的tomcat服务
[root@master1]# kubectl delete pods demo-pod
pod "demo-pod" deleted
[root@master1]# kubectl apply -f pod.yaml
pod/demo-pod created
[root@master1]# kubectl get pods -o wide -w
# 在另一终端进入容器内容
[root@master1]# kubectl exec -it demo-pod -c tomcat-pod-java -- bash
root@demo-pod:/usr/local/tomcat# kill 1
看到容器的状态时Pod的状态是Error,并且没有重启,说明重启策略是Never,那么Pod里容器服务无论如何终止,都不会重启
2.3测试OnFailure重启策略(生产环境中常用)
shell
# 修改 pod.yaml 文件,把 Never 改为 OnFailure
[root@master1]# kubectl delete pods demo-pod
pod "demo-pod" deleted
[root@-master1]# kubectl apply -f pod.yaml
pod/demo-pod created
[root@master1]# kubectl get pods -o wide -w
# 在另一终端进入容器内部,正常停止服务
[root@master1]# kubectl exec -it demo-pod -c tomcat-pod-java -- bash
root@demo-pod:/usr/local/tomcat# bin/shutdown.sh
发现正常通知容器,退出码时0,容器不会重启
shell
# 非正常停止容器里的tomcat服务
[root@master1]# kubectl delete pods demo-pod
pod "demo-pod" deleted
[root@master1]# kubectl apply -f pod.yaml
pod/demo-pod created
[root@master1]# kubectl get pods -o wide -w
# 在另一终端进入容器内部
[root@master1]# kubectl exec -it demo-pod -c tomcat-pod-java -- bash
root@demo-pod:/usr/local/tomcat# kill 1
看到非正常停止的pod里的容器,容器退出码不是0,容器会被重启。