k8s入门教程六:POD资源限制与调度

POD资源限制

简介

容器运行过程中需要分配所需的资源,如何与cggroup联动配合呢?答案是通过定义resource来实现资源的分配,资源的分配单位主要是cpu和memory,资源的定义分两种:requests和limits,requests表示请求资源,主要用于初始kubernetes调度pod时的依据,表示必须满足的分配资源;limits表示资源的限制,即pod不能超过limits定义的限制大小,超过则通过cggroup限制,pod中定义资源可以通过下面四个字段定义:

  • spec.container[].resources.requests.cpu 请求cpu资源的大小,如0.1个cpu和100m表示分配1/10个cpu;
  • spec.container[].resources.requests.memory 请求内存大小,单位可用M,Mi,G,Gi表示;
  • spec.container[].resources.limits.cpu 限制cpu的大小,不能超过阀值,cggroup中限制的值;
  • spec.container[].resources.limits.memory 限制内存的大小,不能超过阀值,超过会发生OOM;

如下以定义nginx-demo为例,容器请求cpu资源为250m,限制为500m,请求内存资源为128Mi,限制内存资源为256Mi,当然也可以定义多个容器的资源,多个容器相加就是pod的资源总资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
name: nginx-demo
labels:
name: nginx-demo
spec:
containers:
- name: nginx-demo
image: nginx:1.7.9
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port-80
protocol: TCP
containerPort: 80
resources:
requests:
cpu: 0.25
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi

创建并查看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
[root@master ~]# kubectl apply -f 1.yaml
pod/nginx-demo created
[root@master ~]# kubectl get pods nginx-demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-demo 1/1 Running 0 79s 10.44.0.3 node1 <none> <none>
[root@master ~]# kubectl describe pods nginx-demo
...
#Containers里面有请求以及限制资源的信息,说明有效果了。
Containers:
nginx-demo:
Container ID: docker://1f99d75fb3a9ee2476c5accc89dec1fceba96f2eab8a1236cde4d9f8437f0361
Image: nginx:1.7.9
Image ID: docker-pullable://nginx@sha256:e3456c851a152494c3e4ff5fcc26f240206abac0c9d794affb40e0714846c451
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Sat, 07 Mar 2020 19:27:28 +0800
Ready: True
Restart Count: 0
Limits:
cpu: 500m
memory: 256Mi
Requests:
cpu: 250m
memory: 128Mi

Pod的资源如何分配呢?毫无疑问是从node上分配的,当我们创建一个pod的时候如果设置了requests,kubernetes的调度器kube-scheduler会执行两个调度过程:filter过滤和weight称重,kube-scheduler会根据请求的资源过滤,把符合条件的node筛选出来,然后再进行排序,把最满足运行pod的node筛选出来,然后再特定的node上运行pod。如下是node1的情况:

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
[root@master ~]# kubectl describe nodes node1
...
Capacity:
cpu: 2
ephemeral-storage: 100101372Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 1881916Ki
pods: 110
Allocatable:
cpu: 2
ephemeral-storage: 92253424283
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 1779516Ki
pods: 110
...
#节点容许分配的资源情况,部分预留的资源会排出在Allocatable范畴
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 270m (13%) 500m (25%)
memory 128Mi (7%) 256Mi (14%)
ephemeral-storage 0 (0%) 0 (0%)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning SystemOOM 99s kubelet, node1 System OOM encountered, victim process: ceph-mon, pid: 15051

资源限制的原理

Pod的定义的资源requests和limits作用于kubernetes的调度器kube-sheduler上,实际上cpu和内存定义的资源会应用在container上,通过容器上的cggroup实现资源的隔离作用,接下来我们介绍下资源分配的原理。

  • spec.containers[].resources.requests.cpu:作用在CpuShares,表示分配cpu 的权重,争抢时的分配比例
  • spec.containers[].resources.requests.memory:主要用于kube-scheduler调度器,对容器没有设置意义
  • spec.containers[].resources.limits.cpu:作用CpuQuota和CpuPeriod,单位为微秒,计算方法为:CpuQuota/CpuPeriod,表示最大cpu最大可使用的百分比,如500m表示允许使用1个cpu中的50%资源
  • spec.containers[].resources.limits.memory:作用在Memory,表示容器最大可用内存大小,超过则会OOM

