首页
/ DeerFlow lark-cli init 镜像(Pattern A):用 init 容器 + emptyDir 为 Kubernetes 沙箱预置 lark-cli 运行时

DeerFlow lark-cli init 镜像(Pattern A):用 init 容器 + emptyDir 为 Kubernetes 沙箱预置 lark-cli 运行时

2026-09-04 14:09:25作者:宣聪麟

DeerFlow 是一个开源的长程 SuperAgent harness,其飞书/Lark 集成依赖沙箱内可用的 lark-cli 运行时。本文以 docker/lark-cli-init/README.md 为核心,完整讲解 “Pattern A” 方案:在镜像构建期下载并校验官方 larksuite/cli Linux 二进制,在运行期通过 init 容器把运行时复制到共享 emptyDir,从而让 Gateway 免去“安装时从 GitHub 下载二进制 + hostPath/PVC 挂载”的环节。读完本文,你能掌握该镜像的构建与版本发布方式、如何把 init 容器接入 provisioner,以及沙箱 PATH 契约(/mnt/integrations/lark-cli/runtime/bin/lark-cli)为何保持不变。

1. Pattern A 要解决什么问题

传统的做法(legacy 路径)是:Gateway 在安装阶段从 GitHub 下载 Linux 二进制,然后通过 hostPath/PVC 把运行时目录挂进沙箱 Pod。这种方式有两个弱点——每次安装都依赖外网下载,并且运行时目录强依赖宿主机路径或 PVC 的可用性。

Pattern A 的思路是把下载前移到镜像构建期,把挂载前移到 init 容器

  • 构建时(有网络):下载官方 larksuite/cli 的 Linux 发布版二进制并做 SHA-256 校验,把运行时布局暂存到镜像内的 /opt/lark-cli

    /opt/lark-cli/bin/lark-cli            # 架构分发启动器(按 uname -m 选择)
    /opt/lark-cli/linux-amd64/lark-cli
    /opt/lark-cli/linux-arm64/lark-cli
    /opt/lark-cli/.deerflow-lark-cli-runtime.json   # {"version": "vX.Y.Z"}
    
  • 运行时(无网络依赖):init 容器执行 cp -a /opt/lark-cli/. -> ${LARK_CLI_RUNTIME_DEST}(默认 /mnt/integrations/lark-cli/runtime),然后以退出码 0 结束。

这个布局与 Gateway 侧的写入函数 _write_lark_cli_sandbox_launcher 的产物字节级一致,并且满足 _validate_lark_cli_sandbox_runtime 的校验,因此无论运行时是由 Gateway 下载写入,还是由 init 容器复制而来,沙箱内的 PATH 契约(/mnt/integrations/lark-cli/runtime/bin/lark-cli)都不变。

2. 镜像构建:源码级剖析

构建入口是 docker/lark-cli-init/Dockerfile,采用多阶段构建:

  1. builder 阶段:基于 debian:bookworm-slim,安装 ca-certificatescurl,执行 build-runtime.sh 完成二进制下载与暂存。支持 APT_MIRROR 构建参数替换 Debian 源地址(便于网络受限环境)。
  2. 最终阶段:只从 builder 复制 /opt/lark-cli 目录和入口脚本 entrypoint.sh,镜像极小、不含网络工具。

2.1 构建期脚本 build-runtime.sh 做了什么

