kubernetes之计算机资源管理

系列目录

当你编排一个pod的时候,你也可以可选地指定每个容器需要多少CPU和多少内存(RAM).当容器请求特定的资源时,调度器可以更好地根据资源请求来确定把pod调度到哪个节点上.当容器请求限制特定资源时,特定节点会以指定方式对容器的资源进行限制.

对于资源请求和资源限制的区别,可以查看QoS

资源类型

CPU和RAM都是资源类型,资源类型有一个基本单位.CPU资源通过单位核数来指定,内存通过单位字节来指定.它们和api资源不同.api资源,例如pod和service是kubernetes api server可以读取和修改的对象.

pod和容器的资源请求与资源限制

pod里的每一个容器都可以指一个或多个以下内容:

  • spec.containers[].resources.limits.cpu

  • spec.containers[].resources.limits.memory

  • spec.containers[].resources.requests.cpu

  • spec.containers[].resources.requests.memory

尽管请求和限制只能通过单个的容器来指定,但是我们通常说pod的资源请求和资源限制,这样更方便.Pod对于某一特定资源的请求(或限制)是pod里单个容器请求资源的和.

CPU的意义

对cpu资源的请求和限制用单位cpu来衡量,一个cpu在kubernetes里和以下任意一个是等价的:

  • 1 AWS vCPU

  • 1 GCP Core

  • 1 Azure vCore

  • 1 IBM vCPU

  • 在祼机因特尔超线程处理器上一个超线程

带小数的请求也是允许的.一个指定spec.containers[].resources.requests.cpu值为0.5的容器会被确保分配1个cpu的一半资源.值0.1和100m是等价的,可以读作100 millicpu,也有人读作100 millicores,实际上意义是一样的.请求资源的值为0.1将会被api转换为100m,请求的精度比1m更小是不允许的,因此100m一个较优的选择.CPU资源的请求总是绝对量,永远不会是一个相对值,值是0.1时对于单核cpu,双核cpu或者甚至48核cpu都是一样的.

内存的意义

对内存资源的请求/限制用字节来衡量.你可以使用一个纯整数或者定点整数带上E, P, T, G, M, K这些后缀,你也可以使用2的幂次方的:Ei, Pi, Ti, Gi, Mi, Ki来表示,比如以下表示的量相等

128974848, 129e6, 129M, 123Mi

以下定义的pod包含两个容器,每一个都请求0.25cpu和64MiB (226 字节)的内存.每一个都限制0.5cpu和128MIB内存.你可以说pod请求0.5cpu和128MiB内存,限制1cpu和256MiB内存

前面说过,pod的资源请求/限制是pod里的容器资源请求/限制的和

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: db
    image: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: wp
    image: wordpress
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

kubernetes如何调度有资源请求的pod

当你创建一个pod,kubernetes会选择一个节点来运行.对于每种资源类型,不论是cpu还是内存,节点可以提供给pod的都是有上限的.对于每种资源,调度器都会确保被调度的pod的请求的总和小于节点的承载能力.请注意即便节点上的cpu或者内存的使用率非常低,但是如果容量检测失败,调度器仍然可能拒绝把某一pod放到指定节点上.这确保后来资源使用增多时不会出现资源短缺的情况.

有资源限制的pod如何运行

当kubelete启动了pod的容器,它会把cpu和内存的限制传入到容器的runtime里

当使用的是docker时:

  • spec.containers[].resources.requests.cpu的值被转换为核数值,它也可能是小数,乘1024.这个值和2两者较大的一个会被作为--cpu-shares标识的值传入docker run命令

  • spec.containers[].resources.limits.cpu被转换为millicore值并且乘以100.结果值是容器每100毫秒可用的cpu时间的总和.

注意默认的时间段配额是100毫秒,最小配额是1毫秒.

  • spec.containers[].resources.limits.memory被转换为整数,然后作为--momory标识的值传入docker run命令.

如果一个容器超过了它的资源限制,则它可能会被终止.如果它是可重启的(根据pod编排时重启策略),则kubelete会重启它,就像对待其它运行时错误一样.

一个容器可能会被允许或者不被允许超过超过它的cpu限制.但是超过cpu资源限制时它不会被杀掉.

问题处理

pod挂起,event消息是:failedScheduling

如果调度器找不到任何可以放置pod的节点,pod一直是未调度状态找到一个合适的节点.调度器每当找不到合适节点来放置pod,就会产生一个事件,信息如下

kubectl describe pod frontend | grep -A 3 Events

