自定义开发容器功能 (Dev Container Features)
2022 年 9 月 15 日,作者:Brigit Murtaugh,@BrigitMurtaugh
我们都经历过设置开发环境时的那一刻——“哦,我只需要再加一个东西!”——而这个“东西”可能是为了处理项目所需的另一种语言或工具集(或者可能不止一个😊)。
开发容器是简化环境设置的好方法——它们提供了一个完整的编码环境,其中包含项目所需的工具。它们使用镜像、Dockerfile 或 Docker Compose 文件以及 devcontainer.json 进行配置。devcontainer.json 是一种元数据格式,用于通过特定于开发的内容和设置来丰富容器。
在创建开发容器时,您可能会反复出现同样的“我只需要再加一个东西!”反应——也许您在 Dockerfile 中使用了 Node.js 镜像,而只需要添加 Git。或者您可能需要添加更复杂的东西,例如在开发容器内使用 Docker 或 Kubernetes。开发容器非常棒,因为任何访问您的代码的人都将拥有与您添加的所有这些工具相同且一致的体验——但是添加它们的最佳方式是什么呢?
如果有一种简单的方法,只需提及工具的名称和版本,即可在开发容器中安装该额外的工具,那会怎样?或者,如果您作为工具用户或作者,可以为其他人创建一种简单的方法来安装它,那会怎样?共享手动脚本有助于重用,但在引用脚本时,您可能会忘记引用容器或工具设置,例如为 Go、Rust 或 C++ 调试启用 ptrace 支持,在容器启动时添加特定的入口点,或者确保包含正确的 VS Code 扩展。
功能
我们很高兴地告诉大家,开发容器的 功能 (Features) 可以帮助您顺利地在开发容器中获得所需的工具!
功能是自包含的安装代码、容器配置和/或设置和扩展单元,旨在为您的开发容器启用新的开发能力。它们可以构建为与各种基础容器镜像配合使用。作为我们开放开发容器规范工作的一部分,我们对获取预创建功能的位置以及如何编写和分发您自己的功能进行了一些改进。
让我们看看有什么新功能,以及如何从任何支持开发容器的工具或服务(例如 VS Code Dev Containers 扩展或 GitHub Codespaces)开始使用功能!
将功能添加到您的开发容器
开发容器功能提供了一种将开发容器元数据与某些安装步骤关联起来的快速方法。您可以通过简单的引用将它们添加到您的开发容器中。
这些功能现在可以作为 OCI 工件存储在任何支持的容器注册表中,这意味着您可以使用与引用容器镜像相同的标识符类型来引用它们。我们已将 vscode-dev-containers 仓库中的一些早期功能移到了新的 devcontainers/features 仓库中,并使用这种新方法发布。
从 devcontainers/features 仓库引用不同的功能非常简单,只需在 devcontainer.json 中添加一个 features 属性即可。每个功能都有一个 README.md,其中显示了如何引用该功能以及可用的选项。
下面的示例安装了 go 和 docker-in-docker 功能。
"name": "my-project-devcontainer",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.18"
},
"ghcr.io/devcontainers/features/docker-in-docker:1": {
"version": "latest",
"moby": true
}
}
您还可以在规范网站上探索官方和公开贡献的功能。任何功能都可以通过编辑 devcontainer.json 添加,并且公开发布的功能可以通过现有的开发容器配置体验添加(例如 VS Code Dev Containers 扩展中提供的功能)。

