Kubernetes:为什么设置系统资源管理如此重要?

通常,总是需要为应用程序提供专用的资源池,以使其正确、稳定地运行。 但是,如果多个应用程序在同一电源上运行怎么办? 如何为他们每个人提供最少的必要资源? 如何限制资源消耗? 如何在节点之间正确分配负载? 当应用负载增加时,如何保证水平扩展机制发挥作用?

Kubernetes:为什么设置系统资源管理如此重要?

您需要从系统中存在哪些主要资源类型开始 - 这当然是处理器时间和 RAM。 在 k8s 清单中,这些资源类型以以下单位进行测量:

  • CPU - 核心数
  • RAM - 以字节为单位

此外,对于每种资源,可以设置两种类型的要求 - 要求 и 范围。 请求 - 描述运行容器(以及整个 Pod)的节点可用资源的最低要求,而限制则对容器可用的资源设置硬性限制。

重要的是要理解清单不必显式定义这两种类型,但行为如下:

  • 如果仅显式指定资源的限制,则对该资源的请求将自动采用等于限制的值(您可以通过调用描述实体来验证这一点)。 那些。 事实上,容器将被限制为其运行所需的相同数量的资源。
  • 如果仅对资源显式指定请求,则不会对该资源设置上限 - 即。 容器仅受节点本身资源的限制。

不仅可以在特定容器级别配置资源管理,还可以使用以下实体在命名空间级别配置资源管理:

  • 限制范围 — 描述 ns 中容器/pod 级别的限制策略,为了描述容器/pod 的默认限制,以及防止创建明显胖的容器/pod(反之亦然),限制其数量,需要使用该限制策略并确定限制和请求中的值可能存在的差异
  • 资源配额 — 描述 ns 中所有容器的一般限制策略,通常用于分隔环境之间的资源(当环境在节点级别没有严格划分时有用)

以下是设置资源限制的清单示例:

  • 在特定容器级别:

    containers:
    - name: app-nginx
      image: nginx
      resources:
        requests:
          memory: 1Gi
        limits:
          cpu: 200m

    那些。 在这种情况下,要运行带有 nginx 的容器,节点上至少需要 1G 的可用 RAM 和 0.2 个 CPU,而容器最多可以消耗 0.2 个 CPU 和节点上的所有可用 RAM。

  • 在整数级别 ns:

    apiVersion: v1
    kind: ResourceQuota
    metadata:
      name: nxs-test
    spec:
      hard:
        requests.cpu: 300m
        requests.memory: 1Gi
        limits.cpu: 700m
        limits.memory: 2Gi

    那些。 默认ns中所有请求容器的总和不能超过CPU 300m,OP 1G,所有限制总和为CPU 700m,OP 2G。

  • ns 中容器的默认限制:

    apiVersion: v1
    kind: LimitRange
    metadata:
      name: nxs-limit-per-container
    spec:
     limits:
       - type: Container
         defaultRequest:
           cpu: 100m
           memory: 1Gi
         default:
           cpu: 1
           memory: 2Gi
         min:
           cpu: 50m
           memory: 500Mi
         max:
           cpu: 2
           memory: 4Gi

    那些。 在所有容器的默认命名空间中,CPU 请求设置为 100m,OP 请求设置为 1G,限制为 1 个 CPU 和 2G。 同时,还对CPU(50m < x < 2)和RAM(500M < x < 4G)的request/limit中可能的值设置了限制。

  • Pod 级别限制 ns:

    apiVersion: v1
    kind: LimitRange
    metadata:
     name: nxs-limit-pod
    spec:
     limits:
     - type: Pod
       max:
         cpu: 4
         memory: 1Gi

    那些。 对于默认 ns 中的每个 pod,将有 4 个 vCPU 和 1G 的限制。

现在我想告诉你设置这些限制可以给我们带来什么好处。

节点间负载均衡机制

大家知道,k8s组件负责pod在节点之间的分发,比如 调度,它根据特定的算法工作。 该算法在选择要启动的最佳节点时经历两个阶段:

  1. 过滤
  2. 测距

那些。 根据所描述的策略,最初选择可以基于一组启动 Pod 的节点 谓词 (包括检查节点是否有足够的资源来运行 pod - PodFitsResources),然后对于每个节点,根据 优先级 奖励积分(包括,节点拥有的空闲资源越多,分配的积分就越多 - LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation),并在积分最多的节点上启动 pod(如果多个节点同时满足此条件,则随机选择一个)。

同时,您需要了解调度程序在评估节点的可用资源时,是由存储在 etcd 中的数据指导的 - 即该节点上运行的每个 Pod 所请求/限制的资源量,而不是实际的资源消耗量。 该信息可以从命令输出中获取 kubectl describe node $NODE例如:

