ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Opik Python Backend 沙箱化代码执行服务实战指南:架构、配置与部署

2026/9/13 19:10:06 拓冰建站 浏览量
Opik Python Backend 沙箱化代码执行服务实战指南:架构、配置与部署 Opik Python Backend 沙箱化代码执行服务实战指南架构、配置与部署【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llm# Opik Python Backend 沙箱化代码执行服务实战指南架构、配置与部署Opik Python Backend 是 Opik 平台中专司安全执行用户 Python 评估代码的独立服务它将在线评测Online Evaluation中提交的 Python 指标/评分脚本放入沙箱执行并以 JSON 形式返回评分结果。本文围绕 apps/opik-python-backend/README.md 展开结合仓库源码完整讲解该服务的执行策略、全部环境变量、本地调试与生产部署方式以及底层容器池/进程池与沙箱安全机制帮助你掌握如何安装、配置并运维这一执行引擎。服务定位为什么需要独立的 Python 执行后端Opik 的整体架构中用户会提交自定义的 Python 评估指标如ScoreResult类型的打分函数参与在线评测Online Evaluation。这些代码来自用户侧、运行在服务端存在两条核心诉求安全隔离用户代码可能访问网络、读写文件系统、死循环或耗尽内存必须与主服务隔离标准化契约执行结果必须是机器可读的结构化数据便于 Java 后端统一消费。Opik Python Backend 就是为此而生的服务。从 README 的定义看它是一个在沙箱环境中运行 Python 代码的服务默认通过 Docker 容器执行生产形态也可以在派生子进程中运行用于开发或非受限环境。它对外暴露一个 Flask 应用核心接口为POST /v1/private/evaluators/python见 evaluator.py。该接口的调用契约非常简洁请求体包含code用户编写的评估代码和data待评估数据服务将其交给执行器Executor运行返回{scores: [...]}。若执行失败则返回对应的 HTTP 错误码。快速开始本地环境准备根据 README运行本服务需要以下前置条件安装 Docker安装 Python创建并启用 Python 虚拟环境从 requirements.txt 安装全部依赖运行测试时额外安装 tests/test_requirements.txt 中的依赖。依赖清单中的核心组件包括FlaskWeb 框架、gunicorn生产 WSGI 服务器、dockerDocker SDK驱动容器执行器、opik与opik-optimizer评估/优化 SDK供被执行的用户代码调用、redis与rq可选的异步优化任务队列、以及一整套opentelemetry-*组件可观测性埋点。两种执行策略Docker 容器 vs. 子进程服务最关键的开关是环境变量PYTHON_CODE_EXECUTOR_STRATEGY它决定代码由哪种执行器承载。初始化逻辑位于 evaluator.pydocker使用 DockerExecutor从镜像池拉取沙箱容器执行代码生产环境默认值process或留空使用 ProcessExecutor在可复用的子进程池中执行适合本地开发或不受限环境其他取值启动时直接抛出ValueError。两个执行器都继承自 executor.py 中的CodeExecutorBase共享并行度、执行超时与池获取超时的配置读取逻辑并各自实现run_scoring(code, data, payload_type)抽象方法。这意味着切换策略不需要改动任何上层调用代码。核心环境变量详解README 完整列出了服务的核心环境变量下面结合源码逐一展开其含义、默认值与取值影响。环境变量作用默认值源码依据PYTHON_CODE_EXECUTOR_STRATEGY执行策略docker或process/空process服务层默认/dockerDocker 镜像默认evaluator.pyPYTHON_CODE_EXECUTOR_PARALLEL_NUM容器/子进程池的最大数量5executor.pyPYTHON_CODE_EXECUTOR_EXEC_TIMEOUT_IN_SECS单次执行的超时秒数3executor.pyPYTHON_CODE_EXECUTOR_ALLOW_NETWORK是否允许沙箱容器访问网络falseexecutor_docker.pyPYTHON_CODE_EXECUTOR_CPU_SHARESDocker CPU 份额值越大优先级越高512Docker 默认 1024executor.pyPYTHON_CODE_EXECUTOR_MEM_LIMIT容器内存上限Docker 格式单字母单位 b/k/m/g256mexecutor.pyPYTHON_CODE_EXECUTOR_CPU_LIMIT每个容器的硬性 CPU 上限小数核如0.5半个核未设置无硬性限制executor.pyPYTHON_CODE_EXECUTOR_METRICS_INTERVAL_IN_SECONDS通过 Docker stats API 采集容器 CPU/内存指标的间隔60executor_docker.py补充说明超出 README 的进阶配置源码中还提供了若干 README 未展开但同样重要的变量属于运维调优的进阶入口PYTHON_CODE_EXECUTOR_POOL_ACQUIRE_TIMEOUT_IN_SECS从池中获取空闲执行器的最长等待时间默认0.0快速失败池满即返回 HTTP 503。源码注释明确指出突发流量应由 HTTP 层的重试退避来吸收而不是让服务端请求线程长时间挂起等待见 executor.py。如果业务流量特征适合短暂等待可以适当调高。PYTHON_CODE_EXECUTOR_POOL_CHECK_INTERVAL_IN_SECONDS后台守护线程检查并补充池内容器/进程的周期默认3秒见 executor_docker.py。镜像相关变量PYTHON_CODE_EXECUTOR_IMAGE_REGISTRY默认ghcr.io/comet-ml/opik、PYTHON_CODE_EXECUTOR_IMAGE_NAME默认opik-sandbox-executor-python、PYTHON_CODE_EXECUTOR_IMAGE_TAG共同拼接出沙箱镜像的完整引用见 executor_docker.py。这些默认值也在 Dockerfile 中以 ENV 形式固化。关键参数的影响面PARALLEL_NUM同时决定容器池/进程池大小与并发能力。在 Docker 执行器里它同时作为scoring_executor线程池的max_workers见 executor_docker.py在生产 entrypoint 中gunicorn 的--threads也取该值保证 Web 线程与执行器池容量匹配见 entrypoint.sh。EXEC_TIMEOUT_IN_SECSDocker 执行器中通过future.result(timeout...)强制限制单次exec_run的等待时间超时返回 HTTP 504EXEC_TIMEOUT_ERROR见 executor_docker.pyProcess 执行器中则通过connection.poll(timeout...)实现等价的超时语义见 executor_process.py。CPU_LIMIT与CPU_SHARES的差异前者是硬性上限内部转换为 Docker SDK 的nano_cpus如0.5转为500000000后者是软性优先级权重二者可以叠加使用见 executor_docker.py。本地运行 Flask 服务Debug 模式README 给出了本地开发的标准启动方式需要在apps/opik-python-backend目录下执行flask --app src/opik_backend --debug run--app src/opik_backend指向模块入口Flask 会调用模块内的create_app()工厂函数构建应用见init.py--debug开启调试模式代码修改后自动重载适合开发期使用服务默认监听http://localhost:5000。需要注意的是调试模式下 Flask 的重载器会启动两个进程为避免执行器尤其是进程池被重复初始化init_executor对process策略做了保护——只有当WERKZEUG_RUN_MAINtrue或非 debug 模式时才调用start_services()见 evaluator.py。应用启动时还会依次注册三个 Blueprint健康检查healthcheck、评估执行evaluator与用户注册后处理post_user_signup并在OPIK_OTEL_SDK_ENABLEDtrue时初始化 OpenTelemetry见init.py。手动验证一次评估请求启动服务后可以向核心接口发起一次评估调用curl -X POST http://localhost:5000/v1/private/evaluators/python \ -H Content-Type: application/json \ -d { code: from opik.evaluation.metrics import base_metric, score_result\nresult {\scores\: [{\value\: 0.95, \name\: \my_metric\, \reason\: \ok\}]}\nprint(json.dumps(result)), data: {input: hello} }返回{scores: [...]}即表示沙箱链路打通。接口会对缺失字段、空评分结果等情况返回 400见 evaluator.py。生产部署Docker 镜像与 entrypoint 启动链路生产环境以 Docker 镜像方式运行Dockerfile 采用多阶段构建构建阶段基于docker:29.5.1Alpine安装python3、gcc、rust/cargo等原生编译工具链用uv将依赖安装进/opt/venv随后把依赖编译为 bytecode-only.pyc布局以缩小镜像体积运行阶段仅保留tiniPID 1 收割器、python3与精简后的 venvEXPOSE 8000并将镜像内默认策略设为docker、并行度 5、超时 3 秒、禁止网络见 Dockerfile沙箱执行器镜像opik-sandbox-executor-python通过 tar 包COPY进镜像运行时由 entrypoint 执行docker load导入若未随镜像打包则会在首次使用时按PYTHON_CODE_EXECUTOR_IMAGE_REGISTRY/NAME/TAG拉取。entrypoint.sh 的启动流程分为三步启动 Docker daemon仅当策略为docker时后台拉起dockerd-entrypoint.sh并以 1 秒间隔最多重试 30 次等待docker info可用若 30 次后仍失败则直接退出见 entrypoint.sh导入沙箱镜像若./images/${PYTHON_CODE_EXECUTOR_ASSET_NAME}.tar.gz非空则docker load见 entrypoint.sh启动 gunicorn单 worker gthread线程模型--threads取PYTHON_CODE_EXECUTOR_PARALLEL_NUM默认 5监听端口由PYTHON_BACKEND_PORT控制默认 8000若开启OPIK_OTEL_SDK_ENABLEDtrue则通过opentelemetry-instrument进行自动埋点见 entrypoint.sh。执行流程深入从 HTTP 到 JSON 结果无论采用哪种策略一次评估的完整链路都是evaluatorBlueprint 收到POST /v1/private/evaluators/python校验code与data字段调用get_executor().run_scoring(code, data, payload_type)执行器从池中获取空闲容器/进程传入代码与数据用户代码运行并输出结果 JSON打印到 stdout 的最后一行执行器解析结果并返回evaluator提取scores数组返回给调用方。Docker 执行器预热的容器池DockerExecutor 的核心机制是预热的容器池初始化时并行创建max_parallel个沙箱容器容器以tail -f /dev/null常驻保持存活并打上managed_byinstance_id标签以便统一管理容器创建参数包含mem_limit、cpu_shares、nano_cpus可选、network_disabled与security_opt[no-new-privileges]共同构成沙箱的资源与安全边界见 executor_docker.py后台调度线程按POOL_CHECK_INTERVAL检查并补充池容量ensure_pool_filled每次执行通过container.exec_run运行scoring_runner.pyc并传入code、data_json、payload_type三个参数见 executor_docker.py执行结束后旧容器被异步停掉并删除同时立即创建一个新容器回填池中——保证每次执行都使用干净的容器状态避免上一次运行的残留数据污染下一次评估。Process 执行器基于 Pipe 的进程池ProcessExecutor 面向开发/受限环境机制类似但基于多进程预热max_parallel个 worker 进程每个进程通过multiprocessing.Pipe与父进程通信子进程入口为 process_worker.py 的worker_process_main创建 worker 时会等待子进程发出READY信号最长 10 秒才入池保证拿到的 worker 一定可用见 executor_process.py执行时通过connection.send({code: ..., data: ..., payload_type: ...})发送任务poll(timeout)等待结果超时后异步终止该 worker 并返回 HTTP 504注册了 SIGINT/SIGTERM 信号处理器实现优雅关停先终止全部 worker 再sys.exit(0)见 executor_process.py。结果解析契约执行结果统一由parse_execution_result解析见 executor.py其约定值得每个评估代码作者注意退出码 0 时取stdout 最后一行解析为 JSON 对象作为结果无输出 / 最后一行不是合法 JSON / 不是 JSON 对象均按 400 处理视为用户指标代码本身有误而非服务故障退出码非 0 时尝试从最后一行 JSON 中提取error字段返回否则返回通用错误文案。因此评估代码必须以print(json.dumps(result))的形式在最后输出一个 JSON 对象其中应包含scores列表。沙箱安全机制网络与文件系统的双重隔离沙箱的安全性由容器参数与测试用例共同保证。test_executor_docker.py 中有两组直接的验证用例网络访问被阻断PYTHON_CODE_EXECUTOR_ALLOW_NETWORK默认false测试代码尝试urllib.request.urlopen(http://example.com)断言结果为失败且 reason 包含urlopen error见 test_executor_docker.py文件系统访问受限容器中读取宿主机文件被拒绝见 test_executor_docker.py。资源层面mem_limit256m与cpu_shares/nano_cpus限制了单容器资源占用security_opt[no-new-privileges]禁止容器内提权执行超时则兜底防止死循环拖垮服务。若确实需要联网例如调用外部模型 API 的评估指标需显式设置PYTHON_CODE_EXECUTOR_ALLOW_NETWORKtrue。隔离子进程执行器更彻底的执行隔离方案除 README 提到的两种策略外仓库还提供了第三种执行器 IsolatedSubprocessExecutor其完整设计文档见 docs/ISOLATED_EXECUTOR_COMPLETE.md。它的定位是解决ProcessExecutor复用 worker 池带来的环境变量泄漏问题每次执行都创建全新子进程环境变量按执行作用域完全隔离不共享任何状态适合多租户场景每个租户携带不同的API_KEY/TENANT_ID。典型用法from opik_backend.executor_isolated import IsolatedSubprocessExecutor executor IsolatedSubprocessExecutor(timeout_secs30) # 环境变量仅对本次执行生效 result executor.execute( file_path/path/to/metric.py, data{}, env_vars{TENANT_ID: tenant_123, API_KEY: secret_key}, )其核心特性包括环境变量作用域隔离、每次执行自动创建/清理子进程、teardown 回调注册、with上下文管理器自动释放、线程安全的并发执行、每个子进程 20MB 栈内存限制RLIMIT_STACK防止无限递归、可选的 HTTP 日志流式收集subprocess_logger.py 中的BatchLogCollector按时间 1 秒/大小 10MB 批量上报支持 gzip 与鉴权头以及基于 OpenTelemetry 的创建/执行延迟指标。相关配置项包括SUBPROCESS_LOG_ENABLED、OPIK_SUBPROCESS_LOG_BACKEND_URL、SUBPROCESS_LOG_FLUSH_INTERVAL、SUBPROCESS_LOG_MAX_SIZE、SUBPROCESS_LOG_REQUEST_TIMEOUT与SUBPROCESS_LOG_FAIL_ON_MISSING_BACKEND。可观测性与错误语义OpenTelemetry 指标服务在OPIK_OTEL_SDK_ENABLEDtrue且配置了OTEL_EXPORTER_OTLP_ENDPOINT时启用 OTLP 导出见init.py。Docker 执行器暴露了丰富的指标便于监控执行引擎的健康度见 executor_docker.pycontainer_creation_latency/container_stop_latency容器创建/销毁耗时直方图scoring_executor_latency单次评估总耗时container_pool_size/scoring_executor_queue_size池容量与排队任务数用于判断饱和度payload_code_size/payload_data_size代码与数据负载大小直方图自定义分桶覆盖 100B 至 100MBexecution_outcome执行结果计数器success/timeout/invalid_code/saturated/error/serialization_errorexecutor_container_cpu_cores/executor_container_memory_bytes由指标采集线程按配置间隔通过 Docker stats API 计算并上报的单容器资源用量。HTTP 错误语义执行失败以结构化错误码返回调用方应依据状态码而非错误文案分支处理状态码含义触发场景400用户代码/请求无效缺字段、指标无输出、结果非 JSON、无scores503执行器饱和或正在关停池耗尽、SATURATED_ERROR/SHUTDOWN_ERROR504单次执行超时超过EXEC_TIMEOUT_IN_SECS500服务内部错误未预期的运行时异常错误常量定义于 executor.pySATURATED_ERROR文案为 Code executor is saturated, please retry提示调用方应带退避重试。小结Opik Python Backend 通过策略化执行器 预热资源池 沙箱隔离 结构化结果契约四层设计为在线评测提供了安全、可控、可观测的代码执行能力。开发期使用flask --app src/opik_backend --debug run配合process策略即可快速迭代生产环境则按 Dockerfile 构建镜像、以docker策略运行并依据流量特征调优PYTHON_CODE_EXECUTOR_PARALLEL_NUM、EXEC_TIMEOUT_IN_SECS与POOL_ACQUIRE_TIMEOUT等参数。若要进一步了解执行器内部细节与隔离方案演进可继续阅读 executor_docker.py、executor_process.py 与 docs/ISOLATED_EXECUTOR_COMPLETE.md。【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考