Skip to content

资源和调度

一、资源配额与限制

资源配额用于管理**命名空间(NameSpace)**中对象使用的资源量,我们可以按 CPU内存用量对象数量来设置配额。通过资源配额,可以确保租户不会使用超过其分配份额的集群资源。

资源配额的工作方式如下:

  • 管理员为每个 namespace 创建一个或多个资源配额对象
  • 用户在 namespace 下创建资源 (pods、 services 等),同时配额系统会跟踪使用情况,来确保其不超过资源配额中定义的硬性资源限额
  • 如果资源的创建或更新违反了配额约束,则请求会失败,并返回 HTTP 状态码 403 FORBIDDEN,以及说明违反配额约束的信息
  • 如果 namespace 下的计算资源(如 cpu 和 memory)的配额被启用,则用户必须为这些资源设定请求值(request) 和约束值(limit),否则配额系统将拒绝 Pod 的创建。

Kubernetes 中主要有3个层级的资源配额控制:

  • 容器:可以对 CPU 和 Memory 进行限制
  • POD:可以对一个 Pod 内所有容器的的资源进行限制
  • Namespace:为一个命名空间下的资源进行限制

其中容器层次主要利用容器本身的支持,比如 Docker 对 CPU、内存等的支持;Pod 方面可以限制系统内创建 Pod 的资源范围,比如最大或者最小的 CPU、memory 需求;Namespace 层次就是对用户级别的资源限额了,包括 CPU、内存,还可以限定 Pod、RC、Service 的数量。

要使用资源配额的话需要确保 api server的 --enable-admission-plugins= 参数中包含 ResourceQuota,当 namespace 中存在一个 ResourceQuota 对象时,该 namespace 即开始实施资源配额的管理工作了,另外需要注意的是一个 namespace 中最多只应存在一个 ResourceQuota 对象

资源配额控制器支持的配额控制资源主要包括:计算资源配额存储资源配额对象数量资源配额

1. 计算资源配额

在namespace下我们可以针对性的设置计算资源配额,有两种方式:ResourceQuotaLimitRange

资源名称资源类型描述
limits.cpucpu所有容器的cpu 请求资源总和
limits.memorymemory所有容器的内存 请求资源总和
requests.cpucpu所有容器的cpu 限制资源总和
requests.memorymemory所有容器的内存 限制资源总和

在创建pod时,无论是 请求资源超出限制 或者是 限制资源超出限制,都无法创建成功!!!

ResourceQuota

下面是一个演示案例

这里要注意的是:

我们在 resourcequota中 设胜多负少的置 cpu 和 memory 的格式 要和 pod 文件中 cpu、 memory 的格式保存统一。

比如: resourcequota memory 赋值时 我们是用的 "300Mi", 那么我们在pod 中配置 memory 时 也应该用相同的格式 "xxMi"

shell
# 首先我们新建一个namespace
kubectl create ns  tmp-quota

# 创建 计算资源配额 文件
vim quota-mem-cpu.yaml
#-------------------------------
apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota-test1
  namespace: tmp-quota
spec:
  hard: # 硬件配额
    requests.cpu: "1"   # cpu 请求资源配额总和,这里有两个单位 100m = 0.1 cpu;1000m cpu = 1 cpu; 精度1m;
    requests.memory: "300Mi"   # 内存 请求资源配额总和,这里和我们平时使用的单位一样,默认是 byte,也可以用 E、P、T、G、M、K为单位后缀或Ei、Pi...
    limits.cpu: "2"   # cpu 限制资源总和
    limits.memory: "600Mi" # 内存 限制资源总和
#-------------------------------

# 运行
kubectl apply -f quota-mem-cpu.yaml
#---------------------------------
resourcequota/quota-test1 created
#---------------------------------

# 然后我们查看资源详情
# kubectl describe quota 资源文件name属性 -n namespace名称
kubectl describe quota quota-test1 -n tmp-quota
#---------------------------------
Name:            quota-test1
Namespace:       tmp-quota
Resource         Used  Hard
--------         ----  ----
limits.cpu       0     2
limits.memory    0     600m
requests.cpu     0     1
requests.memory  0     300m
#---------------------------------

# 而后我们创建一个pod 并申请限额
vim quta-pod-test1.yaml
#---------------------------------
apiVersion: v1
kind: Pod
metadata:
  name: quta-pod-test1
  namespace: tmp-quota
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
      	cpu: 1000m
        memory: 200M
      requests:
      	cpu: 300m
        memory: 150M
#---------------------------------

# 启动pod
kubectl apply -f quta-pod-test1.yaml

# 我们查看 资源占用情况
# 通过describe查询
kubectl describe quota quota-test1 -n tmp-quota
#---------------------------------
Name:            quota-test1
Namespace:       tmp-quota
Resource         Used   Hard
--------         ----   ----
limits.cpu       1      2
limits.memory    200Mi  600Mi
requests.cpu     800m   1500m
requests.memory  150Mi  500Mi
#---------------------------------

# 通过 get resourcequota查询
kubectl get resourcequota -n tmp-quota
#---------------------------------
NAME          AGE    REQUEST                                                  LIMIT
quota-test1   108m   requests.cpu: 800m/1500m, requests.memory: 150Mi/500Mi   limits.cpu: 1/2, limits.memory: 200Mi/600Mi
#---------------------------------

# 下面我们看一下不满足资源限制的情况

# 我们新建一个 quta-pod-test2.yaml
vim quta-pod-test2.yaml
#-------------------------
apiVersion: v1
kind: Pod
metadata:
  name: quta-pod-test2
  namespace: tmp-quota
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: "1000m"
        memory: "200Mi"
      requests:
        cpu: "800m"
        memory: "550Mi" # 这里我们的 request.memory 超过了 资源限制 "300Mi"
#-------------------------

# 创建pod
kubectl apply -f quta-pod-test2.yaml
# 报错信息如下
#-------------------------
The Pod "quta-pod-test2" is invalid: spec.containers[0].resources.requests: Invalid value: "550Mi": must be less than or equal to memory limit
#-------------------------

ResourceQuota 用来限制 namespace 中所有的 Pod 占用的总的资源 request 和 limit

LimitRange

而实际场景中 我们会出现这样的情况:

  • 我们要对单个容器进行配额的限制
  • 我们新建了一个namespace 并未他设置了配额,但是pod 并没有配置request 和limit,这样我们也无法成功创建pod。

使用LimitRange 我们就可以很好的解决上述问题。

LimitRange 用来限制 namespace 中 单个Pod 默认资源 request 和 limit

shell
# 首先我们新建一个namespace
kubectl create ns tmp-quota-range