build-runtime.sh 是保证二进制可信的关键,其流程为:

  • 接收 LARK_CLI_VERSION(必填,可带或不带前导 v,脚本会归一化为 vX.Y.Z 形式的 tag);

  • larksuite/cli 的 GitHub releases 下载 checksums.txt,以及 lark-cli-<version>-linux-{amd64,arm64}.tar.gz 两个资产;

  • 逐个校验 SHA-256:从 checksums.txt 中查找对应资产的期望哈希,与本地 sha256sum 结果比对,不一致即报错退出——这是“SHA-256-verifies”这一说法的实现落点;

  • 从压缩包中只提取 lark-cli 可执行文件,install -m 0755<dest>/linux-<arch>/lark-cli

  • 生成架构分发启动器 <dest>/bin/lark-cli

    #!/bin/sh
    set -eu
    case "$(uname -m)" in
      x86_64|amd64) arch=amd64 ;;
      aarch64|arm64) arch=arm64 ;;
      *) echo "Unsupported sandbox architecture: $(uname -m)" >&2; exit 126 ;;
    esac
    script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
    exec "$script_dir/../linux-$arch/lark-cli" "$@"
    

    该启动器按 uname -mx86_64/amd64 → amd64aarch64/arm64 → arm64 之间分发,未知架构以退出码 126 失败。脚本注释明确要求它与 Gateway 中 LARK_CLI_SANDBOX_LAUNCHER_SCRIPT(定义于 backend/packages/harness/deerflow/integrations/lark_cli.py保持字节级一致,并且 backend/tests/test_lark_cli_integration.py 中的单元测试会断言两者永不漂移(drift)。

  • 最后写出清单文件 .deerflow-lark-cli-runtime.json(内容形如 {"version": "v1.0.65"}),供 Gateway 的运行时校验识别版本。

2.2 运行期入口 entrypoint.sh

entrypoint.sh 逻辑非常短小且防御式:

  • /opt/lark-cli/bin/lark-cli 不存在或不可执行,打印错误并 exit 1(init 容器失败会阻塞整个 Pod 启动,快速失败优于静默缺二进制);
  • mkdir -p 目标目录后 cp -a "${SRC}/." "${DEST}/",注意 /. 会把点文件清单.deerflow-lark-cli-runtime.json)一并复制;
  • 打印 ls -R 输出便于通过 kubectl logs 排查。

目标目录由环境变量 LARK_CLI_RUNTIME_DEST 指定,Dockerfile 中默认值为 /mnt/integrations/lark-cli/runtime

3. 构建与发布镜像

README 的说明,本地构建命令为:

docker build -t deer-flow/lark-cli-init:v1.0.65 \
  --build-arg LARK_CLI_VERSION=v1.0.65 \
  docker/lark-cli-init

两个值得注意的版本策略:

  • 镜像 tag 编码 lark-cli 版本(如 v1.0.65),这样 lark-cli 升级可以独立于上游 all-in-one-sandbox 沙箱镜像单独 bump;
  • CI 发布多架构镜像.github/workflows/lark-cli-images.yaml(该 workflow 文件确实存在于仓库 .github/workflows/lark-cli-images.yaml)会以 linux/amd64,linux/arm64 双架构发布到 ghcr.io/<owner>/deer-flow-lark-cli-init:<lark-cli-version>。触发方式为手动传入 lark_cli_version 输入,或推送 lark-cli-v* 形式的 tag。该发布流程与 DeerFlow 自身的 v* release 解耦,因为镜像追踪的是上游 larksuite/cli 的版本——RELEASING.md 也确认该镜像有自己的 verify-versions 版本门禁,且版本号取 lark-cli 发布版而非 DeerFlow 发布版。

4. 接入 provisioner:opt-in 且默认关闭

