资源配额
什么是资源配额
资源配额,通过 ResourceQuota 对象来定义,对每个命名空间的资源消耗总量提供限制。 它可以限制命名空间中某种类型的对象的总数目上限,也可以限制命名空间中的 Pod 可以使用的计算资源的总上限。
资源配额应用
创建的ResourceQuota对象将在test名字空间中添加限制,每个容器必须设置内存请求(memory request),内存限额(memory limit),cpu请求(cpu request)和cpu限额(cpu limit),所有容器的内存请求总额不得超过2GiB,所有容器的内存限额总额不得超过4 GiB,所有容器的CPU请求总额不得超过2 CPU,所有容器的CPU限额总额不得超过4CPU
[root@master ~]# cat namespace_ResourceQuota.yaml
apiVersion: v1
kind: Namespace
metadata:
name: test
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-quota
namespace: test
spec:
hard:
requests.cpu: "2"
requests.memory: 2Gi
limits.cpu: "4"
limits.memory: 4Gi
[root@master ~]# kubectl apply -f namespace_ResourceQuota.yaml
namespace/test created
resourcequota/mem-cpu-quota created
# 可以通过describe查看到test命名空间中我们设置的资源配额限制
[root@master ~]# kubectl describe ns test
Name: test
Labels: kubernetes.io/metadata.name=test
Annotations: <none>
Status: Active
Resource Quotas
Name: mem-cpu-quota
Resource Used Hard
-------- --- ---
limits.cpu 0 4
limits.memory 0 4Gi
requests.cpu 0 2
requests.memory 0 2Gi
No LimitRange resource.
针对Pod设置资源配额
对于有资源限制的命名空间,下面的pod,创建pod时候必须设置资源限额,否则创建失败。
requests:代表容器启动请求的资源限制,分配的资源必须要达到此要求
limits:代表最多可以请求多少资源
单位m:CPU的计量单位叫毫核(m)。一个节点的CPU核心数量乘以1000,得到的就是节点总的CPU总数量。如,一个节点有两个核,那么该节点的CPU总量为2000m。
该容器启动时请求500/2000的核心(25%)
[root@master ~]# cat pod_resources.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-nginx
namespace: test
labels:
app: nginx
spec:
containers:
- name: nginx
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
resources:
requests:
memory: "100Mi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2"
[root@master ~]# kubectl apply -f pod_resources.yaml
pod/test-nginx created
HorizontalPodAutoscaler(HPA)弹性伸缩
Horizontal Pod Autoscaling(Pod水平自动伸缩),简称HPA。HAP通过监控分析RC或者Deployment控制的所有Pod的负载变化情况来确定是否需要调整Pod的副本数量,这是HPA最基本的原理
HorizontalPodAutoscaler(简称 HPA ) 自动更新工作负载资源(例如 Deployment 或者 StatefulSet), 目的是自动扩缩工作负载以满足需求。
水平扩缩意味着对增加的负载的响应是部署更多的 Pod。 这与“垂直(Vertical)”扩缩不同,对于 Kubernetes, 垂直扩缩意味着将更多资源(例如:内存或 CPU)分配给已经为工作负载运行的 Pod。
如果负载减少,并且 Pod 的数量高于配置的最小值, HorizontalPodAutoscaler 会指示工作负载资源(Deployment、StatefulSet 或其他类似资源)缩减
HorizontalPodAutoscaler支持的指标
HPA支持的指标可以使用kubectl api-versions | grep autoscal命令查询
[root@master ~]# kubectl api-versions | grep autoscal autoscaling/v1 autoscaling/v2 autoscaling/v2beta1 autoscaling/v2beta2 |
autoscaling/v1只支持基于CPU指标的缩放;
autoscaling/v2beta1支持Resource Metrics(资源指标,如pod的CPU,内存)和Custom Metrics(自定义指标)的缩放;
autoscaling/v2beta2支持Resource Metrics(资源指标,如pod的CPU,内存)和Custom Metrics(自定义指标)和ExternalMetrics(额外指标)的缩放,但是目前也仅仅是处于beta阶段
autoscaling/v2(稳定版本)其中包括对基于内存和自定义指标执行扩缩的支持
如下图没有Metrics这个插件,所以需要部署资源清单
部署Metrics
1.上传文件components.yaml
2.上传文件道所有节点,并且加载(metrics-server_v0.6.4.tar.gz)
docker load < metrics-server_v0.6.4.tar.gz
3.执行yaml文件,即可开启Metrics
kubectl apply -f components.yaml
kubectl get pod -n kube-system
准备测试服务
1.在所有节点上传镜像文件,加载镜像(cpu_stress_v3.tar.gz)
docker load < cpu_stress_v3.tar.gz
2.在master节点,编写yaml文件,启动服务
vi stress.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: stress
spec:
replicas: 1
selector:
matchLabels:
app: stress
template:
metadata:
labels:
app: stress
spec:
containers:
- name: stress
image: cpu_stress:v3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
limits:
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: stress
spec:
ports:
- port: 80
targetPort: 80
selector:
app: stress
4.启动服务
kubectl apply -f stress.yaml
配置HPA的两种方式
命令行配置
# --cpu-percent指定pod的cpu使用率维持在50%左右
# --min 指定pod数量最少多少
# --max 指定pod数量最多多少
kubectl autoscale deployment stress --cpu-percent=50 --min=1 --max=10
[root@master ~]# kubectl autoscale deployment stress --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/stress autoscaled
# 查看hpa
[root@master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
stress Deployment/stress 0%/50% 1 10 1 16s
资源清单配置
# scaleTargetRef指定要缩放的目标,在这里是 "stress" 这个 Deployment
# minReplicas: 1缩放的最小 pod 数量
# maxReplicas: 10缩放的最大 pod 数量
#
[root@master ~]# cat stress_hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: stress
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: stress
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource # 指定缩放所用的指标类型,这里是资源利用率指标
resource:
name: cpu # 指定资源类型,这里是 CPU
target:
type: Utilization # 表示基于 CPU利用率百分比来自动扩缩容。
averageUtilization: 50 # 目标 cpu 平均利用率为 50%。当利用率超过这个目标值时会缩放 pod 数量。
[root@master ~]# kubectl apply -f stress_hpa.yaml
horizontalpodautoscaler.autoscaling/stress created
[root@master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
stress Deployment/stress 1%/50% 1 10 1 39s
HPA测试
kubectl get hpa
[root@master ~]# cat test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cpustress
spec:
replicas: 1
selector:
matchLabels:
app: cpustress
template:
metadata:
labels:
app: cpustress
spec:
containers:
- name: cpustress
image: alpine
imagePullPolicy: IfNotPresent
command:
- "sh"
- "-c"
- "sed -i 's/mirrors.aliyun.com/mirrors.ustc.edu.cn/g' /etc/apk/repositories && apk update && apk add curl && while true; do curl stress/stress?duration=30&load=70 ;sleep 32;done"
# 运行测试容器
[root@master ~]# kubectl apply -f test.yaml
deployment.apps/cpustress created
# 查看hpa情况
[root@master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
stress Deployment/stress 62%/50% 1 10 10 51m
# pod也启动了多个
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
cpustress-649d7f6485-gd6ld 1/1 Running 0 2m10s
stress-548b54ff89-2nlzk 1/1 Running 0 78s
stress-548b54ff89-5cz58 1/1 Running 0 93s
stress-548b54ff89-6cfpx 1/1 Running 0 63s
stress-548b54ff89-946kd 1/1 Running 0 78s
stress-548b54ff89-b9k48 1/1 Running 0 78s
stress-548b54ff89-k7d59 1/1 Running 0 93s
stress-548b54ff89-mw786 1/1 Running 0 63s
stress-548b54ff89-pcgc7 1/1 Running 0 93s
stress-548b54ff89-psb2f 1/1 Running 0 52m
stress-548b54ff89-zb2xl 1/1 Running 0 78s
# 停止压力测试
[root@master ~]# kubectl delete -f test.yaml
deployment.apps "cpustress" deleted
# 等待一段时间会发现pod数量降下来了,可能需要几分钟
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
stress-548b54ff89-psb2f 1/1 Running 0 61m
节点选择器
通过nodeSelector
nodeSelector 是节点选择约束的最简单推荐形式。你可以将 nodeSelector 字段添加到 Pod 的规约中设置你希望目标节点所具有的节点标签。 Kubernetes 只会将 Pod 调度到拥有你所指定的每个标签的节点上
[root@master ~]# cat pod_nodeSelector.yaml
apiVersion: v1
kind: Pod
metadata:
name: podnodeselector
namespace: default
labels:
app: nginx
spec:
nodeSelector:
disk: ceph
containers:
- name: podnodeselector
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
resources:
requests:
memory: '100Mi'
cpu: '500m'
limits:
memory: '1Gi'
cpu: '1'
# 部署资源
[root@master ~]# kubectl apply -f pod_nodeSelector.yaml
pod/podnodeselector created
# 可以看到没有节点带有disk=ceph标签,所以pod是Pending
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
podnodeselector 0/1 Pending 0 39s
[root@master ~]# kubectl get node -l disk=ceph
No resources found
# 给node1节点打标签,然后pod就自动运行在有指定标签的节点了
[root@master ~]# kubectl label node node1 disk=ceph
node/node1 labeled
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
podnodeselector 1/1 Running 0 2m46s 10.244.1.3 node1 <none> <none>
通过nodeName
nodeName 是比亲和性或者 nodeSelector 更为直接的形式。nodeName 是 Pod 规约中的一个字段。如果 nodeName 字段不为空,调度器会忽略该 Pod, 而指定节点上的 kubelet 会尝试将 Pod 放到该节点上。 使用 nodeName 规则的优先级会高于使用 nodeSelector 或亲和性与非亲和性的规则。
局限性:
如果所指代的节点不存在,则 Pod 无法运行,而且在某些情况下可能会被自动删除。
如果所指代的节点无法提供用来运行 Pod 所需的资源,Pod 会失败, 而其失败原因中会给出是否因为内存或 CPU 不足而造成无法运行。
在云环境中的节点名称并不总是可预测的,也不总是稳定的
[root@master ~]# cat nodeName.yaml
apiVersion: v1
kind: Pod
metadata:
name: podnodeselector
namespace: default
labels:
app: nginx
spec:
nodeName: node1
containers:
- name: podnodeselector
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
[root@master ~]# kubectl apply -f nodeName.yaml
pod/podnodeselector created
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
podnodeselector 1/1 Running 0 11s 10.244.1.4 node1 <none> <none>
亲和性
Affinity 翻译成中文是“亲和性”,它对应的是 Anti-Affinity,我们翻译成“互斥”。这两个词比较形象,可以把 pod 选择 node 的过程类比成磁铁的吸引和互斥,不同的是除了简单的正负极之外,pod 和 node 的吸引和互斥是可以灵活配置的
优点:
匹配有更多的逻辑组合,不只是字符串的完全相等
调度分成软策略(soft)和硬策略(hard),在软策略下,如果没有满足调度条件的节点,pod会忽略这条规则,继续完成调度
Node亲和性
[root@master ~]# cat pod-node-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-demo
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
- hhd
[root@master ~]# kubectl apply -f pod-node-affinity-demo.yaml
pod/pod-node-affinity-demo created
# 查看pod状态是Pending,因为没有节点带有disktype=ssd标签或者disktype=hhd标签
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pod-node-affinity-demo 0/1 Pending 0 30s
[root@master ~]# kubectl get node -l disktype=ssd
No resources found
[root@master ~]# kubectl get node -l disktype=hhd
No resources found
# 打标签以后发现节点就运行了,并且是运行在打赏标签的哪个节点上
[root@master ~]# kubectl label node node1 disktype=ssd
node/node1 labeled
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-node-affinity-demo 0/1 ContainerCreating 0 3m4s <none> node1 <none> <none>
Pod亲和性
pod自身的亲和性调度有两种表示形式
podaffinity:pod和pod更倾向腻在一起,把相近的pod结合到相近的位置,如同一区域,同一机架,这样的话pod和pod之间更好通信,比方说有两个机房,这两个机房部署的集群有1000台主机,那么我们希望把nginx和tomcat都部署同一个地方的node节点上,可以提高通信效率;
podunaffinity:pod和pod更倾向不腻在一起,如果部署两套程序,那么这两套程序更倾向于反亲和性,这样相互之间不会有影响。
第一个pod随机选则一个节点,做为评判后续的pod能否到达这个pod所在的节点上的运行方式,这就称为pod亲和性;我们怎么判定哪些节点是相同位置的,哪些节点是不同位置的;我们在定义pod亲和性时需要有一个前提,哪些pod在同一个位置,哪些pod不在同一个位置,这个位置是怎么定义的,标准是什么?以节点名称为标准,这个节点名称相同的表示是同一个位置,节点名称不相同的表示不是一个位置。
topologyKey:
位置拓扑的键,这个是必须字段
怎么判断是不是同一个位置:
rack=rack1
row=row1
使用rack的键是同一个位置
使用row的键是同一个位置
labelSelector:
我们要判断pod跟别的pod亲和,跟哪个pod亲和,需要靠labelSelector,通过labelSelector选则一组能作为亲和对象的pod资源
namespace:
labelSelector需要选则一组资源,那么这组资源是在哪个名称空间中呢,通过namespace指定,如果不指定namespaces,那么就是当前创建pod的名称空间
[root@master ~]# cat podAffinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx01
namespace: default
labels:
app01: nginx01
spec:
containers:
- name: mynginx
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Pod
metadata:
name: nginx02
namespace: default
labels:
app02: nginx02
spec:
containers:
- name: mynginx
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app01
operator: In
values:
- nginx01
topologyKey: kubernetes.io/hostname # 每个节点都有kubernetes.io/hostname标签,这个标签通常是主机名,topologyKey指定了这个标签意思就是限定在一个节点上
[root@master ~]# kubectl apply -f podAffinity.yaml
pod/nginx01 created
pod/nginx02 created
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx01 1/1 Running 0 82s 10.244.1.5 node1 <none> <none>
nginx02 1/1 Running 0 82s 10.244.1.6 node1 <none> <none>
反亲和 性
[root@master ~]# cat podAntiAffinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx01
namespace: default
labels:
app01: nginx01
spec:
containers:
- name: mynginx
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Pod
metadata:
name: nginx02
namespace: default
labels:
app02: nginx02
spec:
containers:
- name: mynginx
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app01
operator: In
values:
- nginx01
topologyKey: kubernetes.io/hostname
[root@master ~]# kubectl apply -f podAntiAffinity.yaml
pod/nginx01 created
pod/nginx02 created
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx01 1/1 Running 0 95s 10.244.1.7 node1 <none> <none>
nginx02 1/1 Running 0 95s 10.244.2.2 node2 <none> <none>
污点容忍
节点亲和性 是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点 (这可能出于一种偏好,也可能是硬性要求)。 污点(Taint) 则相反——它使节点能够排斥一类特定的 Pod。
容忍度(Toleration) 是应用于 Pod 上的。容忍度允许调度器调度带有对应污点的 Pod。 容忍度允许调度但并不保证调度:作为其功能的一部分, 调度器也会评估其他参数。
污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod, 是不会被该节点接受的。
污点
我们给节点打一个污点,不容忍的pod就运行不上来,污点就是定义在节点上的键值属性数据,可以定决定拒绝那些pod。
使用kubeadm安装的Kubernetes集群的master节点默认具有node-role.kubernetes.io/master:NoSchedule污点。
每个污点有一个key和value作为污点的标签,effect描述污点的作用。当前taint effect支持如下效果:
- NoSchedule:表示K8S将不会把Pod调度到具有该污点的Node节点上
- PreferNoSchedule:表示K8S将尽量避免把Pod调度到具有该污点的Node节点上
- NoExecute:表示K8S将不会把Pod调度到具有该污点的Node节点上,同时会将Node上已经存在的Pod驱逐出去
添加污点
# 给节点 node1 增加一个污点,它的键名是 key1,键值是 value1,效果是 NoSchedule
kubectl taint nodes node1 key1=value1:NoSchedule
查看污点
# 查询node1节点的污点,找到Taints
kubectl describe nodes node1
[root@master ~]# kubectl describe nodes node1 | grep Taints
Taints: key1=value1:NoSchedule
删除污点
# 去除节点node1的污点,它的键名是 key1,键值是 value1,效果是 NoSchedule
kubectl taint nodes node1 key1=value1:NoSchedule-
容忍
默认情况下,Pod是不会运行在具有污点的节点上,但是我们可以配置容忍,让Pod运行在这个节点
设置污点
[root@master ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready control-plane,master 6d23h v1.23.0
node1 Ready <none> 6d23h v1.23.0
node2 Ready <none> 6d23h v1.23.0
[root@master ~]# kubectl taint node node1 node-type=test:NoSchedule
node/node1 tainted
[root@master ~]# kubectl taint node node2 node-type=production:NoSchedule
node/node2 tainted
运行没有容忍的Pod
[root@master ~]# cat nginx-taint.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- name: http
containerPort: 80
[root@master ~]# kubectl apply -f nginx-taint.yaml
pod/nginx created
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Pending 0 15s
# 使用describe查询
[root@master ~]# kubectl describe pod nginx
Name: nginx
Namespace: default
Priority: 0
Node: <none>
Labels: app=nginx
Annotations: <none>
Status: Pending
IP:
IPs: <none>
Containers:
nginx:
Image: nginx
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-7tgj2 (ro)
Conditions:
Type Status
PodScheduled False
Volumes:
kube-api-access-7tgj2:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
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
---- ------ ---- ---- -------
Warning FailedScheduling 54s default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 1 node(s) had taint {node-type: production}, that the pod didn't tolerate, 1 node(s) had taint {node-type: test}, that the pod didn't tolerate.
运行带有容忍的Pod
[root@master ~]# cat nginx-taint.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- name: http
containerPort: 80
# 容忍key是node-type,value是production,污点级是NoSchedule的污点
tolerations:
- key: "node-type"
operator: "Equal"
value: "production"
effect: "NoSchedule"
[root@master ~]# kubectl apply -f nginx-taint.yaml
pod/nginx configured
# 因为该Pod定义了容忍node-type=production:NoSchedule污点所以可以在node2节点运行
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 3m24s 10.244.2.3 node2 <none> <none>
# 只要对应的键是存在的,exists,其值被自动定义成通配符
tolerations:
- key: "node-type"
operator: "Exists"
value: ""
effect: "NoSchedule
# 有一个node-type的键,不管值是什么,不管是什么效果,都能容忍
tolerations:
- key: "node-type"
operator: "Exists"
value: ""
effect: ""