# 新建 limitRange 资源文件
vim quota-range.yaml
#-------------------------
apiVersion: v1
kind: LimitRange
metadata:
  name: quta-limit-range
  namespace: tmp-quota-range
spec:
  limits:
    - default:
        memory: "130Mi"   # default  limits.memory = 130Mi
        cpu: "600m"
      defaultRequest:
        cpu: "550m"
        memory: "110Mi"   # default request.memory = 110Mi
      max:
        cpu: "800m"
        memory: "200Mi"   # max memory = 200Mi
      min:
        cpu: "500m"
        memory: "100Mi"  # min memory = 100Mi
      type: Container
#-------------------------

# 生效配置
kubectl apply -f quota-range.yaml

# 然后我们查看 describe
kubectl describe limits quta-limit-range -n tmp-quota-range
#-------------------------
Name:       quta-limit-range
Namespace:  tmp-quota-range
Type        Resource  Min    Max    Default Request  Default Limit  Max Limit/Request Ratio
----        --------  ---    ---    ---------------  -------------  -----------------------
Container   cpu       500m   800m   550m             600m           -
Container   memory    100Mi  200Mi  110Mi            130Mi          -
#-------------------------


# 新建一个pod.yaml
vim quta-pod-test3.yaml
#-------------------
apiVersion: v1
kind: Pod
metadata:
  name: quta-pod-test3
  namespace: tmp-quota-range
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr2
    image: nginx
#------------------------

# 运行
kubectl apply -f quta-pod-test3.yaml

# 然后我们检查pod describe
kubectl describe pod quta-pod-test3 -n tmp-quota-range
#------------------------
Name:         quta-pod-test3
Namespace:    tmp-quota-range
Priority:     0
Node:         k8s-slave03/192.168.56.107
Start Time:   Tue, 05 Jul 2022 15:29:46 +0800
Labels:       <none>
Annotations:  cni.projectcalico.org/containerID: bd8322be8529794f3350dcd4ecc5aa579e496ca384a2bfbc990c0f14c768bf55
              cni.projectcalico.org/podIP: 10.244.211.196/32
              cni.projectcalico.org/podIPs: 10.244.211.196/32
              kubernetes.io/limit-ranger:
                LimitRanger plugin set: cpu, memory request for container quota-mem-cpu-demo-ctr2; cpu, memory limit for container quota-mem-cpu-demo-ctr2
Status:       Running
IP:           10.244.211.196
IPs:
  IP:  10.244.211.196
