一、K8s集群跑通义千问的核心痛点:资源预留卡脖子、GPU伸缩跟不上

很多团队把通义千问这类大模型部署到K8s(后面简称K8s)集群时,都会遇到两个绕不开的麻烦:一是资源留不住,明明提前给模型申请了CPU、内存,结果被其他任务抢了,模型跑着跑着就卡成PPT;二是GPU用得浪费,闲的时候GPU空转占资源,忙的时候又不够用,想动态调却调不动。这俩问题不解决,大模型的部署成本高、体验差,完全发挥不出优势。

先给大家补个小常识:K8s里的“资源”分两种,一种是“申请(request)”,相当于你告诉K8s“我至少需要这么多资源”,K8s会保证给你;另一种是“限制(limit)”,相当于“我最多能用这么多”,超过就会被限流。很多人一开始会搞混这俩的区别,后面的配置会用到,大家先记牢。

二、问题拆解:为啥会出现这俩痛点?

2.1 资源预留的坑:K8s的默认规则不适合大模型

K8s默认的调度逻辑是“尽量不浪费资源”,比如你给模型申请了2核CPU,K8s会把节点上剩下的资源分给其他小任务。但大模型不一样,它需要稳定的CPU、内存来加载权重、处理请求,要是节点上有其他任务占用了节点的核心资源(比如节点的CPU被其他任务占满了),就算你给模型的request设了2核,模型实际能用到的也会很少,因为K8s的request只是“调度时的承诺”,不是“运行时的隔离”。

举个例子:你给模型设了request: cpu:2, memory:4G,节点上有个其他任务设了request: cpu:0, memory:0,但实际跑起来占了3核CPU。K8s调度时会觉得“这个节点剩下的CPU够模型用”,就把模型调度到这个节点,结果模型跑的时候,两个任务抢CPU,模型的响应速度直接掉一半。

2.2 GPU弹性伸缩的难:K8s的默认伸缩不识别GPU的实际负载

K8s默认的伸缩器(HPA)是按CPU、内存的使用率来调整副本数的,但大模型的GPU负载很特殊:当没有请求时,GPU的使用率可能只有0,但有请求时,GPU的使用率会瞬间拉满。如果按CPU、内存来伸缩,闲的时候副本数减到1,GPU空转;忙的时候,就算CPU、内存没满,GPU已经不够用了,请求排队严重。

比如你部署了3个模型副本,每个副本带1块GPU。当有10个请求时,3块GPU都跑满了,请求要排队,但这时候每个副本的CPU使用率可能只有20%,HPA觉得“CPU没满,不用加副本”,结果用户体验极差。

三、解决方案:从配置到工具的全流程破解

3.1 资源预留:用“节点亲和性+静态预留”解决卡脖子

要解决资源预留的问题,核心是“给模型专属的资源空间”,不让其他任务抢。具体分两步:

第一步,给节点打标签,把一部分节点专门用来跑大模型。比如给节点打model: qwen的标签,告诉K8s“这个节点是给通义千问用的”。

第二步,用K8s的节点亲和性(Node Affinity)让模型只调度到打了标签的节点,同时给节点做静态资源预留,保证节点上的资源不会被其他任务占用。

示例1:节点标签与亲和性配置

技术栈:Kubernetes(1.24+) 首先给节点打标签,假设节点的名字是k8s-node-01

# 给节点打标签,key是model,value是qwen
kubectl label nodes k8s-node-01 model=qwen

然后给通义千问的Deployment配置节点亲和性,让模型只调度到打了model=qwen标签的节点:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: qwen-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: qwen
  template:
    metadata:
      labels:
        app: qwen
    spec:
      # 节点亲和性配置:必须调度到打了model=qwen标签的节点
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: model
                operator: In
                values:
                - qwen
      containers:
      - name: qwen-container
        image: alibaba/qwen:7b-v1.0 # 假设这是通义千问的官方镜像
        # 资源申请:至少需要2核CPU、4G内存
        resources:
          requests:
            cpu: "2"
            memory: "4Gi"
          # 资源限制:最多能用4核CPU、8G内存
          limits:
            cpu: "4"
            memory: "8Gi"
        ports:
        - containerPort: 8000

第三步,给节点做静态资源预留,防止其他任务占用节点的核心资源。静态资源预留是K8s 1.15+支持的功能,用来给节点的系统组件、指定任务预留资源,其他任务不能用。

示例2:节点静态资源预留配置

技术栈:Kubernetes(1.24+) 修改节点的Kubelet配置,给节点预留2核CPU、4G内存,只给通义千问用:

# 编辑节点的Kubelet配置文件,路径一般是/etc/kubernetes/kubelet.conf
vim /etc/kubernetes/kubelet.conf

在配置文件里添加以下内容(注意:不同版本的K8s,Kubelet配置的路径和格式可能略有不同,比如有些是/etc/kubernetes/kubelet.config):

# 节点静态资源预留配置:给系统组件预留0.5核CPU、1G内存,给通义千问预留2核CPU、4G内存
# 这里的reserved为系统组件预留,extended为自定义任务预留
reserved:
  cpu: "0.5"
  memory: "1Gi"
extended:
  cpu: "2"
  memory: "4Gi"

修改完后,重启Kubelet服务生效:

systemctl restart kubelet

这样配置后,节点上的资源就分成了三部分:系统用的、通义千问用的、其他任务用的。其他任务最多只能用节点剩下的资源,不会抢通义千问的专属资源,模型的运行就稳定了。

3.2 GPU弹性伸缩:用“自定义指标伸缩(HPA v2)”解决动态调不动

要解决GPU弹性伸缩的问题,核心是“让伸缩器识别GPU的实际负载”。K8s的HPA v2支持自定义指标,我们可以把GPU的使用率、请求排队数作为伸缩的依据。

