20 计算资源管理


为Pod中的容器申请资源

1
2
request 资源请求
limits 资源限制

创建包含 requests 请求资源的 Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@k8s-master1 request]# vim requests-pod.yaml 

apiVersion: v1
kind: Pod
metadata:
name: requests-pod
spec:
containers:
- name: main
image: busybox
command: ["dd","if=/dev/zero","of=/dev/null"]
resources:
requests:
cpu: 0.5
memory: 1000Mi
1
2
3
4
5
6
7
8
9
10
11
Mem: 1180484K used, 2699924K free, 1008K shrd, 36844K buff, 666140K cached
CPU0: 27.7% usr 65.0% sys 0.0% nic 6.9% idle 0.0% io 0.0% irq 0.1% sirq
CPU1: 5.6% usr 7.6% sys 0.0% nic 86.1% idle 0.6% io 0.0% irq 0.0% sirq
Load average: 1.02 0.54 0.30 3/353 10
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
1 0 root R 1284 0.0 0 50.0 dd if /dev/zero of /dev/null
6 0 root R 1292 0.0 1 0.0 top

# dd命令会持续消耗尽可能多的cpu 我的node主机是2核心cpu,那么它应该是跑满了1个U
# 说明该Pod的实际cpu消耗 超出了 requests 申请的 0.5 个 cpu
# requests 不会限制容器可以使用的cpu数量

资源 requests 如何影响调度

1
2
3
4
5
6
7
# 调度器判断一个Pod是否适合调度到某个节点
1. 调度器不关注Pod的实际资源使用量,只关心节点上部署的所有Pod的资源申请量之和
2. 调度器首先会对节点列表进行过滤,排除不满足需求的节点,然后根据预先设置的优先级函数对其余节点进行排序
3. 有2个基于资源请求量的优先级排序函数,LeastRequestedPriority 和 MostRequestedPriority
4. LeastRequestedPriority 会将Pod调度到请求量少的节点上(拥有更多未分配资源的节点)
5. MostRequestedPriority 会将Pod调度到请求量最多的节点,在云主机环境可以使资源更紧凑的使用,节省开销
6. 默认只能配置一种优先级函数

查看节点资源总量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@k8s-master1 request]# kubectl describe node k8s-node2


Capacity: # 节点的资源总量
cpu: 2
ephemeral-storage: 41147472Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3880408Ki
pods: 110
Allocatable: # 可分配给Pod的资源总量
cpu: 2
ephemeral-storage: 37921510133
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3778008Ki
pods: 110

模拟超出请求资源总量

1
2
3
4
5
6
# 模拟现在有一个申请3个CPU的Pod 超出我们的node资源总量
# 事件会告诉我们没有足够的cpu
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 Insufficient cpu.

限制容器的可用资源

设置容器可用资源的硬限制

1
2
# cpu是可压缩资源,内存是不可压缩资源
# 如果不限制内存使用量,工作节点上的容器可能会吃掉所有内存,恶意Pod占用内存,可能导致其他Pod无法分配到该Node

创建包含 limits 限制资源的 Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@k8s-master1 request]# vim limits-pod.yaml 

apiVersion: v1
kind: Pod
metadata:
name: limits-pod
spec:
containers:
- name: main
image: busybox
command: ["dd","if=/dev/zero","of=/dev/null"]
resources:
limits:
cpu: 1
memory: 1000Mi

# 如果不填写requests,那么会被设置为limits相同的值
[root@k8s-master1 request]# kubectl describe pod limits-pod
...
Limits:
cpu: 1
memory: 1000Mi
Requests:
cpu: 1
memory: 1000Mi
...

超过 limits

1
2
3
4
5
1. 如果进程尝试申请比限额更多的内存时,会被杀掉(OOM)
2. 如果Pod的重启策略是 Always,进程将会被重启
3. 如果持续这种状态 一边超出 一边重启 会被增加下次的重启时间
4. Pod 会处于 CrashLoopBackOff 状态
5. 如果不想被OOM ,内存的Limits就不应该被设置的太低

