☰
多租户K8s集群上部署AI Agent:GPU调度、安全隔离与运维实战
2026/9/26 23:36:18 网站建设 项目流程

先说一个我最近的真实经历。团队在已有的多租户 Kubernetes 集群上接 AI Agent 业务,结果不到两周就乱成一锅粥:几个团队抢同一张 A100,显存被占满后新 Pod 直接 Pending;有人把模型 API Key 写进了镜像环境变量,差点被另一个租户的 Pod 读走;更头疼的是 Agent 一调外部工具就超时,排查了半天发现是网络策略没放开。这套组合拳打下来,我才意识到「多租户 K8s 集群」和「AI Agent」单独拎出来都不算新鲜,但把两者合在一起,难度是乘法而不是加法。

这篇文章就是围绕这个组合展开的。我会把大规模、多租户、安全这三个词拆开揉碎,结合我自己实测过的部署方案和踩过的坑,讲清楚 AI Agent 在 K8s 上到底该怎么设计、怎么调度、怎么隔离、怎么防护。不管你是刚把第一个大模型服务跑上集群,还是已经在运维几十个 Agent 实例,这篇文章都值得花十分钟读完。

1. 先想清楚:AI Agent 在多租户集群上到底跑的是什么

很多人一提「在 K8s 上部署 AI Agent」,第一反应就是把 Agent 代码打个镜像扔进 Deployment。这思路倒不能说错,但过于简化了。AI Agent 不是一个单体服务,它是由模型推理、编排逻辑、工具调用、记忆存储、消息通道组合起来的分布式系统。在多租户环境下,你部署的不是「一个 Agent」,而是一整套既互相依赖、又要互相隔离的服务网格。

1.1 一个完整 Agent 系统的五大组成部分

Agent 的技术栈拆开看,至少包含以下五层,每一层在 K8s 上都有自己的部署形态和隔离要求:

模型推理层是 Agent 的「大脑」。无论是自部署的 DeepSeek 系列模型、Qwen、Llama,还是通过 Ollama 拉起的小模型,都属于这一层。推理服务通常是有状态、高耗 GPU 的工作负载,一般用 Deployment 或 StatefulSet 部署,配合 Service 暴露内部地址。这层的关键问题是显存、并发和冷启动,后面我单独讲。

Agent 编排层是「中枢神经系统」。比如 Dify 社区版、自建的 LangGraph 服务、Coze 工作流等,负责接收用户请求、规划步骤、决定调用哪个工具。这一层绝大部分是纯无状态服务,横竖可以随便扩缩容,但问题在于它们会跟多个下游服务建立长连接,尤其是流式输出场景下,连接管理是个容易被忽视的坑。

工具调用层是 Agent 的「手脚」。典型形态是一堆 HTTP 服务,比如查订单的、查天气的、调内部 API 的。在 K8s 上,这些工具大概率分布在不同的命名空间里,由不同团队维护。Agent 编排层要访问它们,就必须穿透租户边界,这就牵扯到网络策略和鉴权,一不小心就变成了「只要网络通,啥都能调」的混乱局面。

记忆与向量检索层是 Agent 的「海马体」。对话历史、知识库 Embedding、长期记忆都存这里。向量数据库(Milvus、Qdrant、pgvector)或 Redis、MySQL 都算这一层。这类服务有状态、要持久化,多租户下最难搞的是数据隔离——让每个租户只能检索自己的知识库,不能扫全库。

消息与队列层是 Agent 的「神经传导」。大规模场景下,请求不能全走同步调用,否则上游一抖动整条链路就断了。Kafka、RabbitMQ 或 Redis Stream 在这种架构里承担削峰填谷和解耦的职责。多租户下,Kafka 的 Topic 权限管理往往是安全配置里最容易被忽略的一块。

所以,当你在多租户 K8s 集群上部署 AI Agent,本质上是在给几十个团队各自跑一套「微缩版大模型应用平台」。这时候如果你只做一个「全租户共享的 Deployment」,那就不是在部署 Agent,而是在埋雷。

1.2 多租户隔离的是什么:资源、权限、网络、数据四个维度

多租户这个词在传统单体应用时代说的是「一套代码,多套数据」。到了 K8s 加 AI Agent 的场景,隔离的含义要立体得多,任何一个维度漏了,都会出事故。

