【k8s】k8s 各节点 cpu memory 使用不均

k8s集群信息如下,如何使得内存资源使用比较均匀?

$ kubectl  get nodes
NAME            STATUS   ROLES                     AGE    VERSION
k8s-ingress01   Ready    yzj-ingress-nginx-outer   7d9h   v1.20.10
k8s-ingress02   Ready    yzj-ingress-nginx-outer   7d9h   v1.20.10
k8s-node01      Ready    control-plane,master      7d9h   v1.20.10
k8s-node02      Ready    control-plane,master      7d9h   v1.20.10
k8s-node03      Ready    control-plane,master      7d9h   v1.20.10
k8s-node04      Ready    yzj-ingress-nginx-inner   7d9h   v1.20.10
k8s-node05      Ready    yzj-ingress-nginx-inner   7d9h   v1.20.10
k8s-node06      Ready    <none>                    7d9h   v1.20.10
k8s-node07      Ready    <none>                    7d9h   v1.20.10

$ kubectl  top nodes
NAME            CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
k8s-ingress01   81m          0%     11136Mi         39%
k8s-ingress02   117m         0%     2855Mi          10%
k8s-node01      862m         5%     22130Mi         36%
k8s-node02      1780m        11%    54774Mi         90%
k8s-node03      1133m        7%     46390Mi         76%
k8s-node04      769m         4%     48760Mi         80%
k8s-node05      1617m        10%    53032Mi         87%
k8s-node06      1611m        10%    50438Mi         83%
k8s-node07      1924m        12%    59594Mi         98%


$ kubectl get nodes -o custom-columns="NAME:.metadata.name,TAINTS:.spec.taints" | awk 'NR==1 {print $0} NR>1 {printf "%-30s ", $1; $1=""; print $0}'
NAME            TAINTS
k8s-ingress01                   <none>
k8s-ingress02                   <none>
k8s-node01                      [map[effect:PreferNoSchedule key:node-role.kubernetes.io/master]]
k8s-node02                      [map[effect:PreferNoSchedule key:node-role.kubernetes.io/master]]
k8s-node03                      [map[effect:PreferNoSchedule key:node-role.kubernetes.io/master]]
k8s-node04                      <none>
k8s-node05                      <none>
k8s-node06                      <none>
k8s-node07                      <none>

Kubernetes 节点资源使用不均分析及解决方案

当前情况分析

从提供的信息可以看出:

  • k8s-node07 内存使用率高达 98%,而 k8s-node01 只有 36%
  • 所有带"node"标识的节点都可以调度业务pod
  • 三个master节点(k8s-node01/02/03)有PreferNoSchedule污点,但非强制

内存使用不均的可能原因

  1. 调度策略问题:Kubernetes调度器默认基于资源请求(request)而非实际使用情况进行调度
  2. 节点亲和性/反亲和性:可能有某些Pod配置了特定节点亲和性
  3. 资源请求设置不当:Pod的资源请求与实际使用不符
  4. 污点影响:虽然master节点有PreferNoSchedule污点,但只是"偏好不调度"而非禁止

解决方案

1. 检查当前Pod分布

kubectl get pods -o wide --all-namespaces | grep k8s-node07
kubectl get pods -o wide --all-namespaces | grep k8s-node01

2. 手动迁移部分Pod

对k8s-node07上的Pod进行驱逐,让它们重新调度:

kubectl drain k8s-node07 --ignore-daemonsets --delete-emptydir-data

然后解除不可调度状态:

kubectl uncordon k8s-node07

3. 使用Descheduler重新平衡

安装并配置Kubernetes Descheduler来自动平衡节点负载。

4. 配置资源请求和限制

确保Pod设置了合理的资源请求(request)和限制(limit),特别是内存。

5. 使用Pod拓扑分布约束

添加拓扑分布约束,确保Pod均匀分布:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 5
  template:
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: ScheduleAnyway
        labelSelector:
          matchLabels:
            app: my-app

6. 调整调度器配置

可以修改kube-scheduler的--config参数,配置RequestedToCapacityRatio优先级函数来优化资源平衡。

长期建议

  1. 为所有工作负载设置合理的资源请求和限制
  2. 考虑使用Vertical Pod Autoscaler自动调整资源请求
  3. 监控节点资源使用情况,设置警报
  4. 对于关键工作负载,使用Pod反亲和性避免单节点过载

