# Agent 工程运维（二）：进程、容器与沙箱

作者：zhongling
原文：https://zhixia.pages.dev/posts/agent-infra-runtime/
发布日期：2026-11-12
更新日期：2026-11-12
语言：zh

摘要：从 Excel 分析任务出发，解释 Agent API、Worker 与沙箱怎样运行，梳理 Linux 进程、容器镜像、文件持久化、优雅退出与执行隔离的边界。

---


[系列导读：Agent 工程需要哪些运维知识](/posts/agent-infra-overview/)

网络打通之后，下一个问题是：Agent 到底在哪里运行？

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

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

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

## 先把三个执行角色分开

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

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

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

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

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

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

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

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

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

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

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

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

## Dockerfile、镜像与容器的关系

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

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

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

**启动镜像后，得到容器。** 容器是运行实例，同一个镜像可以启动多个实例。容器运行时产生的文件写入可写层或挂载存储，不会回写到原镜像。[Docker 镜像说明](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/)

镜像标签还需要单独理解。`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 自定义镜像说明](https://help.aliyun.com/zh/functioncompute/custom-container/)

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

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

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

容器可写层随容器删除而丢失，挂载卷可以把数据生命周期与容器分开。阿里云 FC 也明确说明，自定义镜像的可写层会随实例销毁而删除，需要持久化的数据应使用外部存储。[Docker 文件挂载](https://docs.docker.com/engine/containers/run/#filesystem-mounts) · [FC 存储边界](https://help.aliyun.com/zh/functioncompute/custom-container/)

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

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

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

普通容器通过操作系统机制组织隔离和资源控制。运行时可以限制内存、CPU 等资源，但需要明确配置与行为。[Docker 资源约束](https://docs.docker.com/engine/containers/resource_constraints/)

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

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

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

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

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

K8s 等平台会为正常终止提供宽限过程，应用通常先收到终止信号，期限到达后仍未退出的进程可能被强制结束。[Pod 终止流程](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination)

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

容器启动命令也会影响信号能否到达真正的程序。若使用 shell 包装，应理解它如何转发信号、回收子进程。容器内的主进程需要承担相应的生命周期责任。[Docker 运行命令](https://docs.docker.com/engine/containers/run/)

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

## 沙箱需要独立的权限边界

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

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

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

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

## 参考资料

- [Docker：镜像的基本概念](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/)
- [Docker：运行容器](https://docs.docker.com/engine/containers/run/)
- [阿里云 FC：自定义镜像](https://help.aliyun.com/zh/functioncompute/custom-container/)
- [Kubernetes：多租户隔离](https://kubernetes.io/docs/concepts/security/multi-tenancy/)
- [gVisor：安全架构](https://gvisor.dev/docs/architecture_guide/intro/)

上一篇：[从一次请求理解网络](/posts/agent-infra-network/) · 下一篇：[K8s 与 Serverless](/posts/agent-infra-k8s-serverless/)