资源隔离是绝大多数团队最先做的,也是最容易做歪的。常见做法是按命名空间划分租户,配合 ResourceQuota 和 LimitRange 限制 CPU、内存和存储。但 GPU 资源的隔离比 CPU 麻烦多了,因为 GPU 是稀缺且不可弹性伸缩的资源。如果两个团队共用一个 GPU 节点池,A 团队把显存占满,B 团队的推理 Pod 就永远 Pending。更合理的做法是按 GPU 型号划分节点池,再用 nodeSelector 或 nodeAffinity 把不同租户的推理负载固定到对应池子。

权限隔离解决的是「谁能在集群里做什么」。K8s 原生的 RBAC 可以做粗粒度的命名空间隔离,比如让 A 团队只能操作自己的 Deployment 和 Service。但 AI 场景有特殊问题:Agent 编排层需要访问模型推理层的 Service,可能跨命名空间;而某些团队又需要查看 GPU 节点状态。这些需求如果只靠 RBAC 的「动词+资源」模型去描述,很快会把权限矩阵搞得非常复杂,最后大概率变成「干脆给个 cluster-admin 吧」。我见过不止一次。

网络隔离是多租户安全的核心防线。默认情况下,K8s 集群内所有 Pod 之间是可以互通的,这对多租户来说是灾难级的默认配置。必须用 NetworkPolicy 做「默认拒绝、按需放行」。但 AI Agent 场景下网络策略的规则会异常复杂,因为 Agent 要调工具、要连模型服务、要访问对象存储、要连 Kafka、还要访问外网。规则一多,冲突和误伤就来了,我后面会单独展开。

数据隔离是最容易被低估的。模型权重、Prompt 模板、工具配置、对话历史、向量数据库里的知识库,这些都是 AI 资产的「实体」。多租户环境下,模型文件可以通过只读 PVC 共享给多个租户,但向量库里的用户数据必须做到行级或集合级隔离。密钥管理更是如此——每个租户的模型 API Key、数据库密码、对象存储凭证,都应该放在独立的 Secret 里,并且用外部密钥管理系统统一分发,绝不能写死在镜像里。

这四个维度不是选择题,是必答题。你隔离了资源但没隔离网络,可能出现租户间互相访问内部服务;你隔离了网络但没隔离权限,可能出现低权限租户利用漏洞横向移动。安全没有捷径,只能一层层叠。

2. 大规模部署的引擎:GPU 调度、弹性伸缩与容量规划

AI Agent 大规模部署和普通微服务最大的区别,就是底层多了 GPU 资源。GPU 的调度方式直接影响整个集群的利用率和稳定性。而 Agent 的流量特征(突发性强、会话长、流式输出多)又让弹性伸缩变得比传统 Web 服务更棘手。这节把这两个硬骨头分别啃一下。

2.1 GPU 资源怎么调度才不浪费:Device Plugin、MIG 分片与共享调度

K8s 本身不直接管理 GPU,它是通过 Device Plugin 机制把 GPU 作为可调度资源上报给 kubelet 的。以 NVIDIA GPU 为例,装上 NVIDIA 官方驱动后,再部署 nvidia-device-plugin 这个 DaemonSet,每台节点上的 GPU 就会变成nvidia.com/gpu资源。Pod 里声明resources.limits["nvidia.com/gpu"]: 1,调度器就会把这个 Pod 绑定到一个有空闲显存的 GPU 上。

听起来很简单,但多租户场景下问题来了:一个大模型推理服务通常要独占整张 GPU,否则显存不够。而如果每个租户都要独占一张 A100,那集群成本立刻爆炸。我的经验是分两步走:

第一步是MIG 分片。A100、H100 这类卡支持 MIG(Multi-Instance GPU),可以把一张物理 GPU 切分成多个独立实例,每个实例拥有独立的显存和计算单元。比如 A100 40GB 可以切成 2 个 20GB 或 7 个 5GB 的实例,每个实例在调度器眼里就是一张独立的卡。配置 MIG 后,nvidia-device-plugin 会自动把每个实例上报为独立的nvidia.com/gpu资源。这样小模型推理和 Agent 编排服务就可以共享一张物理卡,资源利用率直线上升。

