课程0基础Agent开发课 / 生产化部署 / Kubernetes部署AI服务-从单机到集群
— 15 min read

Kubernetes部署AI服务-从单机到集群

前一章介绍了用 Docker 打包 AI 服务。Docker 解决了"把应用和它的依赖打包在一起"的问题,但它解决不了更复杂的问题:流量高峰时如何自动增加实例?某个实例挂了如何自动恢复?多个服务之间如何发现对方?如何不停机更新模型?

Kubernetes 部署 AI 服务:从单机到集群

前一章介绍了用 Docker 打包 AI 服务。Docker 解决了"把应用和它的依赖打包在一起"的问题,但它解决不了更复杂的问题:流量高峰时如何自动增加实例?某个实例挂了如何自动恢复?多个服务之间如何发现对方?如何不停机更新模型?

Kubernetes(简称 K8s,因为 K 和 s 之间有 8 个字母)是专门解决这些问题的容器编排平台(自动管理大量容器的运行、扩缩容、故障恢复的系统)。对于 AI 服务来说,它尤其重要:LLM 推理流量在白天和夜晚可能相差 10 倍,GPU 资源需要精细调度,多个微服务(Embedding 服务、重排序服务、推理服务)需要统一管理。


1.1 为什么 AI 服务需要 Kubernetes

K8s AI服务部署架构图
Kubernetes AI服务部署架构——Ingress到Service到Deployment到Pod,含HPA自动扩缩容

1.1.1 挑战一:流量波动大

一个 ToC(面向普通消费者,相对于 ToB 企业客户)的 AI 助手应用,白天 QPS(每秒请求数)可能是 500,凌晨只有 50。用单机部署,要么为峰值配置资源(夜间 90% 浪费),要么按平均配置(高峰期宕机)。Kubernetes 的 HPA(水平自动扩缩容,后文详解)可以根据实际负载自动增减实例数量。

1.1.2 挑战二:GPU 资源调度

GPU 是稀缺的昂贵资源,不同 Pod(运行单元,后文详解)不能同时抢占同一张 GPU。Kubernetes 通过资源声明机制,确保每个需要 GPU 的推理服务都能被调度到有 GPU 的节点上,且不会超额分配。

1.1.3 挑战三:多服务依赖管理

一个完整的 RAG 系统通常包含:FastAPI 主服务、Embedding 服务、向量数据库、Redis 缓存、Celery Worker。用 Docker Compose 只能管理单机,Kubernetes 可以把这些服务跨多台机器部署,并统一管理服务发现、负载均衡和配置。


1.2 Kubernetes 核心概念:对照 Java 开发者的世界

在看配置文件之前,先用 Java 开发者熟悉的概念做类比,建立直觉:

K8s 概念 Java 世界的类比 一句话描述
Pod 一个运行中的 Java 进程(JVM 实例) 最小的可运行单元,包含一个或多个容器
Deployment Spring Boot 多实例部署脚本 声明"我要跑 N 个 Pod,并保持它们健康"
Service Nginx 反向代理 + 负载均衡 给一组 Pod 提供统一的访问入口
ConfigMap application.yml 存放非敏感配置(环境变量、配置文件)
Secret 加密的 application.yml 或密钥库 存放敏感配置(API Key、数据库密码)
Namespace Maven 的 groupId / Java 包名 逻辑隔离不同环境(dev、staging、prod)
Node 一台物理服务器或虚拟机 K8s 集群中的一台机器

Kubernetes 集群

Node - 机器2 8核GPU 64GB

Node - 机器1 8核 32GB

监控负载,自动增减

监控负载,自动增减

注入配置

注入配置

注入配置

注入配置

注入配置

注入配置

注入配置

注入配置

Pod: AI服务实例1
gpt-service:v2

Pod: AI服务实例2
gpt-service:v2

Pod: Embedding服务
embedding:v1

Pod: Celery Worker
worker:v1

Service: ai-service
:8080 → Pod1,Pod2 负载均衡

Service: embedding-svc
:8001 → Pod3

ConfigMap: app-config

Secret: api-keys

HPA: 自动扩缩容
1-10个Pod

用户请求


1.3 AI 服务的 Kubernetes 特殊配置

1.3.1 GPU 资源请求

Kubernetes 通过 resources.limitsresources.requests 来声明资源需求。GPU 是扩展资源,需要提前安装 NVIDIA Device Plugin,然后用 nvidia.com/gpu 声明。

关键原则:GPU 不支持超额分配(Overcommit)。如果你声明 nvidia.com/gpu: 1,就必须有一张可用的 GPU,K8s 不会让多个 Pod 共享同一张 GPU(除非使用 MIG,Multi-Instance GPU,英伟达提供的 GPU 分割技术,可以将一张物理 GPU 划分为多个隔离的小 GPU 实例)。

1.3.2 内存限制

