news 2026/9/26 18:13:46

多租户Kubernetes上安全部署AI Agent:隔离、调度与实操指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多租户Kubernetes上安全部署AI Agent:隔离、调度与实操指南

在多租户 Kubernetes 集群上大规模安全部署 AI Agent,这个话题我带着团队在真实生产环境里磕了小半年。刚接到任务时觉得没什么难的,毕竟 Kubernetes 部署早就是常规操作了,可真把 Agent 这种“会自己调用自己、能自主行动”的负载放进去,原有的那套隔离、调度、安全模型全被冲得七零八落。这篇文章我把从方案选型、资源画像、安全加固到故障排查的完整过程整理出来,权当给准备上车的同学一份可以参考的路线图,也顺便说说我踩过哪些不该踩的坑。

1. 多租户场景下的架构设计与隔离方案选型

1.1 为什么多租户集群是 AI Agent 部署的必然选择

先说背景。公司内部做 AI 应用的不止一个团队:客服部要对话机器人,运营部要内容生成助手,研发部要做代码审查 Agent,还有财务、法务都在排队提需求。如果每个团队都自己搭一套 Kubernetes 集群,再去单独管理 GPU 资源池和模型服务,成本很快会失控。我们当时粗算过一笔账:分开部署的话,光 GPU 闲置率就能到 40% 以上,更不用说每套环境都要安排人运维。

共享一套多租户 Kubernetes 集群就成了唯一合理的选择。所有团队的 Agent 工作负载运行在同一批节点上,由平台团队统一管理底层资源、镜像仓库、模型存储和网络出口,各租户之间通过逻辑边界隔离开。这个思路和 SaaS 化改造的底层逻辑是一样的:基础设施共享,数据与权限隔离,错峰填补资源缺口。

但 AI Agent 和普通微服务不一样,它不是单纯的“处理请求-返回结果”,它有状态、有推理上下文、可能要长时间运行、还要动态调用各种工具和外部 API。这就导致集群里跑的不再是“一批无状态 Pod”,而是一批“带大脑、会动手的逻辑单元”。隔离方案如果选得不对,后面所有事情都会跟着别扭。

1.2 隔离方案选型:Namespace 逻辑隔离、虚拟集群还是物理集群

常见的多租户隔离方案有三级,我逐一评估过。

第一级是纯 Namespace 逻辑隔离。每个租户一个 Namespace,配上 ResourceQuota、LimitRange、RBAC 和 NetworkPolicy。这套方案的好处是轻量、易管理,一个集群全部搞定,资源利用率最高。缺点也明显:租户之间跑在同一套内核上,如果某个 Pod 炸了波及宿主机,整个集群都会受影响。此外 Kubernetes 原生的 RBAC 对象是集群级的,要做“租户管理员只能管自己 Namespace”的权限模型需要写一堆 Role 和 RoleBinding,初期配置工作量不小。

第二级是虚拟集群方案,比如用 vcluster 这类工具在每个租户里再拉起一个轻量级 Kubernetes 控制面。租户看到的是“自己有一个集群”,可以自己定义 CRD、装 Operator,互不影响。但虚拟集群会引入额外的控制面开销,对大规模(比如几百个 Agent)场景会产生性能损耗,而且和底层网络插件的适配偶尔会出问题。

第三级是物理隔离,每个租户独占一套集群或一批节点。安全性最强,但成本最高,和我们共享资源池的初衷完全违背。

我最终选了第一级作为主方案:所有租户共享一个 Kubernetes 集群,用 Namespace 划分边界,用 ResourceQuota 限制消耗,用 NetworkPolicy 阻断跨租户网络流量,用自定义 RBAC 收敛权限。同时为高安全等级租户单独划出一个节点池,配合节点污点(Taint)实现物理层面的隔离兜底。用表格看更直观:

方案隔离强度资源利用率运维复杂度适用场景
Namespace 逻辑隔离中最高低多数企业内部多团队共享
虚拟集群中高高中租户需要自定义 CRD / Operator
节点池物理隔离高中中高安全等级租户、合规要求严格

