尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于Kubernetes的Agentic运行时编排:从设计到部署实践

基于Kubernetes的Agentic运行时编排:从设计到部署实践 1. 从“ax”这个标题说起一个被低估的运行时编排命题第一次看到“ax”这个标题很多人会一头雾水。它不像“Kubernetes 入门”那样直白也不像“Agentic RAG 实战”那样有明确的技术指向。但把热搜词摊开来看线索就清楚了ax、agentic、orchestration、runtime、Kubernetes这五个词放在一起指向的是一个非常具体的工程命题——在 Kubernetes 之上如何为 agentic 工作负载构建一套轻量、可编排、可观测的运行时层。我之所以对这个方向感兴趣是因为过去一年里agentic 应用的部署方式发生了明显变化。早期大家把 agent 当成一个普通的 Python 进程塞进一个 Pod 里跑就完事了。但当 agent 开始需要多轮工具调用、需要访问外部状态、需要在多个子任务之间做编排时单进程模型很快就撑不住了。你会遇到几个典型问题agent 的每一步决策都需要持久化否则 Pod 重启就丢状态多个 agent 之间需要协调但用 Kubernetes 原生的 Service 做通信又太重runtime 层面的资源隔离和生命周期管理跟传统微服务完全不是一回事。“ax”这个标题我理解它代表的是这一类运行时抽象的一个代号。它不是一个具体的开源项目名而更像是一个问题域的标记agentic orchestration runtime on Kubernetes。热搜词里出现的 Karmada 毕业、华为云 agentic cloud、仲景 agentic 开源地址其实都在说明同一件事——agentic 负载的编排正在从“能用”走向“好用”而 runtime 层是其中最关键的缺口。这篇文章适合谁看如果你正在把 agentic 应用往 Kubernetes 上搬或者你已经在跑一些多 agent 协作的实验但被状态管理、编排复杂度、runtime 隔离这些问题卡住了那接下来的内容应该对你有直接参考价值。我会从设计思路讲到实操细节包括我踩过的坑和验证过的方案。不会涉及任何敏感内容纯粹是工程层面的经验分享。2. 为什么 agentic 负载需要独立的 runtime 层2.1 传统微服务 runtime 和 agentic runtime 的本质差异先把一个基本问题说清楚为什么不能直接用 Kubernetes 原生的 Deployment Service 来跑 agentic 应用答案在于执行模型的差异。传统微服务的执行模型是“请求-响应”式的。一个 HTTP 请求进来服务处理完返回结果整个生命周期是短促且无状态的。Kubernetes 的 runtime 设计就是围绕这个模型来的Pod 可以随时被杀掉重建因为状态不在 Pod 里。但 agentic 负载不一样。一个 agent 在执行任务时它的“思考过程”本身就是状态。它可能调用了三个工具拿到了中间结果正在决定下一步走哪条路。如果这时候 Pod 被重启这些中间状态全丢了任务就得从头再来。我实测过一个场景一个负责代码审查的 agent需要先拉取 diff、再分析变更、再生成评论、最后提交。整个过程涉及四次外部调用每次调用之间都有依赖关系。用普通 Deployment 跑的时候只要节点发生一次驱逐整个审查流程就断了。后来我把中间状态抽出来放到外部存储Pod 重启后能从断点恢复但这就引入了新的复杂度——状态同步的延迟和一致性问题。所以 agentic runtime 的第一个核心需求是执行上下文必须与计算单元解耦。计算单元可以随时重启但执行上下文要能持久化、能恢复、能迁移。2.2 编排层要解决的三类问题Agentic orchestration 要解决的问题我把它归纳为三类。第一类是任务编排。一个复杂任务往往需要多个 agent 协作或者一个 agent 需要按特定顺序执行多个步骤。这跟传统的工作流引擎比如 Argo Workflows有相似之处但区别在于 agent 的下一步动作可能是动态决定的不是预先定义好的 DAG。你没法在启动前就把所有分支画出来因为 agent 会根据中间结果自己决定走哪条路。第二类是资源编排。Agent 在执行过程中可能需要不同类型的资源有时候需要 GPU 做推理有时候只需要 CPU 做文本处理有时候需要访问特定的外部服务。Runtime 层要能根据 agent 的当前需求动态分配资源而不是一开始就固定死。第三类是生命周期编排。Agent 的生命周期不是简单的“启动-运行-停止”。它可能有“等待外部事件”“暂停等待人工确认”“超时重试”等多种状态。Runtime 要能管理这些状态转换并且在状态变化时触发相应的动作。这三类问题叠加在一起就是为什么我说 agentic 负载需要独立的 runtime 层。用 Kubernetes 原生资源硬凑也能凑出来但你会写大量的 controller 和 operator最后发现自己在重新发明一个 runtime。2.3 为什么选择在 Kubernetes 之上构建既然要独立 runtime为什么不干脆脱离 Kubernetes 自己搞一套我的判断是Kubernetes 提供的底层能力太香了没必要重复造轮子。Kubernetes 已经解决了容器调度、网络、存储、密钥管理、RBAC 这些基础设施问题。Agentic runtime 需要的是在这些基础之上增加 agent 特有的编排逻辑。比如你可以用 Kubernetes 的 CRD 来定义 agent 的规格用 controller 来管理 agent 的生命周期用 Service 来做 agent 之间的发现用 ConfigMap 来注入 agent 的配置。这些能力都是现成的你只需要在它们之上加一层 agent 语义。热搜词里出现的 Karmada 毕业和华为云 agentic cloud其实也印证了这个方向。Karmada 解决的是多集群编排问题而 agentic cloud 要解决的是 agent 负载在云上的编排问题。两者都是在 Kubernetes 生态里做扩展而不是另起炉灶。我自己的做法是用 Kubernetes 做底层资源管理用自定义的 runtime 组件做 agent 编排。Runtime 组件本身也跑在 Kubernetes 上但它管理的是 agent 的逻辑生命周期而不是容器的物理生命周期。这样既利用了 Kubernetes 的稳定性又保留了 agent 编排的灵活性。3. 核心组件拆解一个 agentic runtime 应该长什么样3.1 执行引擎从“进程”到“执行单元”的抽象Runtime 的核心是执行引擎。在传统模型里执行单元是进程或容器。在 agentic 模型里执行单元应该是一次 agent 执行会话。我设计的执行引擎里每个执行会话有这几个关键属性会话 ID、当前状态、执行历史、资源引用、超时策略。会话 ID 用来做幂等和恢复当前状态标记会话处于“运行中”“等待中”“已完成”“已失败”中的哪一种执行历史记录每一步的输入输出用于审计和恢复资源引用指向会话需要的计算资源超时策略定义会话在某个状态下最多停留多久。执行引擎的工作流程是这样的收到一个执行请求后先创建会话记录然后根据请求类型决定是立即执行还是排队。执行过程中每一步的结果都写入执行历史。如果执行中断恢复时从执行历史里找到最后一个已完成步骤从下一步继续。这里有个关键设计决策执行历史存哪里。我试过三种方案。第一种是存在内存里简单但 Pod 重启就丢。第二种是存在 Kubernetes 的 CRD 里好处是跟 K8s 生态集成好坏处是 CRD 的写入频率有限制高频写入会打爆 etcd。第三种是存在外部数据库里比如 PostgreSQL 或 Redis灵活但引入了额外依赖。我最后选了第三种用 PostgreSQL 存执行历史用 Redis 做会话状态的缓存。这个组合在写入性能和查询灵活性之间取得了比较好的平衡。3.2 编排器动态 DAG 与状态机编排器负责决定 agent 下一步做什么。这里有两种主流思路动态 DAG和状态机。动态 DAG 的思路是agent 的每一步执行都产生一个节点节点之间的边在运行时确定。编排器维护一个图结构每次 agent 完成一步就往图里加新节点和新边。这种方式的优点是直观容易可视化缺点是图会越来越大长时间运行的 agent 会产生巨大的图查询和遍历成本高。状态机的思路是预定义一组状态和状态转换规则agent 的执行过程就是在状态之间迁移。这种方式的优点是状态数量可控转换逻辑清晰缺点是灵活性差遇到预定义之外的情况需要扩展状态机。我最后选了一个混合方案用状态机做顶层控制用动态 DAG 做单次任务内部的编排。顶层状态机管理 agent 的整体生命周期比如“初始化”“执行中”“等待人工确认”“已完成”。在“执行中”这个状态内部用动态 DAG 来编排具体的步骤。这样既保证了整体流程的可控性又保留了单次任务的灵活性。状态转换的触发条件也需要仔细设计。我定义了三种触发方式事件触发比如收到外部消息、条件触发比如某个变量达到阈值、超时触发比如在某个状态停留超过指定时间。这三种触发方式覆盖了大部分场景实现起来也不复杂。3.3 资源管理器按需分配与回收资源管理器负责给执行会话分配计算资源。这里的挑战是agent 的资源需求是动态变化的而且往往不可预测。我的做法是分级资源池。把资源分成三个级别轻量级CPU only适合文本处理、中量级CPU 内存适合中等复杂度任务、重量级GPU适合推理任务。每个级别对应一个资源池池子里的资源预先创建好需要时直接分配用完回收。这样做的好处是分配速度快不需要每次从零创建 Pod。坏处是资源利用率可能不高因为池子里的资源在空闲时也占着。我通过动态调整池子大小来缓解这个问题监控每个池子的使用率使用率持续低于阈值就缩容持续高于阈值就扩容。缩容时不是直接删 Pod而是先标记为“可回收”等当前任务完成后再删。资源回收的时机也很关键。Agent 执行完成后资源不是立即回收而是保留一段时间我设的是 5 分钟以防 agent 需要重试或恢复。超过保留时间后资源才真正释放。这个设计在实际运行中减少了很多“刚释放就要重新分配”的抖动。3.4 状态存储执行上下文的持久化方案状态存储是 runtime 的基石。我前面提到用 PostgreSQL 存执行历史、用 Redis 做状态缓存这里展开说一下具体设计。PostgreSQL 里主要存三张表会话表记录每个执行会话的元信息包括会话 ID、创建时间、当前状态、所属 agent 类型步骤表记录每一步执行的详细信息包括步骤 ID、所属会话、步骤类型、输入、输出、开始时间、结束时间、状态事件表记录会话期间发生的所有事件包括事件类型、事件内容、发生时间。Redis 里主要存两类数据会话状态缓存用 Hash 结构存会话的当前状态和最近几步的执行结果读取速度快分布式锁用 SETNX 实现保证同一会话不会被多个执行引擎实例同时处理。这里有个坑我踩过Redis 和 PostgreSQL 的数据一致性。如果先写 Redis 再写 PostgreSQLRedis 写成功但 PostgreSQL 写失败就会导致缓存里有但数据库里没有。反过来先写 PostgreSQL 再写 RedisPostgreSQL 写成功但 Redis 写失败缓存里没有但数据库里有下次读取时会从数据库回填问题不大。所以我最后选了先写 PostgreSQL 再写 Redis的顺序并且 Redis 写入失败时不回滚 PostgreSQL而是记录一条补偿日志由后台任务定期修复。3.5 可观测性日志、指标与追踪Agentic runtime 的可观测性比传统服务复杂得多。传统服务你主要看 QPS、延迟、错误率。Agentic runtime 你还要看每个 agent 的执行步数、每步的耗时、工具调用的成功率、状态转换的频率、资源池的使用率。我的做法是三层可观测性。第一层是结构化日志每个执行步骤都打一条 JSON 日志包含会话 ID、步骤 ID、步骤类型、耗时、结果状态。这些日志直接进 Elasticsearch方便按会话 ID 聚合查询。第二层是指标用 Prometheus 采集主要包括活跃会话数、各状态会话数、步骤执行耗时分布、资源池使用率、状态转换次数。第三层是分布式追踪用 OpenTelemetry 做每个执行会话是一个 Trace每个步骤是一个 Span工具调用是子 Span。这样能直观看到一次 agent 执行的全链路耗时分布。这里有个经验不要试图追踪所有东西。一开始我把每个内部函数调用都打了 Span结果 Trace 数据量爆炸存储成本高得离谱。后来我只追踪跨进程的调用和外部工具调用内部函数调用用日志代替。这样 Trace 数据量降了一个数量级但关键路径的可见性没有损失。4. 实操在 Kubernetes 上部署一个最小可用 runtime4.1 环境准备与依赖检查先说环境要求。我用的 Kubernetes 版本是 v1.26.0这是热搜词里出现的版本也是目前比较稳定的一个版本。节点配置至少 3 个 worker 节点每个节点 4 核 8G 起步。如果要用 GPU 资源池至少一个节点带 GPU。部署前需要检查几个依赖。第一是container runtime确保节点上的容器运行时正常。热搜词里有个报错 “container runtime is not running”这个通常是因为容器运行时服务没启动或者配置有问题。用systemctl status containerd检查一下如果是 containerd确认配置文件/etc/containerd/config.toml里的SystemdCgroup设置为true。第二是WebView2 runtime这个跟 agentic runtime 本身没关系但如果你要在 Windows 节点上跑一些带 UI 的 agent 工具可能会遇到 “could not find the WebView2 runtime” 的报错。解决办法是安装 Microsoft Edge WebView2 Runtime或者直接用无 UI 的版本。第三是CLI 工具确保kubectl和helm可用。如果遇到 “unable to locate the codex cli binary or required runtime components” 这类报错通常是 PATH 配置问题把 CLI 所在目录加到 PATH 里就行。4.2 用 CRD 定义 Agent 资源Kubernetes 的 CRD 是定义 agent 资源的最佳方式。我定义了一个AgentCRD核心字段包括apiVersion: ax.io/v1alpha1 kind: Agent metadata: name: code-review-agent spec: type: code-review image: registry.example.com/agents/code-review:v1.2.0 resourceClass: medium timeoutSeconds: 600 maxRetries: 3 stateStore: type: postgresql connectionSecret: agent-state-db tools: - name: git endpoint: http://git-service:8080 - name: llm endpoint: http://llm-service:8080这个 CRD 定义了一个代码审查 agent指定了镜像、资源级别、超时时间、重试次数、状态存储和工具列表。Controller 监听这个 CRD当有新的 Agent 资源创建时就创建一个对应的执行会话。这里有个设计细节resourceClass 不是直接指定 CPU/内存数值而是指定一个级别。这样做的好处是资源分配逻辑集中在 runtime 层agent 定义不需要关心具体的资源数值。Runtime 层可以根据集群的实际情况调整每个级别对应的资源量而不需要修改 agent 定义。4.3 部署编排器与执行引擎编排器和执行引擎我打包成了两个 Deployment都跑在 Kubernetes 上。编排器的 Deployment 配置apiVersion: apps/v1 kind: Deployment metadata: name: ax-orchestrator spec: replicas: 2 selector: matchLabels: app: ax-orchestrator template: metadata: labels: app: ax-orchestrator spec: containers: - name: orchestrator image: registry.example.com/ax/orchestrator:v0.9.0 ports: - containerPort: 8080 env: - name: STATE_DB_HOST valueFrom: secretKeyRef: name: agent-state-db key: host - name: REDIS_ADDR value: redis-service:6379 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1Gi执行引擎的 Deployment 类似但副本数更多因为执行引擎是实际干活的。我设了 4 个副本每个副本能同时处理 10 个执行会话。副本数可以根据负载动态调整用 HPA 做自动扩缩容。这里有个坑执行引擎的副本数不是越多越好。因为执行引擎需要跟状态存储交互副本数太多会导致状态存储的连接数暴涨。我一开始设了 10 个副本结果 PostgreSQL 的连接池被打满执行引擎反而变慢了。后来降到 4 个配合连接池配置性能反而更好。4.4 配置资源池与自动扩缩资源池我用 Kubernetes 的ResourceQuota和LimitRange来管理。每个资源级别对应一个 NamespaceNamespace 里配置 ResourceQuota 限制总资源量配置 LimitRange 限制单个 Pod 的资源范围。轻量级资源池的配置apiVersion: v1 kind: ResourceQuota metadata: name: light-quota namespace: ax-light spec: hard: requests.cpu: 8 requests.memory: 16Gi limits.cpu: 16 limits.memory: 32Gi自动扩缩用 HPA基于自定义指标活跃会话数来扩缩。HPA 的配置apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: ax-executor-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: ax-executor minReplicas: 2 maxReplicas: 8 metrics: - type: Pods pods: metric: name: active_sessions target: type: AverageValue averageValue: 10这个配置的意思是每个执行引擎 Pod 平均处理 10 个活跃会话时就触发扩容。最少 2 个副本最多 8 个。4.5 验证部署与跑通第一个 Agent部署完成后用kubectl get pods -n ax-system检查所有 Pod 是否 Running。然后创建一个测试 Agentkubectl apply -f - EOF apiVersion: ax.io/v1alpha1 kind: Agent metadata: name: test-agent spec: type: echo image: registry.example.com/agents/echo:v1.0.0 resourceClass: light timeoutSeconds: 60 maxRetries: 1 EOF创建后用kubectl get agents查看状态。如果状态是Running说明 agent 已经启动。用kubectl logs -l appax-executor查看执行日志应该能看到 agent 的执行步骤。跑通第一个 agent 后可以试试更复杂的场景创建一个需要调用外部工具的 agent观察工具调用是否正常、状态是否正确持久化、Pod 重启后是否能恢复。这些验证做完基本就能确认 runtime 的核心功能是通的。5. 常见问题与排查技巧实录5.1 状态不一致会话卡在“运行中”不动了这是最常见的问题。表现是 agent 的会话状态一直是“运行中”但日志里没有任何新输出。原因通常有三种执行引擎 Pod 挂了但会话状态没更新、状态存储连接断了、分布式锁没释放。排查步骤先用kubectl get pods -n ax-system看执行引擎 Pod 是否正常。如果 Pod 频繁重启看 Pod 的 events 和日志通常是 OOM 或配置错误。如果 Pod 正常检查 Redis 里的分布式锁用redis-cli keys lock:session:*看有没有残留的锁。如果有手动删掉然后重启对应的执行引擎 Pod。预防措施给分布式锁设置过期时间我设的是会话超时时间的 2 倍。这样即使执行引擎挂了锁也会自动释放。另外加一个后台任务定期扫描“运行中”但超过超时时间的会话把它们标记为“失败”并触发重试。5.2 资源池耗尽新会话一直排队表现是新的 agent 会话创建后一直处于“等待中”不进入“运行中”。原因是资源池里的资源被占满了没有空闲资源分配给新会话。排查用kubectl describe resourcequota -n ax-light看资源配额的使用情况。如果used接近hard说明资源池满了。再看kubectl get pods -n ax-light看有多少 Pod 在运行。如果 Pod 数量接近配额上限说明确实满了。解决短期可以手动扩容资源池修改 ResourceQuota 的 hard 值。长期需要优化资源回收策略缩短资源保留时间或者提高资源池的自动扩缩灵敏度。我踩过的坑资源回收时没有考虑“僵尸会话”。有些会话状态是“已完成”但对应的 Pod 因为某种原因没被删掉一直占着资源。后来我加了一个清理任务定期扫描“已完成”超过保留时间的会话强制删除对应的 Pod。5.3 工具调用超时外部服务不可达Agent 调用外部工具时超时导致整个会话失败。原因可能是外部服务挂了、网络不通、或者工具配置的 endpoint 错了。排查先看执行日志里工具调用的错误信息。如果是 connection refused检查外部服务的 Service 和 Endpoint 是否正确。如果是 timeout检查网络策略和防火墙规则。如果是 DNS 解析失败检查 CoreDNS 是否正常。我遇到过一次工具调用的 endpoint 写的是http://git-service:8080但 git-service 在另一个 Namespace 里跨 Namespace 访问需要写全称http://git-service.other-namespace:8080。这个错误很隐蔽因为日志里只显示 timeout不显示 DNS 解析失败。后来我在工具调用层加了更详细的错误日志把 DNS 解析、TCP 连接、HTTP 请求三个阶段分开记录排查起来就快多了。5.4 状态存储性能瓶颈写入延迟高表现是 agent 执行步骤的耗时越来越长日志里显示状态写入耗时占比很高。原因是状态存储的写入性能跟不上。排查看 PostgreSQL 的慢查询日志找出耗时最长的写入语句。通常是步骤表的写入因为每个步骤都要写一条记录。如果步骤表数据量很大写入会变慢。优化给步骤表加索引主要是会话 ID 和创建时间的联合索引。另外把步骤表的写入改成批量写入积累一定数量的步骤后一次性写入减少数据库交互次数。我用的是每 10 个步骤或每 1 秒批量写入一次写入延迟从平均 50ms 降到了 5ms。还有一个优化把执行历史的存储和会话状态的存储分开。执行历史用 PostgreSQL会话状态用 Redis。这样高频的状态更新走 Redis低频的历史查询走 PostgreSQL各取所长。5.5 常见问题速查表问题现象可能原因排查方法解决方案会话卡在“运行中”执行引擎挂了/锁未释放检查 Pod 状态和 Redis 锁重启 Pod清理残留锁新会话一直排队资源池耗尽检查 ResourceQuota 使用率扩容资源池或优化回收工具调用超时外部服务不可达检查 Service/Endpoint/DNS修正 endpoint 或网络策略状态写入延迟高存储性能瓶颈检查慢查询日志加索引批量写入Pod 频繁重启OOM 或配置错误检查 Pod events 和日志调整资源限制或配置状态不一致Redis 和 PG 不同步对比两边数据补偿任务修复6. 从“能用”到“好用”几个值得深挖的优化方向6.1 执行引擎的无状态化改造我现在的执行引擎是有状态的每个引擎实例负责一部分会话。这样做的问题是如果某个引擎实例挂了它负责的会话需要迁移到其他实例迁移过程有延迟。而且扩缩容时会话需要重新分配也会造成短暂的中断。无状态化改造的思路是执行引擎不保存任何会话状态所有状态都在外部存储里。引擎实例从队列里拉取任务处理完后把结果写回存储。这样引擎实例可以随时创建和销毁不需要迁移会话。代价是每次处理任务都要从存储里读状态延迟会高一些。但考虑到 agent 执行本身耗时较长这点延迟可以接受。我试过用 Kubernetes 的 Job 来做执行引擎每个会话对应一个 Job。Job 的好处是天然无状态Kubernetes 负责调度和重试。坏处是 Job 的启动开销比常驻进程大而且 Job 的数量多了之后Kubernetes 的 API Server 压力会很大。后来我折中了一下常驻进程做执行引擎但引擎本身无状态从 Redis 队列里拉任务。这样既避免了 Job 的启动开销又实现了无状态化。6.2 多集群编排的初步探索单集群的 runtime 跑通后自然会想到多集群。多集群的好处是可以把 agent 调度到最合适的集群比如 GPU 任务调度到有 GPU 的集群低延迟任务调度到离用户近的集群。Karmada 是一个选择。它提供了多集群的调度和编排能力可以把 Kubernetes 资源分发到多个集群。我用 Karmada 试了一下把 Agent CRD 分发到两个集群一个跑轻量级 agent一个跑重量级 agent。效果还不错但引入 Karmada 后整个架构的复杂度上了一个台阶。状态存储需要跨集群同步网络需要打通权限需要统一管理。如果集群数量不多比如 2-3 个用 Karmada 有点重。如果集群数量多Karmada 的价值就体现出来了。我的建议是先跑通单集群再考虑多集群。单集群的 runtime 稳定运行三个月以上再评估是否需要多集群。多集群带来的收益资源利用率、延迟优化是否值得付出复杂度成本需要根据实际业务来判断。6.3 Agent 之间的通信与协作多个 agent 协作时通信机制很关键。我试过两种方式直接通信和通过编排器通信。直接通信是 agent A 直接调用 agent B 的接口。这种方式延迟低但耦合度高agent A 需要知道 agent B 的地址和接口。而且如果 agent B 挂了agent A 需要自己处理重试和降级。通过编排器通信是 agent A 把消息发给编排器编排器再转发给 agent B。这种方式解耦好编排器可以处理重试、路由、限流。但编排器成了瓶颈所有通信都经过它延迟和吞吐都受影响。我最后选了一个混合方案同步调用走直接通信异步消息走编排器。Agent A 需要立即拿到 agent B 的结果时直接调用 agent B 的接口。Agent A 只是通知 agent B 做某件事不需要立即拿到结果时把消息发给编排器由编排器异步转发。这样既保证了同步调用的低延迟又保证了异步消息的可靠性。6.4 安全隔离与权限控制Agent 执行时可能需要访问敏感资源比如数据库、密钥、外部 API。Runtime 层需要做安全隔离确保一个 agent 不能访问它不该访问的资源。我的做法是基于 ServiceAccount 的权限控制。每个 agent 类型对应一个 ServiceAccountServiceAccount 绑定特定的 Role 和 RoleBinding。Agent 执行时用对应的 ServiceAccount 的 token 访问 Kubernetes 资源。这样 agent 只能访问它被授权的资源不能越权。对于外部资源的访问我用Secret 注入的方式。Agent 需要的密钥存在 Kubernetes Secret 里执行时把 Secret 挂载到 Pod 里。Agent 从文件系统读取密钥而不是从环境变量读。这样密钥不会出现在 Pod 的环境变量里减少了泄露风险。还有一个细节Agent 之间的网络隔离。我用 NetworkPolicy 限制 agent Pod 之间的网络访问默认拒绝所有入站和出站流量只允许必要的通信。这样即使一个 agent 被攻破也不能横向移动到其他 agent。6.5 成本优化让资源利用率再高一点Runtime 跑起来后成本是个绕不开的话题。我算过一笔账如果资源池的利用率只有 30%那 70% 的资源是浪费的。优化资源利用率直接等于省钱。我做了几件事。第一是资源池的细粒度化。原来只有三个级别轻量、中量、重量后来细分成五个级别每个级别的资源量更贴近实际需求。这样 agent 不会因为“级别太高”而浪费资源。第二是执行引擎的混部。把执行引擎和普通业务 Pod 混部在同一个节点上利用业务 Pod 的空闲资源。这需要配置好资源请求和限制避免互相影响。我用的是requests设低、limits设高的策略让执行引擎在资源空闲时能用更多资源资源紧张时自动让出。第三是闲置资源的自动回收。资源池里的 Pod 如果超过一定时间没有被使用自动缩容。缩容时不是直接删而是先标记为“可回收”等当前任务完成后再删。这样既回收了资源又不影响正在执行的任务。实测下来这三项优化把资源利用率从 30% 提到了 65% 左右。虽然还有提升空间但成本已经降了不少。7. 一些个人体会和后续可以尝试的方向这套 runtime 我从零开始搭前后迭代了大概四个月。最大的体会是agentic runtime 的复杂度不在单个组件而在组件之间的交互。执行引擎、编排器、状态存储、资源管理器每个单独看都不复杂但把它们放在一起状态同步、错误处理、一致性保证这些问题就会冒出来。我花在调试组件交互上的时间比写单个组件的时间多得多。另一个体会是不要过早追求完美。我一开始想设计一个通用的、支持所有场景的 runtime结果设计过度实现起来极其复杂。后来我砍掉了很多“可能有用”的功能只保留核心的编排、执行、状态管理反而跑得更稳。等核心稳定了再逐步加功能这样风险可控。后续我想尝试的方向有几个。一是把 runtime 的部分能力下沉到 eBPF用 eBPF 做网络隔离和可观测性性能比用户态方案好很多。二是引入 agent 级别的缓存把 agent 常用的工具调用结果缓存起来减少重复调用。三是探索 agent 的自动扩缩容根据 agent 的历史执行数据预测未来的资源需求提前扩容。这些方向都还在探索阶段没有成熟方案。如果你也在做类似的事情欢迎交流。踩过的坑、验证过的方案都可以分享。这个领域还在快速变化多交流能少走很多弯路。
返回列表