Dagger 使用札记
工具介绍
用于构建 DevOps 工作流的开源平台,旨在简化和标准化复杂的 CI/CD 管道。
Dagger 提供了 Go/Python/TypeScript 等语言的 sdk,使你能使用这些语言来操作 BuildKit 来生成或推送你想要的文件或镜像
用法
以下的安装和初始化给出了国内网络环境下的使用
安装
工欲善其事必先利其器,首先就是需要下载使用
首先可以按照官网 Installation | Dagger 来进行安装
|
|
初始化
我们可以使用如下命令对我们已有的项目进行初始化
|
|
但是你可能会碰到如下错误
|
|
错误提示我们拉取不到镜像,很明显是网络问题
根据以下链接
- 记录如何在私有 Intranet 中运行 dagger · 问题 #6275 · dagger/dagger — Document how to run dagger in a private intranet · Issue #6275 · dagger/dagger
- 缺少有关如何在公司代理后面使用 dagger 的文档 · 问题 #5240 · dagger/dagger — Missing documentation on how to use dagger behind a corporate proxy · Issue #5240 · dagger/dagger
- 支持本地调试远程引擎·问题#25852·airbytehq/airbyte — Support for debugging remote engines locally · Issue #25852 · airbytehq/airbyte
我们可以得到一个解决方案(文档参见 Custom Runner | Dagger)
|
|
但可能还是会报错
|
|
我搜索了一下,在这里找到了方案
- 🐞 Dagger 模块在企业环境中崩溃 · Issue #6599 · dagger/dagger — 🐞 Dagger modules break in corporate environments · Issue #6599 · dagger/dagger
- engine: support for system proxy settings by sipsma · Pull Request #7255 · dagger/dagger
我们可以设置 _DAGGER_ENGINE_SYSTEMENV_GOPROXY
环境变量,让我们试试
|
|
我们会发现还是报同样的错误,我在 🐞 Dagger 模块在企业环境中崩溃 · Issue #6599 · dagger/dagger — 🐞 Dagger modules break in corporate environments · Issue #6599 · dagger/dagger 问了下大家,发现这个环境变量并不是作用于客户端,而是引擎。
通过我们上面 -vvv
可以看到实际调用的 docker run
命令是
|
|
所以最终的处理方案是
|
|
将 _EXPERIMENTAL_DAGGER_RUNNER_HOST
设置为 docker-image://ghcr.nju.edu.cn/dagger/engine:v0.13.7
将会指示 dagger 客户端查找当前是否有对应镜像的容器正在运行,如果没有,则按照内置的命令创建一个。或者你也可以使用 docker-container://dagger-engine-v0.13.7
直接指定容器。
使用样例
这里给出一个使用样例,我们希望有的功能清单为
- 编译出二进制
- 编译多架构 Docker 镜像并推送到远端
目录结构
|
|
文件内容
dagger/main.go
|
|
makefile
|
|
一些 Tips
Dagger CLI 某些功能可以和代码等同
拿 Golang 编译然后导出到本地目录做演示
|
|
可以使用如下命令导出编译产物
1 2
# 编译后可在 dist 目录下查看 dagger call build-app --src=. --token=env:GITEA_TOKEN directory --path="/src/dist" export --path="./dist"
也可以新建一个函数
1 2 3
func (m *PeienEngine) BuildAndExport(ctx context.Context, src *dagger.Directory, token *dagger.Secret) (string, error) { return m.BuildApp(ctx, src, token).Directory("/src/dist").Export("./dist") }
然后执行命令
dagger call build-and-export --src=. --token=env:GITEA_TOKEN
WithExec 不会合并成一层
我们写 Dockerfile 的时候,常常为了体积考虑,会把几行命令写成一行,比如上面的
|
|
如果写成两行的话,则编译出来会占用编译产物的所有体积,然后下面的 mv
又会占用一次体积。
而 Dagger 官方示例中处处都是类似下面这种
|
|
最开始我以为既然官方示例这么写,那应该会自动合并,但是我使用 dive 查看后,发现并没有合并,上面这种写法基本等同下面两行 RUN
|
|
所以需要合并的,最好使用 sh -c
来合并,例如
|
|
操作是有序的
可能我们会看到很多 WithX 操作,比如
|
|
需要注意这些操作都是有序的,比如官方很多样例把 WithDirectory("/src", src)
放在最前面,就导致任何一点变动都会影响后续所有流程,正确的方法是放在编译前,就和我们平时写 Dockerfile 考虑的层级构造一样,是有序的,变动多的往后放。
Dagger 开发环境恢复
TLDR: dagger develop
即可安装 SDK
当我们使用 init
创建一个 Dagger 流程后,会发现它生成的文件夹里面排除了生成的 sdk,参见以下 .gitignore
示例
|
|
也就是这些文件不会签入 git 仓库,和其他人协作开发时,这些文件不会同步,虽说这对于 Dagger 各种命令的运行不影响(Dagger内部会构建环境),这些文件是供我们开发 Dagger 流程的。
我们可以使用命令 dagger develop
来生成这些 SDK 文件,这个命令会找到项目下面生成的 dagger.json 文件,然后根据里面的配置在对应的文件夹生成 SDK
以上说明可参见
- ✨ Cannot update SDK when using newer version of Dagger · Issue #7964 · dagger/dagger
- CLI Reference | Dagger
dagger.gen.go 报错
有时候你想改变 main.go
的导出函数名称,会看到类似这样的报错
|
|
此时你需要使用上面提到的 dagger develop
来重新生成 dagger.gen.go
不过不运行也没关系,就是 IDE 或编辑器会有错误提示而已,实际运行不影响
导出镜像 tarball 设置镜像tag
问题描述:使用 export 导出的镜像 tarball 是没有 tag 的
解决方案:添加 io.containerd.image.name
的 annotation
即可,代码示例如下
|
|
其中 WithAnnotation("io.containerd.image.name", fmt.Sprintf("akkuman/testapp:%s", version))
将会设置镜像 tag
相关 issue:
- ✨
.AsTarball
Add OCI manifest RepoTags · Issue #7368 · dagger/dagger - [Container.Export] Support custom annotations for exporting oci tar. · Issue #6999 · dagger/dagger
- ✨ Import image to local container runtime · Issue #8025 · dagger/dagger
.dockerignore 不起效
我们知道,Docker 构建时可以使用 .dockerignore 来排除一些文件的导入,但似乎 Dagger 不会依照 .dockerignore
查阅文档 Directory Filters | Dagger,发现需要指定
例如
|
|
可以查看其中的 +ignore
,详情参阅 Directory Filters | Dagger
如何限制某个参数是特定选项值
这是一个类似枚举的需求,如果没有仔细查阅文档,可能会这样做
|
|
但实际上 Dagger 支持枚举,可以改成
|
|
这样除了可以简化自己的判断之外,也可以使用 dagger call build-one-image --help
在帮助中查看到
|
|
使用 dag.Container().From(“alpine:latest”) 无法拉取镜像
国内因为网络问题,无法拉取到 docker.io 上的镜像,但是 dagger call
的执行实际上在 dagger engine 容器中,所以拉取时不会依照本地设置的 /etc/docke/daemon.json
这时我们可以采用手动运行 Dagger Engine 服务容器的方式,指定 Dagger Eninge 的 engine.toml
例如,创建文件 engine.toml
,注意:其中的镜像换成自己的要使用的镜像地址
|
|
然后手动运行 Engine
|
|
然后配置环境变量指定 Dagger CLI 使用该 Dagger Engine 服务
|
|
接下来再使用 Dagger CLI 执行 dagger call
各种 From
拉取就不会有问题了
详情参见
Dagger 缓存占据了很大空间,如何清理
缓存大小可以使用 BuildKit 垃圾收集配置来控制,也可以使用如下命令清理 Dagger Engine 的所有缓存
|
|
Github Action 中如何保留缓存
不幸的是,很难用,因为 Docker 构建在 GHA 中是二等公民,这里提供一些链接参考
- No information on caching for CI environments · Issue #6911 · dagger/dagger
- 🐞 Fail to export cache with _EXPERIMENTAL_DAGGER_CACHE_CONFIG (
failed to compute blob by overlay differ (ok=false)
) · Issue #8717 · dagger/dagger - (334) Discord | "actions/cache" | Dagger
- Cache management | Docker Docs
为什么是 Dagger 而不是 Earthly
Earthly 我也用过,好处是和 Dockerfile 语法基本一致,基本上会写 Dockerfile 就会写 Earthfile。
具体来说,Earthly 使用下来没啥太致命的缺点,目前发现的一个问题是有些语法在 Dockerfile 中可以,但在 Earthfile 中就不行了,没有及时与 Dockerfile 上游对齐
另一方面是从 github 仓库来看,Earthly 不如 Dagger 活跃,毕竟 Dagger 是 Docker 创始人牵头,有一定的明星效应,能吸引更多的人来参与开源维护。
其实从语法来看,我觉得分不出太大优劣,使用 Dockerfile 的语法缺点是灵活性不如编程语言高,但优点是便于维护。
如果是之前使用 CUE 语言的 Dagger,那我觉得不如使用 Earthly 了,不需要多学一门语言,但现在 Dagger 改成了使用编程语言维护,使用 Dagger SDK 来构建,这样就比之前方便多了。
我目前发现了 Earthly 的两个 bug
- Unexpected environment variable substitution · Issue #4305 · earthly/earthly
ENV
doesn't support setting multiple values · Issue #3959 · earthly/earthly
官方也不是很积极。
就目前项目前景来看,可能 Dagger 更好一些