提示:不建议一开始就追求所谓“绝对隔离”。多租户安全的本质是把风险控制到可接受范围,而不是在物理上把所有东西分开。先想明白你的威胁模型,是防内部误操作,还是防恶意攻击,还是满足审计合规要求,再决定方案。

1.3 租户资源边界设计:配额配置与命名规范

选定 Namespace 方案后,第一步就是把“租户”这个抽象概念落成具体对象。我按“一个业务团队 = 一个租户 = 一个 Namespace 集合”来建模。注意,高压租户和普通租户的资源配额完全不能一样,否则必然出现某个团队把 GPU 全占了、其他团队排队等推理的情况。

ResourceQuota 是资源边界的第一道闸门。以一个中型租户为例,我给出的初始配置是:

apiVersion: v1 kind: ResourceQuota metadata: name: quota-agent-team namespace: team-a spec: hard: requests.cpu: "8" requests.memory: 16Gi limits.cpu: "16" limits.memory: 32Gi requests.nvidia.com/gpu: "0" persistentvolumeclaims: "5" count/deployments.apps: "20" count/pods: "80"

这里有两个细节值得注意。

一个是 requests 和 limits 必须分开设置,而且差距不能太小也不能太大。差距太小,租户没法应对突发流量;差距太大,会造成严重的资源闲置浪费。我们内部的经验是 CPU limits 控制在 requests 的 1.5 到 2 倍之间,内存 limits 控制在 1.5 倍左右,因为内存是不可压缩资源,超卖太多容易触发 OOM。另一个是 GPU 配额我故意没有写死在 ResourceQuota 里,只给了个 0 作为占位。原因是 GPU 这种稀缺资源如果写死在配额里,调度的灵活性会大打折扣——某个租户如果临时不跑推理任务,剩余的 GPU 权就全部被浪费了。我稍后会在调度章节专门讲这部分怎么处理。

命名规范同样重要。我采用<租户名>-<环境名>的格式,比如team-a-prod、team-a-staging,并通过标签tenant: team-a和env: prod标记所有资源。这样后面做 NetworkPolicy 选择器、日志检索、成本分摊的时候都方便得多。这个看似不起眼的细节,在实际运维中帮我们省了大把时间。

2. AI Agent 负载画像与调度策略设计

2.1 AI Agent 工作负载的资源画像:它和普通 Web 服务的差异有多大

给 AI Agent 做调度策略之前,必须先搞清楚它的资源消耗规律。我和很多同行聊过,发现大家都容易犯一个同样的错误:把 Agent 当成普通高并发 Web 服务来配资源,结果要么扩容姗姗来迟,要么 OOMKilled 一路狂飙。

从负载类型来看,Agent 大体分为三类。对话型 Agent,比如智能客服,特点是请求频率高、单次推理短、对延迟极度敏感,GPU 和内存的消耗呈脉冲状;执行型 Agent,比如代码生成 Agent、自动化测试 Agent,特点是长任务居多,单次执行要反复调用大模型,往往要跑十几分钟甚至几小时,资源占用呈平台期;决策型 Agent,比如多 Agent 协作的调度中枢,除了推理还要做大量工具调用和上下文管理,内存占用最高。

我和一个做客服系统的朋友交流过,他们的对话 Agent 白天高峰期每个实例要同时维持 50 多路会话上下文,内存直接飙到 8GB 以上,而到深夜又能降到 2GB 以下。这种“白天高水位、夜间低水位”的模式,如果用传统微服务的固定副本数去部署,要么白天疯狂排队,要么晚上白白烧钱。

从调度角度看,AI Agent 还带着两个普通工作负载没有的特性。第一,它是带状态推理的,同一个用户的多轮对话要尽量路由到同一个实例上,否则上下文切换会带来极高的额外成本;第二,它对故障恢复的容忍度极低,推理进行到一半实例挂了,重启之后如果上下文没有持久化,整个对话就断了。所以调度策略不能只看 CPU 内存,还得兼顾会话亲和性和恢复效率。