init 容器路径是显式 opt-in:不配置就是旧行为,零风险。接入方式:

  1. 在 provisioner 服务上设置 LARK_CLI_INIT_IMAGE,指向已发布的 tag,例如 deer-flow/lark-cli-init:v1.0.65。留空(默认)⇒ 走 legacy 的 hostPath / Gateway 下载路径,行为完全不变。
  2. 配置后 provisioner 的行为变化(源码位于 docker/provisioner/app.py):
    • 环境变量读取:LARK_CLI_INIT_IMAGE = os.environ.get("LARK_CLI_INIT_IMAGE", ""),同时定义运行时容器路径常量 LARK_CLI_RUNTIME_CONTAINER_PATH = "/mnt/integrations/lark-cli/runtime" 与卷名 lark-cli-runtime
    • 使能判断:_lark_cli_runtime_enabled() 返回 bool(LARK_CLI_INIT_IMAGE) and provision_lark_cli_runtime——即“镜像已配置”且“创建请求带 provision_lark_cli_runtime: true”两个条件同时成立才生效(Gateway 在托管 Lark 技能包安装后会自动发送该标志,见 docker/provisioner/README.md);
    • 满足条件时,provisioner 会添加:一个 lark-cli-runtime emptyDir 卷、一个名为 lark-cli-init 的 init 容器(image_pull_policy=IfNotPresent,注入 LARK_CLI_RUNTIME_DEST 环境变量并挂载该卷、使用安全加固的 security context),以及沙箱主容器上的只读运行时挂载;
    • 此时会忽略任何指向 /mnt/integrations/lark-cli/runtime 的 hostPath/PVC extra mount(init 容器路径取代它);而 per-user 的 config / data 凭据挂载不受影响,照旧挂入沙箱;
    • 若同时配置了 Pattern B 的 broker 镜像,broker 优先_build_lark_cli_init_containers 中 broker 分支先于 runtime 分支返回)。
  3. 就绪信号上报:provisioner 通过 GET /api/capabilities 报告 {"lark_cli_init_image": true|false}(源码中该端点返回 lark_cli_init_imagelark_cli_broker_image 两个布尔值);Gateway 把它作为 Lark 集成的沙箱运行时就绪信号,暴露在 GET /api/integrations/lark/status 上。从源码结构看,Gateway 的 lark_cli.py 按优先级判定:broker 已配置 ⇒ "broker" 模式;否则 init 镜像已配置 ⇒ "init-container" 模式;都未配置则 sandbox_runtime_ready=false 并给出 “The provisioner has no lark-cli runtime image configured (LARK_CLI_INIT_IMAGE / LARK_CLI_BROKER_IMAGE)” 的提示。这样设置页 UI 能如实显示聊天时 lark-cli 是否真的存在于沙箱内,避免“状态绿色、聊天时报 command not found”的假就绪(README.md 主文档对此有同样的描述)。

docker/docker-compose.yamldocker/docker-compose-dev.yaml 中,provisioner 服务已预留 - LARK_CLI_INIT_IMAGE=${LARK_CLI_INIT_IMAGE:-} 环境变量(默认空),与 docker/provisioner/README.md 的环境变量表一致:LARK_CLI_INIT_IMAGE 默认空(feature off)。

5. 测试与验证

该特性的行为契约由测试固化在 backend/tests/test_provisioner_pvc_volumes.py

  • test_no_init_container_when_image_unsetLARK_CLI_INIT_IMAGE 为空时,即使请求带 provision_lark_cli_runtime=True,Pod 中也不出现 init 容器;
  • test_no_init_container_when_flag_disabled:镜像已配置但请求不带标志时同样不注入;
  • test_init_container_and_emptydir_when_enabled:两者齐备时,Pod 得到 init 容器 + emptyDir
  • test_runtime_extra_mount_dropped_when_init_container_enabled:init 容器启用时,指向运行时路径的 extra hostPath 挂载被丢弃;
  • “双镜像双标志”用例断言 broker 胜出(shim init + sidecar),印证第 4 节的优先级。

6. 小结:何时使用 Pattern A

  • 适用场景:Kubernetes 沙箱部署、希望消除安装期外网下载依赖、节点不便于维护 hostPath/PVC 运行时目录的飞书/Lark 集成环境;
  • 启用步骤:构建并发布镜像(tag 编码 lark-cli 版本)→ provisioner 设置 LARK_CLI_INIT_IMAGE → 通过 /api/capabilities/api/integrations/lark/status 验证 sandbox_runtime_ready
  • 不配置时:完全回落到 legacy hostPath / Gateway 下载路径,无行为变化——这是该设计明确的 opt-in 承诺;
  • 后续演进:若目标是让 appSecret/OAuth 令牌文件完全不进入沙箱文件系统,仓库提供了 Pattern B(docker/lark-cli-broker),其 broker 模式在两者同时配置时取代 Pattern A。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
983
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384