LLM 推理需要大量内存(一个 7B 参数的模型用 float16 精度需要约 14GB 内存)。必须根据实际内存需求设置 limits,否则在多租户集群中会被其他 Pod 挤压导致 OOM(内存溢出)崩溃。

1.3.3 健康检查探针调整

Kubernetes 有三种探针(Probe,健康检查机制):

  • startupProbe(启动探针):等待应用启动完成。LLM 服务加载模型可能需要 60-120 秒,必须把超时设长,否则 K8s 会认为服务启动失败并无限重启。
  • livenessProbe(存活探针):定期检查应用是否还活着,失败则重启。
  • readinessProbe(就绪探针):检查应用是否准备好接受流量,失败则从负载均衡摘除。

1.4 完整的 AI 服务 K8s 部署示例

以下为代码示例(YAML 配置文件),非程序员可跳过代码,重点看注释说明。

1.4.1 Deployment:管理 Pod 副本

yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ai-service
  namespace: prod
  labels:
    app: ai-service
spec:
  replicas: 3                   # 初始副本数:3个 Pod
  selector:
    matchLabels:
      app: ai-service
  strategy:
    type: RollingUpdate         # 滚动更新(不停机升级,后文详解)
    rollingUpdate:
      maxSurge: 1               # 升级期间最多多出1个 Pod
      maxUnavailable: 0         # 升级期间最少有多少 Pod 不可用:0,保证不停机
  template:
    metadata:
      labels:
        app: ai-service
    spec:
      containers:
        - name: ai-service
          image: your-registry/ai-service:v2.1.0
          ports:
            - containerPort: 8000

          # 资源声明:类比 JVM 的 -Xms -Xmx
          resources:
            requests:
              memory: "4Gi"           # 启动时最少需要 4GB 内存
              cpu: "1000m"            # 1核 CPU(1000m = 1核)
            limits:
              memory: "8Gi"           # 最多使用 8GB 内存
              cpu: "4000m"            # 最多使用 4核 CPU

          # 环境变量(从 ConfigMap 和 Secret 注入,不硬编码在镜像里)
          env:
            - name: OPENAI_API_KEY
              valueFrom:
                secretKeyRef:
                  name: api-keys      # 对应 Secret 的名字
                  key: openai-key
            - name: APP_ENV
              valueFrom:
                configMapKeyRef:
                  name: app-config    # 对应 ConfigMap 的名字
                  key: app-env

          # 健康检查配置(AI 服务必须调整,否则启动期间会被误杀)
          startupProbe:             # 启动探针:等待应用加载模型完成
            httpGet:
              path: /health
              port: 8000
            failureThreshold: 30    # 最多等 30 次
            periodSeconds: 10       # 每10秒检查一次
            # 意味着最多等 300 秒(5分钟)才判定启动失败

          livenessProbe:            # 存活探针:检查应用是否挂死
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 0
            periodSeconds: 30       # 每30秒检查一次
            timeoutSeconds: 10      # 超过10秒无响应算失败

          readinessProbe:           # 就绪探针:检查应用是否能接受流量
            httpGet:
              path: /ready
              port: 8000
            periodSeconds: 10
            timeoutSeconds: 5

1.4.2 GPU 推理服务 Deployment

yaml
# deployment-gpu.yaml(需要 GPU 的推理服务)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: llm-inference
  namespace: prod
spec:
  replicas: 1           # GPU 资源有限,通常只跑1个副本
  selector:
    matchLabels:
      app: llm-inference
  template:
    metadata:
      labels:
        app: llm-inference
    spec:
      # 节点亲和性:只调度到有 GPU 标签的节点
      nodeSelector:
        accelerator: nvidia-gpu

      containers:
        - name: llm-inference
          image: your-registry/llm-inference:v1.0.0
          resources:
            limits:
              nvidia.com/gpu: 1     # 声明需要1张 GPU(不支持小数)
              memory: "40Gi"        # LLM 需要大内存
              cpu: "8000m"
            requests:
              nvidia.com/gpu: 1
              memory: "32Gi"
              cpu: "4000m"

1.4.3 Service:服务发现和负载均衡

yaml
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ai-service
  namespace: prod
spec:
  selector:
    app: ai-service         # 匹配所有 label 为 app=ai-service 的 Pod
  ports:
    - port: 80              # Service 对外暴露的端口
      targetPort: 8000      # 转发到 Pod 的 8000 端口
  type: ClusterIP           # 只在集群内可访问(通过 Ingress 对外暴露)

Service 创建后,集群内其他服务可以通过 http://ai-service.prod.svc.cluster.local 访问,无需知道具体的 Pod IP。这就是服务发现机制。

1.4.4 ConfigMap 和 Secret

yaml
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: prod
data:
  app-env: "production"
  llm-model: "gpt-4o"
  max-tokens: "4096"
  log-level: "info"