容器中的应用如何看待 limits

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Mem: 1247588K used, 2632820K free, 1008K shrd, 77436K buff, 681992K cached
CPU0: 0.0% usr 0.0% sys 0.0% nic 100% idle 0.0% io 0.0% irq 0.0% sirq
CPU1: 20.0% usr 80.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 0.0% sirq
Load average: 1.13 1.08 0.84 4/351 10
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
1 0 root R 1284 0.0 1 49.9 dd if /dev/zero of /dev/null
6 0 root R 1292 0.0 0 0.0 top

# 在容器中看到的内存数据,始终会是节点内存,而不是容器本身的内存
1. 即使你设置了最大可用内存,top命令显示的是运行该容器的节点的内存数量,而容器无法感知到此限制
2. 对于Java程序 需要设置 -Xmx ,虽然设置了堆大小,但是没有管住 off-heap内存
3. 新版本的JAVA会考虑到容器的limits 缓解该问题
4. cpu也同样,即使我们限制了 cpu配额为1,容器中也无法只显示暴露1个核
5. 如果程序通过查询CPU核数来启动线程数量,可能就不够友好,造成启动大量线程争取资源问题
6. 不能依赖应用程序从系统获取CPU数量,可能需要Downward限制传递给容器,并且使用这个值给程序

了解 Pod QoS 等级

1
2
3
4
5
6
# 当节点无法提供所有Pod指定的资源的Limis之和怎么办
1. 假设有两个Pod PodA使用了节点的90% PodB突然使用到了比之前更多内存
2. 这个时候节点无法提供足够的内存,那么意味着哪个Pod会被杀死
3. 是PodB被杀掉 因为无法满足他的内存使用 还是释放PodA 让PodB运行
4. K8S 无法直接帮我们选择,需要设定哪种Pod在该场景的优先级更高
5. K8S 将Pod 划分成3个Qos等级 (服务质量管理)
1
2
3
1. BestEffort (优先级最低)
2. Burstable
3. Guaranteed (优先级最高)

BestEffort 等级

1
2
3
4
1. 最低优先级的BestEffort 会分配给没有(为任何容器)设置任何requests和limits的Pod 
2. 这个等级运行的容器没有任何资源保障,在最坏的情况下,他们不会被分配任何CPU时间
3. 同时,在需要为其他Pod释放资源内存时,这些容器会被第一批杀死
4. 好处是,由于他们没有设置 内存的Limits,当有充足可用内存时,这些容器可以使用任意多的内存

Guaranteed 等级

1
2
3
4
5
1. CPU和内存都要设置 requests和limits 
2. 每个容器都需要设置资源量
3. 每个容器的每种资源的requests和limits 都必须相等
4. 如果只设定limits,那么requests和limits会相等,所以设置一个容器的限制量limits,就可以让这个Pod的等级为Guaranteed
5. Guaranteed 的 Pod 可以使用它申请的资源的等额资源,但是无法消耗更多的资源(requests和limits相同)

Burstable 等级

1
2
3
4
5
1. Burstable 介于前两者之间,其他的Pod都属于这个等级
2. requests和limits 不相同的Pod ,
3. 有一个容器定义了request但是没有定义limits的Pod
4. 在多容器的Pod中,1个容器定义了requests和limits , 另外一个确没有定义
5. Burstable 的 Pod 可以获得申请的等额资源,并可以使用额外的资源(超出limits)

查看Pod的等级

1
2
3
4
[root@k8s-master1 request]# kubectl describe pod limits-pod
...
QoS Class: Guaranteed
...

内存不足时 哪类进程会先被杀死

1
2
3
4
1. BestEffort (优先级最低)  最先被杀掉
2. Burstable 其次
3. Guaranteed (优先级最高) 最后
4. 如果是两个同级的Burstable,系统会根据Pod的实际使用率占内存申请量比率最高的