Containers:
  quota-mem-cpu-demo-ctr2:
    Container ID:   docker://68ece5793c03b4d7aae8a37977cdb389509ec8e24b3f10fb7be498d691866efd
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Tue, 05 Jul 2022 15:29:48 +0800
    Ready:          True
    Restart Count:  0
    # 看这里,pod 使用了默认的 limit 和 request 配置 ---------------------
    Limits:
      cpu:     600m
      memory:  130Mi
    Requests:
      cpu:        550m
      memory:     110Mi
     # --------------------------------------------------------------  
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-plgww (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  kube-api-access-plgww:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   Burstable
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  53s   default-scheduler  Successfully assigned tmp-quota-range/quta-pod-test3 to k8s-slave03
  Normal  Pulling    52s   kubelet            Pulling image "nginx"
  Normal  Pulled     51s   kubelet            Successfully pulled image "nginx" in 927.414978ms
  Normal  Created    51s   kubelet            Created container quota-mem-cpu-demo-ctr2
  Normal  Started    51s   kubelet            Started container quota-mem-cpu-demo-ctr2
#------------------------

2. 存储资源配额

PersistentVolume (PV) 是外部存储系统中的一块存储空间,由管理员创建和维护。与 Volume 一样,PV 具有持久性,生命周期独立于 Pod。

PersistentVolumeClaim (PVC) 是对 PV 的申请 (Claim)。PVC 通常由普通用户创建和维护。需要为 Pod 分配存储资源时,用户可以创建一个 PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes 会查找并提供满足条件的 PV。

有了 PersistentVolumeClaim,用户只需要告诉 Kubernetes 需要什么样的存储资源,而不必关心真正的空间从哪里分配,如何访问等底层细节信息。这些 Storage Provider 的底层信息交给管理员来处理,只有管理员才应该关心创建 PersistentVolume 的细节信息。

资源名称描述
requests.storage所有的pvc中,存储资源的需求不能超过该值
persistentvolumeclaimsnamespace中所允许的pvc总量
<storage-class-name>.storageclass.storage.k8s.io/requests.storage所有该storage-class-name相关的pvc中,存储资源的需求不能超过改值
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaimsnamespace中所允许的该storage-class-name相关的pvc的总量

这里我们简单带过,只演示下核心的资源文件,使用方法和 计算资源配额 类似

shell
# 不管多少个PVC,现在只要pvc总和请求量超过10G,就不会让其创建了
[root@master volume]# cat storage-resource.yaml 
apiVersion: v1
kind: ResourceQuota
metadata:
  name: storage-resources
  namespace: dev1
spec:
  hard:
    requests.storage: "10G" # 设置pvc 10G
    # managed-nfs-storage.storageclass.storage.k8s.io/requests.storage: "5G"
[root@master volume]# kubectl apply -f storage-resource.yaml 
resourcequota/storage-resources created
[root@master volume]# kubectl get quota -n dev1
NAME                AGE   REQUEST                   LIMIT
storage-resources   12s   requests.storage: 0/10G   
 
 
[root@master volume]# cat pvc-nfs-dy.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
  namespace: dev1
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests: 
      storage: 11Gi  
  storageClassName: managed-nfs-storage
 
[root@master volume]# kubectl apply -f  pvc-nfs-dy.yaml 
Error from server (Forbidden): error when creating "pvc-nfs-dy.yaml": persistentvolumeclaims "nfs-pvc" is forbidden: exceeded quota: storage-resources, requested: requests.storage=11Gi, used: requests.storage=0, limited: requests.storage=10G

3. 对象数量资源配额

这个就比较好理解了,我们可以限制 podsdeploymentservices configmaps 等的数量

shell
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
  namespace: test
spec:
hard:
  pods: "10"
  count/deployments.apps: "3"
  count/services: "3"

二、资源调度

在k8s上有一个非常重要的组件kube-scheduler,它主要作用是监听apiserver上的pod资源中的nodename字段是否为空,如果该字段为空就表示对应pod还没有被调度,此时kube-scheduler就会从k8s众多节点中,根据pod资源的定义相关属性,从众多节点中挑选一个最佳运行pod的节点,并把对应主机名称填充到对应pod的nodename字段,然后把pod定义资源存回apiserver;此时apiserver就会根据pod资源上的nodename字段中的主机名,通知对应节点上的kubelet组件来读取对应pod资源定义,kubelet从apiserver读取对应pod资源定义清单,根据资源清单中定义的属性,调用本地docker把对应pod运行起来;然后把pod状态反馈给apiserver,由apiserver把对应pod的状态信息存回etcd中;整个过程,kube-scheduler主要作用是调度pod,并把调度信息反馈给apiserver,那么问题来了,kube-scheduler它是怎么评判众多节点哪个节点最适合运行对应pod的呢?

在k8s上调度器的工作逻辑是根据调度算法来实现对应pod的调度的;不同的调度算法,调度结果也有所不同,其评判的标准也有所不同,当调度器发现apiserver上有未被调度的pod时,它会把k8s上所有节点信息,挨个套进对应的预选策略函数中进行筛选,把不符合运行pod的节点淘汰掉,我们把这个过程叫做调度器的预选阶段(Predicate);剩下符合运行pod的节点会进入下一个阶段优选(Priority),所谓优选是在这些符合运行pod的节点中根据各个优选函数的评分,最后把每个节点通过各个优选函数评分加起来,选择一个最高分,这个最高分对应的节点就是调度器最后调度结果,如果最高分有多个节点,此时调度器会从最高分相同的几个节点随机挑选一个节点当作最后运行pod的节点;我们把这个这个过程叫做pod选定过程(select);简单讲调度器的调度过程会通过三个阶段,第一阶段是预选阶段,此阶段主要是筛选不符合运行pod节点,并将这些节点淘汰掉;第二阶段是优选,此阶段是通过各个优选函数对节点评分,筛选出得分最高的节点;第三阶段是节点选定,此阶段是从多个高分节点中随机挑选一个作为最终运行pod的节点;大概过程如下图所示 资源调度

提示:预选过程是一票否决机制,只要其中一个预选函数不通过,对应节点则直接被淘汰;剩下通过预选的节点会进入优选阶段,此阶段每个节点会通过对应的优选函数来对各个节点评分,并计算每个节点的总分;最后调度器会根据每个节点的最后总分来挑选一个最高分的节点,作为最终调度结果;如果最高分有多个节点,此时调度器会从对应节点集合中随机挑选一个作为最后调度结果,并把最后调度结果反馈给apiserver;

影响调度的因素:

  • nodeName
  • nodeSelector: 选择节点
  • nodeAffinity:节点亲和性
  • podAffinity:pod亲和性
  • taints:污点
  • tolerations:容忍

解析

集群调度原理

Scheduler调度步骤
  1. 首先用户在通过 Kubernetes 客户端 Kubectl 提交创建 Pod 的 Yaml 的文件,向Kubernetes 系统发起资源请求,该资源请求被提交到Kubernetes 系统。

  2. Kubernetes 系统中,用户通过命令行工具 Kubectl 向 Kubernetes 集群即用 API Server 的方式发送“POST”请求,即创建 Pod 的请求。

  3. API Server 接收到请求后把创建 Pod 的信息存储到 Etcd 中,从集群运行那一刻起,资源调度系统Scheduler 就会定时去监控 API Server

  4. 通过 API Server 得到创建 Pod 的信息,Scheduler 采用 watch 机制,一旦 Etcd 存储 Pod 信息成功便会立即通知 API Server

  5. API Server 会立即把Pod创建的消息通知Scheduler,Scheduler发现 Pod 的属性中 Dest Node 为空时(Dest Node="")便会立即触发调度流程进行调度。

  6. 而这一个创建Pod对象,在调度的过程当中有3个阶段:节点预选、节点优选、节点选定,从而筛选出最佳的节点

    • 节点预选:基于一系列的预选规则对每个节点进行检查,将那些不符合条件的节点过滤,从而完成节点的预选
    • 节点优选:对预选出的节点进行优先级排序,以便选出最合适运行Pod对象的节点
    • 节点选定:从优先级排序结果中挑选出优先级最高的节点运行Pod,当这类节点多于1个时,则进行随机选择
集群调度策略

Kubernetes调度器作为集群的大脑,在如何提高集群的资源利用率、保证集群中服务的稳定运行中也会变得越来越重要Kubernetes的资源分为两种属性。

  1. 可压缩资源(例如CPU循环,Disk I/O带宽)都是可以被限制和被回收的,对于一个Pod来说可以降低这些资源的使用量而不去杀掉Pod。
  2. 不可压缩资源(例如内存、硬盘空间)一般来说不杀掉Pod就没法回收。未来Kubernetes会加入更多资源,如网络带宽,存储IOPS的支持。

集群调度特别复杂,有各种各样的规则。调度器会根据各种规则计算出一个分数,根据分数的大小来选择节点。

常用预选策略
预选策略作用
CheckNodeCondition检查节点网络、磁盘等是否正常
HostName如果Pod对象拥有spec.hostname属性,则检查节点名
称字符串是否和该属性值匹配。
PodFitsHostPortsPod的spec.hostPort属性时,检查端口是否被占用
MatchNodeSelectorPod的spec.nodeSelector属性时,检查节点标签
NoDiskConflictPod依赖的存储卷在此节点是否可用
PodFitsResources检查节点上的资源(CPU、内存)可用性是否满足Pod对
象的运行需求。
PodToleratesNodeTaintsPod的spec.tolerations属性,仅关注NoSchedule和
NoExecute两个效用标识的污点
PodToleratesNodeNoExecuteTaintsPod的spec.tolerations属性,是否能接纳节点的
NoExecute类型污点,默认没有启用
CheckNodeLabelPresence仅检查节点上指定的所有标签的存在性,默认没有启用
CheckServiceAffinity将相同Service的Pod对象放置在同一个或同一类节点上
以提高效率,默认没有启用
MaxEBSVolumeCount检查节点已挂载的EBS(亚马逊弹性块存储)存储卷数量是
否超过设置的最大值,默认为39
MaxGCEPDVolumeCount检查节点上已挂载的GCE PD(谷歌云存储) 存储卷数量
是否超过最大值,默认为16
MaxAzureDiskVolumeCount检查节点上已挂载的Azure Disk存储卷数量是否超过最
大值,默认为16
CheckVolumeBinding检查节点上已绑定和未绑定的PVC是否满足需求
NoVolumeZoneConflict在给定区域zone限制下,检查此节点部署的Pod对象是
否存在存储卷冲突
CheckNodeMemoryPressure检查节点内存压力,如果压力过大,那就不会讲pod调度
至此
CheckPodePIDPressure检查节点PID资源压力
CheckNodeDiskPressure检查节点磁盘资源压力
MatchInterPodAffinity检查节点是否满足Pod对象亲和性或反亲和性条件
常用优先函数
函数名称说明
LeastRequestedPriority节点的优先级就由节点空闲资源与节点总容量的比值,即由(总容量-节点上
Pod的容量总和-新Pod的容量)/总容量)来决定。 CPU和内存具有相同权
重,资源空闲比越高的节点得分越高。 cpu((capacity –
sum(requested)) * 10 / capacity) + memory((capacity –
sum(requested)) * 10 / capacity) / 2
BalancedResourceAllocationCPU和内存使用率越接近的节点权重越高,该策略不能单独使用,必须和
LeastRequestedPriority组合使用,尽量选择在部署Pod后各项资源更均衡
的机器。 如果请求的资源(CPU或者内存)需求大于节点的capacity,那么
该节点永远不会被调度到。
InterPodAffinityPriority通过迭代 weightedPodAffinityTerm 的元素计算和,并且如果对该节点满足
相应的PodAffinityTerm,则将 “weight” 加到和中,具有最高和的节点是最
优选的。
SelectorSpreadPriority为了更好的容灾,对同属于一个service、replication controller或者replica
的多个Pod副本,尽量调度到多个不同的节点上。 如果指定了区域,调度器
则会尽量把Pod分散在不同区域的不同节点上。当一个Pod的被调度时,会
先查找Pod对于的service或者replication controller, 然后查找service或
replication controller中已存在的Pod,运行Pod越少的节点的得分越高。本
质就是往运行同类pod少的节点上分配。
NodeAffinityPriority亲和性机制。Node Selectors(调度时将pod限定在指定节点上), 支持多
种操作符(In, NotIn, Exists, DoesNotExist, Gt, Lt),而不限于对节点labels
的精确匹配。 另外支持两种类型的选择器,一种是
“hard(requiredDuringSchedulingIgnoredDuringExecution)”选择器,
它保证所选的主机必须满足所有Pod对主机的规则要求。 这种选择器更像是
之前的nodeselector,在nodeselector的基础上增加了更合适的表现语法。
另一种是“soft(preferresDuringSchedulingIgnoredDuringExecution)”选
择器, 它作为对调度器的提示,调度器会尽量但不保证满足NodeSelector的
所有要求。
NodePreferAvoidPodsPriority(权重1W)如果 节点的 Anotation (注解信息)没有设置 key-value:scheduler.
alpha.kubernetes.io/ preferAvoidPods = "...",则节点对该 policy 的得分
就是10分, 加上权重10000,那么该node对该policy的得分至少10W分。如
果Node的Anotation设置了,
scheduler.alpha.kubernetes.io/preferAvoidPods = "..." ,如果该 pod 对应
的 Controller 是 ReplicationController 或 ReplicaSet, 则该 node 对该
policy 的得分就是0分。
TaintTolerationPriority使用 Pod 中 tolerationList 与 节点 Taint 列表项进行匹配,配对成功的项越
多,则得分越低。污点越匹配,得分越低
ImageLocalityPriority根据Node上是否存在一个pod的容器运行所需镜像大小对优先级打分,分值
为0-10。遍历全部Node, 如果某个Node上pod容器所需的镜像一个都不存
在,分值为0; 如果Node上存在Pod容器部分所需镜像,则根据满足当前需
求的镜像的大小来决定分值,镜像越大,分值就越高;如果Node上存在pod
所需全部镜像,分值为10。默认没有启用
EqualPriority是一个优先级函数,它给予所有节点相等权重。
MostRequestedPriority在 ClusterAutoscalerProvider 中,替换 LeastRequestedPriority,给使用
多资源的节点,更高的优先级。 计算公式为: (cpu(10 sum(requested)
/ capacity) + memory(10 sum(requested) / capacity)) / 2 默认没
有启用