通过以上方法,应该能够将k8s-node07的部分负载迁移到k8s-node01,实现更均衡的资源使用。

资源调度不均匀跟使用的cgroup 是否有关系?

资源调度不均匀与使用 cgroupfs 驱动(而非 systemd有一定关系,但通常不是主要原因。以下是详细分析:


1. cgroupfs 与资源调度的关系

  • 资源统计差异

    • cgroupfs 直接与内核 cgroup 交互,而 systemd 通过 systemd 的 cgroup 管理器。
    • systemd 对 cgroup 层次结构的管理更严格,可能更精确地统计资源使用(如内存、CPU),但实际差异通常较小。
  • 性能影响

    • cgroupfs 在高负载场景下可能出现性能问题(如统计延迟),导致调度器获取的资源数据不够实时。
    • 但 Kubernetes 调度器依赖的是 kubelet 上报的节点资源数据(通过 kubectl top 或 Metrics Server),而非直接读取 cgroup。
  • 结论

    • cgroupfs 可能轻微影响资源统计的实时性,但通常不会直接导致调度不均。

2. 资源调度不均的更可能原因

(1)调度器依赖的是资源请求(Request),而非实际使用

  • 如果 Pod 未合理设置 resources.requests,调度器无法感知真实需求。
    # 示例:未设置 requests 的 Pod 可能被随机调度
    spec:
      containers:
      - name: nginx
        image: nginx
        # 无 resources 配置
    

(2)节点亲和性/污点干扰

  • k8s-node01PreferNoSchedule 污点,调度器会尽量避免向其调度 Pod,除非 Pod 显式容忍:
    tolerations:
    - key: node-role.kubernetes.io/master
      operator: Exists
      effect: PreferNoSchedule
    

(3)实际资源使用 vs 请求资源

  • 如果 Pod 的 requests.memory 设置过低(例如 100Mi),但实际使用 2Gi,调度器会认为节点资源充足,导致节点过载。

(4)DaemonSet 或其他固定调度负载

  • 某些 Pod(如日志收集器、监控代理)通过 DaemonSet 运行,可能集中在部分节点。

3. 如何验证是否与 cgroupfs 有关

(1)检查当前 cgroup 驱动

# 在节点上执行
docker info | grep -i cgroup
# 或(如果是 containerd)
containerd config default | grep cgroup

(2)对比 kubectl top 与节点实际 cgroup 数据

  • k8s-node07 上检查内存使用:
    # 查看 cgroup 内存统计
    cat /sys/fs/cgroup/memory/kubepods/memory.usage_in_bytes
    # 对比 kubectl top 数据
    kubectl top node k8s-node07
    
    • 如果两者差异较大,可能是 cgroupfs 统计延迟导致调度器误判。

4. 解决方案

(1)优先检查 Pod 资源请求配置

kubectl get pods -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].resources.requests.memory}{"\n"}{end}'
  • 为未设置 requests 的 Pod 添加合理值。

(2)调整调度策略

  • 强制平衡节点负载(需安装 Descheduler):
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      "LowNodeUtilization":
        enabled: true
        params:
          nodeResourceUtilizationThresholds:
            thresholds:
              "memory": 50  # 目标节点内存使用率低于 50%
            targetThresholds:
              "memory": 70  # 高负载节点内存使用率超过 70% 时触发迁移
    

(3)切换到 systemd cgroup 驱动(可选)

  • 修改 /etc/docker/daemon.json(需重启节点):
    {
      "exec-opts": ["native.cgroupdriver=systemd"]
    }
    
  • 注意:切换驱动需重建容器,谨慎操作!

(4)手动迁移高负载 Pod

# 1. 标记 k8s-node07 不可调度
kubectl cordon k8s-node07

# 2. 驱逐 Pod(保留 DaemonSet)
kubectl drain k8s-node07 --ignore-daemonsets --delete-emptydir-data

# 3. 等待 Pod 重新调度到 k8s-node01 后恢复
kubectl uncordon k8s-node07

总结

  • 根本原因:调度不均更可能是由于 Pod 资源请求配置不合理污点/亲和性干扰,而非 cgroupfs
  • 建议操作
    1. 检查并修正 Pod 的 resources.requests
    2. 使用 kubectl drain 手动平衡负载。
    3. 长期方案:部署 Descheduler 或 VPA(Vertical Pod Autoscaler)。

