系列导读:Agent 工程需要哪些运维知识

网络打通之后,下一个问题是:Agent 到底在哪里运行?

写代码时,一个函数调用可以完成很多事情。部署以后,这些事情可能属于不同进程:API 接收请求,Worker 调用模型,沙箱运行生成的分析代码。每个进程都有自己的资源、文件和退出时机,任务也会受到这些边界影响。

这篇继续使用分析 Excel 的例子,把程序从源码变成运行实例的过程整理一下。

本文由 AI 辅助整理。架构、命令及资源配置均用于说明机制,不代表生产环境部署或性能实测。

先把三个执行角色分开

用户上传 Excel 后,API 创建带有 run_id 的任务。Worker 获取任务、准备上下文、调用模型;需要运行分析代码时,再请求隔离沙箱执行对应的 step_id。

角色 主要职责 需要关心的运行条件
API 接收请求、鉴权、查询状态、输出事件 连接数、响应时间、流式连接
Worker 推进任务、调用模型与工具、记录结果 执行并发、外部等待、恢复与退出
沙箱 运行分析代码,读写任务工作目录 CPU、内存、文件权限、网络与时限

这些角色可以部署在同一台机器,也可以分别运行在不同平台。先按职责拆开,有助于判断某个资源限制应该加在哪里。

例如模型响应慢,增加沙箱内存未必有帮助;分析大表时进程被终止,则应检查执行代码的沙箱,而不仅是 API 服务。

进程有自己的资源和生命周期

程序启动后形成进程。进程使用内存、打开文件、创建网络连接,也可能启动线程或子进程。协程通常用于组织一个进程中的并发工作,任务最终仍受所在进程和运行平台的生命周期影响。

对 Agent 来说,先观察几个具体问题就很有价值:

  • 当前进程在计算,还是等待模型、数据库和工具返回?
  • 并发任务是否共享了不该共享的全局变量?
  • 数据库连接池最多建立多少连接?
  • 浏览器或分析脚本是否留下了未回收的子进程?
  • 文件和日志是否持续增长,耗尽了磁盘?

CPU 使用率低,也可能有很多任务在等待。反过来,读取一个压缩文件或展开一张大表,可能瞬间申请大量内存。因此,运行状态应结合任务阶段和资源指标一起看。

下面是观察本机进程与资源的命令片段:

ps -eo pid,ppid,stat,%cpu,%mem,args
free -h
df -h
ss -lntp

它们分别帮助查看进程关系、内存、磁盘和监听端口。容器中看到的资源范围还要结合容器配置解释,不能只看宿主机总量。

Dockerfile、镜像与容器的关系

我会把它们放在三个时间点理解。

写 Dockerfile 时,是在描述如何构建。 例如选择 Python 基础环境,安装依赖,复制代码,定义启动命令。

构建结束后,得到镜像。 镜像保存启动应用所需的文件、依赖和配置模板。镜像内容确定后,修改程序通常需要构建一个新镜像。

启动镜像后,得到容器。 容器是运行实例,同一个镜像可以启动多个实例。容器运行时产生的文件写入可写层或挂载存储,不会回写到原镜像。Docker 镜像说明

镜像标签还需要单独理解。agent-worker:v1 是一个引用名称,标签可以被重新指向其他内容。为了追溯发布,应记录构建对应的代码提交与镜像 digest,避免只知道“运行的是 latest”。

在 Excel 场景中,API 镜像主要包含 Web 服务依赖,Worker 镜像包含任务执行逻辑,沙箱镜像则包含分析工具和运行环境。可以根据实际需求复用基础层,不必把所有依赖都装进每个镜像。

从代码仓库到运行平台

将 Dockerfile、云效、ACR 和 FC 放到同一条交付流程里,可以这样理解:

  1. 代码仓库保存源码、依赖声明和 Dockerfile。
  2. 云效或其他 CI 获取指定提交,执行检查并构建镜像。
  3. 将镜像推送到 ACR。
  4. FC 或 K8s 拉取确定版本的镜像,并注入运行配置。
  5. 启动后检查服务,再接入任务或流量。

这里,ACR 承担镜像的保存与分发;OSS 用来保存输入文件、报告和其他对象。即使把 Dockerfile 放进 OSS,它仍然只是构建说明,必须经过构建才会产生可部署的镜像。

运行配置也应与镜像内容区分。数据库地址、模型地址、凭证等可以通过平台配置、挂载或受控凭证机制提供。这样,同一份构建产物能用于不同环境,敏感信息也不必写进镜像层。

