环境说明:
主机名 | 操作系统版本 | ip | docker version | kubelet version | 配置 | 备注 |
---|---|---|---|---|---|---|
master | Centos 7.6.1810 | 172.27.9.131 | Docker 18.09.6 | V1.14.2 | 2C2G | master主机 |
node01 | Centos 7.6.1810 | 172.27.9.135 | Docker 18.09.6 | V1.14.2 | 2C2G | node节点 |
node02 | Centos 7.6.1810 | 172.27.9.136 | Docker 18.09.6 | V1.14.2 | 2C2G | node节点 |
k8s集群部署详见:Centos7.6部署k8s(v1.14.2)集群
k8s学习资料详见:基本概念、kubectl命令和资料分享
在配置Pod时,我们可以为其中的每个容器指定需要使用的计算资源(CPU和内存)。计算资源的配置项分为两种:Requests和Limits。Requests表示容器希望被分配到的、可完全保证的资源量(资源请求量);Limits是容器最多能使用的资源量的上限(资源限制量)。
资源请求量能够保证Pod有足够的资源来运行,资源限制量则是防止某个Pod无限制地使用资源,导致其他Pod崩溃。
我们创建一个pod时,可以指定容器对CPU和内存的资源请求量及资源限制量,它们并不在pod里定义,而是针对每个容器单独指定。pod对资源的请求量和限制量是它所包含的所有容器的请求量和限制量之和。
CPU和内存的Requests和Limits有如下特点:
[root@master ~]# kubectl describe nodes
[root@master ~]# more requests-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: requests-pod
spec:
containers:
- image: busybox
name: busybox
args:
- /bin/sh
- -c
- sleep 60000
resources:
requests: #资源申请
cpu: 500m #容器申请500毫核(一个CPU核心时间的1/2)
memory: 500Mi #容器申请500M内存
nodeName: node01
[root@master ~]# kubectl apply -f requests-pod.yaml
pod/requests-pod created
在Kubernetes系统上,l个单位的CPU相当于虚拟机上的l颗虚拟CPU(vCPU)或物理机上的一个超线程(Hyperthread,或称为一个逻辑CPU),它支持分数计量方式,一个核心(1core)相当于1000个微核心(millicores),因此500m相当于是0.5个核心,即二分之一个核心。内存的计量方式与日常使用方式相同,默认单位是字节,也可以使用E,P、T、G、M和K作为单位后缀,或Ei、Pi、Ti、Gi、Mi和Ki形式的单位后缀。
[root@master ~]# kubectl get po -o wide
[root@master ~]# kubectl describe po requests-pod
[root@master ~]# kubectl delete all --all
[root@master ~]# more deploy-busybox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
spec:
selector:
matchLabels:
env: prod
replicas: 10
template:
metadata:
labels:
env: prod
spec:
containers:
- name: busybox
image: busybox
args:
- /bin/sh
- -c
- sleep 60000
nodeSelector:
node: node01
[root@master ~]# kubectl apply -f deploy-busybox.yaml
deployment.apps/busybox created
若不指定资源请求量,节点node01上可成功运行10个pod
[root@master ~]# more requests-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: requests-pod
spec:
containers:
- image: busybox
name: busybox
args:
- /bin/sh
- -c
- sleep 60000
resources:
requests: #资源申请
cpu: 500m #容器申请500毫核(一个CPU核心时间的1/2)
memory: 800Mi #容器申请800M内存
nodeName: node01
[root@master ~]# more requests-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: requests-pod-2
spec:
containers:
- image: busybox
name: busybox
args:
- /bin/sh
- -c
- sleep 60000
resources:
requests:
cpu: 500m
memory: 800Mi
nodeName: node01
[root@master ~]# kubectl apply -f requests-pod.yaml
pod/requests-pod created
[root@master ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
requests-pod 1/1 Running 0 4s 10.244.1.67 node01 <none> <none>
[root@master ~]# kubectl apply -f requests-pod-2.yaml
pod/requests-pod-2 created
[root@master ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
requests-pod 1/1 Running 0 14s 10.244.1.67 node01 <none> <none>
requests-pod-2 0/1 OutOfmemory 0 2s <none> node01 <none> <none>
报节点node01 内存资源不足,pod requests-pod-2调度失败
[root@master ~]# kubectl delete all --all
[root@master ~]# more requests-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: requests-pod
spec:
containers:
- image: busybox
name: busybox
args:
- /bin/sh
- -c
- sleep 60000
resources:
requests: #资源申请
cpu: 1 #容器申请一个CPU核心时间
memory: 80Mi #容器申请80M内存
nodeName: node01
[root@master ~]# more requests-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: requests-pod-2
spec:
containers:
- image: busybox
name: busybox
args:
- /bin/sh
- -c
- sleep 60000
resources:
requests:
cpu: 1
memory: 80Mi
nodeName: node01
[root@master ~]# kubectl apply -f requests-pod.yaml
pod/requests-pod created
[root@master ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
requests-pod 1/1 Running 0 4s 10.244.1.68 node01 <none> <none>
[root@master ~]# kubectl apply -f requests-pod-2.yaml
pod/requests-pod-2 created
[root@master ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
requests-pod 1/1 Running 0 13s 10.244.1.68 node01 <none> <none>
requests-pod-2 0/1 OutOfcpu 0 2s <none> node01 <none> <none>
报节点node01 cpu资源不足,pod requests-pod-2调度失败
[root@master ~]# more limited-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: limited-pod
spec:
containers:
- image: busybox
command: ["sleep","600000"]
name: busybox
resources:
requests: #资源申请
cpu: 200m #容器申请200毫核(一个CPU核心时间的1/5)
memory: 80Mi #容器申请80M内存
limits: #资源限制
cpu: 2 #容器最大允许使用2核CPU
memory: 2Gi #容器最大允许使用2GB内存
nodeName: node01
[root@master ~]# kubectl apply -f limited-pod.yaml
pod/limited-pod created
[root@master ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
limited-pod 1/1 Running 0 12s 10.244.1.75 node01 <none> <none>
创建pod limited-pod,资源限制为cpu 2核,内存2G
创建limited-pod和limited-pod-2,cpu为2核,内存2G,pod运行正常。
前面曾提到过,Kubernetes允许节点资源对Limits的过载使用,这意味着节点无法同时满足其上的所有Pod对象以资源满载的方式运行。于是,在内存资源紧缺时,应该以何种次序先后终止哪些Pod对象?Kubernetes无法自行对此做出决策,它需要借助于Pod对象的优先级完成判定。根据Pod对象的requests和limits属性,Kubernetes将Pod象归类到BestEffort、Burstable和Guaranteed三个服务质量(Quality of Service,QoS)类别下,具体说明如下。
在一个overcommitted的系统,QoS等级决定着哪个容器第一个被杀掉,这样释放出的资源可以提供给高优先级的pod使用。BestEffort等级的pod首先被杀掉,其次是Burstable pod, 最后是Guaranteed pod。Guaranteed pod只有在系统进程需要内存时才会被杀掉。
资源的requests、limits和QoS等级
每个行状态容器都有其OOM得分,得分越高越会被优先杀死。OOM得分主要根据两个纬度进行计算:由QoS类别继承而来的默认分值和容器的可用内存资源比例。同等类别的Pod资源的默认分值相同,同等级别优先级的Pod资源在OOM时,与自身的requests属性相比,其内存占用比例最大的Pod对象将被首先杀死。
默认情况下,Kubernetes中所有容器都没有任何CPU和内存限制。LimitRange用来给Namespace增加一个资源限制,包括最小、最大和默认资源。
为单个容器设置资源requests和limits很有必要性:1.提升QoS等级,防止在OOM时被首先kill;2.默认情况下Pod会以无限制的CPU和内存运行,很有可能因故吞掉所在工作节点上的所有可用计算资源。
通过配置Pod的计算资源Requests和Limits,我们可以限制Pod的资源使用,但对于Kubemetes集群管理员而言,配置每一个Pod的Requests和Limits是烦琐且限制性过强的。更多时,我们需要的是对集群内Requests和Limits的配置做一个全局的统一的限制。
[root@master ~]# more limits.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: limitrange
spec:
limits:
- type: Pod #指定整个pod的资源limits
min: #pod中所有容器的Requests值的总和的下限
cpu: 50m
memory: 5Mi
max: #pod中所有容器的Limits值的总和的上限
cpu: 1
memory: 1Gi
- type: Container #指定容器的资源限制
defaultRequest: #容器Requests默认值
cpu: 100m
memory: 10Mi
default: #容器Limits默认值
cpu: 200m
memory: 100Mi
min: #pod中所有容器的Requests值的下限
cpu: 50m
memory: 5Mi
max: #pod中所有容器的Limits值的上限
cpu: 1
memory: 1Gi
maxLimitRequestRatio: #每种资源Requests与Limits的最大比值
cpu: 4
memory: 10
- type: PersistentVolumeClaim #指定请求PVC存储容量的最小值和最大值
min:
storage: 1Gi
max:
storage: 10Gi
[root@master ~]# kubectl apply -f limits.yaml
limitrange/limitrange created
LimitRange资源支持限制Container、Pod和PersistentVolumeClaim三种资源对象的系统资源用量
[root@master ~]# kubectl describe limitranges limitrange
[root@master ~]# more default.yaml
apiVersion: v1
kind: Pod
metadata:
name: default-pod
spec:
containers:
- image: busybox
name: busybox
args:
- /bin/sh
- -c
- sleep 60000
[root@master ~]# kubectl apply -f default.yaml
pod/default-pod created
[root@master ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
default-pod 1/1 Running 0 8s 10.244.2.43 node02 <none> <none>
limited-pod 1/1 Running 1 22h 10.244.1.81 node01 <none> <none>
limited-pod-2 1/1 Running 1 22h 10.244.1.82 node01 <none> <none>
新建pod default-pod,yaml中未指定requests和limits
容器的requests和limits与我们在LimitRange对象中设置的一致。
[root@master ~]# kubectl run cpu-over --image=busybox --restart=Never --requests='cpu=1200m,memory=30Mi' sleep 6000
The Pod "cpu-over" is invalid: spec.containers[0].resources.requests: Invalid value: "1200m": must be less than or equal to cpu limit
[root@master ~]# kubectl run cpu-over --image=busybox --restart=Never --requests='cpu=200m,memory=300Mi' sleep 6000
The Pod "cpu-over" is invalid: spec.containers[0].resources.requests: Invalid value: "300Mi": must be less than or equal to memory limit
Kubemetes可以通过存活探针(liveness probe)检查容器是否还在运行。可以为pod中的每个容器单独指定存活探针。如果探测失败,Kubemetes将定期执行探针并重新启动容器。
资源配额(Resource Quotas)是用来限制用户资源用量的一种机制,限制Pod的请求不会超过配额,需要在namespace中创建一个ResourceQuota对象
资源配额类型:
尽管LimitRange资源能限制单个容器、Pod及PVC等相关计算资源或存储资源的用量,但用户依然可以创建数量众多的此类资源对象进而侵占所有的系统资源。于是,Kubernetes提供了ResourceQuota资源用于定义名称空间的对象数量或系统资源配额。
[root@master ~]# more quota-cpu-memory.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: cpu-and-mem
spec:
hard:
requests.cpu: 1
requests.memory: 1Gi
limits.cpu: 1500m
limits.memory: 1500Mi
[root@master ~]# kubectl apply -f quota-cpu-memory.yaml
resourcequota/cpu-and-mem created
LimitRange应用于单独的pod,ResourceQuota应用于命名空间中所有的pod
[root@master ~]# kubectl describe resourcequotas cpu-and-mem
[root@master ~]# kubectl run quota-test --image=busybox --limits='cpu=200m,memory=90Mi' --replicas=10 sleep 6000
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/quota-test created
[root@master ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
default-pod 1/1 Running 0 31m 10.244.2.52 node02 <none> <none>
quota-test-5d4f79c664-2tq66 1/1 Running 0 5m44s 10.244.1.90 node01 <none> <none>
quota-test-5d4f79c664-cvptj 1/1 Running 0 5m44s 10.244.0.68 master <none> <none>
quota-test-5d4f79c664-ph79j 1/1 Running 0 5m44s 10.244.2.64 node02 <none> <none>
quota-test-5d4f79c664-tvwpz 1/1 Running 0 5m44s 10.244.2.65 node02 <none> <none>
发现只运行了4个pod,原因是requests的cpu为200(requests值未设置时与limits相同),resourcequotas中requests.cpu的限制值为1000m,系统之前使用了100m(default-pod),故只有900m可用,所以最多只能新建4个pod
[root@master ~]# more quota-count.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: count-quota
spec:
hard:
pods: 10
replicationcontrollers: 5
secrets: 10
configmaps: 10
persistentvolumeclaims: 5
services: 5
services.loadbalancers: 1
services.nodeports: 2
ssd.storageclass.storage.k8s.io/persistentvolumeclaims: 2
[root@master ~]# kubectl apply -f quota-count.yaml
resourcequota/count-quota created
该命名空间最多创建10个pod、5个Replication Controller、10个Secret、10个ConfigMap、4个PVC、5个Service、1个LoadBalancer、2个NodePort和2个StorageClass为ssd的PVC。
pod | Replication Controller | Secret | ConfigMap | PVC | Service | LoadBalancer | NodePort | ssd PVC |
---|---|---|---|---|---|---|---|---|
10 | 5 | 10 | 10 | 4 | 5 | 1 | 2 | 2 |
[root@master ~]# kubectl describe resourcequotas count-quota
[root@master ~]# kubectl create service nodeport nodeport01 --tcp=5678:8080
service/nodeport01 created
[root@master ~]# kubectl create service nodeport nodeport02 --tcp=5678:8081
service/nodeport02 created
[root@master ~]# kubectl create service nodeport nodeport03 --tcp=5678:8082
Error from server (Forbidden): services "nodeport03" is forbidden: exceeded quota: count-quota, requested: services.nodeports=1, used: services.nodeports=2, limited: services.nodeports=2
services.nodeports只能创建2个,当创建第三个时报错。
[root@master ~]# more quota-scoped.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: scoped-quota
spec:
scopes:
- BestEffort
- NotTerminating
hard:
pods: 4
[root@master ~]# kubectl apply -f quota-scoped.yaml
resourcequota/scoped-quota created
这个quota只会应用于拥有BestEffort QoS以及没有设置有效期的pod上,这样的pod只允许存在4个。
[root@master ~]# kubectl describe resourcequotas scoped-quota
resourcequota测试思路同cpu-and-mem和count-quota,这里不再赘述。
本文所有脚本和配置文件已上传github:https://github.com/loong576/k8s-Managing-pods-computational-resources.git