Events:
  FirstSeen LastSeen   Count  From          Subobject   PathReason      Message
  36s   5s     6      {scheduler }              FailedScheduling  Failed for reason PodExceedsFreeCPU and possibly others

上面的示例中,由于节点的cpu资源不足,名称为frontend的pod调度失败.类似的错误提示也可能是由于内存资源不足(PodExceedsFreeMemory).一般地,如果类似这样的消息出现,可以进行以下尝试:

  • 集群中添加更多节点

  • 终止一些非必须进程来为挂起的pod腾出资源

  • 检测确保pod小于node,比如节点的容量是cpu:1,如果pod请求的是cpu:1.1将永远不会被调度.

你可以通过kubectl describe nodes命令来查看节点的容量以及可分配的量:

kubectl describe nodes e2e-test-minion-group-4lw4
Name:            e2e-test-minion-group-4lw4
[ ... lines removed for clarity ...]
Capacity:
 cpu:                               2
 memory:                            7679792Ki
 pods:                              110
Allocatable:
 cpu:                               1800m
 memory:                            7474992Ki
 pods:                              110
[ ... lines removed for clarity ...]
Non-terminated Pods:        (5 in total)
  Namespace    Name                                  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------    ----                                  ------------  ----------  ---------------  -------------
  kube-system  fluentd-gcp-v1.38-28bv1               100m (5%)     0 (0%)      200Mi (2%)       200Mi (2%)
  kube-system  kube-dns-3297075139-61lj3             260m (13%)    0 (0%)      100Mi (1%)       170Mi (2%)
  kube-system  kube-proxy-e2e-test-...               100m (5%)     0 (0%)      0 (0%)           0 (0%)
  kube-system  monitoring-influxdb-grafana-v4-z1m12  200m (10%)    200m (10%)  600Mi (8%)       600Mi (8%)
  kube-system  node-problem-detector-v0.1-fj7m3      20m (1%)      200m (10%)  20Mi (0%)        100Mi (1%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests    CPU Limits    Memory Requests    Memory Limits
  ------------    ----------    ---------------    -------------
  680m (34%)      400m (20%)    920Mi (12%)        1070Mi (14%)

通过上面的输出,你可以看到如果有一个pod请求或者 6.23Gi 内存,则它不适合调度到此节点上.

通过查看pods部分,你可以看到你可以看到哪些pod在本节点上占用了资源

可供pod使用的资源数量小于节点的实际容量,这是因为系统守护进程按比例使用可用资源.allocatable字段里的数量是pod实际可用的资源数量,详情请查看可用资源

容器终止

pod可能由于资源不足被终止,你可以使用kubectl describe pod来检测pod是否由于资源达到上限被杀死

kubectl describe pod simmemleak-hra99
Name:                           simmemleak-hra99
Namespace:                      default
Image(s):                       saadali/simmemleak
Node:                           kubernetes-node-tf0f/10.240.216.66
Labels:                         name=simmemleak
Status:                         Running
Reason:
Message:
IP:                             10.244.2.75
Replication Controllers:        simmemleak (1/1 replicas created)
Containers:
  simmemleak:
    Image:  saadali/simmemleak
    Limits:
      cpu:                      100m
      memory:                   50Mi
    State:                      Running
      Started:                  Tue, 07 Jul 2015 12:54:41 -0700
    Last Termination State:     Terminated
      Exit Code:                1
      Started:                  Fri, 07 Jul 2015 12:54:30 -0700
      Finished:                 Fri, 07 Jul 2015 12:54:33 -0700
    Ready:                      False
    Restart Count:              5
Conditions:
  Type      Status
  Ready     False
Events:
  FirstSeen                         LastSeen                         Count  From                              SubobjectPath                       Reason      Message
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {scheduler }                                                          scheduled   Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   pulled      Pod container image "k8s.gcr.io/pause:0.8.0" already present on machine
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   created     Created with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   started     Started with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    spec.containers{simmemleak}         created     Created with docker id 87348f12526a

上面的例子中,Restart Count:5表示simmemleak容器中止并且被重启了五次.

你可以通过kubectl get pod加上-o go-template=...选项来获取上次终止的容器的状态

kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}'  simmemleak-hra99
Container Name: simmemleak
LastState: map[terminated:map[exitCode:137 reason:OOM Killed startedAt:2015-07-07T20:58:43Z finishedAt:2015-07-07T20:58:43Z containerID:docker://0e4095bba1feccdfe7ef9fb6ebffe972b4b14285d5acdec6f0d3ae8a22fad8b2]]

你可以看到容器由于reason:OOM Killed错误被中止,OOM表示out of memory(内存溢出)

Local ephemeral storage(本地暂存容量)

此功能在1.14版本中为beta状态

Kubernetes在1.8的版本中引入了一种类似于CPU,内存的新的资源模式:ephemeral-storage,并且在1.10的版本在kubelet中默认打开了这个特性。ephemeral-storage是为了管理和调度Kubernetes中运行的应用的短暂存储。在每个Kubernetes的节点上,kubelet的根目录(默认是/var/lib/kubelet)和日志目录(/var/log)保存在节点的主分区上,这个分区同时也会被Pod的EmptyDir类型的volume、容器日志、镜像的层、容器的可写层所占用。ephemeral-storage便是对这块主分区进行管理,通过应用定义的需求(requests)和约束(limits)来调度和管理节点上的应用对主分区的消耗。

在节点上的kubelet启动的时候,kubelet会统计当前节点的主分区的可分配的磁盘资源,或者你可以覆盖节点上kubelet的配置来自定义可分配的资源。在创建Pod时会根据存储需求调度到满足存储的节点,在Pod使用超过限制的存储时会对其做驱逐的处理来保证不会耗尽节点上的磁盘空间。

如果运行时指定了别的独立的分区,比如修改了docker的镜像层和容器可写层的存储位置(默认是/var/lib/docker)所在的分区,将不再将其计入ephemeral-storage的消耗。

对Local ephemeral storage的请求/限制

所有的container可以指定以下一个或多个选项

  • spec.containers[].resources.limits.ephemeral-storage

  • spec.containers[].resources.requests.ephemeral-storage

ephemeral-storage资源的请求/限制通过字节来衡量.同上面内存的请求/限制方式一样,这里不再细述.

比如以下pod包含两个容器,每一个都对Local ephemeral storage的请求为2GiB,限制为4GiB.此时,整个pod对资源的请求为4GiB,对资源的限制为8GiB

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: db
    image: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
  - name: wp
    image: wordpress
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"

对有ephemeral-storage请求的pod如何调度

当你创建一个pod,kubernetes调度器选择一个节点来运行它.每个节点都有可供pod使用的最大Local ephemeral storage最大量,详细信息请查看Node Allocatable

调度器保证被调度的容器资源总和小于节点的容量

带有Local ephemeral storage的pod如何运行

对于容器级别的隔离,如果容器的可写层( writable layer)和日志(log)超出了容量限制,容器所在的pod将会被驱离;对于pod级别的隔离,如果pod里所有容器使用的总Local ephemeral storage和pod的emptydir存储卷超过限制,pod将会被驱离.

示例

apiVersion: v1
kind: Pod
metadata:
  name: teststorage
  labels:
    app: teststorage
spec:
  containers:
  - name: busybox
    image:  busybox
    command: ["bash", "-c", "while true; do dd if=/dev/zero of=$(date '+%s').out count=1 bs=10MB; sleep 1; done"] # 持续写入文件到容器的rootfs中
    resources:
      limits:
        ephemeral-storage: 100Mi #定义存储的限制为100M
      requests:
        ephemeral-storage: 100Mi

测试这个Pod就能发现在容器写入超过存储限制时就会被驱逐掉了:

 /tmp kubectl apply -f pod.yaml
pod "teststorage" created
  /tmp kubectl get pod -w
NAME          READY     STATUS              RESTARTS   AGE
teststorage   0/1       ContainerCreating   0          3s
teststorage   1/1       Running   0         7s
teststorage   0/1       Evicted   0         1m

查看kubernetes的pod的事件也能看到它是由于超过了限制的ephemeral-storage被驱逐掉:

Events:
  Type     Reason                 Age   From               Message
  ----     ------                 ----  ----               -------
  Normal   Scheduled              2m    default-scheduler  Successfully assigned teststorage to minikube
  Normal   SuccessfulMountVolume  2m    kubelet, minikube  MountVolume.SetUp succeeded for volume "default-token-l7wp9"
  Normal   Pulling                2m    kubelet, minikube  pulling image "busybox"
  Normal   Pulled                 2m    kubelet, minikube  Successfully pulled image "busybox"
  Normal   Created                2m    kubelet, minikube  Created container
  Normal   Started                2m    kubelet, minikube  Started container
  Warning  Evicted                1m    kubelet, minikube  pod ephemeral local storage usage exceeds the total limit of containers {{104857600 0} {<nil>} 100Mi BinarySI}
  Normal   Killing                1m    kubelet, minikube  Killing container with id docker://busybox:Need to kill Pod
posted @ 2019-06-15 14:52  周国通  阅读(2002)  评论(0编辑  收藏  举报