1. label

label是kubernetes核心概念之一,以key / value 的形式附加到各种对象上,如PodServiceDeploymentNode等。达到识别对象,关联关系等目的。

查看 label

shell
# 通常我们在kubectl 命令上追加`--show-labels` 命令即可
# 下面我们演示一下常用对象的label 查看情况

# 查看node label
kubectl get node --show-labels
#--------------------------
NAME           STATUS                     ROLES                  AGE     VERSION   LABELS
k8s-master01   Ready,SchedulingDisabled   control-plane,master   7d16h   v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master01,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-slave02    Ready                      <none>                 6d22h   v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-slave02,kubernetes.io/os=linux
k8s-slave03    Ready                      <none>                 6d21h   v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-slave03,kubernetes.io/os=linux
#--------------------------

# 查看 pods label
kubectl get pods --show-labels
#--------------------------

NAME                               READY   STATUS    RESTARTS   AGE     LABELS
demonsetdemo-bgtgc                 1/1     Running   0          40h     app=demonsetdemo,controller-revision-hash=84978464f6,pod-template-generation=1
demonsetdemo-xnnf6                 1/1     Running   0          40h     app=demonsetdemo,controller-revision-hash=84978464f6,pod-template-generation=1
deploymentdemo1-5bc649f558-92bc9   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-fwhdr   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-gmpzz   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-pzvmd   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-qtt4r   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-slg5r   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-vskqr   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-xtp9s   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-xwt2s   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
deploymentdemo1-5bc649f558-z5xhr   1/1     Running   0          41h     app=deploymentdemo1,pod-template-hash=5bc649f558
nginx-app-74d589986c-bdf66         1/1     Running   0          5d15h   app=nginx,pod-template-hash=74d589986c
nginx-app-74d589986c-gfmsd         1/1     Running   0          5d15h   app=nginx,pod-template-hash=74d589986c
nginx-app-74d589986c-kdq6r         1/1     Running   0          5d15h   app=nginx,pod-template-hash=74d589986c
nginx-app-74d589986c-p4mbl         1/1     Running   0          5d16h   app=nginx,pod-template-hash=74d589986c
nginx-app-74d589986c-slszp         1/1     Running   0          5d15h   app=nginx,pod-template-hash=74d589986c
#--------------------------

# 查看deployment label
kubectl get deployment --show-labels
#--------------------------
NAME              READY   UP-TO-DATE   AVAILABLE   AGE     LABELS
deploymentdemo1   10/10   10           10          45h     app=deploymentdemo1
nginx-app         5/5     5            5           5d16h   <none>
#--------------------------

# 查看service label
kubectl get service --show-labels
#--------------------------
NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE     LABELS
deploymentdemo1-service   NodePort    10.222.58.200    <none>        8002:32002/TCP   44h     app=deploymentdemo1
kubernetes                ClusterIP   10.222.0.1       <none>        443/TCP          7d16h   component=apiserver,provider=kubernetes
nginx-app-service         NodePort    10.222.191.172   <none>        8001:32001/TCP   4d21h   app=nginx-app
#--------------------------

