一 污点(Taint) 和 容忍(Tolerations)
(一)污点
在Kubernetes(K8s)中,污点(Taints)是一个重要的概念,用于实现Pod的调度控制。以下是关于污点的详细解释:
1.污点定义
污点是什么:污点是一种定义在节点上的键值型属性数据,用于让节点拒绝将Pod调度运行于其上,除非该Pod对象具有接纳节点污点的容忍度(Toleration)。
键值型数据:污点由三个部分组成:key、value和effect。其中key和value是污点的名称和值,而effect则定义了污点的效果。
2.污点效果
污点的效果(Effect)主要有三种类型:
NoSchedule:新的不能容忍此污点的Pod对象不会被调度至当前节点,但已在该节点上运行的Pod对象不受影响。
PreferNoSchedule:Kubernetes会尽量避免将新的不能容忍此污点的Pod对象调度至当前节点,但如果没有其他可用节点,仍然会调度。类似于节点亲和与pod亲和中的软策略
NoExecute:新的不能容忍此污点的Pod对象不会被调度至当前节点,而且已在该节点上运行但不再满足匹配规则的Pod对象将被驱逐。
1.1 污点(taint)
节点亲和性,是Pod的一种属性(偏好或硬性要求),它使Pod被吸引到一类特定的节点。Taint 则相反,它使节点能够排斥一类特定的 Pod。
Taint 和 Toleration 相互配合,可以用来避免 Pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 Pod,是不会被该节点接受的。如果将 toleration 应用于 Pod 上,则表示这些 Pod 可以(但不一定)被调度到具有匹配 taint 的节点上。
使用 kubectl taint 命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去。
污点的组成格式下:
key=value:effect
每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。
当前 taint effect 支持如下三个选项:
●NoSchedule:表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上
●PreferNoSchedule:表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上
●NoExecute:表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去
master 就是因为有 NoSchedule 污点,k8s 才不会将 Pod 调度到 master 节点上
设置污点
kubectl taint node node01 key1=value1:NoSchedule
#节点说明中,查找 Taints 字段
kubectl describe node node-name
#去除污点
kubectl taint node node01 key1:NoSchedule-
kubectl taint node node01 键名=键值:NoSchedule
//增加污点
kubectl taint node node01 键名=键值:NoSchedule-
kubectl taint node node01 键名-
//删除
kubectl describe nodes node01|grep -A5 -i taint
//查看
举例说明:将node02节点设置为NoSchedule
验证污点的作用——NoExecute
此时,给node02也设置为NoExecute 两个都是驱逐
因此所有在node01上面的pod都被驱逐出来,并转移到node02上面,查看 Pod 状态,会发现 node01 上的 Pod 已经被全部驱逐(注:如果是 Deployment 或者 StatefulSet 资源类型,为了维持副本数量则会在别的 Node 上再创建新的 Pod)
自主创建的pod会停止,一直处于pending的状态,因为node01 node02都被设置成了NoExecute,找不到适合的node,才会变成pending
验证污点的作用—NoSchedule
原本在node1上面的pod不会被删除,新建的pod不会调度到node1节点上面
随后,新建一个pod 观察一下,因为node01 有污点,不会将pod调度到node01上,因此pod01 在node02上
验证污点的作用——PreferNoSchedule
分别给node01 node02 创建污点
过滤一下node1 node02的taint污点
随后,创建一个新的pod
污点设置在node上面,容忍设置在pod节点上面
二、容忍(pod可以适应node上面的污点)
设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。但我们可以在 Pod 上设置容忍(Tolerations),意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上。,没有设置污点的node也是可以调度的。
2.容忍的组成
容忍通常包含以下几个部分:
键(Key):与污点的键相匹配。
值(Value):与污点的值相匹配。如果不指定值,Pod将容忍所有值的同名污点。
效应(Effect):与污点的效应相匹配。常见的效应包括NoSchedule、PreferNoSchedule和NoExecute。
容忍期限(TolerationSeconds)(仅对NoExecute效应有效):指定Pod在节点被赋予NoExecute污点后,能够继续在该节点上运行的时间(以秒为单位)。超过这个时间后,Pod将被驱逐。
操作符(Operator):用于指定容忍与污点的匹配方式。常见的操作符包括Equal和Exists。Equal要求键、值和效应都完全匹配,而Exists只要求键和效应匹配。
随后,设置一个pod 跟node02这个节点做容忍
apiVersion: v1
kind: Pod
metadata:
name: myapp01
labels:
app: myapp01
spec:
containers:
- name: with-node-affinity
image: soscscs/myapp:v1
tolerations:
- key: "check"
operator: "Equal"
value: "no"
effect: "NoExecute"
tolerationSeconds: 15
#其中的 key、vaule、effect 都要与 Node 上设置的 taint 保持一致
#operator 的值为 Exists 将会忽略 value 值,即存在即可
#tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Node 上继续保留运行的时间
其它注意事项(equal表示精确匹配,exists表示模糊匹配)
(1)当不指定 key 值时,表示容忍所有的污点 key,就是指明只要key这个键相同即可,里面的值是什么都无所谓。
tolerations:
- operator: "Exists"
举例说明:
随后,写一个yaml文件去创建pod
当超过40秒之后,就会被驱逐
2)作当不指定 effect 值时,表示容忍所有的污点用
tolerations:
- key: "key"
operator: "Exists"
//其它注意事项
(1)当不指定 key 值时,表示容忍所有的污点 key
tolerations:
- operator: "Exists"
(2)当不指定 effect 值时,表示容忍所有的污点作用
tolerations:
- key: "key"
operator: "Exists"
(3)有多个 Master 存在时,防止资源浪费,可以如下设置
kubectl taint node Master-Name node-role.kubernetes.io/master=:PreferNoSchedule
//如果某个 Node 更新升级系统组件,为了防止业务长时间中断,可以先在该 Node 设置 NoExecute 污点,把该 Node 上的 Pod 都驱逐出去
kubectl taint node node01 check=mycheck:NoExecute
//此时如果别的 Node 资源不够用,可临时给 Master 设置 PreferNoSchedule 污点,让 Pod 可在 Master 上临时创建
kubectl taint node master node-role.kubernetes.io/master=:PreferNoSchedule
//待所有 Node 的更新操作都完成后,再去除污点
kubectl taint node node01 check=mycheck:NoExecute-
(三)资源优化
1.多master使用
当有多个master存在时,可以将备用的master的污点状态设置为PreferNoSchedule,这样的话,会尽可能避免此节点,当其它节点不可调用(资源顶峰、节点故障、节点更新等)时,可以使用master进行临时调度,待资源恢复时,再将pod转移
kubectl taint node Master-Name node-role.kubernetes.io/master=:PreferNoSchedule
2.Node更新
当某个node节点需要资源更新时,为防止业务长时间中断,可以依次升级node,首先将需要升级的node节点设置污点,将pod资源调度到其它node节点上(如master资源充足也可以临时调用),等到该节点升级完毕后,去除污点。依次类推,将所有节点更新升级
kubectl taint node node-name key=value:NoExecute
#设置污点
-------------------------------------------------------------------------------
kubectl taint node node-name key:NoExecute-
#去除污点
3.维护操作
作用:阻止新的 Pods 被调度到该节点上。当一个节点被标记为 cordon 时,已经在该节点上运行的 Pods 不会被驱逐,但新的 Pods 不会被调度到这个节点。
使用场景:通常用于节点的维护或升级,确保在维护期间不会有新的工作负载被分配到该节点上。命令示例:kubectl cordon node01
首先,删除node1 node2上面的污点
设置的cordon维护策略,默认的污点为NoSchedule
验证一下:
创建两个pod
恢复调度:
使用 kubectl uncordon node01 命令可以恢复节点的调度状态,允许新的 Pods 调度到该节点上。
drain
//kubectl drain 可以让 Node 节点开始释放所有 pod,并且不接收新的 pod 进程。drain 本意排水,意思是将出问题的 Node 下的 Pod 转移到其它 Node 下运行
kubectl drain <NODE_NAME> --ignore-daemonsets --delete-emptydir-data --force
--ignore-daemonsets:无视 DaemonSet 管理下的 Pod。
--delete-emptydir-data:如果有 mount local volume 的 pod,会强制杀掉该 pod。
--force:强制释放不是控制器管理的 Pod。
注:执行 drain 命令,会自动做了两件事情:
(1)设定此 node 为不可调度状态(cordon)
(2)evict(驱逐)了 Pod
简单来说
cordon的作用类似于NoSchedule
drain的作用类似于NoExecute
四、Pod启动阶段
第一步:controller manager管理的控制器创建pod副本
第二步:scheduler调度器根据调度算法选择最合适的node节点调度pod
第三步:kubelet拉取镜像
第四步:kubelet挂载存储卷
第五步:kubelet创建并运行容器
第六步:kubelet根据容器探针的探测结果设置Pod状态
五、关于pod的五种状态
Pending:Pod已经创建,但是Pod还处于包括未完成调度到node节点或者还处于在拉取镜像的过程中、存储卷挂载失败的情况
Running:Pod所有容器已被创建,且至少有一个容器正在运行
Succeeded:Pod所有容器都已经成功退出,且不再重启。(Completed)
Failed:Pod所有容器都已经退出,且至少有一个容器是异常退出的。(Error)
Unknown:master节点的controller manager无法获取到Pod的状态信息,通常是因为master节点的apiserver与Pod所在node节点的kubelet通信失联导致的(比如node节点宕机或kubelet进程故障)
总结:Pod遵循预定于的生命周期,起始于Pend阶段,如果至少有一个容器正常运行,则进Running阶段,之后取决于Pod是否有容器以失败状态退出而进入Succeeded或Failed阶段。
六、k8s常见的排障手段
针对组件故障
kubectl get nodes 查看node节点运行状态
kubectl describe nodes <node节点名称> 查看node节点的详细信息和资源描述
kubectl get cs 查看master组件的健康状态
kubectl cluster-info 查看集群信息
journalctl -u -f kubelet 跟踪查看kubelet进程日志
针对pod故障
kubectl get pods -o wide 查看Pod的运行状态和就绪状态
kubectl describe <pods|其它资源类型> <资源名称> 查看资源的详细信息和事件描述,主要是针对处于Pending状态的故障
kubectl logs <Pod资源名称> -c <容器名称> -f -p 查看Pod容器的主进程日志,主要是针对进入Running状态后的故障,比如Failed异常问题
kubectl exec -it <Pod资源名称> -c <容器名称> sh|bash 进入Pod容器查看容器内部相关的状态信息,比如进程、端口、文件、流量等状态信息
kubectl debug -it <Pod资源名称> --image=<临时工具容器的镜像名> --target=<目标容器> 在Pod中创建临时工具容器进入目标容器进行调试,主要针对没有调试工具的容器使用
nsenter -n --target <容器ID> 在Pod容器宿主机使用nsenter转换网络namespace,直接在宿主机进入目标容器的网络命名空间进行抓包等调试工作
针对网络故障
kubectl get svc 查看service资源的clusterIP、port、nodePort等信息
kubectl describe svc <svc资源名称> 查看service资源的标签选择器、endpoints端点等信息
kubectl get pods --show-lables 查看Pod的标签
故障排除思路
在k8s的操作中,由于组件较多,任何一步有错误,都可能导致整个k8s集群陷入不可以状态,下面我就结合工作中的一些操作做一总结
1.环境设置
防火墙策略、核心防护可能会导致节点之间无法通信
swap分区会导致kubelet无法启动,kubelet无法启动,意味着网络插件与kube-proxy容器无法启动
集群信息:使用kubectl get node查看集群信息,确保节点之间通信正常
2.pod事件处理
kubectl describe <资源类型> <资源名称>:查看资源详细信息
kubectl get events:指令查看所有事件信息,并使用grep过滤关键字
kubectl exec –it pod_name bash :进入容器查看,只限于处于Running状态
kubectl logs pod_name:查看pod日志,在Failed状态下
journalctl -xefu kubelet:查看kubelet日志