另外要核对目标平台要求:CPU 架构、启动命令、运行用户、监听地址和端口。阿里云 FC 的 Web Server 自定义镜像模式要求服务监听 0.0.0.0:配置端口 或相应通配地址;只监听回环地址会让平台无法正常连接。FC 自定义镜像说明

文件的寿命不等于任务的寿命

沙箱分析 Excel 时,会产生输入副本、临时数据、图片和最终报告。我会按恢复需求区分它们:

文件或数据 保存方式 失去当前实例后怎么办
原始 Excel OSS 等持久对象存储 按任务中的对象引用重新读取
中间计算文件 沙箱工作目录或临时磁盘 能重建则重新计算,不能重建则及时持久化
最终报告 OSS 等持久对象存储 根据权限和对象引用继续访问
任务状态与步骤结果 持久数据库 用于判断完成情况与恢复位置

容器可写层随容器删除而丢失,挂载卷可以把数据生命周期与容器分开。阿里云 FC 也明确说明,自定义镜像的可写层会随实例销毁而删除,需要持久化的数据应使用外部存储。Docker 文件挂载 · FC 存储边界

“已生成报告”最好包含可核验的对象引用、大小或校验信息。上传还没完成就把任务标为成功,用户可能拿到一个不存在的文件。

挂载本身也不等于备份。共享目录仍然需要访问权限和清理策略,实例能重新挂上卷,也不代表误删除的数据能够恢复。

资源限制应该加在实际消耗资源的地方

普通容器通过操作系统机制组织隔离和资源控制。运行时可以限制内存、CPU 等资源,但需要明确配置与行为。Docker 资源约束

比如一个任务启动多个浏览器页面,或者分析代码持续创建大数组,首先受到影响的是沙箱。API 和 Worker 的资源限制不能代替沙箱自己的限制。

我会分别考虑执行时长、内存、CPU、临时磁盘、进程数和输出体积。还需要限制单个用户同时占用多少沙箱,避免一个任务创建大量执行实例。

看到进程退出时,应结合退出原因、平台事件和日志判断。某些退出码只说明收到特定信号,不能单凭一个数字就认定发生了 OOM。资源指标与任务阶段放在一起,才能知道应该调整配额、修复代码,还是降低并发。

发布时,正在执行的任务怎样退出

API 可以停止接收新请求,Worker 则还需要处理已经领取的任务。

K8s 等平台会为正常终止提供宽限过程,应用通常先收到终止信号,期限到达后仍未退出的进程可能被强制结束。Pod 终止流程

Worker 应利用这个窗口:停止领取新任务;完成能及时结束的工作,或保存恢复所需状态;按队列协议处理确认与租约;最后关闭连接和相关子进程。

容器启动命令也会影响信号能否到达真正的程序。若使用 shell 包装,应理解它如何转发信号、回收子进程。容器内的主进程需要承担相应的生命周期责任。Docker 运行命令

不过,优雅退出只能覆盖正常停止的一部分情况。机器故障、OOM 或强制终止可能不给清理机会。持久检查点和幂等仍然是恢复基础,这部分会在长任务可靠执行中继续展开。

沙箱需要独立的权限边界

普通容器通常共享宿主机内核。它能提供进程和文件系统等隔离机制,但不应直接推导成“可以安全运行任意用户代码”。K8s Namespace 也需要结合权限、网络和存储策略,才能形成相应的租户隔离。Kubernetes 多租户说明

对于模型生成的分析代码,我会把授权限制到当前任务:只提供本次 Excel 和工作目录,按需要授予上传报告的能力,限制网络出口,并避免暴露宿主机管理接口或与任务无关的凭证。

隔离要求更高时,可以评估 gVisor、虚拟机类运行环境或托管沙箱。它们有各自的性能、兼容性和隔离边界,需要结合代码可信程度选择。gVisor 通过用户态应用内核处理工作负载的系统调用,是一种增加运行时隔离边界的方式。gVisor 安全架构

第一轮验证可以用同一个分析任务完成三件事:删除运行实例后,仍能找到已经保存的报告;终止 Worker 后,能明确知道哪些步骤完成、哪些需要恢复;让沙箱访问任务范围之外的文件和服务时,权限控制确实生效。这样才能看清程序、文件和权限各自的生命周期。

参考资料

上一篇:从一次请求理解网络 · 下一篇:K8s 与 Serverless