为命名空间的Pod设置 requests 和 limits

LimitRange

1
# 通过该资源为命名空间下的Pod配置资源限制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[root@k8s-master1 request]# vim limits.yaml

apiVersion: v1
kind: LimitRange
metadata:
name: example
spec:
limits:
- type: Pod # 整个Pod
min: # Pod中所有容器的CPU和内存的请求量之和的 最小值
cpu: 100m
memory: 100Mi
max: # Pod中所有容器的CPU和内存的请求量之和的 最大值
cpu: 1
memory: 1Gi
- type: Container # 指定容器的资源限制
defaultRequest: # 容器没有指定CPU或内存请求量时的默认值
cpu: 100m
memory: 100Mi
default: # 容器没有指定 limits 时的默认值
cpu: 200m
memory: 200Mi
min: # 容器的cpu和内存 requests和limits 最小值和最大值
cpu: 100m
memory: 100Mi
max:
cpu: 1
memory: 1Gi
maxLimitRequestRatio: # 每种资源requests和limits的最大比值
cpu: 4
memory: 10
- type: PersistentVolumeClaim # 请求pvc容量的最大值和最小值
min:
storage: 1Gi
max:
storage: 10Gi
1
2
3
4
5
6
7
[root@k8s-master1 request]# vim limits.yaml
[root@k8s-master1 request]# kubectl create -f limits.yaml
limitrange/example created

[root@k8s-master1 request]# kubectl get limitrange
NAME CREATED AT
example 2020-03-27T10:23:28Z
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 测试限制
# 创建一个要求3CPU的Pod

[root@k8s-master1 request]# vim limits-pod-too-big.yaml

apiVersion: v1
kind: Pod
metadata:
name: too-big
spec:
containers:
- image: busybox
args: ["sleep", "9999999"]
name: main
resources:
requests:
cpu: 3

[root@k8s-master1 request]# kubectl create -f limits-pod-too-big.yaml
The Pod "too-big" is invalid: spec.containers[0].resources.requests: Invalid value: "2": must be less than or equal to cpu limit

# 修改一下 不配置资源请求 看看是不是加上默认值
[root@k8s-master1 request]# vim limits-pod-too-big.yaml

apiVersion: v1
kind: Pod
metadata:
name: too-big
spec:
containers:
- image: busybox
args: ["sleep", "9999999"]
name: main

[root@k8s-master1 request]# kubectl describe pod too-big

...
Limits:
cpu: 200m
memory: 200Mi
Requests:
cpu: 100m
memory: 100Mi
...
# QoS Class: Burstable
1
2
3
4
5
# 总结:
1. Limitrange 只是限制Pod和容器的资源申请
2. ResourceQuota 限制命名空间的资源
3. 可以在不同的命名空间中增加 Limitrange 限制资源的使用 比如生产环境和测试环境的Pod资源限制,但是无法限制整体使用多少资源
4. ResourceQuota 可以通过限制命名空间的资源,以免大量Pod吃掉整个集群的所有资源

ResourceQuota

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. Limitrange应用于单独的Pod,ResourceQuota 应用于命名空间中所有的Pod 

[root@k8s-master1 request]# vim quota-cpu-memory.yaml


apiVersion: v1
kind: ResourceQuota
metadata:
name: cpu-and-mem
spec:
hard:
requests.cpu: 500m
requests.memory: 500Mi
limits.cpu: 1
limits.memory: 1024Mi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@k8s-master1 request]# kubectl create -f quota-cpu-memory.yaml 
resourcequota/cpu-and-mem created

[root@k8s-master1 request]# kubectl describe quota
Name: cpu-and-mem
Namespace: default
Resource Used Hard
-------- ---- ----
limits.cpu 0 1
limits.memory 0 1Gi
requests.cpu 0 500m
requests.memory 0 500Mi