# 查看namespace label
kubectl get ns --show-labels
#--------------------------
NAME                   STATUS   AGE     LABELS
default                Active   7d17h   kubernetes.io/metadata.name=default
kube-node-lease        Active   7d17h   kubernetes.io/metadata.name=kube-node-lease
kube-public            Active   7d17h   kubernetes.io/metadata.name=kube-public
kube-system            Active   7d17h   kubernetes.io/metadata.name=kube-system
kubernetes-dashboard   Active   7d15h   kubernetes.io/metadata.name=kubernetes-dashboard
tmp-quota              Active   19h     kubernetes.io/metadata.name=tmp-quota
tmp-quota-range        Active   17h     kubernetes.io/metadata.name=tmp-quota-range
#--------------------------

设置 label

shell
# 我们先选择一个pods 节点
# NAME                               READY   STATUS    RESTARTS   AGE     LABELS
# nginx-app-74d589986c-slszp         1/1     Running   0          5d16h   app=nginx,pod-template-hash=74d589986c

# 然后我们对该节点,设置label
# 语法: kubectl label 对象类型 对象名称 key=value
kubectl label pods nginx-app-74d589986c-slszp myCustomLabel=abc123
#---------------------------
pod/nginx-app-74d589986c-slszp labeled
#---------------------------

# 我们查询验证下
kubectl get pods --show-labels
#---------------------------
NAME                               READY   STATUS    RESTARTS   AGE     LABELS
nginx-app-74d589986c-slszp         1/1     Running   0          5d16h   app=nginx,myCustomLabel=abc123,pod-template-hash=74d589986c
#---------------------------

# 我们发现在 LABELS 项中 多了 我们自定义添加的这一项`myCustomLabel=abc123`

修改 label

shell
# 修改label 与 设置label 一样,只需要在 最后加上`--overwrite`即可
kubectl label pods nginx-app-74d589986c-slszp myCustomLabel=abc456 --overwrite

# 修改后
kubectl get pods --show-labels
#---------------------------
NAME                               READY   STATUS    RESTARTS   AGE     LABELS
nginx-app-74d589986c-slszp         1/1     Running   0          5d16h   app=nginx,myCustomLabel=abc456,pod-template-hash=74d589986c
#---------------------------

删除 label

shell
# 删除label 也很简单,我们只要把 label 的value替换成`-`即可
kubectl label pods nginx-app-74d589986c-slszp myCustomLabel-
#---------------------------
pod/nginx-app-74d589986c-slszp unlabeled
#---------------------------

# 验证
kubectl get pods --show-labels
#---------------------------
nginx-app-74d589986c-slszp         1/1     Running   0          5d16h   app=nginx,pod-template-hash=74d589986c
#---------------------------

2. nodeName

nodeName是节点选择约束的最简单形式,但是由于其限制,通常很少使用它。nodeName是PodSpec的领域。

正常调度我们是不会在 master 节点上创建pod的

nodeName用于选择节点的一些限制是:

  • 如果指定的节点不存在,则容器将不会运行,并且在某些情况下可能会自动删除。
  • 如果指定的节点没有足够的资源来容纳该Pod,则该Pod将会失败,并且其原因将被指出,例如OutOfmemory或OutOfcpu。
  • 云环境中的节点名称并非总是可预测或稳定的。
  • 如果指定的节点不存在,容器不会运行,一直处于Pending状态

资源配置文件

shell
vim scheduler_node-name.yml
#---------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-name-test
  labels:
    app: node-name-test
spec:
  replicas: 3
  template:
    metadata:
      name: node-name-test
      labels:
        app: node-name-test
    spec:
      containers:
        - name: node-name-test
          image: nginx
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      nodeName: k8s-master01 # 指定node节点
  selector:
    matchLabels:
      app: node-name-test
#---------------------------
# 执行
kubectl apply -f scheduler_node-name.yml

# 查询验证
kubectl get pods -o wide
#---------------------------

NAME                               READY   STATUS    RESTARTS   AGE     IP               NODE           NOMINATED NODE   READINESS GATES
node-name-test-7fd854bfc9-4l2kd    1/1     Running   0          12s     10.244.32.135    k8s-master01   <none>           <none>
node-name-test-7fd854bfc9-hgvzf    1/1     Running   0          12s     10.244.32.136    k8s-master01   <none>           <none>
node-name-test-7fd854bfc9-vr995    1/1     Running   0          12s     10.244.32.134    k8s-master01   <none>           <none>
#---------------------------

3. nodeSelector

nodeSelector是节点选择约束的最简单推荐形式。nodeSelector是PodSpec的领域。它指定键值对的映射(label)。

资源配置文件

shell
# 我们先简述下操作流程:
# 演示业务场景: 我们把磁盘io高的服务,部署到有ssd的node节点上。
# > step 1: 我们针对node `k8s-slave02` 设置label,标识当前节点是 ssd
# > step 2: 我们创建deployment资源文件,并指定`nodeSelector=ssd`
# > step 3: 查询验证,是否所有的pod 都运行在 node `k8s-slave02`上

# step 1
kubectl label nodes k8s-slave02 disk=ssd
# 验证
kubectl get nodes --show-labels
# ---------------------
NAME           STATUS                     ROLES                  AGE     VERSION   LABELS
k8s-master01   Ready,SchedulingDisabled   control-plane,master   7d22h   v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master01,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-slave02    Ready                      <none>                 7d3h    v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disk=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-slave02,kubernetes.io/os=linux
k8s-slave03    Ready                      <none>                 7d2h    v1.23.8   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-slave03,kubernetes.io/os=linux
# ---------------------

# step 2
vim scheduler_node-selector.yml
# ---------------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-selector-test
  labels:
    app: node-selector-test
spec:
  replicas: 5
  template:
    metadata:
      name: node-selector-test
      labels:
        app: node-selector-test
    spec:
      containers:
        - name: node-selector-test
          image: nginx
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      # nodeSelector 选择 disk = ssd
      nodeSelector:
        disk: ssd
  selector:
    matchLabels:
      app: node-selector-test
# ---------------------

# 执行
kubectl apply -f scheduler_node-selector.yml

# step 3
kubectl get pods -o wide
# ---------------------

