暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【译】Kubernetes中的资源分配

背井 2021-03-03
1160

本文译自Kirill Goltsman所写的Assigning Computing Resources to Containers and Pods in Kubernetes,原文地址为 https://supergiant.io/blog/assigning-computing-resources-to-containers-and-pods-in-kubernetes/ 。为使文章只关注于k8s,翻译时移除了原文中关于supergiant的部分。译文也同时参考了k8s官方文档中关于Quality of Service for Pods的部分。



Kubernetes的资源模型(resource model)被设计成既能优化容器对资源的利用又能保证Pods的高效调度和应用的高可用。Kubernetes实现这一方法的核心是在Pod创建时规定容器的resource requests和limits(资源请求和限制)。本文将介绍Kubernetes resource model的内部机制,并带着你使用Kubernetes内置的工具和API为容器分配计算资源(CPU和RAM)。希望本文能让你对如何给容器分配资源获得更加深入的理解。


How does the Kubernetes Resource Allocation Model Work?

在Kubernetes中,我们可以通过设定 CPU 和 Memory(RAM) 的requests和limits,来为Pod中的每个容器分配资源。这些设定将作为调度器(scheduler)的依据,为Pods选择合适的运行节点(node)


Kubernetes文档将resource limit定义为 Kubernetes确保能够赋予给容器的计算资源(CPU或memory)的总量相应的,resource limit 被定义为 Kubernetes允许容器所消耗的资源的最大量


需要注意的是,一个Pod是否能被调度到某个节点,取决于这个Pod内所有的容器的resource requests和limits的总和。也就是说,如果Pod内的某个容器所需求的资源量与某个Node可分配的最大的资源量不匹配,这个Pod将无法被调度到该节点上。确保所有容器的资源需求总量在Node的容量范围内,既是调度器又是 kubelet 的任务。


Calculation of Resource Requests and Limits

容器所声明的resource requests应该大于等于0并且不超过节点的可分配容量。这条规则可用以下公式总结:0 <= request <= Node Allocatable。而resource limit 则应该大于等于 resource request 并且其值无上限:request <= limit <= Infinity


应该记住的是,Pods的调度依赖的是 requests 而不是 limits。换句话说,如果一个Pod的resource request在Node的可分配容量之内,即使该Pod的 resource limit过大,Pod也是可以被调度到该Node上的。同样要注意的是,即使一个Node的实际memory或CPU利用率不高,如果pod的resource request超过该Node的容量了,调度器也会拒绝把pod放置在这个Node上。这个特性防止了在突然出现流量高峰的情况下,系统出现资源短缺。


Kubernetes Resource Types

Kubernetes将底层处理器架构抽象为了计算资源,将它们按照需求暴露为原始值或基本单位。对于CPU资源来说,这些基本单位是基于核心(cores)的;对于内存来说,则是基于字节的。内存资源可以使用单纯的数值或带有后缀(E、P、T、G、M、K)的定点整数表示。而一个CPU则相当于:


  • 一个AWS vCPU

  • 一个GCP Core

  • 一个Azure vCore

  • 英特尔处理器上一个 Hyperthread(处理器要支持Hyperthreading)


Kubernetes允许按小数来指定CPU (比如 0.5 CPU)。举个例子,一个 resource request 为 0.5 CPU的容器,将会被确保获得一半CPU的算力。0.5 CPU 等同于 500m,后者的意思是 "500 millicpu" 或者 "500 minicores"。需要注意的是,在Kubernetes中,CPU资源用的是绝对量,意思是无论是在单核、双核或其它机器上,0.1 对应的量是一样的(译者解释: 对于2核的cpu来说,500m意味着用半核;2000m意味着2核都用;0.5意味着用半核;2意味着2核都用;以此类推)


Pod Classes Depending on Resource Requests and Limits

根据 resource requests和limit的 min/max 比例,我们最终会得到3类不同的Pods:guaranted Pods、burstable Pods 和 best-effort Pods。

(译者解释,pod在创建后,会有一个 spec.qosClass字段,其值可以是 Guaranteed、Burstable和BestEffort)



Guaranteed Pods


当一个Pod内的每个容器,其 request.memory等于limit.memory且request.cpu等于limit.cpu时,这个Pod被认为是 Guaranteed。