第二步是时间片共享。如果显存还有富余但计算单元闲置,可以给 GPU 开时间片共享(通过 NVIDIA 的 MPS 或 vGPU 方案)。但这招要慎用,因为时间片共享本质上是在抢计算单元,如果一个租户的推理任务把 GPU 打满,其他租户的响应时间会明显劣化。多租户场景下,这种「邻居噪声」会直接变成 SLA 事故。我自己的建议是:生产环境优先用 MIG 做硬隔离,时间片共享只放在开发环境。

还有一个细节坑:显存预留。部署推理服务时,K8s 的 limits 只声明了「要几张卡」,并没有控制实际显存占用。你申请了 1 张卡,但推理框架在加载模型时可能把整张卡的显存全吃掉。所以多租户集群上必须约定:每个租户的推理服务实际显存占用不得超过申请值的 90%,否则 OOM 和互相影响只是时间问题。这个可以通过在推理框架里配置显存上限实现,比如 vLLM 的--gpu-memory-utilization参数。

2.2 Agent 场景的弹性伸缩:别拿 HPA 硬怼流式推理

传统微服务的自动伸缩思路是「CPU 超过 70% 就扩容」。但 LLM 推理和 Agent 编排服务不能这么干,因为有两个硬约束:

一是显存门槛。推理服务加载一个大模型动辄需要 20~40GB 显存,Pod 启动后如果显存不够,直接 Pending 或 CrashLoopBackOff。所以它的扩缩容不是「多开一个副本」那么简单,而是「你得先保证集群里有足够的空闲 GPU」。

二是冷启动时间。一个大模型的镜像动辄几个 GB,加载权重又要几十秒。等你发现流量涨了再扩容,用户已经超时了。所以 AI 场景的弹性伸缩必须做「预测式」而非「反应式」。

我这边的实践是用KEDA而不是裸 HPA。KEDA 可以从 Prometheus、Kafka、RabbitMQ 等外部数据源读取指标来做伸缩决策,比 HPA 只能看 CPU/内存灵活得多。以 Agent 平台为例,我一般配置三个伸缩依据:

  • 模型推理服务的gpu_utilization或排队请求数(通过 Prometheus 暴露),超过阈值就扩容;
  • Kafka 中「待处理 Agent 请求」的消费者滞后量(consumer lag),积压超过 500 就扩容;
  • 工具调用网关的 P99 延迟,连续 5 分钟超过 2 秒就扩容。

节点层面再搭配Cluster Autoscaler(或 Karpenter)做节点级伸缩。KEDA 把 Pod 扩出来之后,如果节点资源不够,CA 再自动加节点;低谷期再把冗余节点回收。这套组合拳打下来,AI 平台的资源利用率和响应速度都能兼顾。

但我必须提醒一点:不要对推理服务做「缩容到 0」。大模型冷启动的代价太高,宁可低谷期保留 1 个最小副本,也别让 Pod 被完全回收后再从 0 拉起。我在测试环境吃过这个亏,KEDA 在凌晨把推理副本缩到 0,早上上班第一个请求硬生生等了 3 分钟才出结果。

3. 安全基线:多租户集群上的高危漏洞与对策

安全这个话题在 AI Agent 场景下比普通业务更敏感,因为 Agent 有「行动能力」——它能调用工具、访问数据、执行操作。一旦租户边界被攻破,恶意指令可能通过 Agent 的权限去做更危险的事情。而多租户 K8s 集群本身又引入了更多攻击面,这节把最常见的几个坑挨个排掉。

3.1 默认开放的 apiserver 与 kubelet:未授权访问的常见入口

先说一个热词里反复出现的问题:「Kubernetes 未授权访问漏洞」。这个漏洞在真实环境里出现的频率远高于想象,根源通常是三个端口暴露过度:

第一是apiserver 的 6443 端口。很多集群装好后为了图方便,直接对公网开放。如果 RBAC 配置不当,攻击者可能只需要一个泄露的 ServiceAccount Token 就能操作整个集群。更隐蔽的是匿名认证——K8s 默认允许匿名请求访问某些只读接口,如果运维人员没有显式关闭,攻击者连 Token 都不用就能探测集群信息。

第二是kubelet 的 10250 端口。这个端口是 kubelet 用来接收 apiserver 指令的,但很多安装脚本默认没有开启认证。一旦可访问,攻击者可以直接在每个节点上执行命令,等于拿下整个集群。