2.2 从节点池到 GPU 资源:调度器配置实战

集群节点我按用途拆成了三个池子:通用 CPU 池、GPU 推理池和 GPU 高优池。每个池子的节点都打上不同的标签,画成一张心智图的话大概是这样的:控制面节点跑系统组件;CPU 节点池跑 Agent 运行业、API 网关、向量数据库等轻量组件;GPU 推理池跑常规对话类推理任务,允许超卖;GPU 高优池专门跑核心业务 Agent,节点数少但全部预留 GPU。

调度策略的核心是让 Pod 精确落到对应的池子里。我拿 GPU 推理池举例,节点上的标签是node-type=gpu-infer,同时打了 Taintgpu=true:NoSchedule。对应的 Deployment 配置:

tolerations: - key: "gpu" operator: "Equal" value: "true" effect: "NoSchedule" nodeSelector: node-type: gpu-infer

这样普通无 GPU 需求的 Pod 就算被调度器看上,也会因为 Taint 无法落到 GPU 节点上,避免把珍贵的 GPU 节点资源白白吃掉。

GPU 分配这个环节,我试过好几种方案,最后把结论分享出来。早期我图省事给每个 Agent Pod 直接申请完整 GPU 卡,即nvidia.com/gpu: 1,结果显存利用率非常难看,很多 Agent 的推理模型只占了 GPU 显存的三分之一,剩下全空着。后来我尝试过 MIG 切片,让多个 Agent 共享一块物理 GPU,但在一些老型号卡上驱动和 MIG 的兼容性不好,Agent 推理性能波动很大。MPS 模式我没有上生产,因为它在多个租户同时跑时容易互相抢占算力,稳定性风险太高。

最终我采用的折中是:常规租户按整卡分配 GPU,但对显存占用较小的 Agent 配两个 Pod 共享一张卡(前一个 Agent 配nvidia.com/gpu: 0走 GPU 节点池但不用卡,后一个配nvidia.com/gpu: 1,靠调度器的 binpack 策略尽量挤到同一节点)。这样既不牺牲稳定性,又能把 GPU 利用率从 35% 拉到 60% 左右。

2.3 会话亲和性、优先级与自动伸缩

调度器还有一个容易踩坑的点是会话亲和性。对话型 Agent 的上游服务通常是无状态的,Kubernetes Service 的负载均衡会随机把请求打到不同 Pod 上。如果 Agent 没有把会话上下文放到 Redis 之类的共享存储里,那么第二句话就可能落到另一个没有上下文记忆的实例上,用户得到的就是牛头不对马嘴的回答。

我给的解决方案分两层。第一层是尽量在 Agent 应用层做状态外置,把多轮对话的上下文交给外部的向量数据库或 Redis 维护,让每个实例都能快速重建上下文,这是最彻底的解法;第二层是在 Service 层配置会话亲和性,让同一个来源 IP 的会话尽量路由到同一个 Pod,用sessionAffinity: ClientIP加上超时时间来兜底。注意如果 Agent 前面还挂了网关做负载,由网关透传的真实客户端 IP 才能对齐,否则亲和性会失效。

优先级和抢占策略用在跨租户的节点资源竞争场景。我给核心业务租户的 Agent Pod 设置了更高的 priorityClass:

apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000 globalDefault: false description: "核心业务 Agent 专用优先级"

然后通过priorityClassName: high-priority让核心租户的 Pod 在资源紧缺时优先被调度。这里要小心一个副作用:如果低优先级 Pod 已经把节点资源占满,高优先级 Pod 到来时会触发抢占,被抢占的 Pod 会被重新调度。如果同时有几十个低优先级 Pod 被挤掉,集群会经历一次“调度风暴”,CPU 消耗会瞬时时升高。所以在开启抢占前,我先通过 ResourceQuota 把每个租户的资源上限压住,让“配额内调度”优先于“优先级抢占”发生,减少抢占频率。