以上面定义的nginx-demo为例,研究下pod中定义的requests和limits应用在docker生效的参数:

  1. 在node1上面运行如下命令。默认会有两个pod:一个通过pause镜像创建,另外一个通过应用镜像创建。
1
2
3
[root@node1 ~]# docker ps |grep nginx-demo
1f99d75fb3a9 84581e99d807 "nginx -g 'daemon of…" 13 minutes ago Up 13 minutes k8s_nginx-demo_nginx-demo_default_3af104b0-8801-4246-8579-f20806c519b8_0
f1a4045cad3d k8s.gcr.io/pause:3.1 "/pause" 14 minutes ago Up 13 minutes k8s_POD_nginx-demo_default_3af104b0-8801-4246-8579-f20806c519b8_0
  1. 查看docker容器详情信息
1
2
3
4
5
6
[root@node1 ~]# docker inspect 1f99d75fb3a9 |egrep 'CpuShares|Memory"|CpuPeriod|CpuQuota'
"CpuShares": 256, #CPU分配的权重,作用在requests.cpu上
"Memory": 268435456, #内存分配的大小,作用在limits.memory上
"CpuPeriod": 100000, #CPU分配的使用比例,和CpuQuota一起作用在limits.cpu上
"CpuQuota": 50000,
"KernelMemory": 0,

POD调度

pod调度概述

kubernets是容器编排引擎,其中最主要的一个功能是容器的调度,通过kube-scheduler实现容器的完全自动化调度,调度周期分为:调度周期Scheduling Cycle和绑定周期Binding Cycle,其中调度周期细分为过滤filter和weight称重,按照指定的调度策略将满足运行pod节点的node赛选出来,然后进行排序;绑定周期是经过kube-scheduler调度优选的pod后,由特定的node节点watch然后通过kubelet运行。

过滤阶段包含预选Predicate和scoring排序,预选是筛选满足条件的node,排序是最满足条件的node打分并排序。

指定nodeName调度

nodeName是PodSpec中的一个字段,可以通过pod.spec.nodeName指定将pod调度到某个具体的node节点上,该字段比较特殊一般都为空,如果有设置nodeName字段,kube-scheduler会直接跳过调度,在特定节点上通过kubelet启动pod。通过nodeName调度并非是集群的智能调度,通过指定调度的方式可能会存在资源不均匀的情况,建议设置Guaranteed的Qos,防止资源不均时候Pod被驱逐evince。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: nginx-run-on-nodename
annotations:
kubernetes.io/description: "Running the Pod on specific nodeName"
spec:
containers:
- name: nginx-run-on-nodename
image: nginx:latest
ports:
- name: http-80-port
protocol: TCP
containerPort: 80
nodeName: node-3 #通过nodeName指定将nginx-run-on-nodename运行在特定节点node-3上

nodeSelector节点选择

nodeSelector是PodSpec中的一个字段,nodeSelector是最简单实现将pod运行在特定node节点的实现方式,其通过指定key和value键值对的方式实现,需要node设置上匹配的Labels,节点调度的时候指定上特定的labels即可。

可以使用 kubectl label nodes node1 disktype=ssd 增加一个label。

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd

这样就会调度到node1上面了。

系统默认预先定义有多种内置的labels,这些labels可以标识node的属性,如arch架构,操作系统类型,主机名等

  • beta.kubernetes.io/arch=amd64
  • beta.kubernetes.io/os=linux
  • kubernetes.io/arch=amd64
  • kubernetes.io/hostname=node-3
  • kubernetes.io/os=linux

Node affinity节点亲和

affinity/anti-affinity和nodeSelector功能相类似,相比于nodeSelector,affinity的功能更加丰富,未来会取代nodeSelector。node亲和包括2种,affinity增加了如下的一些功能增强:

  • 表达式更加丰富,匹配方式支持多样,如In,NotIn, Exists, DoesNotExist. Gt, and Lt;
  • 通过requiredDuringSchedulingIgnoredDuringExecution来设置,为硬性选择指标,必须满足这个条件;而preference则是优选选择条件,通过preferredDuringSchedulingIgnoredDuringExecution指定,表示的意思是最好是调度到哪里去。
  • affinity提供两种级别的亲和和反亲和:基于node的node affinity和基于pod的inter-pod affinity/anti-affinity,node affinity是通过node上的labels来实现亲和力的调度,而pod affinity则是通过pod上的labels实现亲和力的调度,两者作用的范围有所不同。
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
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: k8s.gcr.io/pause:2.0