# kubectl describe nodes nxs-k8s-s1
..
Non-terminated Pods:         (9 in total)
  Namespace                  Name                                         CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                         ------------  ----------  ---------------  -------------  ---
  ingress-nginx              nginx-ingress-controller-754b85bf44-qkt2t    0 (0%)        0 (0%)      0 (0%)           0 (0%)         233d
  kube-system                kube-flannel-26bl4                           150m (0%)     300m (1%)   64M (0%)         500M (1%)      233d
  kube-system                kube-proxy-exporter-cb629                    0 (0%)        0 (0%)      0 (0%)           0 (0%)         233d
  kube-system                kube-proxy-x9fsc                             0 (0%)        0 (0%)      0 (0%)           0 (0%)         233d
  kube-system                nginx-proxy-k8s-worker-s1                    25m (0%)      300m (1%)   32M (0%)         512M (1%)      233d
  nxs-monitoring             alertmanager-main-1                          100m (0%)     100m (0%)   425Mi (1%)       25Mi (0%)      233d
  nxs-logging                filebeat-lmsmp                               100m (0%)     0 (0%)      100Mi (0%)       200Mi (0%)     233d
  nxs-monitoring             node-exporter-v4gdq                          112m (0%)     122m (0%)   200Mi (0%)       220Mi (0%)     233d
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests           Limits
  --------           --------           ------
  cpu                487m (3%)          822m (5%)
  memory             15856217600 (2%)  749976320 (3%)
  ephemeral-storage  0 (0%)             0 (0%)

在这里,我们可以看到特定节点上运行的所有 Pod,以及每个 Pod 请求的资源。 这是启动 cronjob-cron-events-1573793820-xt6q9 pod 时调度程序日志的样子(当您在启动命令参数中设置第 10 个日志记录级别 -v=10 时,此信息将出现在调度程序日志中):

日志文件

I1115 07:57:21.637791       1 scheduling_queue.go:908] About to try and schedule pod nxs-stage/cronjob-cron-events-1573793820-xt6q9                                                                                                                                           
I1115 07:57:21.637804       1 scheduler.go:453] Attempting to schedule pod: nxs-stage/cronjob-cron-events-1573793820-xt6q9                                                                                                                                                    
I1115 07:57:21.638285       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s5 is allowed, Node is running only 16 out of 110 Pods.                                                                               
I1115 07:57:21.638300       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s6 is allowed, Node is running only 20 out of 110 Pods.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s3 is allowed, Node is running only 20 out of 110 Pods.                                                                               
I1115 07:57:21.638322       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s4 is allowed, Node is running only 17 out of 110 Pods.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, Node is running only 16 out of 110 Pods.                                                                              
I1115 07:57:21.638365       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s12 is allowed, Node is running only 9 out of 110 Pods.                                                                               
I1115 07:57:21.638334       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s11 is allowed, Node is running only 11 out of 110 Pods.                                                                              
I1115 07:57:21.638385       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s1 is allowed, Node is running only 19 out of 110 Pods.                                                                               
I1115 07:57:21.638402       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s2 is allowed, Node is running only 21 out of 110 Pods.                                                                               
I1115 07:57:21.638383       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, Node is running only 16 out of 110 Pods.                                                                               
I1115 07:57:21.638335       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, Node is running only 18 out of 110 Pods.                                                                               
I1115 07:57:21.638408       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s13 is allowed, Node is running only 8 out of 110 Pods.                                                                               
I1115 07:57:21.638478       1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, existing pods anti-affinity terms satisfied.                                                                         
I1115 07:57:21.638505       1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, existing pods anti-affinity terms satisfied.                                                                          
I1115 07:57:21.638577       1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, existing pods anti-affinity terms satisfied.                                                                          
I1115 07:57:21.638583       1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s7 is allowed, Node is running only 25 out of 110 Pods.                                                                               
I1115 07:57:21.638932       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 9        
I1115 07:57:21.638946       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 8           
I1115 07:57:21.638961       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: BalancedResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 9        
I1115 07:57:21.638971       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7        
I1115 07:57:21.638975       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: LeastResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 8           
I1115 07:57:21.638990       1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7           
I1115 07:57:21.639022       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: TaintTolerationPriority, Score: (10)                                                                                                        
I1115 07:57:21.639030       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: TaintTolerationPriority, Score: (10)                                                                                                         
I1115 07:57:21.639034       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: TaintTolerationPriority, Score: (10)                                                                                                         
I1115 07:57:21.639041       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: NodeAffinityPriority, Score: (0)                                                                                                            
I1115 07:57:21.639053       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: NodeAffinityPriority, Score: (0)                                                                                                             
I1115 07:57:21.639059       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: NodeAffinityPriority, Score: (0)                                                                                                             
I1115 07:57:21.639061       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: InterPodAffinityPriority, Score: (0)                                                                                                                   
I1115 07:57:21.639063       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)                                                                                                                   
I1115 07:57:21.639073       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: InterPodAffinityPriority, Score: (0)                                                                                                                    
I1115 07:57:21.639077       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)                                                                                                                    
I1115 07:57:21.639085       1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: InterPodAffinityPriority, Score: (0)                                                                                                                    
I1115 07:57:21.639088       1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)                                                                                                                    
I1115 07:57:21.639103       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)                                                                                                         
I1115 07:57:21.639109       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)                                                                                                          
I1115 07:57:21.639114       1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)                                                                                                          
I1115 07:57:21.639127       1 generic_scheduler.go:781] Host nxs-k8s-s10 => Score 100037                                                                                                                                                                            
I1115 07:57:21.639150       1 generic_scheduler.go:781] Host nxs-k8s-s8 => Score 100034                                                                                                                                                                             
I1115 07:57:21.639154       1 generic_scheduler.go:781] Host nxs-k8s-s9 => Score 100037                                                                                                                                                                             
I1115 07:57:21.639267       1 scheduler_binder.go:269] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10"                                                                                                               
I1115 07:57:21.639286       1 scheduler_binder.go:279] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10": all PVCs bound and nothing to do                                                                             
I1115 07:57:21.639333       1 factory.go:733] Attempting to bind cronjob-cron-events-1573793820-xt6q9 to nxs-k8s-s10

