K

[Kubernetes] 调度策略及相关内容

RoLingG 其他 2024-09-20

Kubernetes调度策略及相关内容

Scheduler调度的过程和策略

事先补充:
NodeKubernetes 集群中的工作节点
一个 Node 可以运行多个 Pod,而一个 Pod 只能运行在一个 Node
使用标签和选择器可以管理 NodePod 之间的关系,从而实现灵活的调度和管理。

SchedulerKubernetes 集群的调度器,把 Pod 分配给集群的节点(Node)

scheduler 是一个单独运行的程序,只要启动之后就会一直监听apiserver。获取报文中的字段(即spec中的nodeName字段)

创建 Pod 时,为每个 Pod 创建一个 binding,表示该往哪个节点上部署。

调度规则:

  1. 公平——每个节点都能够分配资源
  2. 资源高效利用——集群中的资源可以被最大化使用
  3. 效率——调度的性能要好,能够尽快的完成大批量 Pod 的调度工作
  4. 灵活——允许用户根据自己的需求,控制和改变调度的逻辑

kubernetes对Node的选择策略

  1. 预算策略:用于过滤出合适的节点
  2. 优选策略:用于选择出合适的部署节点

选择 Node 的过程:先执行预算策略,再执行优先策略。这两步的操作都必须成功,否则立刻返回报错。以上都是自带的算法策略,由K8s集群自己来选择。

