沙箱、资源配额与网络隔离
正在读取学习记录…
Agent 运行一段统计任务时,需要避免任务拖住主服务。正常任务将 2、3、5 相加,返回 10;超过允许时间的任务则结束,并让主服务继续处理下一次请求。
进程是操作系统运行程序的单位,有自己的内存空间和退出状态。沙箱是在受控边界中运行任务,限制它能读取哪些文件、使用哪些系统能力和连接哪些网络。把任务放进子进程,能管理生命周期;文件、账户和网络权限还要由操作系统或隔离运行平台施加。
资源配额是给任务设定 CPU、内存、进程数或时间上限。时间超限与内存超限是不同问题:等五秒的任务可能不消耗多少 CPU,快速分配内存的任务却可能立即影响主机。
子进程、容器和配额分别解决什么
主程序用 subprocess.run 启动另一个 Python 进程,等待结果并设置超时。执行命令是参数列表,没有经过 shell 字符串解释;任务名映射到固定良性代码。独立临时目录给每次任务单独的工作位置,退出时清理。
-I 是 Python 的隔离启动选项,主要影响 Python 导入路径和 Python 环境变量处理;它并不撤销操作系统授予进程的文件或网络权限。传给子进程的环境变量由显式白名单构建,避免把应用环境里的秘密直接继承过去。程序没有加入任何网络调用,但操作系统仍可能允许该子进程联网。
容器常通过 namespace 提供进程、挂载和网络等视图隔离,通过 cgroup 控制资源。容器里的进程通常仍共享宿主内核,需要减少 capability(内核授予的特殊操作能力)、限制系统调用、使用非特权账户、避免挂载敏感宿主目录。更强的任务边界可能使用独立虚拟机;它也要管理凭据和网络出口。
| 层次 | 控制目标 | 示例实际发生的行为 |
|---|---|---|
| 子进程 | 启动、等待、超时结束 | 固定任务有独立进程 |
| 工作目录 | 工作文件位置与清理 | 创建并清理独立临时目录 |
| CPU / 内存 / 进程配额 | 防止资源耗尽 | 需操作系统或容器运行器配置 |
| 文件权限 / 网络出口 | 限制可访问资源 | 需账户、挂载与网络策略 |
| Python 导入隔离 | 降低路径和环境干扰 | 子进程使用 -I |
在空目录保存并运行两个文件
在 PowerShell 输入以下命令创建练习目录,并进入目录。目录创建好后,在编辑器中新建下面两份文件并保存到这个目录。
New-Item -ItemType Directory -Force l8-sandbox
Set-Location l8-sandboxmacOS 终端:
mkdir -p l8-sandbox
cd l8-sandbox先保存 sandbox_demo.py。它只接受固定任务名,子进程执行的两段代码都已经写在文件里。
"""运行预先写好的良性子进程,观察超时与临时目录。"""
import os
from pathlib import Path
import subprocess
import sys
import tempfile
TIMEOUT = 0.3
# 任务名映射到固定代码;不接收用户提供的 Python 代码。
WORKERS = {
"sum": "print(sum([2, 3, 5]))",
"wait": "import time; time.sleep(5); print('done')",
}
def run_worker(task, timeout):
if task not in WORKERS:
return "拒绝:未知任务"
if timeout <= 0:
raise ValueError("超时必须大于零")
# 临时目录使用系统配置的位置,结束后自动清理。
with tempfile.TemporaryDirectory() as workdir:
# 不继承应用秘密;Windows 系统目录变量用于解释器启动。
env = {"PYTHONIOENCODING": "utf-8"}
if "SystemRoot" in os.environ:
env["SystemRoot"] = os.environ["SystemRoot"]
try:
result = subprocess.run(
[sys.executable, "-I", "-c", WORKERS[task]],
cwd=workdir, env=env, capture_output=True,
text=True, encoding="utf-8", timeout=timeout, check=False,
)
except subprocess.TimeoutExpired:
return "超时:子进程已结束"
if result.returncode:
return "失败:退出码=" + str(result.returncode)
return "完成:" + result.stdout.strip()
def main():
print("固定计算 ->", run_worker("sum", 3))
print("等待任务 ->", run_worker("wait", TIMEOUT))
print("未知任务 ->", run_worker("unknown", 3))
if __name__ == "__main__":
main()再保存 test_sandbox.py。测试通过程序的公开函数检查正常路径与拒绝路径;测试文件和主程序放在同一目录,才能按文件名导入。
import unittest
# 固定良性输入检查正常、拒绝和边界路径,不连接外部服务。
from sandbox_demo import run_worker
class ProcessTests(unittest.TestCase):
def test_fixed_computation(self):
self.assertEqual(run_worker("sum", 3), "完成:10")
def test_timeout(self):
self.assertEqual(run_worker("wait", 0.3), "超时:子进程已结束")
def test_unknown_task(self):
self.assertEqual(run_worker("unknown", 3), "拒绝:未知任务")
def test_invalid_timeout(self):
with self.assertRaises(ValueError):
run_worker("sum", 0)
def test_repeat_after_timeout(self):
run_worker("wait", 0.3)
self.assertEqual(run_worker("sum", 3), "完成:10")
if __name__ == "__main__":
unittest.main()返回同一个终端,先列出当前目录内容,确认两个文件名都以 .py 结尾,然后运行主程序。
ls
python -X utf8 sandbox_demo.py程序输出:
固定计算 -> 完成:10
等待任务 -> 超时:子进程已结束
未知任务 -> 拒绝:未知任务固定计算返回 10。等待任务需要五秒,而 TIMEOUT 为 0.3 秒,所以父进程结束它并返回超时。未知任务在启动前被拒绝。这里管理的是固定单个子进程:任意代码若生成后代进程,需要进程组、Windows Job Object 或容器生命周期管理来回收整个任务树;否则只结束直接子进程会留下后代。
capture_output 把输出收集到父进程内存;这份固定任务输出极小。运行任意任务时还需用限量流式读取处理输出,配合内存和 CPU 配额,才能控制大量输出和资源占用。临时工作目录也不自动阻止绝对路径访问。
运行测试:
python -X utf8 -m unittest -v test_sandbox.py末尾应显示 Ran 5 tests 和 OK。若提示找不到模块,先用 dir 核对目录与文件名。
改一处,观察决定怎样变化
把 TIMEOUT = 0.3 改成 TIMEOUT = 6,重新运行主程序,约五秒后第二行变为 等待任务 -> 完成:done。同一任务在较大的等待时间内完成;计算任务仍然返回 10。再将常量恢复为 0.3。
python -X utf8 sandbox_demo.py恢复原值后,重新运行测试。
为代码执行工具选择实际隔离设施
在 Windows 上,任务账户的文件 ACL 决定能读写哪些路径,Job Object 可管理进程树和资源限制,防火墙或受控代理管理网络出口。在 Linux 容器上,可从非 root 用户、只读根文件系统、受控挂载、CPU/内存/进程数上限和禁用网络的任务配置开始,再按需求开放权限。配置的落实情况需要在部署平台逐项测试。
| 症状 | 检查位置 | 修正方向 |
|---|---|---|
| 计算任务也超时 | 正常分支的超时与机器负载 | 保留启动余量;示例正常分支使用三秒 |
| 超时后仍有后代任务 | 任务树回收机制 | 绑定 Job Object、进程组或容器任务生命周期 |
| 程序能读工作目录外的文件 | 账户权限与挂载 | 限制文件权限,核对敏感路径未挂入 |
| 程序能外连 | 网络策略 | 在系统层关闭网络或只开放受控出口 |
| 主服务内存上涨 | 输出缓存与内存配额 | 流式限量收集,配置内存上限 |
Python subprocess 文档说明超时和进程管理行为;Docker 安全文档解释 namespace、资源控制和守护进程权限。
课后练习展开习题收起习题
正在读取本地记录…
不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。
选择题 · 先判断边界
关于“沙箱、资源配额与网络隔离”,哪种做法更稳妥?
对应知识点:L8-sandbox
简答题 · 解释失败
写出一个失败输入并说明记录字段。
对应知识点:L8-sandbox
代码题 · 边界测试
补充正常、边界、失败三组输入和预期结果。
对应知识点:L8-sandbox
场景题 · 结果未知
证据不足时如何设计状态和人工接管?
对应知识点:L8-sandbox
描述会发送给本站维护者,用于修订文章,保存90天。请只填写文章问题,省略个人信息、密钥和内部资料。