K8s Node 资源管理与调度优化

好的,各位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

注意看CapacityAllocatable这两个部分。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的优先级和驱逐顺序。

五、资源优化实战:让你的集群飞起来

说了这么多理论,咱们来点实际的,分享一些资源优化的实战技巧:

  1. 合理设置资源请求和限制: 这是最基本也是最重要的优化手段。要根据Pod的实际需求来设置资源请求和限制,避免过度分配或分配不足。可以通过监控Pod的资源使用情况来调整资源请求和限制。
  2. 使用Horizontal Pod Autoscaler(HPA): HPA可以根据Pod的CPU利用率或内存利用率自动调整Pod的数量,从而实现动态的资源伸缩。就像动物园里的小动物数量会根据季节变化一样。
  3. 使用Vertical Pod Autoscaler(VPA): VPA可以根据Pod的资源使用情况自动调整Pod的资源请求和限制,从而实现动态的资源调整。就像根据小动物们的生长情况来调整它们的食谱一样。
  4. 使用Resource Quotas: Resource Quotas可以限制Namespace的资源使用总量,防止某个Namespace过度占用资源。就像给动物园里的每个区域设置资源配额一样。
  5. 使用LimitRange: LimitRange可以限制Namespace中Pod的资源请求和限制的范围,防止Pod设置过高或过低的资源请求和限制。就像给动物园里的小动物们设置食谱的上下限一样。
  6. 定期清理不使用的资源: 定期清理不使用的Pod、Service、ConfigMap等资源,释放集群的资源。就像定期清理动物园里的垃圾一样。
  7. 选择合适的镜像: 选择体积小、启动快的镜像,可以减少Pod的资源占用。就像给小动物们选择容易消化的食物一样。
  8. 优化应用程序代码: 优化应用程序代码,减少CPU和内存的使用,可以降低Pod的资源需求。就像训练小动物们减少能量消耗一样。
  9. 使用缓存: 使用缓存可以减少对数据库和外部服务的访问,降低Pod的资源需求。就像给小动物们准备一些零食,减少对主食的需求一样。
  10. 监控和告警: 建立完善的监控和告警系统,及时发现资源瓶颈和问题,及时进行处理。就像给动物园安装监控摄像头和报警器一样。

表格总结:

优化手段 作用 适用场景 注意事项
合理设置资源请求和限制 保证服务质量,防止资源浪费,提高资源利用率 所有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的世界里玩得开心!🎉

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注