Kubernetes Webhook实现GPU资源自动转换与优化调度

发布时间:2026/11/3 7:27:43
Kubernetes Webhook实现GPU资源自动转换与优化调度

1. 项目背景与核心需求

在Kubernetes集群中管理GPU资源一直是个颇具挑战性的任务。传统方式直接使用nvidia.com/gpu资源分配存在诸多限制:无法实现细粒度共享、缺乏灵活的调度策略、资源利用率低下。而vGPU技术能够将物理GPU切分为多个虚拟GPU,显著提升资源利用率。

本项目通过开发一个Kubernetes Webhook,实现了以下核心功能:

  • 自动将Pod声明的nvidia.com/gpu资源转换为Volcano调度器支持的vGPU资源格式
  • 动态设置vGPU内存大小(支持按模型大小预设配置)
  • 强制使用Volcano调度器以获得更好的GPU调度能力
  • 保持资源请求与限制的完整性,避免Kubernetes校验失败

2. 技术架构解析

2.1 整体工作流程

整个系统的工作流程可分为四个关键阶段:

  1. 证书准备阶段:使用cert-manager创建自签名CA,为Webhook服务签发TLS证书
  2. Webhook部署阶段:包含Python处理逻辑的Deployment、对外服务的Service、配置存储的ConfigMap
  3. 注册阶段:通过MutatingWebhookConfiguration注册我们的Webhook到Kubernetes API
  4. 运行时阶段:当用户创建Pod时,API Server会调用我们的Webhook进行资源转换

2.2 核心组件交互

+----------------+ +---------------+ +-----------------+ | kubectl | | API Server | | Webhook Server | | apply pod +-------> (k8s) +-------> (我们的服务) | +----------------+ +-------+-------+ +--------+--------+ ^ | | | | v | +--------+--------+ +---------------+ cert-manager | | (证书管理) | +-----------------+

3. 证书管理实现

3.1 cert-manager部署

首先需要部署cert-manager来处理证书生命周期:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml

3.2 创建自签名ClusterIssuer

创建自签名证书颁发机构(CA):

apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: selfsigned spec: selfSigned: {}

3.3 签发Webhook TLS证书

为Webhook服务创建证书资源:

apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: volcano-injector-cert namespace: gpustack spec: secretName: volcano-injector-tls dnsNames: - volcano-injector.gpustack.svc issuerRef: name: selfsigned kind: ClusterIssuer

关键点:证书必须包含Webhook Service的完整DNS名称<service>.<namespace>.svc

4. Webhook服务实现

4.1 核心处理逻辑

Webhook的核心Python代码实现了以下关键功能:

def mutate(): # 1. 解析请求 req = request.get_json() request_obj = req.get("request", {}) pod = request_obj.get("object", {}) # 2. 初始化patch列表 patch = [] # 3. 强制使用Volcano调度器 if spec.get("schedulerName") != "volcano": add_op(patch, "/spec/schedulerName", "volcano") # 4. 处理每个容器 for i, container in enumerate(containers): base_path = f"/spec/containers/{i}/resources" # 5. GPU→vGPU转换 if "nvidia.com/gpu" in limits: # 先删除原GPU请求 remove_op(patch, f"{base_path}/limits/nvidia.com~1gpu") remove_op(patch, f"{base_path}/requests/nvidia.com~1gpu") # 添加vGPU配置 add_op(patch, f"{base_path}/limits/volcano.sh~1vgpu-number", gpu) add_op(patch, f"{base_path}/limits/volcano.sh~1vgpu-memory", vgpu_memory) # ...同样处理requests... # 确保有CPU和内存配置 add_op(patch, f"{base_path}/limits/cpu", DEFAULT_CPU) add_op(patch, f"{base_path}/requests/cpu", DEFAULT_CPU) # ...内存处理类似... # 6. 返回patch return jsonify({ "response": { "allowed": True, "patchType": "JSONPatch", "patch": base64.b64encode(json.dumps(patch).encode()).decode() } })

4.2 关键设计决策

  1. 操作顺序至关重要:

    • 必须先删除原GPU配置
    • 然后添加vGPU配置
    • 最后补充CPU/内存等必填字段
  2. 默认值设计:

    • 预设了不同模型大小的vGPU内存配置
    • 为CPU和内存提供了合理的默认值
  3. 错误处理:

    • 捕获所有异常并返回允许创建
    • 详细日志记录便于排查问题

5. 部署Webhook服务

5.1 将代码打包为ConfigMap

kubectl create configmap volcano-injector \ --from-file=webhook.py=webhook.py \ --namespace=gpustack \ --dry-run=client -o yaml > configmap-py.yaml

5.2 构建自定义镜像

Dockerfile关键内容:

FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt

requirements.txt包含的依赖:

Flask==2.3.3 Werkzeug==2.3.7 jsonpatch==1.33

5.3 部署Webhook Deployment

apiVersion: apps/v1 kind: Deployment metadata: name: volcano-injector namespace: gpustack spec: replicas: 1 template: spec: containers: - name: injector image: your-registry/python:v4 command: ["python", "/app/webhook.py"] volumeMounts: - name: config mountPath: /app - name: tls mountPath: /tls volumes: - name: config configMap: name: volcano-injector - name: tls secret: secretName: volcano-injector-tls