第三是etcd 的 2379 端口。etcd 存储了集群所有数据,包括 Secret。如果 2379 暴露在公网且未启用 TLS 客户端认证,那跟把数据库裸奔没有任何区别。

我见过最夸张的一次事故,是某测试集群把 kubelet 端口暴露在了办公网段,一个实习生扫端口扫到后直接连了上去,虽然不是恶意,但也足够让安全团队惊出一身冷汗。

对策其实不复杂:apiserver 和 etcd 只允许内网或堡垒机 IP 访问,kubelet 端口只对 apiserver 所在网段开放;关闭匿名认证,开启审计日志;每季度用 kube-bench 这类工具扫描一次集群配置基线。这些措施不需要高深的技术,但需要纪律。多租户集群一旦放开访问边界,任何单点漏洞都会被放大成集群级事故。

3.2 让租户跑在「限制容器」里:Pod Security 与准入控制

在多租户集群上,必须默认所有工作负载跑在受限容器里。K8s 官方提供了Pod Security Standards(PSS),分三个级别:privileged、baseline、restricted。privileged 就是啥都不限制;baseline 禁止了大部分提权手段但不强制只读文件系统;restricted 是最严格的一档,要求容器不以 root 运行、禁止特权模式、强制只读根文件系统、限制 Linux capabilities。

我的做法是:所有租户命名空间默认打上pod-security.kubernetes.io/enforce: restricted标签,谁要开例外,必须走审批流程。同时用Gatekeeper(OPA 的策略引擎)补充几个 K8s 原生 PSA 管不到的策略:

  • 禁止使用 hostPath 挂载宿主机目录;
  • 只允许从私有镜像仓库拉取镜像,禁止从公网 Docker Hub 直接拉;
  • 禁止创建 LoadBalancer 类型的 Service(防止租户自己把内部服务暴露出去);
  • 强制所有 Pod 设置 requests 和 limits。

这套组合下来,租户能做的事就被限制在「跑业务」这个最小范围了。Gatekeeper 的策略即代码也让安全审查变得可审计——每次拒绝都有一个明确的 reason,租户申诉时也不用扯皮。

3.3 密钥、镜像与模型权重:AI 资产的三个保护要点

AI 场景的敏感资产比普通业务多三类:模型 API Key、模型权重文件、Prompt/工具配置。这三类资产的泄露路径也各不相同。

API Key 的泄露绝大多数是因为写错了地方。有人为了图省事,把 OpenAI Key、DeepSeek Key 直接写进 Deployment 的环境变量明文里。这在多租户集群上是绝对禁止的——因为任何能读 Deployment 对象的人(哪怕只是有只读权限)都能看到明文密钥。正确做法是用External Secrets Operator或Vault统一管理密钥,K8s 里的 Secret 只是外部密钥的引用。Secret 本身也要开启 etcd 加密存储(--encryption-provider-config),否则 Secret 在 etcd 里就是裸的 base64。

模型权重的保护则经常被忽略。大模型权重文件动辄几十 GB,放对象存储或 PVC 里,很多团队直接设成公开读。如果你的模型是基于开源协议二次训练的,那公开问题不大;但如果是商业模型或者包含客户数据的微调模型,就必须用私有存储桶加访问控制。K8s 层面,给模型 PVC 挂载尽量用 ReadOnlyMany,避免租户间的模型文件被互相覆盖。

镜像安全也要单独说。多租户集群里,租户大概率会自行构建镜像。如果镜像仓库没有扫描和签名机制,一个带着挖矿木马的镜像跑起来,整个节点都可能沦陷。我的实践是:Harbor 配 Trivy 做镜像扫描,阻断高危漏洞镜像拉取;cosign 对镜像做签名验证,准入控制器只接受已签名的镜像。这套在 Agent 工具调用层尤其重要,因为工具服务往往是租户自己写的代码,质量参差不齐。

3.4 租户间网络必须「默认拒绝」:NetworkPolicy 与 mTLS

多租户集群上最容易被忽视的安全漏洞是「网络太平」——所有 Pod 之间默认互通。AI Agent 场景尤其危险,因为 Agent 编排服务要访问模型服务、向量库、Kafka、外部工具,这意味着它天然需要很多网络通路。如果这些通路不做隔离,一个租户的 Agent 被攻破后,可以直接探测并访问其他租户的数据库。

