ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

多租户K8s集群上部署AI Agent:GPU调度、安全隔离与运维实战

多租户K8s集群上部署AI Agent:GPU调度、安全隔离与运维实战 先说一个我最近的真实经历。团队在已有的多租户 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 这类卡支持 MIGMulti-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 StandardsPSS分三个级别privileged、baseline、restricted。privileged 就是啥都不限制baseline 禁止了大部分提权手段但不强制只读文件系统restricted 是最严格的一档要求容器不以 root 运行、禁止特权模式、强制只读根文件系统、限制 Linux capabilities。我的做法是所有租户命名空间默认打上pod-security.kubernetes.io/enforce: restricted标签谁要开例外必须走审批流程。同时用GatekeeperOPA 的策略引擎补充几个 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通过标签选择器限定出口允许访问 DNSkube-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: Container4.2 推理服务层vLLM 部署 DeepSeek 与 Ollama 的选型推理层是整个平台的底座也是最容易出问题的。我的经验是分两级生产级大模型用 vLLM 部署开发测试用 Ollama 拉起小模型。以近期很火的 DeepSeek 系列模型为例。如果要在私有集群上部署 deepseek-r1 的蒸馏版比如 7B 或 14BvLLM 是很稳的选择。一个标准的 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: 1Ollama 则更适合本地小模型。它的优势是安装简单、模型管理方便但并发能力一般。我把它放在开发环境让各租户自己拉一些小模型做测试生产环境的推理统一走 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 任务先投递到 TopicConsumer 从 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 台空闲节点作为故障转移的缓冲池同时给关键推理服务配置 PodDisruptionBudgetminAvailable: 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复杂度最高的不是模型本身而是「边界」——资源的边界、权限的边界、网络数据的边界。把边界设计清楚后面运维会省太多事。我在实际反复调整后最大的收获是四个字默认拒绝。从网络到镜像到权限全部先从「禁止」开始再按需放行这比我早期「先放开再收紧」的路线省了无数沟通和救火成本。如果你正准备在这个方向动手这一条值得先记下来。
返回列表