# Per-user sandbox base image (DESIGN §7.5). # 长驻 per-user 容器,工具调用走 `docker exec --user 1000:1000 --workdir /workspace/ setsid `(Step 3 接入)。 # # 启动协议:tini(PID 1,reap 僵尸) → init.sh(root,跑 iptables 配 blocklist) → sleep infinity。 # `docker exec` 进来的进程由 --user flag 决定身份 ── 应用层(DockerExecutor)硬编 --user 1000, # 不继承 PID 1 的 root 上下文。 # # 构建上下文 = repo 根(用 -f 指定 Dockerfile 路径): # docker build -f deploy/sandbox/Dockerfile -t zcbot-sandbox \ # --build-arg HOST_UID=$(id -u zcbot) --build-arg HOST_GID=$(id -g zcbot) . FROM python:3.11-slim # - iptables / ip6tables: init.sh 配 blocklist 需要(NET_ADMIN cap 在 docker run 处加) # - iproute2: ip 命令(调试 / 排查) # - netbase: /etc/protocols /etc/services(curl / 多数网络库依赖) # - ca-certificates: HTTPS 出网(经 proxy 也要,Step 4) # - tini: PID 1 信号转发 + 僵尸 reaper(setsid 包出的进程组结束时 PID 1 兜底回收) RUN apt-get update && apt-get install -y --no-install-recommends \ iptables iproute2 netbase ca-certificates tini \ && rm -rf /var/lib/apt/lists/* # 容器内非 root 用户的 uid/gid 必须与 host 上 `zcbot` 用户对齐(bind mount 保 host owner; # 错配会导致 exec 进来后 write /workspace 时 EACCES)。build-arg 默认 1000,部署时按 # `id -u zcbot` 实际值显式传(详 RUN.md "sandbox 部署"段)。 ARG HOST_UID=1000 ARG HOST_GID=1000 RUN groupadd -g ${HOST_GID} zcbot && useradd -u ${HOST_UID} -g ${HOST_GID} -m -s /bin/bash zcbot # 装全套 requirements ── 模型在 run_python 里写的脚本可能用 fastapi / sqlalchemy / litellm # 等,装齐免 "ModuleNotFoundError" 摩擦。镜像偏大(~1G)是接受成本。 COPY requirements.txt /tmp/requirements.txt RUN pip install --no-cache-dir -r /tmp/requirements.txt && rm /tmp/requirements.txt COPY deploy/sandbox/init.sh /init.sh RUN chmod +x /init.sh # 默认 cwd /workspace ── 但每次 `docker exec --workdir /workspace/` 会覆盖 WORKDIR /workspace ENTRYPOINT ["/usr/bin/tini", "--"] CMD ["/init.sh"]