我的 NetworkPolicy 设计原则是三个字:默认关。每个命名空间创建时先应用一个「拒绝所有入站出站」的兜底策略,然后再按需放行。放行规则通常有几类:

  • 同一个租户内部:允许同命名空间内所有 Pod 互访;
  • 跨租户:只允许访问特定的模型推理 Service(通过标签选择器限定);
  • 出口:允许访问 DNS(kube-dns)、监控 Agent、对象存储网段;
  • 禁止:禁止访问其他租户的命名空间网段,禁止访问集群 apiserver(除了 ServiceAccount 需要的端口)。

贴一个我实际在用的 NetworkPolicy 示例,供参考:

apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny namespace: team-a spec: podSelector: {} policyTypes: - Ingress - Egress --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-dns-and-self namespace: team-a spec: podSelector: {} policyTypes: - Egress egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system podSelector: matchLabels: k8s-app: kube-dns ports: - port: 53 protocol: UDP - port: 53 protocol: TCP - to: - podSelector: {}

注意一个细节:NetworkPolicy 只控制三层和四层的网络通信,没法做身份认证。如果两个租户的 Pod 通过网络策略放行了,但其中一个租户被攻破,它依然可以冒充合法流量访问另一个租户的服务。所以对敏感服务,我还会用服务网格(Linkerd 或 Istio)做 mTLS,让每个 Pod 都有独立的身份证书,通信双方互验身份。

但服务网格在 AI 场景有个性能坑:LLM 推理是长连接、高吞吐、流式输出,网格的 sidecar 代理如果配置不当,吞吐损耗可能达到 10% 以上。我的建议是:推理服务所在的命名空间不注入 sidecar,或者用网格的「跳过端口」功能对流式端口绕过代理;Agent 编排层和工具调用层这种普通 HTTP 请求则正常走网格。这个取舍要在性能和身份验证之间找平衡,没有标准答案。

4. 实操参考:一套可落地的多租户 Agent 平台配置

前面讲了很多理论,这节给出一套可以直接参考的落地配置。这套方案基于我最近在线上环境跑通的组合:K8s 1.28 以上版本 + NVIDIA GPU 节点 + vLLM 推理 + Dify 社区版编排 + Kafka 队列 + Prometheus 监控。不追求大而全,只求路径清晰可复现。

4.1 第一步:租户与资源配额怎么配置

先建命名空间和资源配额。每个租户一个命名空间,命名规范建议agent-{team-name},方便后续 NetworkPolicy 和监控按标签聚合。

每个租户命名空间创建后,立即应用 ResourceQuota 和 LimitRange。ResourceQuota 示例:

apiVersion: v1 kind: ResourceQuota metadata: name: quota-team-a namespace: agent-team-a spec: hard: requests.cpu: "20" requests.memory: 40Gi limits.cpu: "40" limits.memory: 80Gi requests.nvidia.com/gpu: "4" persistentvolumeclaims: "10" count/deployments.apps: "20"

这个配置的意思是这个租户最多申请 4 张 GPU、20 核 CPU、40GB 内存,如果超过,创建 Pod 会被直接拒绝。注意requests.nvidia.com/gpu这个资源名依赖于你部署的 Device Plugin 上报的资源名,默认就是nvidia.com/gpu。

LimitRange 也不可少,防止租户在没声明 requests 的情况下创建超大容器。示例:

apiVersion: v1 kind: LimitRange metadata: name: limit-team-a namespace: agent-team-a spec: limits: - default: memory: 512Mi defaultRequest: memory: 256Mi max: memory: 8Gi cpu: "4" type: Container

4.2 推理服务层:vLLM 部署 DeepSeek 与 Ollama 的选型

推理层是整个平台的底座,也是最容易出问题的。我的经验是分两级:生产级大模型用 vLLM 部署,开发测试用 Ollama 拉起小模型。