自动伸缩方面,HPA 的指标不能只用 CPU。Agent 这种负载,CPU 使用率和 QPS 之间相关性很弱,高峰期往往是内存先爆。我建议把 HPA 的触发指标从 CPU 换成 QPS(由外部 metrics API 提供)和内存双维度,同时设置一个最小副本数兜底,避免冷启动把第一个用户活活卡死。

3. 安全部署:AI Agent 带来的安全模型冲击

3.1 Agent 自主性带来的安全边界问题

传统 Kubernetes 的安全模型我总结成一句话:容器内的进程被当做一个“不会主动出格的程序”,安全重点在于防止外部入侵。但 AI Agent 完全不一样,它被设计成“会主动调用工具、会读写数据、会访问外部 API”的执行者。

一个看起来无害的 Agent,为了完成“帮用户整理今天的工作日志”的任务,可能会自动去拉取邮件、访问文档系统、调用内部 BI 接口、甚至执行一段自动生成的代码。在多租户环境下,这就成了一个大问题:一个租户的 Agent 如果被提示注入攻击诱导,完全有可能拿着它自己的合法权限,去访问另一个租户的数据接口。传统的“一个 Pod 绑定一个 ServiceAccount”模型,根本没考虑到这种“内部员工带着合法工牌搞破坏”的场景。

我做的第一道防御是收敛 ServiceAccount 权限。默认的 default ServiceAccount 必须禁用,每个 Agent 配一个只读的专用 ServiceAccount,且权限范围严格限定在自身 Namespace 内。我把这个要求直接写进了平台规范:任何 Agent 的 Deployment 不允许使用默认 ServiceAccount,这成了日常安全审计的第一检查项。

第二道防御是给 Agent 的“自主行动范围”套护栏。凡是需要调用外部服务的 Agent,必须在应用层配置允许清单,只放行目标域名或目标 API 前缀。像内部文档系统、代码仓库这类敏感数据源,一律先过一层代理,由平台统一鉴权后再放行。等于说 Agent 可以“想得多”,但不许“走太远”。

3.2 RBAC 权限模型设计:租户管理员应该拥有什么

多租户模式下 RBAC 的设计核心原则是:租户管理员只拥有他自己 Namespace 的管理权限,不能触碰集群级别的对象。

我创建了三种角色:平台管理员,管整个集群,只有平台 team 的人能拿到;租户管理员,可以管理指定 Namespace 下的 Deployment、Service、ConfigMap、Secret 等资源,但删不了 ResourceQuota,更碰不了 Node;租户只读成员,只能查看资源状态和日志,用于开发排查问题。

具体实现时用 Role 和 RoleBinding 绑定到租户自己的 Namespace:

apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: team-a name: tenant-admin rules: - apiGroups: ["apps", ""] resources: ["deployments", "pods", "services", "configmaps", "secrets"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: ["", "events.k8s.io"] resources: ["events"] verbs: ["get", "list", "watch"]

注意 Role 里我刻意没有给 secrets 的 delete 权限。有个租户管理员曾经误删了正在被 Agent 引用的模型 API Key,导致生产 Agent 集体罢工半小时。从那以后,所有高危操作(删除 Secret、修改 ResourceQuota、修改 NetworkPolicy)的权限都收到平台层,租户管理员要变更必须提工单由平台执行。

租户之间的跨 Namespace 访问在 Kubernetes 里默认就是禁止的(RBAC 按 Namespace 隔离),但要注意别被容器镜像里的“隐藏需求”绕过去:如果你的 Agent 需要访问共享的模型服务 Namespace,不要直接给租户管理员开通跨 Namespace 权限,而是在模型服务 Namespace 里创建一个专用的只读 ServiceAccount,把凭证作为 Secret 挂载到租户 Namespace 里。这样模型服务被访问的权限是固定的、可控的,而不是泛化的。

3.3 网络策略、镜像供应链与密钥管理

NetworkPolicy 是隔离租户网络的第二道关键闸门。默认的 Kubernetes 集群 Pod 之间是全部互通的,这和裸地把所有租户丢进同一个大房间没有区别。我在每个租户 Namespace 里都配置了“白名单”模式的 NetworkPolicy:拒绝所有入站流量,只放行从网关 Namespace 和同租户 Pod 进来的流量。

一个典型的配置:

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-from-gateway namespace: team-a spec: podSelector: {} policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: name: ingress-gateway

注意第二段策略里我用的是 namespaceSelector,而不是 ipBlock。原因是网关服务后面如果做弹性扩容,Pod 的 IP 会频繁变化,用 namespace 标签选择器可以自动适配新扩出来的 Pod,不用手动维护 IP 列表。

镜像供应链这一块,多租户集群里最容易出问题的是镜像拉取凭据管理。如果有租户把私有镜像仓库的认证 Secret 直接放到他们的 Namespace 里,这个凭证就相当于给了租户访问整个仓库的权限,哪怕是没拉过的其他租户镜像也能拉。我的做法是统一侧用镜像仓库的“项目级”权限:每个租户对应仓库里的一个项目,只授权拉取该项目下的镜像。如果 Agent 需要跨项目引用公共基础镜像,由平台把这些基础镜像同步到租户项目里,而不是给租户开跨项目权限。

密钥管理上,我不建议把模型 API Key、数据库密码这类敏感信息直接写进 ConfigMap 或明文 env。Kubernetes 的 Secret 本身只是 base64 编码,不是加密,需要配合外部密钥管理系统(如 Vault)或云厂商的 KMS 加密。我的最低要求是:所有敏感信息必须放 Secret,Secret 必须开启字段级加密,且禁止任何非平台成员查看明文。

最后补一条经验:多租户环境务必开启 Kubernetes 的审计日志,并采集到独立的 SIEM 平台。Agent 部署后最大的风险是“自导自演”的异常行为——一个 Agent 静默调用了某个内部系统接口,如果没有审计,出了问题根本无从追溯。开启审计日志会带来少量性能开销,但相比事后排查的成本,这笔开销太值了。

4. 从 0 到 1 实操记录:一套可复现的部署路径

4.1 集群基础环境与多租户初始化

我拿一套中等规模集群做演示:3 台控制面节点、6 台 CPU 节点、4 台 GPU 节点(每卡 32GB 显存),Kubernetes 版本选 1.28 以上(对 GPU 调度和 NetworkPolicy 的支持更成熟),容器运行时用 containerd,网络插件用 Calico(开启 NetworkPolicy 支持),存储用 Longhorn 提供 ReadWriteMany 卷。

集群装好之后,第一步是把多租户的基础设施铺好。我写了个初始化脚本,对一个新租户执行四件事:创建 Namespace 并打标签;写入 ResourceQuota 和 LimitRange;创建专用 ServiceAccount 和 RBAC 绑定;部署默认拒绝的 NetworkPolicy。

命令大概是这样:

kubectl create namespace team-a --dry-run=client -o yaml | kubectl apply -f - kubectl label namespace team-a tenant=team-a env=prod kubectl apply -f quota-team-a.yaml kubectl apply -f sa-team-a.yaml kubectl apply -f rbac-team-a.yaml kubectl apply -f netpol-team-a.yaml

这一套流程跑通后,租户管理员拿到的就是一个“被圈住”的 Namespace,他可以在里面自由部署自己的 Agent 应用,但无法越界。我在这里特别强调一下 LimitRange 的必要性:ResourceQuota 管的是总量,LimitRange 管的是单 Pod 的上下限。如果只有配额没有 LimitRange,一个粗心的租户可能提交一个请求 100Gi 内存的 Pod——配额可能拦得住,但在那之前调度器已经因为这个离谱请求浪费了资源评估的时间。

4.2 一个标准 Agent 工作负载的完整部署示例

Agent 工作负载的典型架构是三层:前端入口(负责会话接收)、Agent 运行时(负责意图识别、工具调用编排)、模型推理服务(LLM 后端,通常跑 GPU 池)。我贴一段核心的运行时 Deployment,稍作简化,但保留了最关键的安全和调度配置:

apiVersion: apps/v1 kind: Deployment metadata: name: agent-runtime-team-a namespace: team-a labels: app: agent-runtime tenant: team-a spec: replicas: 4 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: agent-runtime tenant: team-a template: metadata: labels: app: agent-runtime tenant: team-a spec: serviceAccountName: agent-runner-sa automountServiceAccountToken: false securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: agent-runtime image: registry.example.com/team-a/agent-runtime:1.2.0 imagePullPolicy: IfNotPresent ports: - containerPort: 8080 resources: requests: cpu: 500m memory: 1Gi limits: cpu: "1" memory: 1.5Gi env: - name: LLM_BASE_URL value: http://llm-inference-gateway.default.svc.cluster.local - name: AGENT_API_KEY valueFrom: secretKeyRef: name: agent-secrets key: api-key readinessProbe: httpGet: path: /healthz port: 8080

这里有几个我特别坚持的配置。一是automountServiceAccountToken: false,只有当 Agent 确实需要调 Kubernetes API 时才把它置为 true,否则一律不自动挂载 token。二是 securityContext 里强制runAsNonRoot和 seccomp 限制,Agent 一旦被攻破,能做的事情会被大幅压缩。三是环境变量里的密钥走 Secret 引用而不是明文写入镜像。

模型推理服务我单独跑在 GPU 池,Deployment 里声明nvidia.com/gpu: 1,并通过 nodeSelector 确保落在 GPU 节点上。为了让三个层之间的调用链清晰可控,我在模型推理服务前挂了一个独立的 Service,Agent 运行时只能通过它访问模型,不允许直连 GPU Pod 的 IP。

4.3 部署后的自检清单:上线前必须确认的 10 项

很多团队 Agent 上线翻车,不是部署失败,而是上线前的自检没做到位。我整理了一份自检清单,Agent 上线前逐条打勾:

  • 命名空间标签和 NetworkPolicy 选择器匹配,跨租户连通性测试通过
  • ServiceAccount 配置正确,没有用 default;如果 Agent 需要调 Kubernetes API,确认权限是最小集
  • SecurityContext 已设置 runAsNonRoot 和 seccomp 限制
  • 镜像来自私有仓库的白名单项目,且已经过漏洞扫描
  • ResourceQuota 配额充足,LimitRange 的请求值在合理范围
  • GPU Pod 能正常调度到 GPU 节点,显存申请量与实际推理需要匹配
  • 会话上下文已外置,或者 Service 已配置 ClientIP 会话亲和
  • 所有敏感信息都存 Secret 并开启字段级加密
  • 日志采集和审计日志已配置,Agent 的工具调用记录能查到
  • 灰度发布策略就绪,异常回滚路径畅通

这十项看起来很多,但真正落下去之后,后续维护的压力会小一个量级。自检清单不是流程走形式,是救命的东西。有一次一个租户上线前没做跨租户连通性测试,结果他们的 Agent 呼叫另一个租户的模型服务,被 NetworkPolicy 静静挡掉,生产环境立刻白屏,排查花了四个小时。后来把自检清单立成硬规矩,这种事故再也没有发生过。

5. 常见问题与排查技巧实录

5.1 Agent 集体 OOMKilled:资源配额设计失衡的典型案例

我们遇到过最典型的生产事故:客服部门上线了新版对话 Agent,上线一小时之内 8 个副本全部 OOMKilled。排查时先看 Pod 事件,发现反复重启,然后在节点上查 dmesg,确认是内存配额被顶穿。

问题出在 ResourceQuota 的设计上:当初给租户的内存 limits 设置的 32Gi,看似够用,但 Agent 的每个 Pod 声明的内存 limits 只有 1.5Gi,4 个副本加起来 6Gi,按理说远没有触顶。真正的原因是模型推理时的临时峰值内存:新版 Agent 在长上下文对话时,单 Pod 内存能冲到 3GB 以上,直接超过了 Pod 自身的 limits,被 cgroup 杀掉。

调优方向有两个。一是把 Pod 内存 limits 提高到正常峰值的 1.5 倍,比如 4Gi;二是给租户的总配额也相应上调。同时我引入了hpa按内存使用率弹性伸缩,让高峰期的会话能分布在更多副本上,而不是堆在少数几个胖副本里。这个案例给我的教训是:Agent 的内存模型和普通 API 服务差异很大,上下文长度和并发数共同影响内存消耗,配 resource 时不能凭感觉拍脑袋,一定要做压测拿到真实曲线。

5.2 跨租户网络不通:NetworkPolicy 选择器匹配的坑

另一个高频问题是跨租户访问模型服务失败。现象很诡异:明明 Model Service 在 default Namespace 里可以通,Agent 换到租户 Namespace 后调用就超时。排查了半天,发现是 NetworkPolicy 的 namespaceSelector 没匹配上。

Kubernetes 的 namespaceSelector 匹配的是 Namespace 的标签,而不是 Namespace 的名字。我一开始写的是matchLabels: name: default,结果 default Namespace 没有name: default这个标签,策略自然失效。正确的做法是给所有涉及跨租户访问的 Namespace 都打上统一的标签,比如name: model-serving,然后 selector 里精确匹配这个标签。

这类问题隐蔽性很强,因为策略配置是生效的,只是一直在拒绝流量,而不是报错。我的排查方法是:先临时创建一个测试 Pod,把它放到源租户 Namespace,手动 curl 目标服务地址,再用kubectl describe networkpolicy查看策略命中情况,定位是选择器写错还是策略顺序写反。

5.3 GPU 显存碎片化与调度失败:靠什么解决

GPU 资源相关的调度失败是让人最头疼的。我们早期遇到过一个现象:GPU 节点的显存明明还有剩余,但新 Pod 一直 Pending,提示nvidia.com/gpu: insufficient。

原因是几个 Pod 各占了一部分的 GPU 显存(通过 MIG 或共享模式),但这些显存分散在不同的物理卡上,没有一张卡剩余显存能凑够新 Pod 申请的量。Kubernetes 的设备插件调度是按整卡或整设备的粒度来算的,不会自动“拼凑”不同卡上的空闲显存。

我给了两个解决方案。第一个是三思后统一到整卡分配:所有 Agent 推理 Pod 一律申请整张 GPU,显存不够就直接拒绝该 Pod,避免“挤牙膏式”的分配方式。第二个是启用调度器扩展,用自定义调度插件实现“显存凑量”能力,这个技术门槛高、维护成本大,小团队不建议上手。

长期看,真正的解法是把 GPU 节点池按型号统一。不要在同一个池里混用 A100、A10、RTX 3090 这种显存差异巨大的卡,否则调度器面临的约束会复杂得多。统一型号之后,继续用 binpack 策略把 Pod 尽量压实到少数节点上,让空闲节点能整节点掉电或休眠,省电又省心。

5.4 提示注入攻击与模型服务滥用:AI Agent 特有的安全事件

最后说一个和传统 Kubernetes 安全截然不同的场景:多租户下 Agent 被提示注入攻击,拿合法权限干了“不该干的事”。

有次审计日志发现某个租户的 Agent 在凌晨两点频繁调用内部代码仓库的查询接口,每次调用间隔均匀,像是自动化脚本。排查后确定是有人构造了恶意请求,诱导该 Agent 的模型上下文,让它把内部代码搜索接口当成普通工具去反复调用。传统 WAF 和网络策略对这类攻击基本没用,因为流量的来源是 Agent 自己的逻辑,IP 合法、账号合法、接口调用也合法。

应对策略有三道。

第一,Agent 应用层要对工具调用做权限和频控,不能无限制调用任何系统工具,尤其要限制写操作类工具的调用次数。

第二,模型推理层要加输出过滤和敏感操作复核。凡是 Agent 要执行“写库、改配置、发消息、执行代码”这类高风险动作,必须经过一个人工审批或者二次确认机制,这是多租户环境下的底线。

第三,模型服务的入口要限流。Agent 的模型调用端点独立限流,防止某个 Agent 被攻击后把共享的模型服务流量打满,影响其他租户。

这个领域的防护还在快速演进中,我也一直在调整策略。但有一个原则是确定的:Agent 的权限设计必须遵循最小权限,而且在执行任何有副作用的操作前,默认要“停下来问一下”,这比任何静态安全规则都可靠。

最后分享一点我的实际体会

这套多租户 Kubernetes 集群跑起来之后,我最大的感受是:AI Agent 的安全部署,难点不在“部署”,而在“对人性的预判”——你永远不知道哪个租户会在凌晨提交一个占满全集群资源的怪物请求,也永远不能假设 Agent 不会在中途突然干出让你瞠目结舌的事情。把所有的不确定性用配额、策略、审计和流程约束起来,让系统在多数情况下即使受损也能快速恢复,这才是可持续的多租户运营思路。如果你正准备把 Agent 大规模搬上集群,我的建议是从小规模试点开始,把配额、网络安全、权限模型和自检清单全部跑顺,再放开给所有团队用,这会让你少踩很多坑。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 18:12:26

MCP Prompt 模板化实战:用 TaoToken 统一 Key 让 AI 输出不再抽风

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 18:11:12

YOLO26室内家居数据集实战:从一条命令训练到RKNN边缘部署

做视觉方案这些年&#xff0c;我一直觉得模型结构迭代从来不是最让人头疼的事&#xff0c;真正卡人的是数据和对场景的理解。YOLO26这波直接把室内家居数据集一起放出&#xff0c;算是把“数据—训练—部署”这条路做了个完整梳理&#xff0c;一条命令就能让模型开始学认家里的…

作者头像 李华
网站建设 2026/9/26 18:10:52

多机房动力环境集中监控实战:从协议对接、告警收敛到部署运维

1. 从一次深夜机房告警说起&#xff1a;为什么分布式机房必须做集中监控凌晨两点&#xff0c;手机响了。某分厂机房的温湿度传感器触发高温告警&#xff0c;值班同事赶到现场发现是空调外机被杂物堵住导致散热失效。等处理完回到值班室&#xff0c;另一台UPS的电池组又报了电压…

作者头像 李华
网站建设 2026/9/26 18:10:51

BurpSuite 插件探测 Log4j2、Fastjson 与 Log4j 漏洞实战

简介&#xff1a;这份资源面向Java安全测试人员与渗透测试学习者&#xff0c;聚焦Log4j、Log4j2与Fastjson三类常见组件的漏洞检测场景。包内提供适配BurpSuite的扫描插件&#xff0c;可帮助使用者在目标系统中识别相关组件并评估远程代码执行等安全风险&#xff0c;兼容新旧版…

作者头像 李华
网站建设 2026/9/26 18:07:13

YOLOv8大豆叶病检测实战:环境搭建到模型部署完整链路

简介&#xff1a;面向计算机视觉初学者与农业智能化研究者&#xff0c;这份压缩包以YOLOv8实现大豆叶病目标检测&#xff0c;完整展示从数据集构建、模型设计到训练推理的YOLO框架搭建流程。资源共7个文件&#xff0c;包含6个Python脚本和1个Markdown说明&#xff0c;脚本分别覆…

作者头像 李华
网站建设 2026/9/26 18:07:11

hping.win32:Windows下手工构造TCP/UDP/ICMP报文的网络探测工具

简介&#xff1a;hping.win32 是一份面向网络安全学习者与运维人员的 Windows 平台 hping 源码包&#xff0c;基于 Dev-C 工程组织&#xff0c;适合研究 TCP/IP 数据包组装与分析原理、开展防火墙测试与端口扫描实验的中级读者。压缩包共 102 个文件&#xff0c;以 43 个 .o 目…

作者头像 李华