---
# secret.yaml
# 注意:Secret 的 value 需要 base64 编码,但 K8s 1.25+ 支持 stringData 直接写明文
apiVersion: v1
kind: Secret
metadata:
  name: api-keys
  namespace: prod
type: Opaque
stringData:
  openai-key: "sk-your-actual-key-here"   # 真实部署时从 Vault 或 CI/CD 注入
  redis-password: "your-redis-password"

1.4.5 HPA:水平自动扩缩容

HPA(Horizontal Pod Autoscaler,水平 Pod 自动扩缩容,即根据负载自动增加或减少运行实例数量)根据 CPU/内存使用率或自定义指标,自动增减 Pod 数量。

yaml
# hpa.yaml
apiVersion: autoscaling/v2
kind: HPA
metadata:
  name: ai-service-hpa
  namespace: prod
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ai-service          # 对哪个 Deployment 生效
  minReplicas: 2              # 最少保持 2 个 Pod(保证高可用)
  maxReplicas: 20             # 最多扩到 20 个 Pod
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70    # CPU 超过 70% 就扩容
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80    # 内存超过 80% 就扩容

1.5 滚动更新与金丝雀发布

1.5.1 滚动更新:不停机升级

Deployment 默认的更新策略就是滚动更新(Rolling Update)。当你更新镜像版本时,K8s 会:

  1. 启动一个新版本的 Pod
  2. 等它通过 readinessProbe(就绪探针)
  3. 关掉一个旧版本的 Pod
  4. 重复直到全部替换完成

整个过程服务不中断,用户无感知。

bash
# 触发滚动更新(只需更新镜像版本)
kubectl set image deployment/ai-service \
  ai-service=your-registry/ai-service:v2.2.0 \
  -n prod

# 查看更新进度
kubectl rollout status deployment/ai-service -n prod

# 如果新版本有问题,一键回滚到上个版本
kubectl rollout undo deployment/ai-service -n prod

1.5.2 金丝雀发布:灰度上线

金丝雀发布(Canary Release,灰度发布的一种,得名于煤矿工人用金丝雀测试矿井空气质量的做法)——先让少量流量(比如 5%)访问新版本,观察稳定后再全量切换。

在 K8s 中,最简单的金丝雀发布利用 Pod 数量比例来控制流量:

yaml
# 旧版本 Deployment:19 个 Pod(95% 流量)
# deployment-stable.yaml
metadata:
  name: ai-service-stable
spec:
  replicas: 19
  selector:
    matchLabels:
      app: ai-service        # 与 Service 的 selector 一致
      version: v2.1.0
  template:
    metadata:
      labels:
        app: ai-service
        version: v2.1.0
    spec:
      containers:
        - image: your-registry/ai-service:v2.1.0

---
# 新版本 Deployment:1 个 Pod(5% 流量)
# deployment-canary.yaml
metadata:
  name: ai-service-canary
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ai-service        # 与 Service 的 selector 一致
      version: v2.2.0
  template:
    metadata:
      labels:
        app: ai-service
        version: v2.2.0
    spec:
      containers:
        - image: your-registry/ai-service:v2.2.0

Service 的 selector 只匹配 app: ai-service,所以流量会按 Pod 数量比例(19:1 = 95%:5%)自动分配到两个 Deployment。


1.6 Docker Compose vs Kubernetes 选型对比

维度 Docker Compose Kubernetes
适用规模 单机,开发/测试环境 多机,生产环境
学习成本 低(1天入门) 高(1-2周入门)
自动扩缩容 不支持 原生支持(HPA)
服务自愈 基础(容器崩溃重启) 强(Pod 重调度、节点故障迁移)
配置管理 .env 文件 ConfigMap + Secret(加密,版本化)
滚动更新 手动,有停机 原生支持,无停机
GPU 调度 需手动指定 声明式自动调度
运维复杂度 高(需要 K8s 运维知识)
建议场景 本地开发、CI 测试、小型内部工具 对外服务、高可用、弹性需求

决策原则:如果只有 1 台服务器,或者项目处于 MVP 阶段,用 Docker Compose 完全足够。当服务需要高可用、弹性扩缩容、或管理 5 个以上微服务时,迁移到 Kubernetes。


1.7 小结

Kubernetes 对 AI 服务的价值体现在三个核心能力:

  1. 弹性:HPA 根据负载自动扩缩容,应对 AI 应用的流量波动。
  2. 可靠性:探针机制确保不健康的实例被自动替换,滚动更新保证零停机上线。
  3. 资源隔离:GPU 声明和内存限制确保 LLM 推理服务不会被其他服务抢占资源。

对 Java 开发者来说,Kubernetes 的 YAML 配置与 Spring Boot 的 application.yml 类似,都是声明式配置——你声明"我要什么状态",框架负责实现并维持这个状态。这个心智模型一旦建立,K8s 的配置就不再陌生。

本页目录