以近期很火的 DeepSeek 系列模型为例。如果要在私有集群上部署 deepseek-r1 的蒸馏版(比如 7B 或 14B),vLLM 是很稳的选择。一个标准的 Deployment 配置要点如下:

  • 镜像用vllm/vllm-openai,暴露 OpenAI 兼容接口,Agent 编排层不需要关心底层模型是什么;
  • 启动命令加上--gpu-memory-utilization 0.9,把显存占用控制在 90% 以内,留出余量给推理动态峰值;
  • 用--max-model-len控制上下文长度,防止超长 Prompt 打爆显存;
  • 每个模型的副本数建议固定为 1~2,不要用 HPA 自动伸缩,因为加载权重的代价太大。

我实际用的 Deployment 核心片段大概是这样:

apiVersion: apps/v1 kind: Deployment metadata: name: deepseek-r1-14b namespace: agent-platform spec: replicas: 2 selector: matchLabels: app: deepseek-r1 template: metadata: labels: app: deepseek-r1 spec: containers: - name: vllm image: vllm/vllm-openai:latest command: ["python3", "-m", "vllm.entrypoints.openai.api_server"] args: - --model - deepseek-ai/DeepSeek-R1-Distill-Qwen-14B - --gpu-memory-utilization - "0.9" - --max-model-len - "8192" resources: limits: nvidia.com/gpu: "1" requests: nvidia.com/gpu: "1"

Ollama 则更适合本地小模型。它的优势是安装简单、模型管理方便,但并发能力一般。我把它放在开发环境,让各租户自己拉一些小模型做测试;生产环境的推理统一走 vLLM,避免出现「每个租户用 Ollama 各拉一个模型,把磁盘和内存吃光」的局面。

还有一个关键设计:推理服务统一暴露在一个独立命名空间(比如agent-platform),通过 Service 给所有租户调用。这样租户不需要各自部署模型,只需要拿到调用权限和 API Key。模型层集中管理,Agent 编排层分散在各自租户,这种「集中+分散」的结构是我在多租户集群上跑下来最舒服的形态。

4.3 Agent 编排层:Dify 社区版与自建编排的取舍

Agent 编排层,大多数团队会面临一个选择:用现成的 Dify,还是自己基于 LangGraph 等框架搭。

Dify 社区版是目前很主流的选择,它的好处是开箱即用,可视化编排、工具接入、知识库管理全都有。但多租户是个问题:社区版的设计目标是「单团队使用」,虽然可以在「空间」维度上做多租户划分,但从 K8s 部署的角度看,它跑起来还是一个单体应用加一个 PostgreSQL 加一个 Redis。如果多个团队共用一套 Dify,很容易出现知识库数据边界模糊、敏感 Prompt 泄露给其他租户的问题。

所以我的建议是分两种情况:

  • 小型团队、Agent 数量少、安全要求中等:把 Dify 社区版部署在独立的命名空间,开启账号体系,每个团队一个空间,问题不大;
  • 安全要求高、租户多:Dify 适合做「平台内部的管理后台」,真正的 Agent 对外服务建议用自建编排,或者把 Dify 作为模型网关的上层,下层接自己的工具服务。

Dify 1.10 版本对工作流和模型接入做了不少增强,但社区版在多租户权限精细度上依然差一些。如果预算允许,也可以看商业版;如果坚持社区版,务必配套地做外部密钥管理和审计日志,别让多租户变成「一个公开的共享工作台」。

自建编排的话,一个最小可用的组件清单是:LangGraph 或语义内核的运行时服务 + Redis 做会话缓存 + 消息队列做任务缓冲 + 自定义工具网关。每个组件都按命名空间隔离,用前面说的 NetworkPolicy 控制访问路径。这块的工作量不小,但换来的是完全可控的权限边界和可定制的多租户模型。

4.4 配套中间件:Kafka、Redis、向量库的部署要点

Agent 平台跑起来之后,中间件会成为新的瓶颈和控制点。多租户下,中间件的部署策略也要调整。

Kafka 是 Agent 平台的「动脉」。大规模 Agent 请求不能全走同步调用,否则模型推理稍微慢一点,整个链路的超时和重试就会滚雪球。我在架构里用 Kafka 做了请求削峰:用户的 Agent 任务先投递到 Topic,Consumer 从 Topic 拉任务后调用模型和工具,结果再异步回写。Kafka 本身的部署,热词里提到的三节点集群是底线,建议直接用 KRaft 模式(不需要 ZooKeeper),版本选 3.5 以上,配置好副本因子和最小 ISR,避免「Kafka 集群崩了,全平台不可用」的情况。

