容器时代的 Shell:介绍 Dagger Shell

2025 年 3 月 26 日

Unix Shell 已经有 50 多年的历史了,但它仍然定义了程序员使用计算机的方式。我们在终端中输入几个单词,几毫秒后,一个短暂的工厂上线了:Unix 管道。数据流经一个由简单程序组成的网络,这些程序像工厂车间里的机器人一样并发工作,执行我们几秒钟前编写的计算流程。任务完成后,工厂消失。然后执行下一个命令。这个循环构建了互联网,并且今天仍在运行。

Unix 的设计原则是永恒的:编写简单的程序,使用标准接口组合它们。但 50 年是很长的时间……软件栈已经扩展,Unix 接口被埋在多层抽象之下。我们仍然使用 Shell,但它不再是它原本应该成为的通用组合系统。如果我们改变这一点呢?

如果 Unix Shell 采用来自 docker、make、powershell 和 nix 的最佳想法会怎样?对容器、密钥和服务端点等原语的本机支持;类型化对象;声明式执行;内容寻址的工件;一切都默认沙盒化和缓存。如果这些都成为标准 Shell 功能,通过标准 Shell 语法可用,并由现代 API 提供支持会怎样?我们是否还需要学习十几个 DSL 来自动化交付软件的基本任务?或者现代软件栈的复杂性会消失,只留下两个基本要素:Shell 和代码?让我们来一探究竟!

今天,我们推出 Dagger Shell:一个用于我们最先进的运行时和组合系统 Dagger Engine 的 bash 语法前端。将其用于构建、测试、临时环境、部署或您想要从终端自动化的任何其他任务。这也是组合 AI 代理的好方法……但更多内容稍后介绍。

要开始使用,只需安装最新版本的 Dagger,然后键入 dagger。或者继续阅读示例。

系统 Shell 的补充

Dagger Shell 并非旨在替代您的系统 Shell,而是对其进行补充。当工作流程过于复杂而无法容纳在常规 Shell 中时,下一个可用的选择通常是一个脆弱的整体:不像 Shell 脚本那么简单,也不像完整的软件那么强大。Dagger Shell 旨在帮助您用一组简单的模块替换该整体,这些模块通过标准接口组合在一起。

container |
 from alpine |
 with-exec apk add git

Shell 和代码就是您所需要的

当脚本变得过于复杂并超出 Shell 语法时,为什么不编写真正的代码而要学习一种奇怪的 DSL 呢?

Unix 建立在其 Shell 和 C 编程环境的双重支柱之上。Dagger 遵循相同的模型,提供 Go、Python、Typescript、Java 和 PHP 的 SDK。选择一种语言,编写一个函数……恭喜,您刚刚使用自己的原语扩展了 Dagger!无论您的系统多么复杂,您都应该只需要两个构建块:Shell 和代码。

Shell,遇见 API

Dagger Shell 只是另一个 Dagger API 客户端,为您提供类型化对象、内置文档和对可重用模块的跨语言生态系统的访问。

没错,Dagger Shell 可以加载 Daggerverse 中的数千个模块,检查它们的 API,并运行它们的函数。在这里,我们探索一个 Trivy module:

默认情况下进行沙盒化

Dagger Shell 命令作为沙盒化函数运行,仅在显式提供作为参数时才访问主机资源(文件、密钥、服务)。

这会使命令稍微冗长一些,但也更具可重复性,让您有信心快速迭代而无需反复猜测。

例如,这是一个使用密钥、本地目录和数据库访问的开发环境:

简单的容器构建

这将创建一个 Alpine 容器,放入一个包含您的消息的文本文件,将其设置为在运行时显示该消息,并将其发布到临时注册表。所有这些都在一个管道中完成 - 无需在 Dockerfile 创建、构建命令和注册表推送之间切换上下文。

测试环境

CI 中的一个常见问题是创建临时测试环境:您需要容器化正在测试的软件,并将其连接到其依赖项。Dagger 通过对服务绑定的本机支持,使这变得容易。

例如,让我们创建一个包含 Dagger 文档的多个实时实例的环境,并使用 curl “测试”它们。

这也突出了如何无缝地混合和匹配第三方模块。

# 构建一个带有 curl 的 wolfi linux 容器,然后测试与 stable 和 dev 文档的连接
github.com/dagger/dagger/modules/wolfi | container --packages=curl |
 with-service-binding docs-stable $(github.com/dagger/dagger/docs@v0.17.1 | server) |
 with-service-binding docs-dev $(github.com/dagger/dagger/docs@main | server) |
 with-exec curl http://docs-stable |
 with-exec curl

多阶段构建

使用清晰、模块化的语法实现复杂的构建工作流程。保持对管道每个阶段的可见性和控制。保存和重用管道的各个部分。当您可以一次完成所有操作时,为什么要在编辑 Dockerfile、运行构建和推送命令之间切换呢?

repo=$(git https://github.com/dagger/hello-dagger | head | tree)
env=$(container | from node:23 | with-directory /app $repo | with-workdir /app)
build=$($env | with-exec npm install | with-exec npm run build | directory ./dist)
container | from nginx | with-directory /usr/share/nginx/html $build | terminal --cmd

此示例提取源代码,创建一个 Node.js 构建环境,运行构建过程,并将仅构建的文件打包到最小的 nginx 容器中。每个阶段都是一个您可以检查、修改或重用的命名变量 - 状态是显式的,而不是隐藏在 Dockerfile 或构建缓存的层中。

从我们的存储库中获取 Dagger 的开发版本:

container |
 from golang:latest |
 with-directory /src $(git https://github.com/dagger/dagger | head | tree) |
 with-workdir /src |
 with-exec go build ./cmd/dagger |
 file ./dagger |
 export

接下来是什么?

我们希望您尝试 Dagger Shell 并分享您的反馈。在 Dagger Shell 文档中了解更多信息,加入我们的 Discord 并告诉我们 Dagger Shell 如何改善您的开发工作流程!

祝您编码愉快!🚀