在这里我们看到,调度程序最初会过滤并生成可以启动它的 3 个节点的列表(nxs-k8s-s8、nxs-k8s-s9、nxs-k8s-s10)。 然后,它根据每个节点的多个参数(包括 BalancedResourceAllocation、LeastResourceAllocation)计算分​​数,以确定最合适的节点。 最终,Pod 被调度到点数最多的节点上(这里两个节点的点数相同 100037,所以随机选择一个 - nxs-k8s-s10)。

结论:如果一个节点运行没有设置任何限制的 pod,那么对于 k8s(从资源消耗的角度来看)这将相当于该节点上根本没有这样的 pod。 因此,如果你有条件有一个 pod 有贪吃进程(比如 wowza),并且没有对其设置任何限制,那么可能会出现这样一种情况:这个 pod 实际上吃掉了该节点的所有资源,但是对于 k8s 来说这个节点被视为已卸载,并且在排名时将获得与没有工作 Pod 的节点相同数量的分数(准确地说是评估可用资源的分数),这最终可能导致节点之间的负载分布不均匀。

Pod 的驱逐

如您所知,每个 pod 都被分配了 3 个 QoS 类别之一:

  1. 有保证的 — 当为 pod 中的每个容器指定内存和 cpu 的请求和限制时分配,并且这些值必须匹配
  2. 可爆裂的 — Pod 中至少有一个容器有请求和限制,且请求 < 限制
  3. 尽力而为 — 当 pod 中没有一个容器资源受限时

同时,当节点遇到资源(磁盘、内存)不足时,kubelet 开始根据考虑 pod 优先级及其 QoS 类别的特定算法对 pod 进行排名和驱逐。 例如,如果我们谈论 RAM,则根据 QoS 等级,根据以下原则奖励积分:

  • 保证:-998
  • 最大的努力:1000
  • 爆裂: min(max(2, 1000 - (1000 * 内存请求字节) / machineMemoryCapacityBytes), 999)

那些。 具有相同的优先级,kubelet 将首先从节点中驱逐具有尽力服务 QoS 级别的 pod。

结论:如果您想减少所需 pod 在缺少资源时被从节点驱逐的可能性,那么除了优先级之外,您还需要注意为其设置请求/限制。

应用程序 Pod 水平自动缩放机制 (HPA)

当任务是根据资源的使用情况(系统 - CPU/RAM 或用户 - rps)自动增加和减少 pod 数量时,例如 k8s 实体 HPA (水平 Pod 自动缩放器)。 其算法如下:

  1. 确定观察到的资源的当前读数(currentMetricValue)
  2. 确定资源的期望值(desiredMetricValue),系统资源的期望值使用 request 设置
  3. 确定当前副本数量(currentReplicas)
  4. 以下公式计算所需的副本数量(desiredReplicas)
    desiredReplicas = [ currentReplicas * ( currentMetricValue /desiredMetricValue )]

