Kubernetes 作为当下最流行的的容器集群管理平台,需要统筹集群整体的资源使用情况,将合适的资源分配给pod容器使用,既要保证充分利用资源,提高资源利用率,又要保证重要容器在运行周期内能够分配到足够的资源稳定运行。
配置容器资源限制
对于一个pod来说,资源最基础的2个的指标就是:CPU和内存。
Kubernetes提供了个采用requests和limits 两种类型参数对资源进行预分配和使用限制。
limit 会限制pod的资源利用:
- 当pod 内存超过limit时,会被oom。
- 当cpu超过limit时,不会被kill,但是会限制不超过limit值。
测试内存限制
部署一个压测容器,压测时会分配250M内存,但实际pod的内存limit为100Mi
apiVersion: v1
kind: Pod
metadata:
name: memory-demo
namespace: example
spec:
containers:
- name: memory-demo-2-ctr
image: polinux/stress
resources:
requests:
memory: "50Mi"
limits:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]
部署后查看pod状态,可以看到pod被OOM,
kubectl -n example get po
NAME READY STATUS RESTARTS AGE
memory-demo 0/1 OOMKilled 1 11s
测试CPU限制
apiVersion: v1
kind: Pod
metadata:
name: cpu-demo
namespace: example
spec:
containers:
- name: cpu-demo-ctr
image: vish/stress
resources:
limits:
cpu: "1"
requests:
cpu: "0.5"
args:
- -cpus
- "2"
查看容器信息,可以看到pod 虽然不会被kill掉,但是实际使用cpu被限制只有1000m。
kubectl -n example top po cpu-demo
NAME CPU(cores) MEMORY(bytes)
cpu-demo 1000m 0Mi
容器服务质量(QoS)
Kubernetes 提供服务质量管理,根据容器的资源配置,将pod 分为Guaranteed, Burstable, BestEffort 3个级别。当资源紧张时根据分级决定调度和驱逐策略,这三个分级分别代表:
- Guaranteed: pod中所有容器都设置了limit和request, 并且相等(设置limit后假如没有设置request会自动设置为limit值)
- Burstable: pod中有容器未设置limit, 或者limit和request不相等。这种类型的pod在调度节点时, 可能出现节点超频的情况。
- BestEffort: pod中没有任何容器设置request和limit。
计算qos代码:https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/core/helper/qos/qos.go
不同QoS对容器影响
oom:
Kubernetes会根据QoS设置oom的评分调整参数oom_score_adj
,oom_killer 根据 内存使用情况算出oom_score, 并且和oom_score_adj
综合评价,进程的评分越高,当发生oom时越优先被kill。
QoS | oom_score_adj |
---|---|
Guaranteed | -998 |
BestEffort | 1000 |
Burstable | min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
当节点内存不足时,QoS为Guaranteed 的pod 最后被kill。 而BestEffort 级别的pod优先被kill。 其次是Burstable,根据计算公式 oom_score_adj 值范围2到999,设置的request越大,oom_score_adj越低,oom时保护程度越高。
实践
节点信息:
# kubectl describe no cn-beijing.i-2zeavb11mttnqnnicwj9 | grep -A 3 Capacity
Capacity:
cpu: 4
memory: 8010196Ki
pods: 110
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-qos-1
namespace: example
spec:
containers:
- name: memory-demo-qos-1
image: polinux/stress
resources:
requests:
memory: "200Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "50M", "--vm-hang", "1"]
---
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-qos-2
namespace: example
spec:
containers:
- name: memory-demo-qos-2
image: polinux/stress
resources:
requests:
memory: "400Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "50M", "--vm-hang", "1"]
---
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-qos-3
namespace: example
spec:
containers:
- name: memory-demo-qos-3
image: polinux/stress
resources:
requests:
memory: "200Mi"
cpu: "2"
limits:
memory: "200Mi"
cpu: "2"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "50M", "--vm-hang", "1"]
单个节点可分配内存为8010196Ki, 大约7822.45Mi。
根据Burstable 的计算方式:
request 200Mi: (1000 - 1000*200/7822.45) 约为975
request 400Mi: (1000 - 1000*400/7822.45) 约为950
我们分别查看这3个pod的oom参数
// request 200Mi
kubectl -n example exec memory-demo-qos-1 cat /proc/1/oom_score_adj
975
// request 400Miß
kubectl -n example exec memory-demo-qos-2 cat /proc/1/oom_score_adj
949
// Guaranteed
kubectl -n example exec memory-demo-qos-3 cat /proc/1/oom_score_adj
-998
设置oom 规则代码: https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/qos/policy.go
pod 驱逐:
当节点的内存和cpu资源不足,开始驱逐节点上的pod时。QoS同样会影响驱逐的优先级。顺序如下:
- kubelet 优先驱逐 BestEffort的pod 和 实际占用资源大于requests的Burstable pod。
- 接下来驱逐实际占用资源小于request的Burstable pod。
- QoS为Guaranteed的pod最后驱逐, kubelet 会保证Guaranteed的pod 不会因为其他pod的资源消耗而被驱逐。
- 当QoS相同时,kubelet 根据 Priority 计算驱逐的优先级
ResourceQuota
Kubernetes提供ResourceQuota对象,用于配置限制namespace内的每种类型的k8s对象数量和资源(cpu,内存)。
- 一个namespace中可以创建一个或多个ResourceQuota
- 如果namespace中配置了ResourceQuota, 部署时必须设置request和limit, 否则会拒绝创建请求。
- 可以通过这是limitRange配置每个pod默认的requests和limits避免上述问题
- 1.10以后支持扩展资源 详见:https://kubernetes.io/docs/tasks/configure-pod-container/extended-resource/
apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-demo
namespace: example
spec:
hard:
requests.cpu: "3"
requests.memory: 1Gi
limits.cpu: "5"
limits.memory: 2Gi
pods: "5"
LimitRange
LimitRange 是用来设置 namespace 中 Pod 的默认的资源 request 和 limit 值,以及大小范围。
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
namespace: example
spec:
limits:
- default: # default limit
memory: 512Mi
cpu: 2
defaultRequest: # default request
memory: 256Mi
cpu: 0.5
max: # max limit
memory: 800Mi
cpu: 3
min: # min request
memory: 100Mi
cpu: 0.3
maxLimitRequestRatio: # max value for limit / request
memory: 2
cpu: 2
type: Container # limit type, support: Container / Pod / PersistentVolumeClaim
limitRange支持的参数如下:
- default 代表默认的limit
- defaultRequest 代表默认的request
- max 代表limit的最大值
- min 代表request的最小值
- maxLimitRequestRatio 代表 limit / request的最大值。由于节点是根据pod request 调度资源,可以做到节点超卖,maxLimitRequestRatio 代表pod最大超卖比例。
总结
- Kubernetes 提供request 和 limit 两种方式设置容器资源。
- 为了提高资源利用率,k8s调度时根据pod 的request值计算调度策略,从而实现节点资源超卖。
- k8s根据limit限制pod使用资源,当内存超过limit时会触发oom。 且限制pod的cpu 不允许超过limit。
- 根据pod的 request和limit,k8s会为pod 计算服务质量,并分为Guaranteed, Burstable, BestEffort 这3级。当节点资源不足时,发生驱逐或者oom时, Guaranteed 级别的pod 优先保护, Burstable 节点次之(request越大,使用资源量越少 保护级别越高), BestEffort 最先被驱逐。
- Kubernetes提供了RequestQuota和LimitRange 用于设置namespace 内pod 的资源范围 和 规模总量。 RequestQuota 用于设置各种类型对象的数量, cpu和内存的总量。 LimitRange 用于设置pod或者容器 request和limit 的默认值,最大最小值, 以及超卖比例(limit / request)。
- 对于一些重要的线上应用,我们应该合理设置limit和request,limit和request 设置一致,资源不足时k8s会优先保证这些pod正常运行。
- 为了提高资源利用率。 对一些非核心,并且资源不长期占用的应用,可以适当减少pod的request,这样pod在调度时可以被分配到资源不是十分充裕的节点,提高使用率。但是当节点的资源不足时,也会优先被驱逐或被oom kill。