4. Kubernetes核心概念与医学影像AI工作负载编排

医学影像AI推理服务与普通Web服务有一个根本差异:它必须独占GPU、加载数百MB到数GB的模型权重、在秒级内完成一次CT或MR序列的推理,并且要满足医院内网对数据不出域、多科室隔离、审计可追溯的合规要求。Kubernetes之所以成为这类工作负载的主流编排平台,正是因为它用一组声明式对象把"我要什么"和"集群怎么给"解耦。本节围绕Pod、Deployment、Service、Ingress、GPU调度、资源配额与命名空间隔离七个核心概念,说明它们在影像AI场景中的真实用法。

Pod:最小的调度与GPU绑定单元

Pod是Kubernetes调度的最小单位,一个Pod内可包含一个或多个容器,它们共享网络命名空间和存储卷。对影像AI而言,Pod是GPU资源被绑定的边界:nvidia.com/gpu这类扩展资源以整数形式申请,一旦Pod被调度到某节点,该GPU卡在Pod生命周期内被独占,不会与其他Pod共享。因此推理Pod通常采用"单容器单模型"结构,把预处理、推理、后处理放在同一容器内,避免跨容器传递大体积像素数据。

一个典型的推理Pod资源声明如下,注意GPU只写数量、不写具体型号,型号由节点标签和调度策略决定:

apiVersion: v1
kind: Pod
metadata:
  name: lung-nodule-infer
  namespace: radiology-ai
spec:
  containers:
  - name: infer
    image: registry.hosp.local/ai/lung-nodule:1.4.2
    resources:
      limits:
        nvidia.com/gpu: 1
        memory: "8Gi"
        cpu: "4"
      requests:
        memory: "6Gi"
        cpu: "2"
    volumeMounts:
    - name: dicom-in
      mountPath: /data/dicom
  volumes:
  - name: dicom-in
    persistentVolumeClaim:
      claimName: pvc-dicom-study

这里requests与limits的差异值得注意:requests决定调度器把Pod放到哪台节点,limits决定容器运行时上限。GPU没有"部分申请"概念,requests和limits必须相等,否则API Server会拒绝。

Deployment:模型版本与副本的声明式管理

直接管理Pod无法应对节点故障和模型迭代。Deployment通过ReplicaSet管理一组同构Pod,声明期望副本数、镜像版本和更新策略。影像AI服务常用RollingUpdate配合maxSurge: 1、maxUnavailable: 0,保证新模型Pod就绪后再下线旧Pod,避免推理中断。判断"就绪"不能只看进程存活,必须配置readinessProbe去请求一个真实的健康检查接口,例如让探针调用/healthz,该接口内部执行一次小尺寸张量的前向推理,确认CUDA上下文和模型权重都可用后才返回200。

GPU节点上滚动更新会短暂出现新旧Pod同时占用两张卡的情况。若节点GPU数量紧张,应把maxSurge设为0,改为先缩容再扩容,或预留一张空闲卡作为滚动缓冲。

Service与Ingress:从集群内寻址到院内统一入口

Pod的IP随重建而变化,Service提供稳定的虚拟IP和DNS名。影像AI平台通常按用途拆分多个Service:infer-svc暴露推理gRPC端口,pacs-gw-svc暴露DICOM网关端口。集群内PACS网关通过infer-svc.radiology-ai.svc.cluster.local:8500寻址,无需关心后端Pod数量。

Ingress则负责集群外的HTTP/HTTPS入口,把不同科室的请求按域名或路径转发到对应Service。例如ai.hosp.local/radiology/*转发到放射科推理服务,ai.hosp.local/pathology/*转发到病理科服务。Ingress本身不含负载均衡实现,需要Ingress Controller(如ingress-nginx)落地。对医学影像,Ingress层还应启用TLS、请求体大小限制(DICOM Multipart上传常超过默认1MB)和超时时间调整。

医学影像AI推理服务的Kubernetes编排结构 院内客户端 PACS / 阅片工作站 Ingress ai.hosp.local/radiology Namespace: radiology-ai(ResourceQuota + LimitRange) Service infer-svc ClusterIP :8500 (gRPC) Deployment lung-nodule:v1.4.2 Pod-1 GPU:1 mem:8Gi Pod-2 GPU:1 mem:8Gi GPU Node (nvidia.com/gpu: 2) device-plugin 上报可分配卡数 ResourceQuota requests.gpu: 4 requests.mem: 32Gi pods: 10

GPU调度:从设备插件到节点亲和

Kubernetes原生不认识GPU。要让nvidia.com/gpu成为可调度资源,节点上必须部署NVIDIA Device Plugin,它通过DaemonSet运行,向kubelet注册GPU数量与健康状态。调度器随后把GPU当作整数扩展资源分配。若集群中混有A100与T4节点,仅靠数量无法保证模型落到合适硬件上,需要配合节点标签与nodeAffinity:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: gpu.model
          operator: In
          values: ["a100"]

对于需要多卡张量并行的模型,可申请nvidia.com/gpu: 2,调度器会寻找单节点剩余卡数足够的节点。若使用MIG(Multi-Instance GPU)把A100切分为多个实例,则需在节点侧配置MIG策略,Pod申请的资源名会变为nvidia.com/mig-1g.5gb这类形式,从而实现一张物理卡服务多个轻量推理任务。

资源配额与命名空间隔离

医院通常按科室划分命名空间,如radiology-ai、pathology-ai、cardiology-ai。命名空间本身只提供名字作用域,真正的资源约束来自ResourceQuota和LimitRange。ResourceQuota限制整个命名空间可申请的GPU总数、内存总量和Pod数量,防止某一科室的批量任务耗尽全院GPU;LimitRange则为未显式声明resources的容器设置默认requests/limits,避免"裸Pod"挤占资源。

隔离还体现在网络与存储层面:NetworkPolicy可限制只有PACS网关命名空间能访问推理Service,阻止跨科室横向访问;RBAC则约束各科室运维人员只能操作本命名空间的Deployment。这样,即使某个模型服务出现异常,其影响范围也被限制在所属命名空间内。

ResourceQuota只统计已声明的requests。若容器未写requests,配额不会计入,但Pod仍会实际占用资源,导致配额形同虚设。务必同时配置LimitRange设置默认值。

一次完整的编排落地检查

把上述对象串起来,部署一个放射科肺结节推理服务的顺序是:先创建命名空间并附加ResourceQuota与LimitRange;再确认GPU节点已打上gpu.model标签且Device Plugin处于Running;然后提交Deployment,观察kubectl get pods -n radiology-ai -o wide确认Pod落到GPU节点且状态为Running;接着创建Service并用kubectl get endpoints infer-svc -n radiology-ai确认后端端点已注册;最后配置Ingress并验证curl -k https://ai.hosp.local/radiology/healthz返回200。若Pod长期Pending,优先用kubectl describe pod查看Events,常见原因是GPU资源不足或nodeAffinity无匹配节点;若Running但请求超时,则检查readinessProbe是否通过、Service selector是否与Pod标签一致。

掌握这七个对象之后,影像AI服务的部署就从"手工登录GPU服务器跑脚本"转变为可版本化、可回滚、可审计的声明式交付,这也是后续CI/CD流水线与多院区交付的基础。