在这种情况下,当系数(currentMetricValue /desiredMetricValue)接近1时,不会发生缩放(这种情况下,我们可以自己设置允许误差,默认为0.1)。

让我们使用 app-test 应用程序(描述为部署)的示例来看看 hpa 是如何工作的,其中需要根据 CPU 消耗来更改副本数量:

  • 应用清单

    kind: Deployment
    apiVersion: apps/v1beta2
    metadata:
    name: app-test
    spec:
    selector:
    matchLabels:
    app: app-test
    replicas: 2
    template:
    metadata:
    labels:
    app: app-test
    spec:
    containers:
    - name: nginx
    image: registry.nixys.ru/generic-images/nginx
    imagePullPolicy: Always
    resources:
    requests:
    cpu: 60m
    ports:
    - name: http
    containerPort: 80
    - name: nginx-exporter
    image: nginx/nginx-prometheus-exporter
    resources:
    requests:
    cpu: 30m
    ports:
    - name: nginx-exporter
    containerPort: 9113
    args:
    - -nginx.scrape-uri
    - http://127.0.0.1:80/nginx-status

    那些。 我们看到应用程序 pod 最初在两个实例中启动,每个实例包含两个 nginx 和 nginx-exporter 容器,每个容器都有一个指定的 要求 对于CPU。

  • HPA 宣言

    apiVersion: autoscaling/v2beta2
    kind: HorizontalPodAutoscaler
    metadata:
    name: app-test-hpa
    spec:
    maxReplicas: 10
    minReplicas: 2
    scaleTargetRef:
    apiVersion: extensions/v1beta1
    kind: Deployment
    name: app-test
    metrics:
    - type: Resource
    resource:
    name: cpu
    target:
    type: Utilization
    averageUtilization: 30

    那些。 我们创建了一个 hpa,它将监视 Deployment app-test 并根据 cpu 指标调节应用程序的 pod 数量(我们预计 pod 应消耗其请求的 CPU 的 30%),副本数量为在2-10的范围内。

    现在,让我们看看当我们向其中一个炉床施加负载时 hpa 运行的机制:

     # kubectl top pod
    NAME                                                   CPU(cores)   MEMORY(bytes)
    app-test-78559f8f44-pgs58            101m         243Mi
    app-test-78559f8f44-cj4jz            4m           240Mi

我们总共有以下内容:

  • 期望值(desiredMetricValue)——根据hpa设置,我们有30%
  • 当前值(currentMetricValue) - 用于计算,controller-manager计算资源消耗的平均值,以%为单位,即有条件地执行以下操作:
    1. 从metric server接收pod指标的绝对值,即101m 和 4m
    2. 计算平均绝对值,即(101m + 4m) / 2 = 53m
    3. 获取所需资源消耗的绝对值(为此,将所有容器的请求相加) 60m + 30m = 90m
    4. 计算相对于请求 pod 的 CPU 消耗的平均百分比,即53m / 90m * 100% = 59%

现在我们已经拥有了确定是否需要更改副本数量所需的一切;为此,我们计算系数:

ratio = 59% / 30% = 1.96

那些。 副本数量应增加约 2 倍,达到 [2 * 1.96] = 4。

结论: 正如您所看到的,为了使该机制发挥作用,必要条件是观察到的 pod 中存在对所有容器的请求。

节点水平自动缩放机制(Cluster Autoscaler)

为了消除负载激增期间对系统的负面影响,仅配置 hpa 是不够的。 例如,根据hpa控制器管理器中的设置,它决定需要将副本数量增加2倍,但节点没有空闲资源来运行如此数量的pod(即节点无法提供向 requests pod 请求资源),并且这些 pod 切换到 Pending 状态。

在这种情况下,如果提供商有相应的IaaS/PaaS(例如GKE/GCE、AKS、EKS等),则可以使用类似 节点自动缩放器。 它允许您设置集群中的最大和最小节点数,并在集群和 Pod 资源不足时自动调整当前节点数(通过调用云提供商 API 来订购/删除节点)无法安排(处于 Pending 状态)。

结论: 为了能够自动缩放节点,需要在 pod 容器中设置请求,以便 k8s 能够正确评估节点上的负载,并相应地报告集群中没有资源来启动下一个 pod。

结论

需要注意的是,设置容器资源限制并不是应用程序成功运行的必要条件,但最好还是这样做,原因如下:

  1. 为了让调度器在k8s节点之间的负载均衡方面更加精准的运行
  2. 减少发生“pod 驱逐”事件的可能性
  3. 用于应用程序 Pod (HPA) 的水平自动缩放工作
  4. 用于云提供商的节点水平自动缩放(集群自动缩放)

另请阅读我们博客上的其他文章:

来源: habr.com

添加评论