Kubernetes有一种与资源配额管理有关的对象:ResourceQuota,此种类型的对象由集群管理员创建,创建好后作用于某个名称空间,限制名称空间内可以创建的各类对像的总数、CPU、内存等。用户、用户组则工作在名称空间之下,以此实现多用户、多用户共享同一集群,公平合理的分配资源。本文描述在一个名称空间之下创建容器时,对容器所使用的计算资源进行管理。
计算资源包含cpu与memory,就是通常计算机上的硬件资源,不包含存储。计算资源可以被计量、申请、分配、消费。在Kurnetes中cpu的计量单位是core,就是几个核。memory的计算单位是byte,就是字节数。
在定义pod时,以上与计算资源相关的字段只能指定给其中所定义的容器。当谈讨pod的limit与request时,指的是pod所有容器limit或者是request的总和。
在Kubernetes层面中,一个CPU表示一个核心处理器的core,但其具体含义取决于低层基础设施供应商,有可能是物理core、虚拟core、一个线程等,以下是一个CPU核在常见的基础设施供应商的含义:
CPU的核数,最终应该是被转换成表示线程占用CPU的时长。首先换算的单位是100m(100毫秒),100m对应0.1个核。比如,0.4表示400毫秒的CPU内核运行时间,1表示1000毫秒的CPU内核运行时间,1.5表示1500毫秒的CPU内核运行时间,可以直接指定如400m、1500m,这种方法更直观更通用。如果一个节点有4个核则在1秒内有4000毫秒的时间片可供分配。所以无论宿主机节点有多少个核,CPU术数表示的意义不变,它是一个绝对值。另外它只能精确到小数点后1位或者是100m
其含义是字节数,表示多少个字节。在书写时可以用整数、指数、表示计算机容器的K、M等。
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"
调度器计算pod中所有容器对cpu、memory需求总数,从候选节点中将不符合条件的节点过滤,如果没有节点符合条件则pod等待下一次调试并将调度失败的原因输出到pod事件中。
容器运行环境一般是docker,在启动容器时由kubernetes将与limit有关的字段传递给kubelet,再由kubelet传递给容器运行时环境如docker run。
如果容器在运行时内存使用超过request但小于limit,容器不会被杀死,但当节点上的操作系统或者kubernetes认为节点内存紧张时,内存超过request的容器应该优先被杀死。如果容器在运行时内存使用超过limit应该立即被kubelet杀死,然后根据重启策略重启。
CPU的使用与内存不同,在定义容器时对CPU的request与limit作了限制,但是CPU的真实使用情况决定于操作系统的调度。即使CPU超过limit的限制,kubelet也不会杀死容器,因为这是操作系统的责任,操作系统有可能会杀死容器。
pod调度失败:
当pod因为计算资源问题调度失败时,它处于pending状态,并且生成调度失败的错误事件,指明调度失败原因。系统重复调度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
出现此类问题的解决方法:
执行如下命令确认node的资源问题及使用情况:
$ 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的资源需求超过了未占用资源数量,则此node会被调度程序排除,后半部分的内容显示pod占用资源的详细情况。一般情况下资源总量小于节点实际总量,因为一般不允许kubernetes占用全部的节点资源。
容器运行失败
运行中的容器有可能因为资源问题被终止,运行如下指令确认容器终止原因:
[12:54:41] $ 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"被重启了5次,并且Ready为false。Pod对象描述中包含一个数组,其中列出了所有容器历史退出的状态码与错误消息,运行如下命令进一步查看:
[13:59:01] $ 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]]
可以看到是因为内存溢出被杀死。
在每个节点上,Kubernetes的根目录(/var/lib/kubelet by default)、日志目录(/var/log)通常位于节点上的根分区,此分区也同时被 EmptyDir volumes, container logs, image layers and container writable layers等消费。用户可以为image layers、容器可读写层指定其它分区,此时的临时存储空间不受本节内容的限制。那么如何限制pod对根分区的消耗呢?
为container设置对根分区存储的request与limit字段:
示例:
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"
如果节点提供的存储能力达不到pod的要求,则调度程序将节点从候选列表删除。
这个与内存不同,内存是杀死后重新启动。存储的话即使杀死容器也不会释放存储空间, 因此kubernetes采用的是将pod从当前节点排挤出去。