K8s集群GPU算力弹性伸缩:从零搭建ElasticityOps
在Kubernetes集群里做GPU算力弹性伸缩,核心是让GPU节点能按任务负载自动增减,而不是一直空转烧钱。
下面按零基础也能跟做的顺序,从环境确认到弹性验证走一遍,重点解决“怎么配、怎么验、怎么不踩坑”。
先确认集群和GPU节点是否就绪
弹性伸缩依赖两个前提:K8s集群本身能正常调度Pod,GPU节点上驱动和容器运行时能识别显卡。
登录任意GPU节点,执行:
nvidia-smi
如果能看到显卡型号、驱动版本和显存占用,说明驱动正常。
接着检查容器运行时是否已配置NVIDIA支持:
docker info | grep -i nvidia
在containerd环境下,查看配置文件:
cat /etc/containerd/config.toml | grep -A5 nvidia
确认默认运行时为nvidia。
如果这里没配好,后面设备插件会报no NVIDIA devices found之类的错误。
集群侧用kubectl get nodes确认GPU节点处于Ready状态,并给节点打上标签,便于后续调度:
kubectl label nodes accelerator=nvidia
安装NVIDIA设备插件,让K8s看见GPU
K8s本身不直接管理GPU,需要设备插件把GPU资源注册成nvidia.com/gpu。
推荐用Helm安装官方插件:
helm repo add nvidia https://nvidia.github.io/k8s-device-plugin
helm repo update
helm install nvidia-device-plugin nvidia/k8s-device-plugin \
--namespace kube-system \
--set affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key=accelerator \
--set affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator=In \
--set affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0]=nvidia
安装后检查DaemonSet是否在GPU节点上运行:
kubectl get pods -n kube-system -l name=nvidia-device-plugin-ds
再查看节点可分配资源:
kubectl describe node | grep -A5 Allocatable
看到nvidia.com/gpu: 1(或对应卡数)就说明注册成功。
此时提交一个测试Pod申请GPU,能正常Running即通过。
配置基于GPU利用率的弹性伸缩
GPU算力弹性伸缩通常分两层:Pod副本数伸缩用HPA,节点数伸缩用Cluster Autoscaler。
这里先讲最常用的HPA按GPU利用率扩缩容。
前提是集群已安装Metrics Server,并且有能暴露GPU利用率的指标适配器,例如DCGM Exporter配合Prometheus Adapter。
确认自定义指标可用:
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | head
然后为业务Deployment创建HPA,按nvidia_gpu_utilization指标伸缩:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: gpu-workload-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: gpu-inference
minReplicas: 1
maxReplicas: 4
metrics:
- type: Pods
pods:
metric:
name: nvidia_gpu_utilization
target:
type: AverageValue
averageValue: "70"
应用后观察:
kubectl get hpa gpu-workload-hpa -w
当GPU利用率持续超过70%,副本数应逐步增加;
低于阈值一段时间后回落。
注意minReplicas不要设为0,除非业务能接受冷启动。
如果要做节点级弹性,需要提前配置Cluster Autoscaler并让GPU节点池支持自动扩缩,且云厂商节点组要允许GPU实例类型。
这部分因平台而异,建议先确认节点池是否支持再动手。
避坑与验证要点
驱动与插件版本不匹配是最常见故障。
设备插件日志里出现Failed to initialize NVML,优先检查节点驱动版本和插件镜像是否兼容,必要时固定插件版本。
HPA无法读取GPU指标时,先确认Metrics Server正常,再检查自定义指标API是否返回数据。
如果kubectl top pods正常但HPA显示unknown,多半是Prometheus Adapter规则没匹配上指标名。
扩缩容延迟属于正常现象。
HPA默认有稳定窗口,避免指标抖动导致频繁伸缩。
测试时不要刚提交负载就断定没生效,观察3到5分钟。
验证弹性是否真正生效,用压测工具拉高GPU利用率,同时开两个终端分别执行kubectl get hpa -w和kubectl get pods -w,看到副本数随利用率变化即算通过。
缩容后确认GPU节点没有被误删,避免影响其他任务。
常见疑问
GPU节点能不能和CPU节点混用同一个HPA? 可以,
但调度时要用nodeSelector或亲和性把GPU Pod固定到GPU节点,
否则副本可能落到没有GPU的机器上一直Pending。
弹性伸缩会不会导致正在跑的任务中断? 缩容时如果Pod正在执行长任务,需要配置terminationGracePeriodSeconds和优雅退出逻辑,否则可能被强制终止。
没有云厂商自动扩缩节点,只做Pod级弹性够用吗? 如果GPU节点数量固定,Pod级弹性只能复用已有显卡,无法真正增加总算力。
要突破单节点上限,仍需节点级弹性或预留足够GPU节点。
整体思路就是:先让K8s识别GPU,再让指标可读,最后用HPA或节点池策略驱动伸缩。
每一步都用kubectl describe和日志验证结果,不要跳过确认环节。