# 重新创建默认pod看看是否增加变化
[root@k8s-master1 request]# kubectl create -f limits-pod-too-big.yaml
pod/too-big created

[root@k8s-master1 request]# kubectl get pod
NAME READY STATUS RESTARTS AGE
too-big 1/1 Running 0 17s

[root@k8s-master1 request]# kubectl describe quota
Name: cpu-and-mem
Namespace: default
Resource Used Hard
-------- ---- ----
limits.cpu 200m 1
limits.memory 200Mi 1Gi
requests.cpu 100m 500m
requests.memory 100Mi 500Mi
1
2
3
4
5
6
# 总结
1. ResourceQuota 和 LimitRange 应该一起创建,如果我们没有创建LimitRange,不指定任何资源限制的pod比如上一个,会无法创建成功
2. 也就是既然有了指定的配额,就必须为Pod创建 requests和limits,否则API不会接收Pod的创建请求\
3. 创建这些资源时,需要指定命名空间创建,否则会在默认的命名空间中创建
4. Quota 定额
5. limit 限制

监控 Pod 的资源使用量

1
2
1. Kubernetes 中的组件 Metrics Server 持续采集所有 Pod 副本的指标数据。 (heapster 在k8s 1.13 版本后 被弃用了)
2. cadvisor 采集容器的资源利用率: CPU、内存 的使用率

部署 Metrics Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
[root@k8s-master1 demo]# unzip metrics-server.zip 
[root@k8s-master1 demo]# cd metrics-server
# 修改参数:
...
containers:
- name: metrics-server
image: lizhenliang/metrics-server-amd64:v0.3.1
command:
- /metrics-server
- --kubelet-insecure-tls # 忽略证书
- --kubelet-preferred-address-types=InternalIP # 找到nodeIP,默认使用注册节点名称,修改成IP
...

# 部署
[root@k8s-master1 metrics-server]# kubectl apply -f .
[root@k8s-master1 metrics-server]# kubectl get pods -n kube-system

# 检查是否正常工作
[root@k8s-master1 metrics-server]# kubectl logs metrics-server-7dbbcf4c7-vzdn2 -n kube-system
[root@k8s-master1 metrics-server]# kubectl get apiservice
...
v1beta1.metrics.k8s.io kube-system/metrics-server True 86s
...

# 获取资源利用率 metrics-server 工作正常 采集到了数据

[root@k8s-master1 metrics-server]# kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
k8s-master1 118m 5% 1169Mi 31%
k8s-node1 81m 4% 543Mi 14%
k8s-node2 83m 4% 545Mi 14%

# 显示Pod的资源使用情况
[root@k8s-master1 metrics-server]# kubectl top pods
NAME CPU(cores) MEMORY(bytes)
nfs-client-provisioner-56f4b98d47-v4nf6 1m 6Mi

[root@k8s-master1 metrics-server]# kubectl top pods --all-namespaces
NAMESPACE NAME CPU(cores) MEMORY(bytes)
default nfs-client-provisioner-56f4b98d47-v4nf6 1m 6Mi
ingress-nginx nginx-ingress-controller-6txhq 4m 111Mi
ingress-nginx nginx-ingress-controller-jqxrm 4m 109Mi
ingress-nginx nginx-ingress-controller-m8ljl 4m 107Mi
kube-system coredns-6d8cfdd59d-w2xnx 3m 12Mi
kube-system kube-flannel-ds-amd64-q29r8 2m 13Mi
kube-system kube-flannel-ds-amd64-xh7c4 2m 9Mi
kube-system kube-flannel-ds-amd64-xq7gq 2m 11Mi
kube-system metrics-server-7dbbcf4c7-6qrsb 1m 13Mi
kubernetes-dashboard dashboard-metrics-scraper-566cddb686-t8fw4 1m 11Mi
kubernetes-dashboard kubernetes-dashboard-c4bc5bd44-4prrf 2m 17Mi

# 如果 top pod命令拒绝,有可能是收集工作正在执行,等等再看即可