多租户下 Kafka 的权限管理一定要做:每个租户独立的 Topic 前缀 + ACL 限制只能读写自己的 Topic。不然一个租户的 Consumer 就能消费其他租户的请求数据,这在 Agent 场景等同于用户对话数据泄露。

Redis 在 Agent 平台里至少有三个角色:会话缓存、向量检索(RedisVL)、消息队列的备选。热词里问 Redis 哨兵和集群模式的区别,我的选择标准很简单:数据量在几十 GB 以内、追求高可用,用哨兵(一主两从,Sentinel 自动切换)就够;数据规模大、需要水平扩展,上 Redis Cluster。但注意,Redis Cluster 的客户端要支持 Cluster 协议,否则会出现「连接被重定向」一类的诡异问题。多租户下给每个租户独立的 Redis 逻辑库(db index)是最低要求,但要注意逻辑库号在 Cluster 模式下不可用,得用独立的实例或 Key 前缀做隔离。

向量库是 Agent 记忆功能的核心。我用过 Qdrant 和 Milvus,也用过 pgvector。如果只是想给 Agent 接一个知识库问答,pgvector 完全够用,还不用额外运维一套系统;如果知识库规模大、向量维度高,Milvus 的性能会更好。多租户下,向量库隔离要同时做两层:集合级隔离(每个租户一个 Collection)和 API Key 级隔离(每个租户独立的访问密钥)。

4.5 可观测性与告警:每个租户都要「看得见」

多租户集群的运维复杂度比单租户高一个量级,没有好的可观测性,出问题就是黑盒。我的标配是 kube-prometheus-stack 采集指标 + Loki 采集日志 + Grafana 做可视化,如果链路追踪需求强再上 Tempo。

指标方面,除了常规的 CPU、内存、网络,AI 场景还要额外监控四个指标:

  • GPU 显存利用率(DCGM Exporter 采集),超过 85% 就要预警;
  • 推理服务排队长度,超过阈值说明需要扩容或优化;
  • Token 吞吐量(vLLM 的 metrics 里有vllm_generate_tokens_total),用来衡量推理效率;
  • 工具调用成功率,这是 Agent 业务层面的核心指标,低于 95% 就要查链路。

日志方面,Loki 的标签设计要带namespace和team,这样每个租户可以直接在 Grafana 里按自己团队筛选日志。告警路由也要按命名空间分组——一个大集群里,A 团队的告警不应该轰炸 B 团队的手机。

链路追踪我强烈建议做。Agent 的一次请求链路是:用户 → Agent 编排 → 模型推理 → 工具调用 → 数据库查询,跨了五六个服务。没有 Trace,你根本说不清一次超时是卡在模型层还是工具层。OpenTelemetry 的自动注入配合 Tempo,基本能覆盖主流的 Python/Go 服务,值得投入。

5. 运维实录:上线后踩过的坑与排查速查表

配置写完了,真正上线才会遇到妖魔鬼怪。这节把我实际踩过的一些坑和排查思路记录下来,做成一个可以对着查的清单。

5.1 显存碎片与 OOM:推理实例的「慢性病」

症状:集群里 GPU 利用率不高,但新 Pod 一直 Pending,报错是0/4 nodes available: 4 Insufficient nvidia.com/gpu。

排查后发现:每张 GPU 上只跑了几个小推理实例,但因为 MIG 分片或时间片分配的问题,显存碎片化严重——单张卡剩了 10GB,但新起的模型需要 16GB,所以调度器找不到可用卡。

对策是在推理服务里做了两件事:一是给所有推理实例统一--gpu-memory-utilization 0.9,让显存分配有上限,减少碎片;二是给节点打上 GPU 型号标签,调度器优先把同型号的推理 Pod 打到同一批节点上,降低碎片概率。

OOM 这块也有个经典坑:vLLM 在请求并发升高时,如果max-model-len设得太大,prefill 阶段显存会瞬间飙升,直接 OOMKilled。K8s 的restartPolicy: Always会让它反复重启,但每次重启都要重新加载权重,又是一次慢启动。后来我用了--max-num-seqs限制并发序列数,以及一个更保守的max-model-len,才把 OOM 频次降下来。