上述YAML表示 ,POD必须调度到kubernetes.io/e2e-az-name这个kEY为e2e-az1或者是e2e-az2的上面,不然就不满足需求。如果another-node-label-key这个KEY和value有匹配上的node,就优先调度到这个node。如果没有这个KEY,也没有关系。

如果你同时指定了 nodeSelector 和 nodeAffinity,两者必须都要满足,才能将 pod 调度到候选节点上。preferredDuringSchedulingIgnoredDuringExecution里面的 weight 取值范围为1~100。值越高,为最优选择。

https://kubernetes.io/docs/concepts/configuration/assign-pod-node/

pod affinity/anti-affinity

pod间的亲和与反亲和使你可以基于已经在节点上运行的pod的标签来约束 pod 可以调度到的节点,而不是基于节点上的标签。规则为:如果 X 节点上已经运行了一个或多个满足规则 Y 的pod,则这个 pod 应该(或者在非亲和的情况下不应该)运行在 X 节点。

和 Node不同,由于 Pod 都是有命名空间的,所以基于 Pod 标签的标签选择器(Label Selector)必须指定命名空间。可以通过 namespaces(与 labelSelector 和 topologyKey 同一级) 指定,默认情况下为拥有亲和性(或反亲和性)的 Pod 所属的命名空间,如果定义了 namespaces 但值是空的,则表示使用 all 命名空间。

topology 就是 拓扑 的意思,这里指的是一个 拓扑域,是指一个范围的概念,比如一个 Node、一个机柜、一个机房或者是一个地区(如杭州、上海)等,实际上对应的还是 Node 上的标签。这里的 topologyKey 对应的是 Node 上的标签的 Key(没有Value),可以看出,其实 topologyKey 就是用于筛选 Node 的。通过这种方式,我们就可以将各个 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
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: failure-domain.beta.kubernetes.io/zone
containers:
- name: with-pod-affinity
image: k8s.gcr.io/pause:2.0

这里 Pod 的亲和性规则是:这个 Pod 要调度到的 Node 必须有一个标签为 security: S1 的 Pod,且该 Node 必须有一个 Key 为 failure-domain.beta.kubernetes.io/zone 的 标签,即 Node 必须属于 failure-domain.beta.kubernetes.io/zone 拓扑域。

Pod 的反亲和性规则是:这个 Pod 尽量不要调度到这样的 Node,其包含一个 Key 为 kubernetes.io/hostname 的标签,且该 Node 上有标签为 security: S2 的 Pod。

污点与容忍

tains表示给一个node设置一个污点,这样就可以实现普通的POD就不会被调度到这个node上;而toleration表示可以容忍哪些污点,当出现不可使用的node时,就可以调度到出现这个污点的node上。

设置tains

使用 kubectl taint 命令可以给某个 Node 节点设置污点,Node被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去。每个污点的组成如下:key=value:effect。

每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。当前taint effect支持如下三个选项:

  • NoSchedule表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上
  • PreferNoSchedule表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上
  • NoExecute 表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去

NoExecute 可以用在node节点需要维护时,可以把pod全部清理;而PreferNoSchedule可以用在master节点上,当node节点资源不够时,这时pod就可以调度到master上。

设置方法

1
2
3
4
5
6
# 设置污点
kubectl taint nodes node1 key1=value1:NoSchedule
# 节点说明中,查找 Taints 字段
kubectl describe pod pod-name
# 去除污点
kubectl taint nodes node1 key1:NoSchedule-

Tolerations

设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生 互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。 但我们可以在 Pod 上设置容忍 ( Toleration ) ,意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上。

1
2
3
4
5
6
7
8
9
10
11
12
13
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerationSeconds: 3600
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "key2"
operator: "Exists"
effect: "NoSchedule"
  • key, vaule, effect 要与 Node 上设置的 taint 保持一致

  • operator 的值为 Exists 将会忽略 value 值

  • tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Pod 上继续保留运行的时间

  • 当不指定 key 值时,表示容忍所有的污点 key

    1
    2
    tolerations:
    - operator: "Exists"
  • 当不指定 effect 值时,表示容忍所有的污点作用

    1
    2
    3
    tolerations:
    - key: "key"
    operator: "Exists"

https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/

https://blog.51cto.com/newfly/2066630

https://cloud.tencent.com/developer/article/1516593

0%