如何在已有集群中 安装 Descheduler ?

在已有 Kubernetes 集群中安装 Descheduler 可以通过 Helm 或直接应用 YAML 清单文件完成。以下是详细步骤:


方法 1:使用 Helm 安装(推荐)

1. 添加 Helm 仓库

helm repo add descheduler https://kubernetes-sigs.github.io/descheduler/
helm repo update

2. 创建配置文件(values.yaml

创建自定义配置,例如以下示例启用 LowNodeUtilization 策略:

# values.yaml
deschedulerPolicy:
  strategies:
    "LowNodeUtilization":
      enabled: true
      params:
        nodeResourceUtilizationThresholds:
          thresholds:
            "cpu": 20
            "memory": 20
            "pods": 20
          targetThresholds:
            "cpu": 50
            "memory": 50
            "pods": 50

3. 安装 Descheduler

helm upgrade --install descheduler descheduler/descheduler \
  -n kube-system \
  -f values.yaml

4. 验证安装

kubectl get pods -n kube-system | grep descheduler
kubectl logs -n kube-system <descheduler-pod-name>

方法 2:手动通过 YAML 安装

1. 下载官方 Descheduler 配置文件

wget https://raw.githubusercontent.com/kubernetes-sigs/descheduler/master/kubernetes/base/rbac.yaml
wget https://raw.githubusercontent.com/kubernetes-sigs/descheduler/master/kubernetes/base/configmap.yaml
wget https://raw.githubusercontent.com/kubernetes-sigs/descheduler/master/kubernetes/base/deployment.yaml

2. 修改策略配置(可选)

编辑 configmap.yaml,例如启用 LowNodeUtilization

apiVersion: v1
kind: ConfigMap
metadata:
  name: descheduler-policy-configmap
  namespace: kube-system
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      "LowNodeUtilization":
        enabled: true
        params:
          nodeResourceUtilizationThresholds:
            thresholds:
              "cpu": 20
              "memory": 20
              "pods": 20
            targetThresholds:
              "cpu": 50
              "memory": 50
              "pods": 50

3. 部署 Descheduler

kubectl apply -f rbac.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml

4. 检查运行状态

kubectl get pods -n kube-system -l app=descheduler
kubectl logs -n kube-system <descheduler-pod-name>

验证 Descheduler 是否生效

1. 查看日志

kubectl logs -n kube-system <descheduler-pod-name> | grep -i "evicting pod"

2. 手动触发一次运行(测试)

kubectl create job --from=cronjob/descheduler test-run -n kube-system
kubectl logs -n kube-system -l job-name=test-run

3. 观察 Pod 重新调度

kubectl get pods -o wide -A | grep -E "Evicted|Terminating"

关键配置说明

配置项 说明
LowNodeUtilization 平衡节点 CPU/内存/Pod 数量
RemoveDuplicates 避免同一应用 Pod 集中在同一节点
RemovePodsViolatingInterPodAntiAffinity 强制反亲和性规则
thresholds 低于此值视为低利用率节点
targetThresholds 高于此值视为高利用率节点

卸载 Descheduler

Helm 安装方式

helm uninstall descheduler -n kube-system

YAML 安装方式

kubectl delete -f rbac.yaml -f configmap.yaml -f deployment.yaml

总结

  • 推荐使用 Helm,便于管理策略更新。
  • 关键策略LowNodeUtilization 适用于平衡节点资源。
  • 调度频率:默认每 2小时 运行一次(可通过 --schedule 参数调整)。

通过以上步骤,Descheduler 会自动将 Pod 从高负载节点(如 k8s-node07)迁移到低负载节点(如 k8s-node01),实现集群资源均衡。

以下是针对 descheduler-policy-configmap 配置文件的详细解读,以及如何自定义阈值的指南:


配置文件逐行解析

apiVersion: v1
kind: ConfigMap
metadata:
  name: descheduler-policy-configmap  # ConfigMap名称,供Deployment挂载
  namespace: kube-system              # 必须部署在kube-system命名空间
data:
  policy.yaml: |                      # 策略文件内容(YAML格式)
    apiVersion: "descheduler/v1alpha1" # Descheduler策略API版本
    kind: "DeschedulerPolicy"
    strategies:
      "LowNodeUtilization":           # 启用"低节点利用率"策略
        enabled: true                 # 必须设为true才能生效
        params:
          nodeResourceUtilizationThresholds:
            thresholds:               # 低利用率节点的阈值(低于此值视为空闲)
              "cpu": 20               # CPU利用率阈值20%
              "memory": 20            # 内存利用率阈值20%
              "pods": 20              # Pod数量利用率阈值20%
            targetThresholds:         # 高利用率节点的阈值(高于此值视为过载)
              "cpu": 50               # CPU利用率阈值50%
              "memory": 50            # 内存利用率阈值50%
              "pods": 50              # Pod数量利用率阈值50%

阈值参数详解

1. thresholds(低利用率阈值)

  • 作用:当节点资源使用率 低于 这些值时,Descheduler 会将其标记为"低负载节点"。
  • 示例
    • cpu: 20 表示节点CPU使用率 <20% 时视为空闲。
    • memory: 20 表示节点内存使用率 <20% 时视为空闲。

2. targetThresholds(高利用率阈值)

  • 作用:当节点资源使用率 高于 这些值时,Descheduler 会将其标记为"高负载节点"。
  • 示例
    • cpu: 50 表示节点CPU使用率 >50% 时视为过载。
    • memory: 50 表示节点内存使用率 >50% 时视为过载。

3. 策略逻辑

  • Descheduler 会 从高负载节点驱逐Pod,并尝试将其调度到低负载节点。
  • 触发条件:必须同时存在高负载节点和低负载节点才会触发平衡。

如何自定义阈值?

场景1:针对内存优化(解决你的k8s-node07问题)

strategies:
  "LowNodeUtilization":
    enabled: true
    params:
      nodeResourceUtilizationThresholds:
        thresholds:
          "cpu": 10
          "memory": 15    # 内存<15%视为低负载
          "pods": 10
        targetThresholds:
          "cpu": 60
          "memory": 70    # 内存>70%视为高负载(优先迁移)
          "pods": 60

场景2:严格平衡(频繁触发迁移)

thresholds:
  "cpu": 30
  "memory": 30
  "pods": 30
targetThresholds:
  "cpu": 40
  "memory": 40    # 较小的差值会触发更频繁的平衡
  "pods": 40

场景3:仅关注内存(忽略CPU和Pods)

strategies:
  "LowNodeUtilization":
    enabled: true
    params:
      nodeResourceUtilizationThresholds:
        thresholds:
          "memory": 20    # 仅配置内存
        targetThresholds:
          "memory": 80    # 仅配置内存

高级调优建议

  1. 逐步调整阈值

    • 首次部署建议从宽松阈值开始(如示例配置),观察日志后再逐步收紧。
    • 避免设置过于激进的阈值(如memory: 90),可能导致频繁迁移。
  2. 结合资源请求

    • 确保Pod设置了合理的 resources.requests,否则Descheduler无法准确计算利用率。
  3. 排除系统Pod

    strategies:
      "LowNodeUtilization":
        params:
          excludeKubeSystem: true  # 不驱逐kube-system命名空间的Pod
    
  4. 调度频率调整

    • 默认每2小时运行一次,可通过修改CronJob调整:
      kubectl edit cronjob descheduler -n kube-system
      
      修改 schedule: "*/2 * * * *" 为更短间隔(如 "*/30 * * * *")。

验证配置生效

  1. 检查策略是否加载

    kubectl logs -n kube-system <descheduler-pod-name> | grep "Loaded policy"
    

    输出应包含你的自定义阈值。

  2. 手动触发测试

    kubectl create job --from=cronjob/descheduler test-run -n kube-system
    kubectl logs -n kube-system -l job-name=test-run | grep "Evicting"
    
  3. 观察节点变化

    watch kubectl top nodes  # 查看节点资源利用率变化
    

常见问题

  1. 为何没有Pod被驱逐?

    • 可能没有同时满足高负载和低负载的节点。
    • 检查日志:kubectl logs -n kube-system <descheduler-pod-name>
  2. 如何排除特定命名空间?

    strategies:
      "LowNodeUtilization":
        params:
          namespaces:
            exclude:
              - "monitoring"  # 不驱逐监控相关的Pod
    

通过合理调整这些阈值,你可以精确控制Descheduler在集群中的平衡行为,解决类似 k8s-node07 内存过高的问题。

k8s-node01 k8s-node02 k8s-node03 是 master 节点,如何让其像其他work节点一样正常地被调度业务pod,使得整个k8s集群 各节点内存使用率均匀?

$ kubectl get nodes -o custom-columns="NAME:.metadata.name,TAINTS:.spec.taints" | awk 'NR==1 {print $0} NR>1 {printf "%-30s ", $1; $1=""; print $0}'
NAME            TAINTS
k8s-ingress01                   <none>
k8s-ingress02                   <none>
k8s-node01                      [map[effect:PreferNoSchedule key:node-role.kubernetes.io/master]]
k8s-node02                      [map[effect:PreferNoSchedule key:node-role.kubernetes.io/master]]
k8s-node03                      [map[effect:PreferNoSchedule key:node-role.kubernetes.io/master]]
k8s-node04                      <none>
k8s-node05                      <none>
k8s-node06                      <none>
k8s-node07                      <none>

要让 Kubernetes Master 节点(k8s-node01、k8s-node02、k8s-node03)也能调度业务 Pod,并实现集群负载均衡(各节点内存使用率接近),可以按照以下步骤操作:


1. 移除 Master 节点的 NoSchedule 污点(Taint)

默认情况下,Master 节点有 node-role.kubernetes.io/master:PreferNoSchedule 污点,阻止 Pod 调度。
执行以下命令移除污点

kubectl taint nodes k8s-node01 node-role.kubernetes.io/master:PreferNoSchedule-
kubectl taint nodes k8s-node02 node-role.kubernetes.io/master:PreferNoSchedule-
kubectl taint nodes k8s-node03 node-role.kubernetes.io/master:PreferNoSchedule-

说明

  • - 表示移除污点。
  • 如果污点是 NoSchedule(而非 PreferNoSchedule),需替换对应的污点名称。

2. 确保 Master 节点有足够的资源

检查 Master 节点的资源是否足够运行业务 Pod:

kubectl describe nodes k8s-node01 | grep -A 10 "Allocatable"

如果资源紧张,可以:

  • 扩容 Master 节点(增加 CPU/内存)。
  • 限制业务 Pod 的资源请求(避免占用过多资源影响控制平面)。

3. 启用集群负载均衡

为了让 Pod 均匀分布在所有节点(包括 Master),需配置调度策略:

方案 1:使用 PodTopologySpread 调度规则(推荐)

在 Pod 的 spec 中添加以下规则,强制均匀分布:

spec:
  topologySpreadConstraints:
    - maxSkew: 1              # 允许的最大不均衡数(1 表示尽量均匀)
      topologyKey: kubernetes.io/hostname  # 按节点分布
      whenUnsatisfiable: ScheduleAnyway  # 即使不均衡也调度
      labelSelector: {}       # 对所有 Pod 生效

适用场景:Deployment、StatefulSet 等。

方案 2:使用 Descheduler 重新平衡

安装 Kubernetes Descheduler,定期驱逐不均衡的 Pod 并重新调度:

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/descheduler/master/kubernetes/base/rbac.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/descheduler/master/kubernetes/base/configmap.yaml

配置 LowNodeUtilization 策略,关注内存均衡:

apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
  LowNodeUtilization:
    enabled: true
    params:
      nodeResourceUtilizationThresholds:
        thresholds:
          memory: 20  # 节点内存使用率 <20% 视为 "underutilized"
          cpu: 20
        targetThresholds:
          memory: 50  # 目标内存使用率 <=50%
          cpu: 50

方案 3:手动设置反亲和性(Fallback)

在业务 Pod 中指定反亲和性,避免集中到某个节点:

spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          podAffinityTerm:
            labelSelector:
              matchLabels:
                app: your-app  # 替换为你的业务 Pod 标签
            topologyKey: kubernetes.io/hostname

4. 监控节点资源使用率

使用以下命令实时监控节点内存/CPU 使用情况:

kubectl top nodes

或部署 Prometheus + Grafana 监控集群负载。


最终效果

  • Master 节点可以调度业务 Pod。
  • Pod 会均匀分布在所有节点(包括 Master)。
  • 集群内存/CPU 使用率趋于均衡。

注意:如果 Master 节点资源较少,建议仍保留部分污点(如 PreferNoSchedule),避免控制平面过载。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。