您甚至可以使用 dev container CLI、GitHub Actions 或 Azure DevOps 任务,从您最喜欢的 CI 系统中使用带功能 (Features) 的开发容器。我们在 devcontainers/ci 仓库中提供了 GitHub Action 和 Azure DevOps 任务。dev container CLI、GitHub Action 或 Azure DevOps 任务也可用于预构建包含功能内容的镜像,以加快启动时间。
如果您不仅想使用公开可用的功能,还想创建自己的私有或公共功能来共享,请继续阅读!
编写功能 (Authoring Features)
开始创建自己的功能的一个好地方是新的 Features template 仓库。除了包含给定功能内容的良好模板之外,该模板还包括一个 GitHub Actions 工作流,可以使用 GitHub Container Registry (GHCR) 快速发布它们,以便您尽快开始使用。我们稍后会详细讨论发布。
功能的源代码有两个组成部分:安装脚本 (install.sh) 和配置文件 (devcontainer-feature.json)。
+-- feature
| +-- devcontainer-feature.json
| +-- install.sh
| +-- (other files)
install.sh:安装入口点脚本——在概念上,它被添加为镜像 Dockerfile 的一层,并在构建时执行。这个入口点脚本可以安装工具,例如语言(例如 Ruby)和工具(GitHub CLI)。
devcontainer-feature.json:这包含有关功能的元数据、一组可以在安装过程中传递给功能安装脚本的选项,以及将合并到最终开发容器中的 devcontainer.json 的“片段”。例如,如果任何功能在其配置中指示 "privileged": true,则整个开发容器将以 --privileged 标志启动。
功能可以用各种语言编写,最直接的是 shell 脚本。如果功能是用不同的语言编写的,则应在元数据中包含有关它的信息,以便用户可以就此做出明智的选择。
注意:虽然 install.sh 会运行任何语言的功能,但如果功能是用开发容器中不存在的解释型语言编写的,则代码将无法执行。请确保在 install.sh 中获取您需要的语言。
您应该确保公开发布的功能除了功能本身之外,还检查并安装依赖项。
此外,公共功能很可能在 arm64 或 x86_64 机器上使用——因此请尽可能适应这一点。
您可以在规范中查看 devcontainer-feature.json 属性,以及 devcontainers/features 仓库中的公共示例。
既然我们已经看到了如何创建功能,那么如何将其分发给其他人呢?
分布
功能以 tarball 形式分发。tarball 包含功能子目录的全部内容,包括 devcontainer-feature.json、install.sh 以及目录中的任何其他文件。
开放容器倡议 (OCI) 定义了容器和容器资源的行业标准。我们将功能视为 OCI 工件,并使用 OCI 注册表的概念来分发功能。
上面提到的 Features template 仓库包含一个 GitHub Actions 工作流来自动化发布过程。它将功能打包成 tarball,并将资产作为 OCI 工件发布到 GHCR。通过在 GitHub 上的 Actions 选项卡左侧选择 release.yaml 工作流,从模板仓库触发它。GitHub Action 会将功能发布到 GHCR,位于 <owner>/<repo> 命名空间下。只有当其 devcontainer-feature.json 中的版本属性更新时,功能才会重新发布。
注意:使用 GHCR 的一个手动步骤是将 OCI 包标记为“公共”。这只需要为每个功能执行一次。私有功能不需要此步骤,只要您使用注册表的凭据登录到 Docker CLI,就可以访问它们。
与社区共享您的功能
如果您希望您的贡献出现在 VS Code Dev Containers 或 GitHub Codespaces UI 中供开发容器创建使用,您可以执行以下步骤:
- 转到 devcontainers.github.io(支持 containers.dev 的 GitHub 仓库)
- 打开一个 PR 来修改
collection-index.yml文件
合并后,您的更改将出现在 containers.dev/collections 上。
功能安装顺序
如果我的功能应该在另一个功能之后安装怎么办?作为功能作者,您可能会发现您的功能应该在其他功能之前或之后安装。在 devcontainer-feature.json 中,您可以使用 installsAfter 属性列出应在其之前执行的功能。
作为最终用户,您可以使用 devcontainer.json 中的 overrideFeatureInstallOrder 属性进一步控制执行顺序。此数组中的任何功能 ID 都将在所有其他功能之前安装,并按提供的顺序安装。例如:
"features": {
"ghcr.io/devcontainers/features/java:1",
"ghcr.io/devcontainers/features/node:1",
},
"overrideFeatureInstallOrder": [
"ghcr.io/devcontainers/features/node"
]
默认情况下,功能以实现工具确定的最佳顺序安装在基础镜像之上。
如果在功能的 devcontainer-feature.json 或用户的 devcontainer.json 中提供了以下任何属性,则会遵守这些属性指示的顺序(按优先级递减)。
- 用户
devcontainer.json中的overrideFeatureInstallOrder属性。允许用户控制其功能的执行顺序。 - 作为功能
devcontainer-feature.json一部分定义的installsAfter属性。
您可以在规范中阅读有关功能执行和安装顺序的更多信息。
还有什么新功能?
除了新的 Features 仓库之外,我们最近还开放了一个新的 devcontainers/images 仓库,其中托管了一组以前位于 vscode-dev-containers 仓库中的特定镜像。
我们正在制定一个开发容器模板(我们在 vscode-dev-containers 中将其称为“定义”)的社区分发计划,我们预计它会类似于功能。当宣布新的 Features 和 images 仓库时,我们会在 vscode-dev-containers 仓库中发布更新。
如何了解更多信息?
这篇文章只是介绍了您可以使用功能做什么,我们很高兴您能尝试一下!
正如上面内容中链接的那样,了解功能内容以及如何分发的最佳位置是开发容器规范的功能和功能分发页面。
我们期待您在使用、创建和发布功能时的反馈——我们很想听听它们在功能和功能分发问题提案中的使用情况。
如果您有兴趣参与整个规范或连接另一个工具来利用它,请查看 dev container spec 和 CLI 仓库。
祝您开发容器创建愉快,编码愉快!
Brigit Murtaugh, @BrigitMurtaugh