5.4 创建Service

apiVersion: v1 kind: Service metadata: name: volcano-injector namespace: gpustack spec: selector: app: injector ports: - port: 443 targetPort: 443 type: ClusterIP

6. 注册Webhook配置

6.1 MutatingWebhookConfiguration

apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: name: volcano-injector webhooks: - name: volcano.injector.k8s.io admissionReviewVersions: ["v1"] sideEffects: None failurePolicy: Fail namespaceSelector: matchExpressions: - key: kubernetes.io/metadata.name operator: NotIn values: [kube-system, cert-manager, gpustack] clientConfig: service: name: volcano-injector namespace: gpustack path: "/mutate" port: 443 caBundle: <base64编码的CA证书> rules: - operations: ["CREATE"] apiGroups: [""] apiVersions: ["v1"] resources: ["pods"]

6.2 关键配置说明

  1. namespaceSelector:

    • 排除了系统命名空间
    • 避免对Webhook自身Pod的递归调用
  2. failurePolicy:

    • 设置为Fail确保转换失败时Pod创建会被拒绝
    • 生产环境建议从Ignore开始
  3. caBundle:

    • 必须包含验证Webhook服务器证书的CA证书
    • 可通过kubectl get secret volcano-injector-tls -n gpustack -o jsonpath='{.data.ca\.crt}'获取

7. 测试与验证

7.1 测试Pod定义

apiVersion: v1 kind: Pod metadata: name: gpustack-test spec: containers: - name: test image: nvidia/cuda:12.2.0-base-ubuntu22.04 command: ["bash", "-c", "nvidia-smi && sleep 3600"] resources: limits: nvidia.com/gpu: 1

7.2 验证转换结果

查看最终Pod配置:

kubectl get pod gpustack-test -o yaml

预期看到:

  • schedulerName设置为volcano
  • nvidia.com/gpu被移除
  • 替换为volcano.sh/vgpu-number和volcano.sh/vgpu-memory
  • 自动添加了CPU和内存资源

7.3 查看Webhook日志

kubectl logs -n gpustack deployment/volcano-injector -f

正常日志应显示:

  • 接收到的Pod名称
  • 执行的转换操作
  • 生成的完整patch内容

8. 常见问题与解决方案

8.1 证书相关问题

问题现象:

Error from server (InternalError): failed calling webhook: Post "https://...": x509: certificate signed by unknown authority

解决方案:

  1. 确保证书Secret已正确创建
  2. 检查MutatingWebhookConfiguration中的caBundle是否正确
  3. 验证证书的DNS名称匹配Service地址

8.2 Webhook调用失败

问题现象:

failed calling webhook: service "volcano-injector" not found

排查步骤:

  1. 检查Service是否存在且selector匹配Pod标签
  2. 确认Service和Webhook配置在同一个namespace
  3. 验证网络策略是否允许API Server访问Webhook Pod

8.3 资源校验失败

问题现象:

The Pod is invalid: spec.containers[0].resources.limits: Required value

原因分析:

  • Webhook没有正确补充CPU/内存资源
  • 操作顺序不当导致校验失败

修复方法:

  1. 确保在移除GPU后立即添加vGPU配置
  2. 最后才补充CPU/内存等必填字段
  3. 检查Python代码中的操作顺序

9. 性能优化建议

  1. 增加就绪探针:

    readinessProbe: httpGet: path: /healthz port: 443 scheme: HTTPS
  2. 实现缓存机制:

    • 缓存模型大小到vGPU内存的映射
    • 减少每次请求的解析时间
  3. 优化日志级别:

    • 生产环境降低默认日志级别
    • 关键操作保留INFO级别
  4. 横向扩展:

    • 根据负载增加Deployment副本数
    • 考虑使用HPA自动扩缩容

10. 安全加固措施

  1. 细粒度访问控制:

    • 通过namespaceSelector和objectSelector限制Webhook作用范围
    • 避免处理系统关键Pod
  2. 证书轮换:

    • 设置cert-manager自动轮换证书
    • 监控证书过期时间
  3. 请求验证:

    • 验证请求来源IP范围
    • 检查User-Agent是否为Kubernetes API Server
  4. 资源限制:

    resources: limits: cpu: 500m memory: 256Mi

11. 项目扩展方向

  1. 支持动态vGPU配置:

    • 根据Pod注解动态设置vGPU内存
    • 支持百分比形式的显存分配
  2. 多GPU卡支持:

    • 识别不同型号GPU卡
    • 提供异构GPU分配能力
  3. 监控集成:

    • 暴露Prometheus指标
    • 记录转换统计信息
  4. 策略引擎:

    • 基于资源使用情况自动调整vGPU大小
    • 实现超额订阅策略

这个Webhook实现已经过生产环境验证,能够稳定处理每秒数十个Pod创建请求。关键在于确保证书配置正确和操作顺序合理。实际部署时建议先从少量命名空间开始试用,逐步扩大范围。