好的,各位K8s界的英雄们,今天咱们来聊聊K8s Node的资源管理和调度优化,这可是决定咱们集群“跑得快、吃得少、活得长”的关键!🚀
想象一下,咱们的K8s集群就像一个大型的动物园,每个Node就是一只笼子,里面住着各种各样的Pod,也就是咱们的小动物们。有些小动物(Pod)是温顺的小兔子,吃得少、睡得多;有些则是凶猛的老虎,吃得多、动得欢。如果咱们不合理地分配这些笼子,那可就要出乱子了!轻则小兔子饿肚子,老虎没地方撒欢;重则整个动物园秩序崩塌,一片混乱。😱
所以,资源管理和调度优化,就是咱们动物园园长(K8s管理员)的必备技能,要做到“物尽其用,人尽其才”,让每个小动物都能健康快乐地成长。
一、Node资源知多少:家底要摸清
首先,咱们得搞清楚每个Node都有哪些资源,就像摸清动物园的家底一样。主要的资源包括:
- CPU: 相当于动物园里的活动场地,越多越能让小动物们尽情奔跑。
- 内存: 相当于动物园里的食物储备,越多越能保证小动物们吃饱喝足。
- 存储: 相当于动物园里的仓库,用来存放各种饲料、工具等。
- 网络: 相当于动物园里的交通运输系统,保证各种物资的流通。
咱们可以使用kubectl describe node <node-name>
命令来查看Node的资源信息。就像这样:
kubectl describe node node-01
你会看到类似这样的输出:
Name: node-01
Roles: <none>
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/hostname=node-01
Annotations: node.alpha.kubernetes.io/ttl=0
CreationTimestamp: Tue, 26 Mar 2024 10:00:00 +0800
Taints: <none>
Unschedulable: false
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
MemoryPressure False Tue, 26 Mar 2024 10:30:00 +0800 Tue, 26 Mar 2024 10:00:00 +0800 KubeletHasSufficientMemory Kubelet has sufficient memory available
DiskPressure False Tue, 26 Mar 2024 10:30:00 +0800 Tue, 26 Mar 2024 10:00:00 +0800 KubeletHasNoDiskPressure Kubelet has no disk pressure
PIDPressure False Tue, 26 Mar 2024 10:30:00 +0800 Tue, 26 Mar 2024 10:00:00 +0800 KubeletHasSufficientPID Kubelet has sufficient PID
Ready True Tue, 26 Mar 2024 10:30:00 +0800 Tue, 26 Mar 2024 10:00:00 +0800 KubeletReady kubelet is posting ready status
Addresses:
Type Address
---- -------
InternalIP 192.168.1.101
Hostname node-01
Capacity:
cpu: 4
ephemeral-storage: 20Gi
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 8Gi
pods: 110
Allocatable:
cpu: 4
ephemeral-storage: 18Gi
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 7Gi
pods: 110
注意看Capacity
和Allocatable
这两个部分。Capacity
表示Node的总资源量,Allocatable
表示Node可以分配给Pod的资源量。通常Allocatable
会比Capacity
小一些,因为Node自身也要占用一些资源。
二、资源请求与限制:给小动物们量身定制食谱
知道了Node的家底,接下来就要给每个Pod(小动物)设置合理的资源请求和限制,就像给它们量身定制食谱一样。
- Resource Request(资源请求): Pod希望使用的最小资源量,相当于小动物们每天必须吃到的食物量,保证它们不会饿肚子。
- Resource Limit(资源限制): Pod最多可以使用的资源量,相当于小动物们每天最多可以吃到的食物量,防止它们暴饮暴食。
咱们可以在Pod的YAML文件中设置resources
字段来指定资源请求和限制。就像这样:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx:latest
resources:
requests:
cpu: 100m # 100 millicores,相当于0.1个CPU核心
memory: 256Mi
limits:
cpu: 200m # 200 millicores,相当于0.2个CPU核心
memory: 512Mi
在这个例子中,my-pod
这个Pod的my-container
容器请求0.1个CPU核心和256MB内存,最多可以使用0.2个CPU核心和512MB内存。
为什么要设置资源请求和限制?
- 保证服务质量: 设置了资源请求,K8s才能保证Pod有足够的资源运行,避免服务崩溃。
- 防止资源浪费: 设置了资源限制,可以防止Pod过度占用资源,影响其他Pod的运行。
- 提高资源利用率: K8s可以根据资源请求来合理地调度Pod,提高集群的整体资源利用率。
如果只设置了Resource Request,没有设置Resource Limit会怎么样?
K8s会默认将Resource Limit设置为与Resource Request相同的值。
如果Resource Request设置得过低,Resource Limit设置得过高会怎么样?
Pod可能会因为资源不足而崩溃,或者因为过度占用资源而影响其他Pod的运行。
三、调度策略:安排小动物们的住所
设置好了资源请求和限制,接下来就要让K8s来安排Pod(小动物)的住所了,也就是选择合适的Node来运行Pod。K8s提供了多种调度策略,咱们可以根据实际情况选择合适的策略。
- 默认调度器(kube-scheduler): K8s自带的调度器,会根据Pod的资源请求、Node的资源情况、NodeSelector、Affinity等因素来选择合适的Node。
- NodeSelector: 通过标签来指定Pod只能运行在特定的Node上。就像给小动物们指定特定的笼子一样。
- Affinity/Anti-Affinity: 比NodeSelector更灵活的调度策略,可以根据Node的标签、Pod的标签等因素来选择合适的Node。就像根据小动物们的习性来选择合适的笼子一样。
- Taints and Tolerations: 用于控制哪些Pod可以运行在特定的Node上。就像给笼子上贴上标签,只有特定的动物才能进入一样。
1. NodeSelector
咱们可以在Pod的YAML文件中使用nodeSelector
字段来指定Node的标签。就像这样:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx:latest
nodeSelector:
disktype: ssd
这个例子中,my-pod
这个Pod只能运行在具有disktype=ssd
标签的Node上。
如何给Node打标签?
使用kubectl label node <node-name> <label-key>=<label-value>
命令。就像这样:
kubectl label node node-01 disktype=ssd
2. Affinity/Anti-Affinity
Affinity(亲和性)用于指定Pod更倾向于运行在具有特定标签的Node上。Anti-Affinity(反亲和性)用于指定Pod不应该运行在具有特定标签的Node上。
Affinity有两种类型:
- requiredDuringSchedulingIgnoredDuringExecution: 必须满足亲和性条件才能调度Pod,否则Pod会一直处于Pending状态。
- preferredDuringSchedulingIgnoredDuringExecution: 尽量满足亲和性条件来调度Pod,如果找不到满足条件的Node,也会选择其他Node来运行Pod。
Anti-Affinity也有两种类型:
- requiredDuringSchedulingIgnoredDuringExecution: 必须不满足反亲和性条件才能调度Pod,否则Pod会一直处于Pending状态。
- preferredDuringSchedulingIgnoredDuringExecution: 尽量不满足反亲和性条件来调度Pod,如果找不到满足条件的Node,也会选择其他Node来运行Pod。
咱们可以在Pod的YAML文件中使用affinity
字段来指定亲和性和反亲和性。就像这样:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx:latest
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: zone
operator: In
values:
- zone-a
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: kubernetes.io/hostname
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
这个例子中:
nodeAffinity
:requiredDuringSchedulingIgnoredDuringExecution
:Pod必须运行在具有disktype=ssd
标签的Node上。preferredDuringSchedulingIgnoredDuringExecution
:Pod尽量运行在具有zone=zone-a
标签的Node上,权重为1。
podAffinity
:requiredDuringSchedulingIgnoredDuringExecution
:Pod必须和具有security=S1
标签的Pod运行在同一个Node上。
podAntiAffinity
:preferredDuringSchedulingIgnoredDuringExecution
:Pod尽量不要和具有security=S2
标签的Pod运行在同一个Node上,权重为100。
3. Taints and Tolerations
Taint(污点)用于标记Node,表示该Node不应该运行某些Pod。Toleration(容忍度)用于允许Pod运行在具有特定污点的Node上。
咱们可以使用kubectl taint node <node-name> <key>=<value>:<effect>
命令来给Node打污点。就像这样:
kubectl taint node node-01 disktype=hdd:NoSchedule
这个例子中,给node-01
这个Node打了一个污点,表示该Node不应该运行没有容忍disktype=hdd
的Pod。NoSchedule
表示如果没有容忍度的Pod,则不会调度到该节点。
effect
有三种类型:
- NoSchedule: 如果没有容忍度的Pod,则不会调度到该节点。
- PreferNoSchedule: 尽量不调度到该节点,但如果找不到其他合适的节点,也会调度到该节点。
- NoExecute: 如果Pod已经在该节点上运行,则会被驱逐。
咱们可以在Pod的YAML文件中使用tolerations
字段来指定容忍度。就像这样:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx:latest
tolerations:
- key: "disktype"
operator: "Equal"
value: "hdd"
effect: "NoSchedule"
这个例子中,my-pod
这个Pod可以运行在具有disktype=hdd:NoSchedule
污点的Node上。
四、资源超卖:刀尖上的舞蹈
资源超卖(Resource Overselling)是指允许Pod的资源请求总量超过Node的资源总量。就像动物园里的小动物们都报了更高的饭量,但实际食物总量是不变的。
为什么要进行资源超卖?
- 提高资源利用率: 有些Pod在大部分时间都处于空闲状态,只有在特定情况下才会占用大量资源。如果咱们按照Pod的资源请求来分配资源,就会造成资源浪费。
- 降低成本: 通过资源超卖,咱们可以在相同的硬件资源上运行更多的Pod,从而降低成本。
资源超卖的风险是什么?
- 资源竞争: 当所有Pod都同时需要大量资源时,可能会出现资源竞争,导致服务性能下降甚至崩溃。
- 不可预测性: 资源超卖会增加系统的不可预测性,难以保证每个Pod都能获得足够的资源。
如何进行资源超卖?
资源超卖是一把双刃剑,需要谨慎使用。咱们可以通过以下方式来降低资源超卖的风险:
- 监控: 密切监控Node的资源使用情况,及时发现资源瓶颈。
- 告警: 设置资源使用率告警,当资源使用率超过阈值时及时通知管理员。
- QoS(Quality of Service): 为Pod设置不同的QoS等级,保证重要Pod的资源供应。
K8s的QoS等级
K8s提供了三种QoS等级:
- Guaranteed: 资源请求和限制都设置了,并且请求和限制的值相等。具有最高的优先级,不会被驱逐。
- Burstable: 资源请求设置了,但资源限制没有设置,或者请求和限制的值不相等。优先级中等,可能会被驱逐。
- BestEffort: 资源请求和限制都没有设置。优先级最低,最容易被驱逐。
K8s会根据Pod的QoS等级来决定Pod的优先级和驱逐顺序。
五、资源优化实战:让你的集群飞起来
说了这么多理论,咱们来点实际的,分享一些资源优化的实战技巧:
- 合理设置资源请求和限制: 这是最基本也是最重要的优化手段。要根据Pod的实际需求来设置资源请求和限制,避免过度分配或分配不足。可以通过监控Pod的资源使用情况来调整资源请求和限制。
- 使用Horizontal Pod Autoscaler(HPA): HPA可以根据Pod的CPU利用率或内存利用率自动调整Pod的数量,从而实现动态的资源伸缩。就像动物园里的小动物数量会根据季节变化一样。
- 使用Vertical Pod Autoscaler(VPA): VPA可以根据Pod的资源使用情况自动调整Pod的资源请求和限制,从而实现动态的资源调整。就像根据小动物们的生长情况来调整它们的食谱一样。
- 使用Resource Quotas: Resource Quotas可以限制Namespace的资源使用总量,防止某个Namespace过度占用资源。就像给动物园里的每个区域设置资源配额一样。
- 使用LimitRange: LimitRange可以限制Namespace中Pod的资源请求和限制的范围,防止Pod设置过高或过低的资源请求和限制。就像给动物园里的小动物们设置食谱的上下限一样。
- 定期清理不使用的资源: 定期清理不使用的Pod、Service、ConfigMap等资源,释放集群的资源。就像定期清理动物园里的垃圾一样。
- 选择合适的镜像: 选择体积小、启动快的镜像,可以减少Pod的资源占用。就像给小动物们选择容易消化的食物一样。
- 优化应用程序代码: 优化应用程序代码,减少CPU和内存的使用,可以降低Pod的资源需求。就像训练小动物们减少能量消耗一样。
- 使用缓存: 使用缓存可以减少对数据库和外部服务的访问,降低Pod的资源需求。就像给小动物们准备一些零食,减少对主食的需求一样。
- 监控和告警: 建立完善的监控和告警系统,及时发现资源瓶颈和问题,及时进行处理。就像给动物园安装监控摄像头和报警器一样。
表格总结:
优化手段 | 作用 | 适用场景 | 注意事项 |
---|---|---|---|
合理设置资源请求和限制 | 保证服务质量,防止资源浪费,提高资源利用率 | 所有Pod | 需要根据Pod的实际需求来设置,可以通过监控Pod的资源使用情况来调整 |
使用Horizontal Pod Autoscaler (HPA) | 动态调整Pod的数量,实现动态的资源伸缩 | CPU或内存利用率波动较大的Pod | 需要设置合理的伸缩策略,避免过度伸缩或伸缩不足 |
使用Vertical Pod Autoscaler (VPA) | 动态调整Pod的资源请求和限制,实现动态的资源调整 | 资源需求不稳定的Pod | 需要设置合理的调整策略,避免过度调整或调整不足 |
使用Resource Quotas | 限制Namespace的资源使用总量,防止某个Namespace过度占用资源 | 多租户环境 | 需要根据Namespace的实际需求来设置,避免限制过度或限制不足 |
使用LimitRange | 限制Namespace中Pod的资源请求和限制的范围,防止Pod设置过高或过低的资源请求和限制 | 所有Pod | 需要根据Namespace的实际需求来设置,避免限制过度或限制不足 |
定期清理不使用的资源 | 释放集群的资源 | 所有集群 | 需要谨慎操作,避免误删重要资源 |
选择合适的镜像 | 减少Pod的资源占用 | 所有Pod | 需要权衡镜像的大小和功能,选择合适的镜像 |
优化应用程序代码 | 降低Pod的资源需求 | 所有Pod | 需要专业的开发人员进行优化 |
使用缓存 | 降低Pod的资源需求 | 需要频繁访问数据库或外部服务的Pod | 需要选择合适的缓存策略,避免缓存失效或缓存雪崩 |
监控和告警 | 及时发现资源瓶颈和问题,及时进行处理 | 所有集群 | 需要设置合理的监控指标和告警阈值,避免误报或漏报 |
六、总结:打造高效稳定的K8s集群
好了,各位英雄们,今天咱们就聊到这里。希望通过今天的分享,大家能够对K8s Node的资源管理和调度优化有更深入的了解,并能够将其应用到实际工作中,打造一个高效稳定的K8s集群。💪
记住,K8s的资源管理和调度优化是一个持续不断的过程,需要咱们不断地学习和实践,才能做到“运筹帷幄之中,决胜千里之外”。祝大家在K8s的世界里玩得开心!🎉