Example:

    containers:
    name: first
    resources:
    limits:
    cpu: 20m
    memory: 1Gi
    requests:
    cpu: 20m
    memory: 1Gi
    name: second
    resources:
    limits:
    cpu: 300m
    memory: 400Mi
    requests:
    cpu: 300m
    memory: 400Mi


    在本例中,名为first和second的容器,各自内部的request和limit都是相等的。这使得它们所处的Pod 为 guaranted。


    Burstable Pods

    需要满足2个条件: 

        a. 不是 Guaranteed Pod; 

        b. Pod内至少有一个容器设置了 memory 或 cpu request。


    Example:

      containers:
      name: first
      resources:
      limits:
      memory: 2Gi
      requests:
      memory: 1Gi
      name: second
      resources:
      limits:
      cpu: 500m
      requests:
      cpu: 300m


      Best-Effort Pods

      如果一个Pods内的所有容器都没有设置resource requests和limits,则这个pod被认为是 best-effort。


      Example:

        containers:
        name: first
        resources:
        name: second
        resources:


        Kubernetes根据上述不同类型的pod,将给出不同的资源使用权和优先级。Best-Effort Pods 有着最低的优先级,在系统内存不足时,它们是第一批被清理的对象。Guaranteed Pods有着最高的优先级,通常不会被杀死或节流,除非资源使用超过了limits的限制并且没有其它更低优先级的pods可清理了。最后,Burstable Pods有着最小的资源保证(译者注,因为设置了resource request)但是条件允许时允许使用更多的计算资源。在没有Best-Effort Pods存在并且系统容量不足时,Burstable Pods将是集群中第一批被杀死的。


        下面的规则适用于所有这3类pods:

        • 如果节点中内存充足,容器可以使用比memory request规定的更多的内存。但不能超过limit所限制的量。这类容器有被终止的可能。如果终止的容器被设置为可重启的,kubelet 会负责这一块。

        • 容器的cpu使用可能会被允许超限一小段时间。是否允许取决于容器的运行时环境(Docker、rkt)。因此有些容器在CPU使用上可以超限运行一小段时间,而有些则不能。CPU是否能超限使用还取决于Node中剩余多少空闲CPU。如果有其它容器也在争取这些资源,则要参考容器的resource request。如果没有别的容器想要这些资源,容器可以使用尽可能多的资源。


        Why Use Resource Requests and Limits at All?


        对于 resource requests和limits如何影响Pods的命运,我们现在有了基本的理解。然而,我们为什么要使用它们呢?


        我们有2个主要动机:安全高效地使用计算资源;确保高优先级的Pods正常运行。更确切地说:

        • CPU和memory requests比较低的pods,有更好的机会被调度

        • 如果你把resource limits设置的高于resource requests,则在资源充足时,Pods可以利用更多的资源。同时,设定resource limits后,可以保证资源不会过度使用


        在创建Pod时,也允许用户不设定resource requestst和limits,这种情况则符合以下规则:

        • 容器可以使用Node上的所有资源

        • 如果容器所在的namespace设置了memory或CPU limit,集群管理员可以使用 LimitRange 为所有容器指定limit的默认值


        Tutorial


        接下来,我们将向你展示,使用Kubernetes内置的工具给容器分配resource requests和limits是多么容易。


        要完成本示例,得满足以下前提:

        • 一个运行中的Kubernetes集群。你可以使用 Minikube安装一个单节点集群

        • 一个配置好了的可以与集群通信的 kubectl 命令行工具

        • 集群中需要运行着 Heapster service。可通过 kubectl get services --namespace=kube-system 来查看是否安装了该服务


        好了,我们可以开始给Kubernetes中的容器分配资源了。


        Step 1: Create a new namespace


        创建一个新的namespace是一个好的实践,它能帮我们将项目中的计算资源和集群中其它的部分进行分离。我们使用以下命令来创建namespace:

          kubectl create namespace assigning-resources-tut

          控制台会输出以下内容:

            namespace "assigning-resources-tut" created


            Step 2: Specify CPU requests and limits


            本教程将使用单容器Pod,这也是Kubernetes中最常见的Pod类型。用的容器镜像是NGINX。为了指定 CPU和memory request,我们在Pod manifest中使用 spec.containers.resources.requests。对于resource limits,我们使用 spec.containers.resources.limits字段。我们决定把NGINX容器的resource request设置为 0.5 CPU、CPU limit设置为 1 CPU。同时 memory request 设置为 500MiB 且 memory limit 为 700MiB。


            下面是Pod的配置文件:

              apiVersion: v1
              kind: Pod
              metadata:
              name: test-pod
              namespace: assigning-resources-tut
              spec:
              containers:
              - name: nginx
              image: nginx:latest
              resources:
              limits:
              cpu: "1"
              memory: "700Mi"
              requests:
              cpu: "0.5"
              memory: "500Mi"


              Step 3: Create a Pod


              执行以下命令:

                kubectl create -f test-pod.yaml --namespace=assigning-resources-tut


                Step 4: Check whether the pod's container is running in our namespace


                运行以下命令:

                  kubectl get pod test-pod --namespace=assigning-resources-tut


                  可以看到如下输出:

                    NAME       READY     STATUS    RESTARTS   AGE
                    test-pod 1/1 Running 0 8s


                    这意味着 test-pod 在运行中,没重启过并且存活了8秒了。


                    也可以运行以下命令,以yaml格式查看pod的详细信息:

                      kubectl get pod test-pod --output=yaml --namespace=assigning-resources-tut


                      一部分输出如下:

                        spec:
                        containers:
                        - image: nginx:latest
                        imagePullPolicy: Always
                        name: nginx
                        resources:
                        limits:
                        cpu: "1"
                        memory: 700Mi
                        requests:
                        cpu: 500m
                        memory: 500Mi


                        Step 5: Checking the actual resource usage

                        能够追踪Pod的资源使用情况是很方便的。我们可以使用 Heapster service 做检查。Heapster可以对Kubernetes进行集群监控和性能分析。


                        要使用Heapster,我们首先要启动一个proxy:

                          kubectl proxy


                          该proyx在当前终端窗口运行。为了使用Heapster,我们需要再开启一个终端窗口。要获取CPU使用率,在新的终端窗口中执行:

                            curl http://localhost:8001/api/v1/namespaces/kube-system/services/heapster/proxy/api/v1/model/namespaces/assigning-resources-tut/pods/test-pod/metrics/cpu/usage_rate


                            可以看到,我们访问了指向了我们namespace下的test-pod下的 cpu/usage_rate 接口。得到的结果类似于下面:

                              {
                              "metrics": [
                              {
                              "timestamp": "2018-05-13T21:43:40Z",
                              "value": 0.1
                              },
                              {
                              "timestamp": "2018-05-13T21:44:40Z",
                              "value": 0.2
                              },
                              {
                              "timestamp": "2018-05-13T21:45:00Z",
                              "value": 0.22
                              }


                              它展示的是每个时间点上的CPU使用情况。这便于我们追踪CPU随时间的变化。


                              同样,要获取内存使用情况,访问 memory/usage

                                curl http://localhost:8001/api/v1/namespaces/kube-system/services/heapster/proxy/api/v1/model/namespaces/assigning-resources-tut/pods/test-pod/metrics/memory/usage


                                下面的输出意味着Pod使用了13246454的RAM。

                                  {
                                  "timestamp": "2018-05-13T22:00:40Z",
                                  "value": 13246464
                                  },
                                  {
                                  "timestamp": "2018-05-13T22:01:00Z",
                                  "value": 13246464
                                  }


                                  Step 6: Deleting the pod

                                  本文快要结束了,我们把Pod清理掉吧。

                                    kubectl delete pod test-pod --namespace=assigning-resources-tut


                                    Conclusion

                                    希望本文能帮你理解Kubernetes resource model实际上是如何工作的。如我们所学的这样,resource requests 和 limits这一概念简单且强大。合理地使用它们能帮助我们控制pods如何利用集群中的计算资源。Kubernetes也提供了灵活的方式来创建不同优先级的pods,方便最有效的方式来分发有限的资源;同时也确保了最重要的应用和容器总是处于运行状态,降低了突发的流量高峰和node故障带来的影响。




                                    文章转载自背井,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                    评论