dagger 0.21.10 SDK 与 API 使用详解

dagger v0.21.10 SDK 与 API 实战:Python SDK 最小 module(hello/build/report 三函数)dagger call 跑通构建→输出全链路,本地 GraphQL 会话 API 直调、引擎容器生命周期实测与四类真实报错排障样本(2026-10-19 实测)。

daggerCICDDocker容器GoPython2026/10/19

dagger 0.21.10 SDK 与 API 使用详解

目标:用 Python SDK 写一个最小 dagger module 并通过 dagger call 实测跑通"构建→输出"全链路,同时给出发动机般驱动引擎的本地 GraphQL 会话 API 直调方法、引擎生命周期实测结论与四类真实报错排障样本。2026-10-19 于 Ubuntu 22.04.4(dagger v0.21.10)实测,回显逐字照抄。

一、环境信息

  • 操作系统:Ubuntu 22.04.4 LTS(x86_64),内核 6.8.0-138-generic
  • dagger CLI / 引擎版本(实测回显):
dagger v0.21.10 (image://registry.dagger.io/engine:v0.21.10) linux/amd64
  • SDK:Python SDK(随 dagger develop --sdk=python 生成,本地落盘于模块 sdk/ 目录,与引擎版本同源)
  • 实测模块:/opt/workspace/dagger-demo(demo 模块,含 hello/build/report 三个函数)
  • 引擎实例:dagger-engine-v0.21.10(容器运行时实例,组件本职形态,随用随起)
  • 受限网络说明:引擎实例已注入代理环境变量(注入方法与原因见第七章)

二、前置条件

本章说明调用 API 前需要就位的三件事:引擎、端点与认证、客户端工具。

  1. 引擎可用:CLI 首次执行任务时自动创建并启动引擎实例(实例名含版本号),无需手工部署;确认命令与实测回显:
docker ps -a --filter name=dagger-engine --format '{{.Names}}  {{.Status}}'
dagger-engine-v0.21.10  Up 2 minutes
  1. API 端点与认证:引擎不暴露固定端口——每次 CLI/SDK 会话建立时,本地动态起一个 GraphQL 会话端点,凭据通过环境变量注入:DAGGER_SESSION_PORT(端口)与 DAGGER_SESSION_TOKEN(Bearer 认证)。手工获取端点的标准方式是 dagger run 包裹目标进程(见第六章实测),进程内直接读这两个变量即可。
  2. 客户端工具三选一:dagger query(直发 GraphQL,免代码)、官方 SDK(Go/Python/TypeScript/Rust/Elixir/PHP,GitHub Release 提供 sdk/<语言>/v0.21.10 标签)、任意 HTTP 客户端(curl 等,走会话端点)。

三、API 总览

  • 协议:GraphQL(HTTP POST,JSON 载荷);CLI 的 dagger call、dagger query 与所有 SDK 最终都编译为对同一 API 的查询
  • 版本约定:API 版本与引擎版本一致(本文 v0.21.10);模块的 dagger.json 记录 engineVersion 作为兼容基线
  • 认证:本地会话用 DAGGER_SESSION_TOKEN 做 HTTP Basic/Bearer 认证(-u $DAGGER_SESSION_TOKEN: 形式),无全局管理端口、无跨机暴露面,安全边界即本机
  • 限流:本地 API 无限流约定;资源隔离由引擎的执行层与缓存层管理(v0.21.10 起支持可选的引擎 CPU/内存指标导出,见 Release Notes telemetry.resourceMetrics)
  • 三种访问形态对比:
形态适用说明
dagger call日常调用模块函数自动加载模块、渲染进度
dagger query调试 API 结构手写 GraphQL,返回原始 JSON
SDK / dagger run+HTTP把流水线写进应用代码函数即代码,类型化客户端

四、SDK 接入与模块初始化

本章实测从零建模块到 Python SDK 就位的完整过程。初始化空模块:

mkdir -p /opt/workspace/dagger-demo && cd /opt/workspace/dagger-demo
dagger init --name demo
cat dagger.json
{
  "name": "demo",
  "engineVersion": "v0.21.10"
}

⚠️ 模块已存在时再执行 init 会报错(实测):Error: module already exists。补装 SDK 用 develop。

接入 Python SDK(实测):

dagger develop --sdk=python --progress plain
10  : develop
11  : ┆ develop . DONE [2.3s]
10  : develop DONE [2.3s]

生成的关键文件(实测清单):

  • dagger.json:模块清单,sdk.source 变为 python
  • pyproject.toml:模块工程文件,实测内容关键字段为 requires-python = ">=3.14"、依赖 dagger-io(以本地 sdk/ 目录 editable 方式安装)
[project]
name = "demo"
version = "0.1.0"
requires-python = ">=3.14"
dependencies = ["dagger-io"]

[build-system]
requires = ["uv_build>=0.8.4,<0.9.0"]
build-backend = "uv_build"

[tool.uv.sources]
dagger-io = { path = "sdk", editable = true }

解读:requires-python >= 3.14 由 SDK 运行时镜像保证,宿主机无需安装该版本 Python(模块函数跑在引擎管理的运行时里);dagger-io 指向模块内 sdk/ 目录,SDK 与引擎版本天然对齐。 sdk/(SDK 源码与运行时)、src/demo/__init__.py 与 src/demo/main.py(函数实现入口)同步生成。

五、最小 module 实战:构建→输出全链路

本章给出可复制的最小 module(三个函数覆盖"纯计算、容器构建、构建后取产物"三种典型形态),并逐条实测调用。

5.1 函数实现(src/demo/main.py 全文,实测跑通)

import dagger
from dagger import dag, function, object_type


@object_type
class Demo:
    @function
    def hello(self, name: str) -> str:
        """返回一句问候语"""
        return f"Hello, {name}!"

    @function
    def build(self, src: dagger.Directory) -> dagger.Container:
        """把源码目录挂进 alpine 容器,把 msg.txt 复制为 output.txt 并追加构建标记"""
        return (
            dag.container()
            .from_("alpine:3.22")
            .with_directory("/src", src)
            .with_workdir("/src")
            .with_exec(["sh", "-c", "cp msg.txt output.txt && echo built-by-dagger >> output.txt"])
        )

    @function
    async def report(self, src: dagger.Directory) -> str:
        """构建并把输出文件内容作为字符串返回"""
        return await self.build(src).file("/src/output.txt").contents()

结构解读:@object_type 把类注册进模块 schema;@function 声明可调用函数,类型注解即 API 类型(dagger.Directory/dagger.Container 是引擎的强类型对象);dag.container() 是 API 根入口,链式调用在引擎侧执行。函数间可直接互相调用(report 内部调用 self.build)。

5.2 准备源码与函数清单

echo 'hello from dagger module' > msg.txt
dagger functions -s
Name     Description
build    把源码目录挂进 alpine 容器,把 msg.txt 复制为 output.txt 并追加构建标记
hello    返回一句问候语
report   构建并把输出文件内容作为字符串返回

5.3 逐函数实测

调用纯计算函数(实测):

dagger call hello --name dagger -s

Hello, dagger!

调用构建→返回内容链路(实测):

dagger call report --src . -s

hello from dagger module
built-by-dagger

构建产物导出为本地文件(实测,--output 保存 File/Directory 结果):

dagger call build --src . file --path /src/output.txt --output ./exported-output.txt -s

Saved to "/opt/workspace/dagger-demo/exported-output.txt".
cat exported-output.txt
hello from dagger module
built-by-dagger

至此最小 module 的"构建→输出"闭环跑通:build 返回容器对象、file --path 链式取产物、--output 落盘,全程无本地语言环境依赖。

六、会话 API 直调实测

本章不借助 SDK,用 HTTP 客户端直调引擎会话端点,验证 API 认证与调用协议。dagger run 包裹 curl,会话端口与令牌由 CLI 注入(实测一条命令复现官方帮助中的示例):

echo '{"query":"{ container { from(address: \"alpine:3.22\") { withExec(args: [\"echo\", \"api-works\"]) { stdout } } } }"}' | dagger run -s sh -c 'curl -s -u $DAGGER_SESSION_TOKEN: -H "content-type:application/json" -d @- http://127.0.0.1:$DAGGER_SESSION_PORT/query'

实测响应:

{"data":{"container":{"from":{"withExec":{"stdout":"api-works\n"}}}}}

同样的查询用 dagger query 直发(实测):

dagger query -s -M <<'EOF'
{ container { from(address: "alpine:3.22") { withExec(args: ["echo", "query-works"]) { stdout } } } }
EOF
{
    "container": {
        "from": {
            "withExec": {
                "stdout": "query-works\n"
            }
        }
    }
}

错误响应长什么样(字段拼写错误,实测 422):

Error: make request: returned error 422: {"data":null,"errors":[{"message":"Cannot query field \"containr\" on type \"Query\". Did you mean \"container\"?","locations":[{"line":1,"column":3}],"extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}]}

结论:会话端点 = 标准 GraphQL over HTTP + Basic 认证;任何语言零依赖接入,SDK 只是把这套 API 类型化封装。

七、引擎生命周期与执行观测

引擎实例由 CLI 全自动管理,生命周期实测结论如下(实例名 dagger-engine-v0.21.10):

  1. 自动创建:本机无实例时,执行任意任务自动创建(CLI 进度日志实测出现 create container → exec docker start dagger-engine-v0.21.10)。
  2. 连接与会话:连接成功日志(实测):INF connected name=55f142d61e1e client-version=v0.21.10 server-version=v0.21.10;随后 starting session 建立会话端点。
  3. 停止后自动拉起:手工停掉实例后执行查询,CLI 自动启动实例并完成任务(实测查询返回正常,事后实例状态 Up 2 seconds)。
  4. 删除后自动重建:删除实例后执行查询,CLI 按内嵌镜像地址重建,查询正常返回(实测)。
  5. 实例环境变量实测(docker inspect 节选):DAGGER_GO_SDK_MANIFEST_DIGEST=sha256:9ae7d4707fe9417d08503ab769c6d6101cc9f23c5f50a5a676f339885592b79b、DAGGER_PYTHON_SDK_MANIFEST_DIGEST=sha256:814d48c0cd65bd6e0a3232742614ab66d1c1489120f8ac585ede2b5d46d7d058、DAGGER_TYPESCRIPT_SDK_MANIFEST_DIGEST=sha256:2d26582288d350b6d6beea98dc7defc084d7d5ddccc42446cff5236e613、_EXPERIMENTAL_DAGGER_RUNNER_HOST=unix:///run/dagger/engine.sock——SDK 运行时镜像指纹在实例创建时即固化,保证函数执行环境可复现。
  6. 受限网络注意:实例被删除后 CLI 自动重建,但重建实例不带手工注入的代理变量(实测验证);需要代理的环境按第八章 Q1 手工重建。

八、常见报错排查(实测样本)

Q1:任务执行报镜像解析失败 connection refused(受限网络最常见)

实测原文:

Error: make request: input: container.from failed to resolve image "docker.io/library/hello-world:latest"
(platform: "linux/amd64"): failed to do request: Head
"https://registry-1.docker.io/v2/library/hello-world/manifests/latest": dial tcp 202.160.128.40:443:
connect: connection refused

原因与解决:引擎实例访问镜像源走自身网络栈,不走容器运行时守护进程的代理;手工重建实例注入代理(地址按实际环境替换):

docker rm -f dagger-engine-v0.21.10
docker run -d --name dagger-engine-v0.21.10 --privileged \
  -e HTTP_PROXY=http://192.168.1.62:7897 \
  -e HTTPS_PROXY=http://192.168.1.62:7897 \
  -e NO_PROXY=localhost,127.0.0.1 \
  registry.dagger.io/engine:v0.21.10

重建后重跑任务即恢复正常(实测 hello-world 查询成功返回完整 stdout)。

Q2:dagger call 报 unknown command "nope" for "dagger call"

实测原文:

Error: unknown command "nope" for "dagger call" [traceparent:bdd7e1bad9fef396be046cb3b5ab4c38-2ee3d63a3adf7713]
Run 'dagger call --help' for usage.

定位:同日志上方有 schema 概要(实测 query_function_count=3 query_functions="[build hello report]"),对照清单改函数名;若清单里没有目标函数,检查类是否标注 @object_type、函数是否标注 @function,改后重跑 dagger develop。

Q3:dagger query 返回 422 GRAPHQL_VALIDATION_FAILED

字段拼错即触发,错误体自带修正建议(实测样本见第六章末),按 "Did you mean" 修复。

Q4:dagger init 报 module already exists

目录已有 dagger.json 时 init 拒绝重初始化(实测);补装 SDK 走 dagger develop --sdk=<语言>,改模块名用 dagger config 或直接编辑 dagger.json。

九、注意事项

  • 模块函数运行在引擎管理的运行时中,宿主机语言版本无关紧要(requires-python >= 3.14 由运行时镜像满足);
  • 函数签名即 API 契约:改参数/返回类型后必须重跑 dagger develop 重新生成 schema;
  • 敏感值用引擎的 Secret 类型传递,避免进入日志与缓存明文;
  • dagger.json 的 engineVersion 是兼容基线,升级 CLI 后重跑 dagger develop 对齐;
  • 会话端点随会话生灭(端口动态分配),不要硬编码;需要长连进程统一用 dagger run 包裹。

十、参考链接