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
污点,但非强制
内存使用不均的可能原因
- 调度策略问题:Kubernetes调度器默认基于资源请求(request)而非实际使用情况进行调度
- 节点亲和性/反亲和性:可能有某些Pod配置了特定节点亲和性
- 资源请求设置不当:Pod的资源请求与实际使用不符
-
污点影响:虽然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
优先级函数来优化资源平衡。
长期建议
- 为所有工作负载设置合理的资源请求和限制
- 考虑使用Vertical Pod Autoscaler自动调整资源请求
- 监控节点资源使用情况,设置警报
- 对于关键工作负载,使用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-node01
有PreferNoSchedule
污点,调度器会尽量避免向其调度 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
。 -
建议操作:
- 检查并修正 Pod 的
resources.requests
。 - 使用
kubectl drain
手动平衡负载。 - 长期方案:部署 Descheduler 或 VPA(Vertical Pod Autoscaler)。
- 检查并修正 Pod 的
如何在已有集群中 安装 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 # 仅配置内存
高级调优建议
-
逐步调整阈值:
- 首次部署建议从宽松阈值开始(如示例配置),观察日志后再逐步收紧。
- 避免设置过于激进的阈值(如
memory: 90
),可能导致频繁迁移。
-
结合资源请求:
- 确保Pod设置了合理的
resources.requests
,否则Descheduler无法准确计算利用率。
- 确保Pod设置了合理的
-
排除系统Pod:
strategies: "LowNodeUtilization": params: excludeKubeSystem: true # 不驱逐kube-system命名空间的Pod
-
调度频率调整:
- 默认每2小时运行一次,可通过修改CronJob调整:
修改kubectl edit cronjob descheduler -n kube-system
schedule: "*/2 * * * *"
为更短间隔(如"*/30 * * * *"
)。
- 默认每2小时运行一次,可通过修改CronJob调整:
验证配置生效
-
检查策略是否加载:
kubectl logs -n kube-system <descheduler-pod-name> | grep "Loaded policy"
输出应包含你的自定义阈值。
-
手动触发测试:
kubectl create job --from=cronjob/descheduler test-run -n kube-system kubectl logs -n kube-system -l job-name=test-run | grep "Evicting"
-
观察节点变化:
watch kubectl top nodes # 查看节点资源利用率变化
常见问题
-
为何没有Pod被驱逐?
- 可能没有同时满足高负载和低负载的节点。
- 检查日志:
kubectl logs -n kube-system <descheduler-pod-name>
-
如何排除特定命名空间?
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
),避免控制平面过载。