您现在的位置是:首页 >技术杂谈 >浅析 Kubernetes 驱逐网站首页技术杂谈

浅析 Kubernetes 驱逐

若即 2023-06-21 08:00:03
简介浅析 Kubernetes 驱逐

Kubernetes 驱逐 Pod 的方式主要分为主动驱逐和被动驱逐,在官方文档中有三节有介绍 Pod 的驱逐,分别是 污点和容忍度节点压力驱逐API发起的驱逐

污点和容忍度驱逐

污点就是给一个节点添加一个标记,用于排斥一些 pod 。

kubectl taint nodes node1 key1=value1:NoSchedule

给节点 node1 增加一个污点,它的键名是 key1,键值是 value1,效果是 NoSchedule。 这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 node1 这个节点。

要编写一个能够调度到该节点的 pod 的描述文件如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  - key: "key1"
    operator: "Equal"
    value: "value1"
    effect: "NoSchedule"

污点有三个效果 NoSchedulePreferNoScheduleNoExecute。 他们的效果如下:

  • NoSchedule 表示不会将不容忍该污点的 Pod 调度到该节点

  • PreferNoSchedule 表示尽量避免将不容忍该污点的 Pod 调度到该节点

  • NoExecute 表示不会将不容忍该污点的 Pod 调度到该节点,而且会将不容忍的 Pod 驱逐

你可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。 Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点。需要注意以下情况:

  • 如果未被忽略的污点中存在至少一个 effect 值为 NoSchedule 的污点, 则 Kubernetes 不会将 Pod 调度到该节点。

  • 如果未被忽略的污点中不存在 effect 值为 NoSchedule 的污点, 但是存在至少一个 effect 值为 PreferNoSchedule 的污点, 则 Kubernetes 会 尝试 不将 Pod 调度到该节点。

  • 如果未被忽略的污点中存在至少一个 effect 值为 NoExecute 的污点, 则 Kubernetes 不会将 Pod 调度到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。

基于污点的驱逐

上面提到过污点的效果值 NoExecute 会影响已经在节点上运行的如下 Pod:

  • 如果 Pod 不能忍受这类污点,Pod 会马上被驱逐。

  • 如果 Pod 能够忍受这类污点,但是在容忍度定义中没有指定 tolerationSeconds, 则 Pod 还会一直在这个节点上运行。

  • 如果 Pod 能够忍受这类污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。

在某些情况下,当节点不可达时,API 服务器无法与节点上的 kubelet 进行通信。 在与 API 服务器的通信被重新建立之前,删除 Pod 的决定无法传递到 kubelet。 同时,被调度进行删除的那些 Pod 可能会继续运行在分区后的节点上。

节点压力驱逐

节点压力由一系列驱逐信号决定,驱逐信号是特定资源在特定时间点的当前状态。 kubelet 使用驱逐信号,通过将信号与驱逐条件进行比较来做出驱逐决定, 驱逐条件是节点上应该可用资源的最小量。

可定义的驱逐信号

驱逐信号描述
memory.availablememory.available := node.status.capacity[memory] - node.stats.memory.workingSet
nodefs.availablenodefs.available := node.stats.fs.available
nodefs.inodesFreenodefs.inodesFree := node.stats.fs.inodesFree
imagefs.availableimagefs.available := node.stats.runtime.imagefs.available
imagefs.inodesFreeimagefs.inodesFree := node.stats.runtime.imagefs.inodesFree
pid.availablepid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc

kubelet 默认硬驱逐条件:

  • memory.available<100Mi
  • nodefs.available<10%
  • imagefs.available<15%
  • nodefs.inodesFree<5%(Linux 节点)

Pod 驱逐顺序

Pod 驱逐的顺序由下面的参数确定:

  1. Pod 的资源使用是否超过其请求

  2. Pod 优先级

  3. Pod 相对于请求的资源使用情况

因此,kubelet 按以下顺序排列和驱逐 Pod:

  1. 首先考虑资源使用量超过其请求的 BestEffort 或 Burstable Pod。 这些 Pod 会根据它们的优先级以及它们的资源使用级别超过其请求的程度被逐出。

  2. 资源使用量少于请求量的 Guaranteed Pod 和 Burstable Pod 根据其优先级被最后驱逐

节点压力驱逐的本质

节点压力驱逐实际上使用了基于污点的驱逐。当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:

  • node.kubernetes.io/not-ready:节点未准备好。这相当于节点状况 Ready 的值为 “False”。

  • node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状况 Ready 的值为 “Unknown”。

  • node.kubernetes.io/memory-pressure:节点存在内存压力。

  • node.kubernetes.io/disk-pressure:节点存在磁盘压力。

  • node.kubernetes.io/pid-pressure: 节点的 PID 压力。

  • node.kubernetes.io/network-unavailable:节点网络不可用。

  • node.kubernetes.io/unschedulable: 节点不可调度。

  • node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个“外部”云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。

在节点被驱逐时,节点控制器或者 kubelet 会添加带有 NoExecute 效果的相关污点。 如果异常状态恢复正常,kubelet 或节点控制器能够移除相关的污点。

API 发起的驱逐

你可以使用 Kubernetes 语言客户端来访问 Kubernetes API 并创建 Eviction 对象。 要执行此操作,使用 POST 发出要尝试的请求,类似于下面的示例:

{
  "apiVersion": "policy/v1",
  "kind": "Eviction",
  "metadata": {
    "name": "quux",
    "namespace": "default"
  }
}
curl -v -H 'Content-type: application/json' https://your-cluster-api-endpoint.example/api/v1/namespaces/default/pods/quux/eviction -d @eviction.json

当发出请求后,API Server 会执行一系列检查,并作出响应,可能的响应包括

  • 200 OK:允许驱逐,子资源 Eviction 被创建,并且 Pod 被删除, 类似于发送一个 DELETE 请求到 Pod 地址。

  • 429 Too Many Requests:当前不允许驱逐,因为配置了 PodDisruptionBudget。 你可以稍后再尝试驱逐。你也可能因为 API 速率限制而看到这种响应。

  • 500 Internal Server Error:不允许驱逐,因为存在配置错误, 例如存在多个 PodDisruptionBudgets 引用同一个 Pod。

如果 API Server 允许驱逐,具体的驱逐流程如下所示:

  1. API 服务器中的 Pod 资源会更新上删除时间戳,之后 API 服务器会认为此 Pod 资源将被终止。 此 Pod 资源还会标记上配置的宽限期。

  2. 本地运行状态的 Pod 所处的节点上的 kubelet 注意到 Pod 资源被标记为终止,并开始优雅停止本地 Pod。

  3. 当 kubelet 停止 Pod 时,控制面从 Endpoint 和 EndpointSlice 对象中移除该 Pod。因此,控制器不再将此 Pod 视为有用对象。

  4. Pod 的宽限期到期后,kubelet 强制终止本地 Pod。

  5. kubelet 告诉 API 服务器删除 Pod 资源。

  6. API 服务器删除 Pod 资源。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。