预算策略

  • podfitsresourcesPod 的适应策源,检查节点上剩余的资源是否满足pod请求的资源(主要是CPU内存
  • podfitshostPod 适应主机,如果Pod指定了 Nodename,检测主机名是否存在,如果存在要和 Pod 指定的名称匹配,这才能调度过去
  • podselectormarchesPod 选择器匹配,根据选择器选择的 Node 标签进行匹配,如果 Node 上存在匹配的标签,则选择该 Node
  • nodeskconflict:无磁盘冲突,确保已经挂载的卷和Pod的卷不发生冲突,除非目录是只读。

如果预算策略不满足Pod 将始终处于pending状态,不断的重试调度,直到节点满足条件为止。

优选策略

  • leastrequestedpriority:最低请求优先级,通过算法计算节点上的CPU和内存使用率,确定节点的权重。使用率越低的节点,相应的权重就越高。调度时会更倾向于这些使用率低的节点。实现资源合理的利用。

    越低,被使用的资源越少,则被调用的权重越高。
  • balancereasourceallocation:平衡资源分配,算CPU和内存的使用率,给节点赋予权重。权重算的是CPU和内存使用率接近,权重越高。和上面的最低请求优先级一起使用。

    使用率越接近,两种资源的量越接近,因为其中一个资源不够用的概率越小,被调用的权重越高。

    例如:

    Node① CPU:20%,内存:80%

    Node② CPU:50%,内存:50%

    如果选择Node①,则因为内存不足的可能极大,导致使用出错;如果使用Node②,两个资源剩余的量都很平均,出现其中一个资源不足导致使用出错的问题概率变小;

    主打一个要么都够用,要么都不够用。

  • imagelocalitypriority:节点上是否已经有了要部署的镜像。镜像的总数成正比,满足的镜像数越多,权重越高。

    已经有的镜像越多,则要做的准备工作就越少(因为未部署的镜像要拉取配置啥的)。满足的镜像越多部署的就越快。

kubernetes对Pod的调度策略

Kubernetes 中,调度是指将 Pod 放置到合适的节点上,以便对应节点上的 Kubelet 能够运行这些 Pod

Kubelet Kubernetes 集群中的一个关键组件,它是集群中的每个节点(Node)上运行的代理服务。Kubelet 的主要作用是确保容器都运行在 Pod 中,并且这些 Pod 符合集群的状态。它负责维护容器的生命周期,包括创建、更新和销毁容器。
  • 定向调度:使用 nodeName 字段指定 Node 名称;使用 nodeSelector 字段指定 Node 标签。
  • 亲和性调度:使用 Node/Pod 亲和性(NodeAffinityPodAffinityPodAntiAffinity);
  • 污点与容忍:使用 Node 设置污点,结合 Pod 设置容忍
  • 全自动调度:运行在哪个节点上完全由 Scheduler 经过一系列的算法计算得出。

定向调度

nodeName:指定节点对应的节点名称,用于将 Pod 调度到指定的 Node 上,不经过调度器。

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: my-container
      image: my-image
  nodeName: "node-name"

在参数中设置了nodeName,指定了节点的名称。这个规则是强制匹配,会跳过scheduler的调度策略。

相当于说用了这个 Pod 有了指定的主人,不会被调到别的 Node

nodeSelector:在 Pod 定义文件的 spec 字段下的 nodeSelector 字段中设置一个标签选择器,在 Pod 调度的时候,只有具有这些标签的 Node 才会被考虑用来运行这个 Pod。

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: my-container
      image: my-image
  nodeSelector:
    disktype: "ssd"    #←这就是一例子,本质上就是tag-key: value
指定节点标签的Pod,这些Pod会尽可能的在满足条件的情况下给予这些Node节点使用,如果节点不满足条件,Pod会进入pending状态。直到节点满足条件为止。

亲和性调度

这里其实不止 Pod 方面,Node 也有,所以就一起讲了。但本质上都是在 Pod 上进行配置的。

亲和性分为:①Node亲和性、②Pod亲和性

亲和性策略:①软策略、②硬策略

Node节点亲和性(Node Affinity)

Preferred During Scheduling Ignored During Execution:软策略

这意味着这些亲和性规则在调度时会被考虑,以帮助调度器做出决策,但一旦 Pod 被调度并运行,这些规则就不再被考虑。如果在 Pod 运行期间,节点不再满足这些偏好规则,Pod 将继续在该节点上运行,不会被迁移。

举个简单的例子:选择 Pod 节点时,声明了 Pod 最好能部署在Node①。如果是软策略,他会尽量满足这个条件,不一定会完全部署在Node①节点上。

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: my-container
      image: my-image
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
        weight: 1

preference: 定义了节点选择的偏好条件。这里使用 matchExpressions 来指定节点必须具有 disktype 标签,且其值为 ssd

当调度器在集群中寻找合适的节点来调度 Pod 时,它会首先考虑所有必需的亲和性规则(如果有的话)。然后,它会根据 preferredDuringSchedulingIgnoredDuringExecution 中定义的偏好规则来选择最佳节点。如果多个节点都满足必需的亲和性规则,调度器会根据这些偏好规则的权重来决定将 Pod 调度到哪个节点上。

required During Scheduling Ignored During Execution:硬策略

这意味着这些亲和性规则在调度时会被强制执行,如果节点不满足这些规则,Pod 将不会被调度到该节点上。然而,一旦 Pod 被调度并运行,即使节点不再满足这些规则,Pod 也会继续在该节点上运行,不会被迁移。

举个简单的例子:选择 Pod 时,声明了 Pod 部署在Node①上。如果是硬策略,Pod 在满足硬策略的条件时,会被强制性要求部署在Node①上。

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: my-container
      image: my-image
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
            - matchExpressions:
             - key: disktype
              #匹配策略
                operator: In
                #节点信息内标签的需匹配的值
                values:
                - ssd
硬策略通过 matchExpressions 去搜索有相关 key 和相关 valuePod,然后根据匹配策略 operator 去进行筛选匹配符合策略的节点。
Pod亲和性(Pod Affinity)

PodAffinity(Pod 亲和性)

允许你指定一个 Pod 应该(或优选)被调度到与具有特定标签的 Pod同一节点拓扑域上的规则。

PodAntiAffinity(Pod 反亲和性)

允许你指定一个 Pod 应该避免被调度到与具有特定标签的 Pod同一节点拓扑域上的规则。

  • PodAffinity 和 PodAntiAffinity 也有 requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution 两种形式,分别作为硬性和软性规则。
  • 硬性 Pod 亲和性规则要求调度器必须满足这些规则才能调度 Pod,而软性规则则是优选的,调度器会尝试满足,但不是必须的。

亲和性实例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.22
      affinity:
      #选择Node/Pod亲和性
        nodeAffinity:
        #选择亲和性规则
          requiredDuringSchedulingIgnoredDuringExecution:
          #调度器根据设置的节点标签,选择哪个Node执行该亲和性规则
            nodeSelectorTerms:
            - matchExpressions:
            #选择一个符合条件的节点
              - key: tag3
              #匹配策略
                operator: In
                #节点信息内标签的需匹配的值
                values:
                - tagVal1

这里插入一个匹配策略的表格:

匹配策略关键词意义解释
In选择的标签值在node节点上存在
Notin不在选择的标签值不在node节点上存在
Gt大于仅限Node有;要大于选择的标签值(只能比较整数)
Lt小于仅限Node有;要小于选择的标签值(只能比较整数)
Exists存在选择具有指定标签的对象(不考虑值,不能使用values字段)
DoesNotExist不存在选择具有指定标签的对象(不考虑值,不能使用values字段)

假设您有以下两个节点:

  • 节点 A,带有标签:label3: labelVal1
  • 节点 B,带有标签:label3: labelVal2

根据上面的配置,节点需要带的标签得是key为label3value为labelVal1,K8s就必定会选择拥有label3: labelVal1标签的节点,上面节点A符合条件,而节点B不符合,固然只选择节点A。

相应的命令

配置标签

如果有需要的话,可以使用下面的命令进行配置节点标签:

kubectl label nodes <node-name> <labelKey>=<labelValue>

替换 <node-name> 为您的实际节点名称,<labelKey>为对应的键,<labelValue>为对应的键值。

如果要删除一个 Pod 的标签的话,可以使用下面命令删除:

kubectl label nodes <node-name> <labelKey>-

如果要更新一个 Pod 的标签的话,可以使用下面命令更改:

kubectl label nodes <node-name> memory=1000 --overwrite

部署配置到 Kubernetes 集群

使用以下命令将 Deployment 部署到您的 Kubernetes 集群:

kubectl apply -f nginx-deployment.yaml

验证 Pod 是否调度到正确的节点

kubectl get pods -o wide    #获取集群内所有Pod的详细信息

不过一般直接用 kubectl get pod 就够了,直接看STATUSAGE列的内容就能辨认出来 Pod 部署的是否正确。

举个我实习中遇到的 Pod 实例:

NAMEREADYSTATUSRESTARTSAGEIPNODENOMINATED NODEREADINESS GATES
gitee-code-xxxxxx1/1Running0277dxx.xx.xxx.xxxx.xx.xxx.xx
gitee-api-xxxxx0/1Error101(41d ago)7d19hxx.xx.xxx.xxxx.xx.xxx.xx
如果是直接使用 kubectl get pod 的话,AGE 之后的 Pod 信息是没有的。

上面的例子是硬策略,强制规则匹配,想换成软策略主要改的就是 requiredDuringSchedulingIgnoredDuringExecution 变成 preferredDuringSchedulingIgnoredDuringExecution ,然后后续的配置根据具体的关键词在进行细小的调整就行。

另外补充一点K8s deployment 里面常见的字段:

  1. annotations:注解(Annotations)是一种附加于 Kubernetes 对象的非标识性信息。它们通常用于存储工具或库可以检索的额外信息,但不会用于识别和选择对象。注解可以包含构建信息、版本控制信息、监控和日志链接、调试信息等。例如,kubectl describe deployment 命令会显示 Deployment 的注解,如 deployment.kubernetes.io/revisionkubernetes.io/change-cause。这些注解有助于追踪 Deployment 的版本和变更原因。
  2. replicas:这个字段指定了期望运行的 Pod 副本数量。Deployment 控制器会确保始终有指定数量的 Pod 在运行。如果 Pod 副本数少于这个值,Deployment 控制器会创建新的 Pod 以补足数量;如果多于这个值,它会删除多余的 Pod。
  3. strategyType:这个字段定义了更新策略的类型,可以是 RollingUpdateRecreateRollingUpdate 是默认值,它允许逐步更新 Pod,而 Recreate 会先删除所有旧的 Pod,然后再创建新的 Pod。(如果想要服务不怎么受影响,能够动态更新,最好还是用 RollingUpdate 进行 Pod 的滚动更新比较好。)
  4. minReadySeconds:这是一个可选字段,用于指定新创建的 Pod 应该准备好并且不崩溃的最小秒数,才会被视为可用。这个字段的默认值是 0,意味着 Pod 一旦准备就绪就会被视为可用。通过设置这个值,可以减缓 Deployment 的滚动更新速度,确保在继续更新之前新 Pod 已经稳定运行一段时间。(如果不配置这个字段,默认行为就是一旦 Pod 准备好就立即被视为可用。)
  5. rollingUpdateStrategy:在使用 RollingUpdate 更新策略时,这个字段定义了更新过程中可以同时处于不可用状态的最大 Pod 数量(maxUnavailable)和可以超出期望 Pod 数量的最大 Pod 数量(maxSurge)。这些参数可以设置为绝对数量或百分比。默认情况下,这两个值都设置为 25%,意味着在更新过程中,可以有最多 25% 的 Pod 处于不可用状态,同时可以有最多 25% 的额外 Pod 被创建。
  6. pod template:这是定义 Pod 规格的模板,它指定了 Pod 的容器、卷、环境变量等。在 Deployment 中,Pod 模板定义了 Deployment 所管理的 Pod 的期望状态。当 Deployment 更新时,Pod 模板中的任何更改都会应用于新的 ReplicaSet 和随后创建的 Pod。

综上,如果 Pod 使用软策略,一般来说都是多个软策略看权重实施,权重高的,则执行指定的软策略;如果 Pod 使用硬策略,一般来说就是指定标签去进行匹配实施,符合规则的,就执行指定的硬策略;当然也有软、硬策略一起实施的,先满足硬策略,再考虑软策略。若硬策略无法满足,软策略一个都不会执行。

额外

面试题:

①你在部署pod的时候选择什么样的策略:

根据node的亲和性:性能不一致,尽量把 Pod 往性能高的多部署,选择软策略;节点故障或者节点维护中,只能选择硬策略,把故障节点剔除。

调度策略匹配标签操作符拓扑域调度目标
Node的亲和性主机的标签In、NotIn、Exists、DoesNotExist、Gt、Lt不支持指定主机
Pod的亲和性Pod的标签In、NotIn、Exists、DoesNotExist支持Pod和指定标签的Pod部署在同一个拓扑域
Pod的反亲和性Pod的标签In、NotIn、Exists、DoesNotExist支持Pod和指定标签的Pod部署在不同拓扑域

拓扑域:K8S集群节点当中的一个组织结构,可以根据节点的物理关系或者逻辑关系进行划分。可以用来标识节点之间的空间关系,网络关系,或者其他类型的关系。

上面讲的拓扑域指的是Pod的标签,标签可以用来对Node/Pod进行逻辑关系上的划分。

Pod亲和性里一般的拓扑域会用关键字段 topologyKey 进行标注出来,例如: topologyKey: test1 ←这里的拓扑域就是 test1

注意点:

  1. 在配置 Pod 的亲和性策略时,必须加上拓扑域的关键字topologykey指向的是节点标签
  2. Pod 亲和性的策略分为硬策略软策略
  3. Pod 亲和性的NotIn可以替代反亲和性
②在部署的时候,该怎么考虑Node节点:

部署 Node 节点的时候,需要考虑软硬策略的实施,以及污点和容忍度的配置,其中这里配置还可以和 Node 的亲和性一起使用。

被设为污点的 Node,不会被部署 Pod,这点和反亲和性一样,脏了,就不会被 Pod 亲和,会被 Pod 远离。

污点(taint)

污点的种类:

污点种类作用
NoSchedule这个污点会阻止新的 Pod 被调度到该节点上,除非这些 Pod 明确地容忍(tolerate)这个污点。
PreferNoSchedule这个污点的作用是建议调度器尽量不要将 Pod 调度到该节点上。如果调度器找不到其他合适的节点,它仍然可以调度 Pod 到这个节点。
NoExecute这个污点不仅会阻止新的 Pod 被调度到该节点上,还会驱逐该节点上所有没有容忍(tolerate)这个污点的 Pod。

NoExecute 驱逐细节:

因为 NoExecute 类型而从节点上被驱逐出去的非容忍该污点的 Pod,对于由控制器(如 DeploymentStatefulSetDaemonSet)管理的 Pod,它们会被控制器注意到并重新创建。这意味着这些 Pod 会由控制器在其他合适的节点上重新调度。但对于没有控制器管理的”孤儿” Pod(例如,直接使用 kubectl run 创建的 Pod),它们将被直接杀死,并且不会自动重新创建。

注意点:节点服务器需要维护的,服务器如果关机了或者宕机了,节点上的业务将会全部失效。在工作中主要部署 Pod 的方式是控制器部署,尤其 deployment 控制器最多。

所以 NoExecute 污点类型适用的其中一个场景类型就是维护升级场景。在这种场景下,节点再升级维护后,就可以将对应的驱逐(NoExecute)污点给去除了。

一旦设置为驱逐,控制器创建的 Pod 会在其他节点重新部署。所以驱逐的业务场景主要在于业务维护业务回收

但是系统集群组件不会被驱逐。

污点相关命令

#添加污点
kubectl taint nodes <node-name> KEY=VALUE:TAINT_EFFECT
 
#删除污点
kubectl taint nodes <node-name> KEY:TAINT_EFFECT-    #删除指定Node指定类型的污点
kubectl taint nodes <node-name> -    #删除指定Node所有类型的污点
 
#查看污点
kubectl describe nodes <node-name>  | grep -i taints
  • NODE_NAME 是您要添加污点的节点的名称。
  • KEY 是污点的键。
  • VALUE 是与键相关联的值(可选)。
  • TAINT_EFFECT 是污点的效果,可以是 NoSchedulePreferNoScheduleNoExecute

容忍

允许调度到有污点的节点:如果一个节点有污点,只有那些在其配置中声明了相应容忍的 Pod 才能被调度到该节点上。

保持服务连续性:在节点维护或升级期间,通过设置容忍,可以确保关键的 Pod 继续在节点上运行,即使节点被添加了污点。

管理 Pod 生命周期:对于 NoExecute 污点,容忍可以指定一个生命周期,在这个生命周期内,Pod 可以继续在节点上运行,但一旦超过这个时间,Pod 将被驱逐。

注意: NoExecute 污点设置的容忍 Pod 必须指定生命周期。

Pod 被驱逐之后是否重新创建取决于这些被驱逐的 Pod 是否被控制器管理。

容忍污点Pod配置示例

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: my-image
  tolerations:    #设置容忍
  - key: "key1"
    operator: "Exists"    #匹配规则:存在,不用设置value
    effect: "NoSchedule"    #阻止类型
  - key: "key2"
    operator: "Exists"    #匹配规则:存在,不用设置value
    effect: "NoExecute"        #驱逐类型
    tolerationSeconds: 3600 # 容忍时间,单位为秒    生命周期

在 Kubernetes 中,如果您想要创建一个容忍(Toleration),它不特定于任何特定的污点类型,您可以省略 effect 字段。这样,这个容忍将适用于所有可能的污点类型。

#容忍所有污点类型示例:
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: my-image
  tolerations:
  - key: "my-key"
    operator: "Exists"
应用场景

这种类型的容忍通常用于以下场景:

  1. 通用污点处理:需要确保 Pod 可以被调度到所有可能带有特定键的污点的节点上时。
  2. 灵活的调度策略:在某些自动化系统中,可能会动态地向节点添加不同类型的污点,使用这种通用容忍可以确保 Pod 总是有资格被调度。
注意事项
  • 使用这种通用容忍时要小心,因为它可能会使 Pod 被调度到不希望它们运行的节点上。
  • 确保这种容忍的使用符合应用程序的部署策略和集群的运维策略。

这种配置提供了高度的灵活性,但也需要谨慎使用,以避免潜在的调度问题。

cordon和drain

cordon:将 Node 标记为不可用状态

#标记命令
kubectl cordon <node-name>

#取消标记命令
kubectl uncordon <node-name>
被标记了 cordon 的节点不可以部署。

cordon 使用的场景和驱逐污点 noExecute 类似,可以用在节点升级维护的场景

注意:因为被 cordon 标记之后,对应的节点会不可部署,所以在节点升级维护完成之后,记得取消标记。

drain:将该节点下的 Pod 全部转移到其他的 Node 节点下运行。

一旦执行了drain,被执行该命令的 Node 就会变成不可调度状态,且该 Node 上的所有 Pod 都将会被驱逐。

举例命令:kubectl drain <node-name> --ignore-daemonsets --delete-local-data --force

命令关键字段功能
--ignore-daemonsets忽视 daemonset 方式部署的 Poddaemonset部署的 Pod 不会被转移(daemonset 要部署的一般是后台运行较为重要的 Pod,例如系统 Pod,所以不动)
--delete-local-data有本地挂载的 Pod 会被强制杀死
--force强制释放不是控制器管理的 Pod

如果要取消 Node 的状态的话,使用kubectl uncordon <node-name>即可。

注意:uncordon 操作不会自动重启被驱逐的 Pods,它只是允许新的 Pods 被调度到该节点上。如果需要,你需要手动重启之前被驱逐的 Pods。

额外补充

资源调整

除了上述这种配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.22
      affinity:
      #选择Node/Pod亲和性
        nodeAffinity:
        #选择亲和性规则
          requiredDuringSchedulingIgnoredDuringExecution:
          #调度器根据设置的节点标签,选择哪个Node执行该亲和性规则
            nodeSelectorTerms:
            - matchExpressions:
            #选择一个符合条件的节点
              - key: tag3
              #匹配策略
                operator: In
                #节点信息内标签的需匹配的值
                values:
                - tagVal1

通常实际应用场景上还会在 containers 下加上 requestlimit

Containers:
   code-go:
    Image:      docker-hub.xxx/xxx-xxx/xxx-xxx:v8.19.0
    Port:       3000/TCP
    Host Port:  0/TCP
    Limits:
      cpu:     4
      memory:  6Gi
    Requests:
      cpu:        100m
      memory:     256Mi
    Environment:  <none>
    Mounts:
      /app/config.yaml from xxx (rw,path="config.yaml")
      /app/configs/xxx_setting.yaml from xxx-xxx-setting (rw,path="xxx_setting.yaml")

Reques :容器使用的最小资源需求,作为容器调度时资源分配的判断依赖。

只有当节点上可分配资源量 >= 容器资源请求数时才允许将容器调度到该节点。但 Request 参数不限制容器的最大可使用资源。

Limit:容器能使用资源的资源的最大值,设置为0表示使用资源无上限。

Request 能够保证 Pod 有足够的资源来运行,而 Limit 则是防止某个 Pod 无限制地使用资源,导致其他 Pod 崩溃。两者之间必须满足关系: 0 <= Request <= Limit <= Infinity

如果 Limit 为0表示不对资源进行限制,这时可以小于 Request

实际场景

  1. 在 K8s 集群中部署服务时不设置 CPU requests 或将 CPU requests 设置得过低(这样"看上去"就可以在每个节点上容纳更多 Pod )。在业务比较繁忙的时候,节点的 CPU 全负荷运行。业务延迟明显增加,有时甚至机器会莫名其妙地进入 CPU 软死锁等“假死”状态。

    其原因就在于在业务繁忙的情况,节点的 CPU 全负荷运行下, CPU requests 设置得过低得 Pod 的资源会被其他高设置的 Pod 削减能使用的资源量,直至到 requests 设置的最低资源请求点。

    因为 Pod 对应的服务所能使用的 CPU 资源少,导致业务服务延迟明显增加。

  2. 类似地,部署服务时,不设置内存 requests 或者内存 requests 设置得过低,这时会发现有些 Pod 会不断地失败重启。而不断重启的这些 Pod 通常是遭遇了OOM。

    被不断的OOM导致服务重启其主要的原因在于 Pod 所占用的内存经常超过了设置的内存占用量,导致K8S觉得你这个 Pod 不正常,就让操作系统把你 OOMKilled 了。重启又因为设置的量太少超过了占用量就又重复上述情况。

相反如果给你的应用设置较高的 request 资源需求限制,而实际占用资源长期远小于它的 request 资源需求限制,则会导致节点整体的资源利用率较低

当然这对时延非常敏感的业务除外,因为敏感的业务本身不期望节点利用率过高,影响网络包收发速度。但是对于一些非核心、资源不长期占用的应用,可以适当减少 request 资源需求限制,以提高资源利用率。

驱逐节点优先级

根据前面的调度策略我们了解到,Pod 在资源不足的时候,会根据调度策略决定 Pod 的优先级。当一些节点(一般是核心业务节点)资源不足时,会触发自动驱逐,将一些低优先级的 Pod 删除掉以释放资源让节点自愈。

在没有设置 requestlimit 的 Pod 会被调度策略认为优先级最低,容易被驱逐;

因为没有限制,相当于这个 Pod 有多少资源都无所谓,放养。

request 不等于 limit 的 Pod 其次;

request 等于 limit 的 Pod 优先级较高,不容易被驱逐。

所以如果是重要的线上应用,不希望在节点故障时被驱逐导致线上业务受影响,就建议将 requestlimit 设成一致,也就是pod中的每个容器都必须指定limits.memory=requests.memory,limits.cpu=limits.cpu

尽量避免使用过大的 Request 与 Limit

如果你的服务使用单副本或者少量副本,给很大的 requestlimit,让它分配到足够多的资源来支撑业务,那么某个副本故障对业务带来的影响可能就比较大,并且由于 request 较大,当集群内资源分配比较碎片化,如果这个 Pod 所在节点挂了,其它节点又没有一个有足够的剩余可分配资源能够满足这个 Pod 的 request 时,这个 Pod 就无法实现漂移,也就不能自愈,加重对业务的影响。

相反,建议尽量减小 requestlimit,通过增加副本的方式来对你的服务支撑能力进行水平扩容,让你的系统更加灵活可靠。

总结上面的话:与其让 Pod 多拿资源防止它崩溃,不如让它拿适当的资源去进行多副本部署。

这样既不会发生资源不够导致 Pod 启动不了,也不会因为 Pod 服务没启动起来业务受到影响。

多副本一个挂了其他能顶上,资源占用也不算多,够了就重启再顶回去。

PREV
[Golang] Select实现优先级并发技巧
NEXT
[24年秋招 | Funplus笔试第一题] 扑克牌顺子

评论(0)

发布评论