NAME                                  READY   STATUS    RESTARTS   AGE     IP               NODE          NOMINATED NODE   READINESS GATES
node-selector-test-8485477cbf-492lg   1/1     Running   0          11s     10.244.220.252   k8s-slave02   <none>           <none>
node-selector-test-8485477cbf-8vgx8   1/1     Running   0          11s     10.244.220.250   k8s-slave02   <none>           <none>
node-selector-test-8485477cbf-d2gwf   1/1     Running   0          11s     10.244.220.251   k8s-slave02   <none>           <none>
node-selector-test-8485477cbf-nzmbp   1/1     Running   0          11s     10.244.220.249   k8s-slave02   <none>           <none>
node-selector-test-8485477cbf-r8bk8   1/1     Running   0          11s     10.244.220.248   k8s-slave02   <none>           <none>
# ---------------------

# 验证通过,我们发现所有的 pod 都运行在了 ssd 节点 node `k8s-slave02` 上

4. nodeAffinity 亲和性

nodeAffinity 比 nodeSelector 更灵活。它可以在nodeSelector的基础上进行一些简单的逻辑组合。不只是简单的匹配。调度可以分成软策略硬策略两种。其中逻辑支持操作符

节点亲和性规则

required(硬亲和性,硬策略,不能商量,必须执行) 、preferred(软亲和性,软策略,可以商量,选择执行)。

  • 硬亲和性规则不满足时,Pod会置于Pending状态,软亲和性规则不满足时,会选择一个不匹配的节点
  • 当节点标签改变而不再符合此节点亲和性规则时,不会将Pod从该节点移出,仅对新建的Pod对象生效

nodeAffinity可对应的两种策略: preferredDuringSchedulingrequiredDuringScheduling

类型描述
requiredDuringSchedulingpod资源在配置中声明一种标签,只有node节点跟我声明的标签一致,pod才能
被调度到此节点,如果没有匹配上,那我就一直等有匹配上的节点。
preferredDuringSchedulingpod资源在配置中声明一种标签,只有node节点跟我声明的标签一致,pod才能
被调度到此节点,但如果没有匹配上,那我就不等了,随机找节点。
IgnoredDuringExecution如果pod已经部署到此节点,但如果此节点labels发生变化,已经运行的pod会
怎么办?pod也会继续运行,直到停止此pod生命周期。
RequiredDuringExecution如果pod已经部署到此节点,但如果此节点labels发生变化,已经运行的pod会
怎么办?立刻停止生命周期,pod会重新调度到满足条件的节点。

如果同时部署硬策略、软策略,当然两种策略都会执行,但是硬策略会苛刻一些,所以必须满足硬策略的节点,然后在这些节点中选择满足软策略的节点,但如果软策略一个都不满足,触发软策略的忽略这个软策略规则,让默认调度算法,调度这个满足硬策略的节点,直到遇到同时满足硬策略、软策略。

硬策略

一个pod资源声明了一个策略,这个策略中可以写限制条件,比如标签,在此pod资源交付到k8s集群后,他会在集群中所有节点找到符合我这个策略的节点,比如定义的标签,如果在所有节点中找到符合我定义的标签,我就在这个节点部署我的pod,如果所有节点都没有满足条件的话,就不断重试直到遇见满足条件的node节点为止,不然这个pod就别启动。将一直处于挂起,直到有符合要求才会在匹配上的节点上创建pod。硬策略适用于 pod 必须运行在某种节点,否则会出现问题的情况,比如集群中节点的架构不同,而运行的服务必须依赖某种架构提供的功能。

  • 方式一:Pod使用 spec.nodeSelector (基于等值关系);Pod使用 spec.nodeName
  • 方式二:Pod使用 spec.affinity 支持matchExpressions属性 (复杂标签选择机制)

requiredDuringScheduling 表示pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试。其中IgnoreDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,pod也会继续运行,直到停止此pod生命周期。

requiredDuringScheduling 表示pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试。其中RequiredDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,停止此pod生命周期,重新选择符合要求的节点。

软策略

跟硬策略一样,都是pod资源声明了一个策略,这个策略中可以写限制条件,比如标签,如果有节点能满足我的条件,就在此节点部署pod,但是如果所有节点全部没有满足调度要求的话,POD 就会忽略这条规则,通过默认的Scheduler调度算法,进行pod部署,直到有符合要求node节点,才会重新按照软策略匹配到符合的节点上创建pod,说白了就是满足条件最好了,没有的话也无所谓了的策略,此调度适用于服务最好运行在某个区域,减少网络传输等。这种区分是用户的具体需求决定的,并没有绝对的技术依赖。

  • 柔性控制逻辑,当条件不满足时,能接受被编排于其他不符合条件的节点之上
  • 权重 weight 定义优先级,1-100 值越大优先级越高

preferredDuringScheduling 表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。其中IgnoreDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,pod也会继续运行,直到停止此pod生命周期。

preferredDuringScheduling 表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。其中RequiredDuringExecution表示如果后面节点标签发生了变化,满足了条件,停止此pod生命周期,重新调度到满足条件的节点。

操作符

  • In: label的值在某个列表中
  • NotIn:label的值不在某个列表中
  • Exists:某个label存在
  • DoesNotExist:某个label不存在
  • Gt:label的值大于某个值(字符串比较)
  • Lt:label的值小于某个值(字符串比较)

资源配置文件

shell
# 节点亲和性,相对来说要复杂一些。也更灵活。
# 完整的文件太多,这里我们只挑重点。
# 推荐参考文章: https://blog.csdn.net/Jerry00713/article/details/124003264

# 硬策略(硬亲和性)
# pod.sepc.affinity
#----------------------------
    affinity:
        nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                    - matchExpressions:
                        - key: kubernetes.io/hostname #node节点的标签
                          operator: In
                          values:
                        	- k8s-node05 #集群真实节点名称

#----------------------------

# 软亲和性
# pod.sepc.affinity
#----------------------------
...
    affinity:
        nodeAffinity:
            preferredDuringSchedulingIgnoredDuringExecution:
                - preference:
                	matchExpressions:
                        - key: kubernetes.io/hostname
                          operator: In
                          values:
                        - k8s-node02
                weight: 1 # 配置权重分
#----------------------------

5. podAffinity 亲和性

podAffinity 和 nodeAffinity 相似。

同样 适用上述:硬亲和性软亲和性操作符

pod硬亲和性调度

Pod亲和性描述一个Pod与具有某特征的现存Pod运行位置的依赖关系;即需要事先存在被依赖的pod对象。

pod软亲和性调度

pod软亲和性调度用于 分散同一类应用,调度至不同区域、机架、节点等。

6. taints 污点与容忍

上的键值型属性数据,用于让节点拒绝将Pod调度运行于其上,除非Pod有接纳节点污点的容忍度。上的键值属性数据,用于配置可容忍的污点,且调度器将Pod调度至其能容忍该节点污点的节点上或没有污点的节点上 。

