6. kubernetes 资源和调度
一、资源配额与限制
资源配额用于管理命名空间(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下我们可以针对性的设置计算资源配额,有两种方式:ResourceQuota
和 LimitRange
资源名称 | 资源类型 | 描述 |
---|---|---|
limits.cpu | cpu | 所有容器的cpu 请求资源总和 |
limits.memory | memory | 所有容器的内存 请求资源总和 |
requests.cpu | cpu | 所有容器的cpu 限制资源总和 |
requests.memory | memory | 所有容器的内存 限制资源总和 |
在创建pod时,无论是
请求资源超出限制
或者是限制资源超出限制
,都无法创建成功!!!
ResourceQuota
下面是一个演示案例
这里要注意的是:
我们在 resourcequota中 设胜多负少的置 cpu 和 memory 的格式 要和 pod 文件中 cpu、 memory 的格式保存统一。
比如: resourcequota memory 赋值时 我们是用的 "300Mi", 那么我们在pod 中配置 memory 时 也应该用相同的格式 "xxMi"
# 首先我们新建一个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
# 首先我们新建一个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:
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:
Host Port:
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:
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:
DownwardAPI: true
QoS Class: Burstable
Node-Selectors:
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中,存储资源的需求不能超过该值 |
persistentvolumeclaims | namespace中所允许的pvc总量 |
所有该storage-class-name相关的pvc中,存储资源的需求不能超过改值 | |
namespace中所允许的该storage-class-name相关的pvc的总量 |
这里我们简单带过,只演示下核心的资源文件,使用方法和 计算资源配额
类似
# 不管多少个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. 对象数量资源配额
这个就比较好理解了,我们可以限制 pods
、deployment
、 services
configmaps
等的数量
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调度步骤
首先用户在通过 Kubernetes 客户端 Kubectl 提交创建 Pod 的 Yaml 的文件,向Kubernetes 系统发起资源请求,该资源请求被提交到Kubernetes 系统。
Kubernetes 系统中,用户通过命令行工具 Kubectl 向 Kubernetes 集群即用
API Server
的方式发送“POST”请求,即创建 Pod 的请求。API Server
接收到请求后把创建 Pod 的信息存储到 Etcd 中,从集群运行那一刻起,资源调度系统Scheduler 就会定时去监控API Server
通过
API Server
得到创建 Pod 的信息,Scheduler 采用 watch 机制,一旦 Etcd 存储 Pod 信息成功便会立即通知API Server
,API Server
会立即把Pod创建的消息通知Scheduler,Scheduler发现 Pod 的属性中 Dest Node 为空时(Dest Node="")便会立即触发调度流程进行调度。-
而这一个创建Pod对象,在调度的过程当中有3个阶段:节点预选、节点优选、节点选定,从而筛选出最佳的节点
- 节点预选:基于一系列的预选规则对每个节点进行检查,将那些不符合条件的节点过滤,从而完成节点的预选
- 节点优选:对预选出的节点进行优先级排序,以便选出最合适运行Pod对象的节点
- 节点选定:从优先级排序结果中挑选出优先级最高的节点运行Pod,当这类节点多于1个时,则进行随机选择
集群调度策略
Kubernetes调度器作为集群的大脑,在如何提高集群的资源利用率、保证集群中服务的稳定运行中也会变得越来越重要Kubernetes的资源分为两种属性。
- 可压缩资源(例如CPU循环,Disk I/O带宽)都是可以被限制和被回收的,对于一个Pod来说可以降低这些资源的使用量而不去杀掉Pod。
- 不可压缩资源(例如内存、硬盘空间)一般来说不杀掉Pod就没法回收。未来Kubernetes会加入更多资源,如网络带宽,存储IOPS的支持。
集群调度特别复杂,有各种各样的规则。调度器会根据各种规则计算出一个分数
,根据分数
的大小来选择节点。
常用预选策略
预选策略 | 作用 |
---|---|
CheckNodeCondition | 检查节点网络、磁盘等是否正常 |
HostName | 如果Pod对象拥有spec.hostname属性,则检查节点名 称字符串是否和该属性值匹配。 |
PodFitsHostPorts | Pod的spec.hostPort属性时,检查端口是否被占用 |
MatchNodeSelector | Pod的spec.nodeSelector属性时,检查节点标签 |
NoDiskConflict | Pod依赖的存储卷在此节点是否可用 |
PodFitsResources | 检查节点上的资源(CPU、内存)可用性是否满足Pod对 象的运行需求。 |
PodToleratesNodeTaints | Pod的spec.tolerations属性,仅关注NoSchedule和 NoExecute两个效用标识的污点 |
PodToleratesNodeNoExecuteTaints | Pod的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 |
BalancedResourceAllocation | CPU和内存使用率越接近的节点权重越高,该策略不能单独使用,必须和 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 的形式附加到各种对象上,如Pod
、Service
、Deployment
、Node
等。达到识别对象,关联关系等目的。
查看 label
# 通常我们在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 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 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
#--------------------------
# 查看service label
kubectl get service --show-labels
#--------------------------
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
deploymentdemo1-service NodePort 10.222.58.200 8002:32002/TCP 44h app=deploymentdemo1
kubernetes ClusterIP 10.222.0.1 443/TCP 7d16h component=apiserver,provider=kubernetes
nginx-app-service NodePort 10.222.191.172 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
# 我们先选择一个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
# 修改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
# 删除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的
pod.spec.nodeName将Pod直接调度到指定的Node节点上(包括master),会【跳过Scheduler的调度策略】,该匹配规则是【强制】匹配。可以越过Taints污点进行调度。
nodeName用于选择节点的一些限制是:
- 如果指定的节点不存在,则容器将不会运行,并且在某些情况下可能会自动删除。
- 如果指定的节点没有足够的资源来容纳该Pod,则该Pod将会失败,并且其原因将被指出,例如OutOfmemory或OutOfcpu。
- 云环境中的节点名称并非总是可预测或稳定的。
- 如果指定的节点不存在,容器不会运行,一直处于
Pending
状态
资源配置文件
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
node-name-test-7fd854bfc9-hgvzf 1/1 Running 0 12s 10.244.32.136 k8s-master01
node-name-test-7fd854bfc9-vr995 1/1 Running 0 12s 10.244.32.134 k8s-master01
#---------------------------
3. nodeSelector
nodeSelector是节点选择约束的最简单推荐形式。nodeSelector是PodSpec的领域。它指定键值对的映射(label)。
Pod.spec.nodeSelector是通过Kubernetes的label-selector机制选择调度到那个节点。可以为一批node节点打上指定的标签,由调度器去匹配符合的标签,并让Pod调度到这些节点,该匹配规则属于【强制】约束。由于是调度器调度,因此不能越过Taints污点进行调度。
资源配置文件
# 我们先简述下操作流程:
# 演示业务场景: 我们把磁盘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 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 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
node-selector-test-8485477cbf-8vgx8 1/1 Running 0 11s 10.244.220.250 k8s-slave02
node-selector-test-8485477cbf-d2gwf 1/1 Running 0 11s 10.244.220.251 k8s-slave02
node-selector-test-8485477cbf-nzmbp 1/1 Running 0 11s 10.244.220.249 k8s-slave02
node-selector-test-8485477cbf-r8bk8 1/1 Running 0 11s 10.244.220.248 k8s-slave02
# ---------------------
# 验证通过,我们发现所有的 pod 都运行在了 ssd 节点 node `k8s-slave02` 上
4. nodeAffinity 亲和性
nodeAffinity 比 nodeSelector 更灵活。它可以在nodeSelector的基础上进行一些简单的逻辑组合。不只是简单的匹配。调度可以分成软策略
和硬策略
两种。其中逻辑支持操作符
节点亲和性规则
required(硬亲和性,硬策略,不能商量,必须执行) 、preferred(软亲和性,软策略,可以商量,选择执行)。
硬亲和性规则不满足时,Pod会置于Pending状态,软亲和性规则不满足时,会选择一个不匹配的节点
-
当节点标签改变而不再符合此节点亲和性规则时,不会将Pod从该节点移出,仅对新建的Pod对象生效
nodeAffinity可对应的两种策略:
preferredDuringScheduling
和requiredDuringScheduling
类型 描述 requiredDuringScheduling pod资源在配置中声明一种标签,只有node节点跟我声明的标签一致,pod才能
被调度到此节点,如果没有匹配上,那我就一直等有匹配上的节点。preferredDuringScheduling pod资源在配置中声明一种标签,只有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属性 (复杂标签选择机制)
requiredDuringSchedulingIgnoredDuringExecution
表示pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试。其中IgnoreDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,pod也会继续运行,直到停止此pod生命周期。
requiredDuringSchedulingRequiredDuringExecution
表示pod必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试。其中RequiredDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,停止此pod生命周期,重新选择符合要求的节点。
软策略
跟硬策略一样,都是pod资源声明了一个策略,这个策略中可以写限制条件,比如标签,如果有节点能满足我的条件,就在此节点部署pod,但是如果所有节点全部没有满足调度要求的话,POD 就会忽略这条规则,通过默认的Scheduler调度算法,进行pod部署,直到有符合要求node节点,才会重新按照软策略匹配到符合的节点上创建pod,说白了就是满足条件最好了,没有的话也无所谓了的策略,此调度适用于服务最好运行在某个区域,减少网络传输等。这种区分是用户的具体需求决定的,并没有绝对的技术依赖。
- 柔性控制逻辑,当条件不满足时,能接受被编排于其他不符合条件的节点之上
- 权重 weight 定义优先级,1-100 值越大优先级越高
preferredDuringSchedulingIgnoredDuringExecution
表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。其中IgnoreDuringExecution表示pod部署之后运行的时候,如果节点标签发生了变化,不再满足pod指定的条件,pod也会继续运行,直到停止此pod生命周期。
preferredDuringSchedulingRequiredDuringExecution
表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署。其中RequiredDuringExecution表示如果后面节点标签发生了变化,满足了条件,停止此pod生命周期,重新调度到满足条件的节点。
操作符
- In: label的值在某个列表中
- NotIn:label的值不在某个列表中
- Exists:某个label存在
- DoesNotExist:某个label不存在
- Gt:label的值大于某个值(字符串比较)
- Lt:label的值小于某个值(字符串比较)
资源配置文件
# 节点亲和性,相对来说要复杂一些。也更灵活。
# 完整的文件太多,这里我们只挑重点。
# 推荐参考文章: 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 污点与容忍
污点taints 是定义在node节点 上的键值型属性数据,用于让节点拒绝将Pod调度运行于其上,除非Pod有接纳节点污点的容忍度。
容忍度 tolerations 是定义在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 上, 同时会将 Node 上已经存在的Pod 驱逐出去 |
PreferNoSchedule | 柔性约束 节点现存Pod不受影响,如果实在是没有符合的节点,也可以被调度 |
表示 k8s 将尽量不会将 Pod 调度到具有该污点的 Node 上 。 尽量不把pod调度到此节点上运行,若POD资源无法容忍所有节点,改成可以容忍 所有污点,通过默认调度算法启动POD,要容忍就容忍所有的污点,不会按照污点数少进行优先调度 |
为了不影响之前的环境,我们先创建一个namespace :taint-demo-ns
kubectl create namespace taint-demo-ns
环境情况:
Node effect k8s-master01 - k8s-slave02 - k8s-slave03 -
创建污点
# 创建单个污点
kubectl taint nodes k8s-slave03 taintKey=abc123:NoSchedule
# 创建多个污点
kubectl taint nodes k8s-slave03 taintKey=abc123:NoSchedule key2=def456:NoSchedule key3=ghj789:NoSchedule
删除污点
kubectl taint nodes k8s-slave03 taintKey=abc123:NoExecute-
修改污点
kubectl taint nodes k8s-slave03 taintKey=abc456:NoSchedule --overwrite
查看污点
kubectl describe node k8s-slave03 | grep -i taint
#-----------------------------------
Taints:
#-----------------------------------
# 我们对`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情况
Node effect k8s-master01 - k8s-slave02 - k8s-slave03 NoSchedule
# 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
taint-test1-74dd797cc4-ncffj 1/1 Running 0 50s 10.244.220.253 k8s-slave02
taint-test1-74dd797cc4-qwhtw 1/1 Running 0 50s 10.244.220.255 k8s-slave02
#----------------------------
# 结论,符合我们的期望。k8s-slave02 设置了NoSchedule,新建的pods不会被调度给 k8s-slave02
污点effect - NoExecute
测试案例:
node 节点 设置 污点-effect-NoExecute后,此节点上的pod 会被驱逐。(我们会驱逐
k8s-slave03
上的所有pods)> step 1:
我们先查看node`k8s-slave03` 上 的pods,记录下来。
> step 2:
我们针对 node `k8s-slave03` 设置污点, effect 规则为`NoSchedule`。
> step 3:
我们查看pods所在节点,是否存在在 `k8s-slave02`节点上
污点effect情况
Node effect k8s-master01 - k8s-slave02 - k8s-slave03 NoExecute
# 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情况
Node effect k8s-master01 - k8s-slave02 PreferNoSchedule
k8s-slave03 NoSchedule
# 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
taint-test2-6878fdbc99-mmzrj 1/1 Running 0 26s 10.244.220.194 k8s-slave02
taint-test2-6878fdbc99-xfl57 1/1 Running 0 26s 10.244.220.193 k8s-slave02
#---------------------------
# 结论,符合我们的期望,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情况
Node effect k8s-master01 - k8s-slave02 NoSchedule
k8s-slave03 NoExecute
# 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
tolerations-test-f47f9d965-7rwvm 1/1 Running 0 9s 10.244.211.198 k8s-slave03
tolerations-test-f47f9d965-87n4v 1/1 Running 0 9s 10.244.211.202 k8s-slave03
tolerations-test-f47f9d965-dnjcs 1/1 Running 0 9s 10.244.211.204 k8s-slave03
tolerations-test-f47f9d965-fdlm8 1/1 Running 0 9s 10.244.211.207 k8s-slave03
tolerations-test-f47f9d965-gkjbf 1/1 Running 0 9s 10.244.211.200 k8s-slave03
tolerations-test-f47f9d965-kps9d 1/1 Running 0 9s 10.244.211.199 k8s-slave03
tolerations-test-f47f9d965-nq2fm 1/1 Running 0 9s 10.244.211.197 k8s-slave03
tolerations-test-f47f9d965-p4s2k 1/1 Running 0 9s 10.244.211.203 k8s-slave03
tolerations-test-f47f9d965-pf5mb 1/1 Running 0 9s 10.244.211.206 k8s-slave03
#----------------------------------
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资源调度方式