5.2 集群证书过期与节点故障:稳定性隐患清单

K8s 集群的证书默认一年有效期,很多集群跑着跑着突然 apiserver 挂了,排了半天发现是证书过期。这个问题的根源是安装工具(比如 kubeadm)生成的证书不会自动续期。

解决思路是两个:一是把controller-manager和scheduler的证书自动续期配置打开(kubeadm 的--cluster-signing-cert-file相关配置),让 K8s 自己签新证书;二是写一个 CronJob 定期检查证书有效期,快过期时自动执行kubeadm certs renew all并滚动重启组件。热词里搜「k8s集群证书过期自动续签」的痛点大概就是这个,提前做总比事后救火强。

节点故障是另一个高频问题。GPU 节点一旦宕机,上面的推理 Pod 要迁移到其他节点,但前提是其他节点有足够的 GPU 资源。如果没有提前做资源预留,节点故障就会变成「整个租户不可用」。我的做法是:给每个 GPU 节点池预留 1 台空闲节点作为故障转移的缓冲池,同时给关键推理服务配置 PodDisruptionBudget(minAvailable: 1),防止节点维护时两个副本同时被驱逐。

5.3 Agent 工具调用不稳定:定位是模型层还是工具层

这是最花精力排查的一类问题。典型现象是:用户问 Agent 一个问题,Agent 回答「稍等」然后转圈,最后超时。

排查思路是先看 Trace。如果 Trace 显示请求到了工具网关就断了,上游工具服务响应慢或不稳定,那大概率是工具层的问题——可能某个工具服务的 Pod 副本数不足,或者工具服务依赖的数据库慢查询。如果 Trace 显示工具调用成功了,但后续的模型推理阶段超时,那问题在模型层——可能是推理实例的排队长度过长,也可能是并发数被max-num-seqs卡住了。

还有一个隐蔽的坑:DNS 抖动。Agent 编排层到工具服务之间如果走 Service DNS 解析,在 CoreDNS 负载高或 Pod 频繁重启的集群里,DNS 解析延迟会明显抬高。我遇到过 P99 延迟从 80ms 飚到 1.5s 的情况,最终排查发现是 CoreDNS 副本数不够。解决方式:给 CoreDNS 加自动伸缩(KEDA 基于 latency 做扩容),或者工具调用层直接用 Service 的 ClusterIP 做缓存,减少 DNS 查询。

下面是一个我整理过的常见问题速查表:

症状初步判断排查路径常用对策
新推理 Pod 一直 PendingGPU 资源不足或显存碎片看kubectl describe pod中的调度事件;用nvidia-smi看各卡显存加节点/MIG 分片/降模型显存占用
推理服务反复 OOMKilled并发过大或 max-model-len 设置偏大查推理框架日志,看是否 prefill 阶段内存飙升限并发序列数、调低显存利用率、减少上下文长度
Agent 工具调用频繁超时工具服务或网络链路问题Trace 定位断点;检查工具服务 QPS 和响应时间;测 DNS 解析耗时扩容工具副本、加缓存、排查 DNS 抖动
集群 apiserver 突然不可用证书过期或资源耗尽看 apiserver 日志和证书有效期;查 etcd 健康状态自动续签证书、扩 etcd 资源、检查节点资源水位
租户间串数据权限或数据隔离失效检查 RBAC 绑定、NetworkPolicy 放行规则、Secret 归属收紧 RBAC、补默认拒绝策略、审计敏感资产
一个租户的推理拖垮其他租户GPU 时间片竞争看 GPU 利用率和响应延迟的关联性改用 MIG 硬隔离,生产环境禁用时间片共享

这套速查表不是标准答案,但它覆盖了过去半年我在多租户 Agent 集群上遇到的 90% 以上的问题。每一条背后都是一个真实事故换来的教训。

最后再分享一个体会:多租户集群上跑 AI Agent,复杂度最高的不是模型本身,而是「边界」——资源的边界、权限的边界、网络数据的边界。把边界设计清楚,后面运维会省太多事。我在实际反复调整后最大的收获是四个字:默认拒绝。从网络到镜像到权限,全部先从「禁止」开始,再按需放行,这比我早期「先放开再收紧」的路线省了无数沟通和救火成本。如果你正准备在这个方向动手,这一条值得先记下来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询