对于nodeAffinity无论是硬策略(硬亲和)还是软策略(软亲和)方式,都是调度 pod 到预期节点上,而Taints恰好与之相反,如果一个节点标记为 Taints ,除非 pod 也被标识为可以容忍污点节点,否则该Taints 节点不会被调度 pod。

节点亲和性,是 pod 的一种属性(偏好或硬性要求),它使 pod 被吸引到一类特定的节点。Taint 则相反,它使节点 能够 排斥 一类特定的 pod

Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于pod上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上

定义污点和容忍度

污点定义:nodes.spec.taints

容忍度定义:pods.spec.tolerations

使用kubectl taint命令可以给某个Node节点设置污点,Node被设置上污点之后就和Pod之间存在一种互斥的关系,可以让node 拒绝 pod的调度执行,甚至将node 已经存在的pod驱逐出去

污点

污点的命令和 label比较像,除了要设置key / value 之外 还要指定 排斥等级 effect

排斥等级说明说明
NoSchedule不能容忍
但仅影响调度过程,已调度上去的pod不受影响,仅对新增的pod效
表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上
NoExecute不能容忍
当污点变动时,Pod对象会被驱逐。
表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,
同时会
PreferNoSchedule柔性约束
节点现存Pod不受影响,如果实在是没有符合的节点,也可以被调度
表示 k8s 将尽量不会将 Pod 调度到具有该污点的 Node 上
尽量不把pod调度到此节点上运行,若POD资源无法容忍所有节点,改成可以容忍 所有污点,通过默认调度算法启动POD,要容忍就容忍所有的污点,不会按照污点数少进行优先调度

为了不影响之前的环境,我们先创建一个namespace :taint-demo-ns

shell
kubectl create namespace taint-demo-ns

环境情况:

Nodeeffect
k8s-master01-
k8s-slave02-
k8s-slave03-
创建污点
shell
# 创建单个污点
kubectl taint nodes k8s-slave03 taintKey=abc123:NoSchedule
# 创建多个污点
kubectl taint nodes k8s-slave03 taintKey=abc123:NoSchedule key2=def456:NoSchedule key3=ghj789:NoSchedule
删除污点
shell
 kubectl taint nodes k8s-slave03 taintKey=abc123:NoExecute-
修改污点
kubectl taint nodes k8s-slave03 taintKey=abc456:NoSchedule --overwrite
查看污点
shell
kubectl describe node k8s-slave03 | grep -i taint
#-----------------------------------
Taints:             <none>
#-----------------------------------

# 我们对`k8s-slave03` 添加污点后再查看
kubectl describe node k8s-slave03 | grep -i taint
#-----------------------------------
Taints:             taintKey=abc123:NoSchedule
#-----------------------------------
资源配置文件
污点effect - NoSchedule

测试案例:

新部署的pod不会被调度到具有污点标记且 effect = NoSchedule 的节点上。

> step 1:

​ 我们针对 node k8s-slave03 设置污点, effect 规则为NoSchedule

> step 2:

​ 我们编写一个deployment,然后执行。

> step 3:

​ 我们查看pods所在节点,是否存在在 k8s-slave03节点上

污点effect情况

Nodeeffect
k8s-master01-
k8s-slave02-
k8s-slave03 NoSchedule
shell
# step 1
kubectl taint nodes k8s-slave03 taintKey=abc123:NoSchedule

# step 2
vim scheduler_taint-test1.yml
#----------------------------
# 污点测试1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taint-test1
  # 指定命名空间
  namespace: taint-demo-ns
  labels:
    app: taint-test1
spec:
  replicas: 3
  template:
    metadata:
      name: taint-test1
      labels:
        app: taint-test1
    spec:
      containers:
        - name: taint-test1
          image: nginx
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
  selector:
    matchLabels:
      app: taint-test1
#----------------------------
kubectl apply -f scheduler_taint-test1.yml

# step 3
kubectl get pods -n taint-demo-ns -o wide
#----------------------------
NAME                           READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
taint-test1-74dd797cc4-hr9qc   1/1     Running   0          50s   10.244.220.254   k8s-slave02   <none>           <none>
taint-test1-74dd797cc4-ncffj   1/1     Running   0          50s   10.244.220.253   k8s-slave02   <none>           <none>
taint-test1-74dd797cc4-qwhtw   1/1     Running   0          50s   10.244.220.255   k8s-slave02   <none>           <none>
#----------------------------

# 结论,符合我们的期望。k8s-slave02 设置了NoSchedule,新建的pods不会被调度给 k8s-slave02
污点effect - NoExecute

测试案例:

node 节点 设置 污点-effect-NoExecute后,此节点上的pod 会被驱逐。(我们会驱逐k8s-slave03上的所有pods)

> step 1:

​ 我们先查看nodek8s-slave03 上 的pods,记录下来。

> step 2:

​ 我们针对 node k8s-slave03 设置污点, effect 规则为NoSchedule

> step 3:

​ 我们查看pods所在节点,是否存在在 k8s-slave02节点上

污点effect情况

Nodeeffect
k8s-master01-
k8s-slave02-
k8s-slave03 NoExecute
shell
# step 1
kubectl describe node k8s-slave03
#-------------------------
...

  Namespace                   Name                                CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                ------------  ----------  ---------------  -------------  ---
  default                     demonsetdemo-xnnf6                  0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d17h
  default                     deploymentdemo1-5bc649f558-92bc9    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     deploymentdemo1-5bc649f558-qtt4r    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     deploymentdemo1-5bc649f558-vskqr    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     deploymentdemo1-5bc649f558-xtp9s    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     deploymentdemo1-5bc649f558-z5xhr    0 (0%)        0 (0%)      0 (0%)           0 (0%)         2d18h
  default                     nginx-app-74d589986c-bdf66          0 (0%)        0 (0%)      0 (0%)           0 (0%)         6d16h
  default                     nginx-app-74d589986c-gfmsd          0 (0%)        0 (0%)      0 (0%)           0 (0%)         6d16h
  default                     nginx-app-74d589986c-p4mbl          0 (0%)        0 (0%)      0 (0%)           0 (0%)         6d16h
  default                     nginx-app-74d589986c-slszp          0 (0%)        0 (0%)      0 (0%)           0 (0%)         6d16h
  kube-system                 calico-node-q789c                   250m (12%)    0 (0%)      0 (0%)           0 (0%)         7d22h
  kube-system                 kube-proxy-wwn68                    0 (0%)        0 (0%)      0 (0%)           0 (0%)         5d19h
  tmp-quota-range             quta-pod-test3                      550m (27%)    600m (30%)  110Mi (6%)       130Mi (7%)     41h
  tmp-quota                   quta-pod-test1                      800m (40%)    1 (50%)     150Mi (8%)       200Mi (11%)    42h