具体分两步:

第一步,部署“自定义指标适配器”(比如Prometheus Adapter),用来收集GPU的负载数据,并且把数据提供给HPA。

第二步,给通义千问的Deployment配置HPA v2,让它根据GPU的负载动态调整副本数。

示例3:自定义指标适配器部署(Prometheus Adapter)

技术栈:Kubernetes(1.24+)、Prometheus(2.30+) 首先,我们假设集群已经部署了Prometheus,用来收集GPU的负载数据。然后部署Prometheus Adapter,把Prometheus的数据转换成K8s能识别的自定义指标:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus-adapter
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus-adapter
  template:
    metadata:
      labels:
        app: prometheus-adapter
    spec:
      containers:
      - name: prometheus-adapter
        image: directxman12/k8s-prometheus-adapter:v0.9.0
        args:
        - --config=/etc/adapter/config.yaml
        - --prometheus-url=http://prometheus:9090 # 指向集群内的Prometheus服务
        ports:
        - containerPort: 6443
        volumeMounts:
        - name: config-volume
          mountPath: /etc/adapter
      volumes:
      - name: config-volume
        configMap:
          name: prometheus-adapter-config

然后配置ConfigMap,告诉适配器要收集哪些GPU指标:

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-adapter-config
  namespace: monitoring
data:
  config.yaml: |
    rules:
    # 把Prometheus里的GPU使用率指标(假设指标名是nvidia_gpu_utilization)转换成K8s的自定义指标
    - seriesQuery: 'nvidia_gpu_utilization{container!="", pod!=""}'
      resources:
        overrides:
          namespace: {resource: "namespace"}
          pod: {resource: "pod"}
      name:
        matches: "^(.*)$"
        as: "gpu_utilization"
      metricsQuery: 'sum(nvidia_gpu_utilization{container!="", pod!="", namespace="<<.Namespace>>", pod="<<.Pod>>"}) by (pod)'

示例4:HPA v2配置(按GPU负载伸缩)

技术栈:Kubernetes(1.24+) 给通义千问的Deployment配置HPA v2,让它根据GPU的使用率动态调整副本数:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: qwen-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: qwen-deployment
  minReplicas: 1 # 最小副本数:闲的时候至少留1个
  maxReplicas: 5 # 最大副本数:忙的时候最多扩到5个
  metrics:
  # 自定义指标:GPU使用率,目标值是80%
  - type: Pods
    pods:
      metric:
        name: gpu_utilization
      target:
        type: AverageValue
        averageValue: 80 # 当GPU平均使用率超过80%时,增加副本数

这样配置后,HPA会实时监控通义千问副本的GPU使用率:如果GPU平均使用率超过80%,就会增加副本数;如果低于80%,就会减少副本数。闲的时候副本数是1,GPU空转的时间短;忙的时候副本数最多到5,能应对高并发的请求。

四、应用场景、优缺点、注意事项

4.1 应用场景

这个方案适合所有把通义千问部署到K8s集群的团队,尤其是以下几种场景:

  • 企业内部的大模型服务,需要稳定的响应速度,不能被其他任务抢资源;
  • 高并发的大模型服务,比如在线客服、智能助手,需要根据请求量动态调整GPU的使用;
  • 成本敏感的团队,需要尽量减少GPU的空转时间,降低部署成本。

4.2 技术优缺点

优点

  • 资源预留稳定:节点亲和性+静态预留的组合,能保证通义千问的专属资源,不会被其他任务抢;
  • GPU伸缩灵活:自定义指标伸缩能根据GPU的实际负载调整副本数,既不会空转浪费,也不会不够用;
  • 成本可控:闲的时候减少副本数,忙的时候增加副本数,能有效降低GPU的使用成本;
  • 兼容性好:用的都是K8s的原生功能,不需要额外的复杂工具,维护起来方便。

缺点

  • 配置复杂:节点亲和性、静态预留、自定义指标伸缩的配置都比较复杂,需要对K8s有一定的了解;
  • 静态预留的灵活性差:如果节点的资源不够,静态预留的资源不能动态调整,需要手动修改;
  • 依赖Prometheus:自定义指标伸缩依赖Prometheus收集GPU的负载数据,如果Prometheus出问题,伸缩就会失效。

4.3 注意事项

  • 节点标签的管理:如果节点的标签被修改,模型的调度可能会出问题,所以要做好节点标签的权限控制;
  • 静态预留的数值设置:静态预留的数值不能超过节点的总资源,否则K8s会调度失败;
  • HPA的阈值设置:GPU使用率的目标值(比如80%)要根据实际情况调整,如果设得太低,副本数会频繁变动;如果设得太高,请求会排队;
  • GPU的兼容性:这个方案适合NVIDIA的GPU,如果是其他品牌的GPU,需要修改Prometheus的指标配置;
  • 资源的回收:当副本数减少时,要保证GPU能被及时回收,不会出现资源泄漏的情况。

五、总结

通义千问在K8s集群部署的两个核心痛点——资源预留和GPU弹性伸缩,其实是大模型部署的共性问题。解决这两个问题的核心思路是:针对大模型的特性,定制化配置K8s的原生功能,而不是直接用默认的配置。

资源预留的关键是“给模型专属的资源空间”,通过节点亲和性把模型绑定到指定节点,再通过静态预留保证节点上的资源不会被其他任务占用;GPU弹性伸缩的关键是“让伸缩器识别GPU的实际负载”,通过自定义指标适配器把GPU的负载数据提供给HPA,再通过HPA v2动态调整副本数。

这个方案虽然配置复杂,但效果稳定、成本可控,适合大部分团队的大模型部署需求。只要大家理解了核心思路,再根据自己的实际情况调整配置,就能解决通义千问在K8s集群部署的这两个难题。