...
#-------------------------

# step 2
kubectl taint nodes k8s-slave03 taintKey=abc123:NoExecute 

# step 3
kubectl describe nodes k8s-slave03
#-------------------------
...
  Namespace                   Name                 CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                 ------------  ----------  ---------------  -------------  ---
  kube-system                 calico-node-q789c    250m (12%)    0 (0%)      0 (0%)           0 (0%)         7d23h
  kube-system                 kube-proxy-wwn68     0 (0%)        0 (0%)      0 (0%)           0 (0%)         5d19h
...
#-------------------------

# 结论,符合我们的期望。node `k8s-slave03` 上的所有节点被驱逐。我们发现`kube-system` namespace 还存在两个节点`calico` 和 `kube-proxy`
污点effect - PreferNoSchedule

测试案例:

node 节点 设置 污点-effect-PreferNoSchedule后,如果没有其他满足条件的节点,那么新建的pods会被分配到 有PreferNoSchedule的节点上。

> step 1:

​ 我们针对 node k8s-slave02 设置污点, effect 规则为PreferNoSchedule

> step 2:

​ 我们针对 node k8s-slave03 设置污点, effect 规则为NoSchedule

​ 我们新建deployment并运行。

> step 3:

​ 查看驱逐过程,查看pods所在节点,是否存在在 k8s-slave02节点上

污点effect情况

Nodeeffect
k8s-master01-
k8s-slave02PreferNoSchedule
k8s-slave03 NoSchedule
shell
# step 1
kubectl taint nodes k8s-slave02 taintKey=abc123:PreferNoSchedule

# step 2
vim scheduler_taint-test2.yml
#---------------------------
# 污点测试2
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taint-test2
  namespace: taint-demo-ns
  labels:
    app: taint-test2
spec:
  replicas: 3
  template:
    metadata:
      name: taint-test2
      labels:
        app: taint-test2
    spec:
      containers:
        - name: taint-test2
          image: nginx
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
  selector:
    matchLabels:
      app: taint-test2
#---------------------------
kubectl apply -f scheduler_taint-test2.yml

# step 3
kubectl get pods -n taint-demo-ns -o wide
#---------------------------
NAME                           READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
taint-test2-6878fdbc99-j2tvj   1/1     Running   0          26s   10.244.220.195   k8s-slave02   <none>           <none>
taint-test2-6878fdbc99-mmzrj   1/1     Running   0          26s   10.244.220.194   k8s-slave02   <none>           <none>
taint-test2-6878fdbc99-xfl57   1/1     Running   0          26s   10.244.220.193   k8s-slave02   <none>           <none>
#---------------------------

# 结论,符合我们的期望,PreferNoSchedule 是柔性约束,如果除了他之外没有满足条件的node,那么pods 会被调度给 具有PreferNoSchedule的节点上。

容忍度

容忍度是针对pod的配置。 通过 Pod.spec.tolerations属性设置

资源配置文件

测试案例:

node k8s-slave02: 我们设置 污点 NoSchedule;

node k8s-slave03:我们设置污点 NoExecute;

正常情况下,我们新建的pods是无法被调度到 k8s-slave02 或 k8s-slave03 上的。

我们配置容忍度,然后再观察调度情况

> step 1:

​ 我们针对 node k8s-slave02 设置污点, effect 规则为NoSchedule

> step 2:

​ 我们针对 node k8s-slave03 设置污点, effect 规则为NoExecute

​ 我们新建deployment,配置容忍度,容忍 k8s-slave03污点的所有情况

​ 执行

> step 3:

​ 查看pods所在节点,是否存在在 k8s-slave03节点上

污点effect情况

Nodeeffect
k8s-master01-
k8s-slave02NoSchedule
k8s-slave03 NoExecute
shell
# step 1
kubectl taint nodes k8s-slave02 taintKey=abc123:NoSchedule

# step 2
kubectl taint nodes k8s-slave03 taintKey=abc123:NoExecute
vim  scheduler_tolerations.yml
#----------------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tolerations-test
  namespace: taint-demo-ns
  labels:
    app: tolerations-test
spec:
  replicas: 10
  template:
    metadata:
      name: tolerations-test
      labels:
        app: tolerations-test
    spec:
      containers:
        - name: tolerations-test
          image: nginx
          imagePullPolicy: IfNotPresent
      # 容忍度配置,要与对应的污点 设置信息【完全匹配】,才会生效
      tolerations:
        - key: "taintKey"
          operator: "Equal"
          value: "abc123"
          effect: "NoExecute"
      restartPolicy: Always
  selector:
    matchLabels:
      app: tolerations-test
#----------------------------------
kubectl apply -f scheduler_tolerations.yml

# step 3
kubectl get pods -n taint-demo-ns -o wide
#----------------------------------
NAME                               READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
tolerations-test-f47f9d965-56zvf   1/1     Running   0          9s    10.244.211.205   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-7rwvm   1/1     Running   0          9s    10.244.211.198   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-87n4v   1/1     Running   0          9s    10.244.211.202   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-dnjcs   1/1     Running   0          9s    10.244.211.204   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-fdlm8   1/1     Running   0          9s    10.244.211.207   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-gkjbf   1/1     Running   0          9s    10.244.211.200   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-kps9d   1/1     Running   0          9s    10.244.211.199   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-nq2fm   1/1     Running   0          9s    10.244.211.197   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-p4s2k   1/1     Running   0          9s    10.244.211.203   k8s-slave03   <none>           <none>
tolerations-test-f47f9d965-pf5mb   1/1     Running   0          9s    10.244.211.206   k8s-slave03   <none>           <none>
#----------------------------------
kubectl describe node k8s-slave03 | grep -i taint
#----------------------------------
Taints:             taintKey=abc123:NoExecute
  taint-demo-ns               tolerations-test-f47f9d965-56zvf    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-7rwvm    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-87n4v    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-dnjcs    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-fdlm8    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-gkjbf    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-kps9d    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-nq2fm    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-p4s2k    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
  taint-demo-ns               tolerations-test-f47f9d965-pf5mb    0 (0%)        0 (0%)      0 (0%)           0 (0%)         70s
#----------------------------------

# 结论,符合我们的期望;我们对`k8s-slave03`配置了容忍度,因此在创建新的pods时,有可能会被调度到`k8s-slave03`上。 注意:是有可能,具体还要看每个节点的effect配置。

附录

参考资料

Kubernetes 多租户:资源配额

Kubernetes最佳实践(四):资源请求和限制

Kubernetes 资源配额 ResourceQuota

k8s资源调度

【k8s】kubectl label命令(对node添加、删除label)

Kubernetes K8S节点选择

K8s资源调度方式