自定义开发容器功能

2022年9月15日,由Brigit Murtaugh发布,@BrigitMurtaugh

我们都有过这样的时刻,当设置我们的开发环境时——“哦,我只需要再多一样东西!”——那“东西”就是再多一种语言或工具集(或者可能再多几种😊)来继续你的项目。

开发容器是简化环境设置的好方法——它们提供了一个完整的编码环境,包含项目所需的工具。它们通过使用镜像、Dockerfile 或 Docker Compose 文件以及 devcontainer.json 进行配置,后者是一种元数据格式,用于通过开发特定的内容和设置来丰富容器。

在创建开发容器时,您可能会反复出现“我只需要再多一个东西!”的反应——也许您在Dockerfile中使用了一个Node.js镜像,只需要添加Git。或者您可能需要添加更复杂的东西,比如在开发容器中使用Docker或Kubernetes。开发容器非常棒,因为任何访问您代码的人都会拥有与您添加的所有工具相同的、一致的体验——但是添加它们的最佳方式是什么?

如果有一种简单的方法可以通过提及工具的名称和版本来在您的开发容器中安装那个额外的工具,那会怎样?或者,如果作为工具用户或作者,您可以为其他人创建一种简单的方式来安装它,那会怎样?共享手动脚本可以帮助重用,但在引用脚本时,您可能会忘记引用容器或工具设置,例如为Go、Rust或C++调试启用ptrace支持,添加在容器启动时触发的特定入口点,或确保包含正确的VS Code扩展。

功能

我们很高兴分享,开发容器功能帮助您顺利获取开发容器中所需的工具!

功能是独立的安装代码单元、容器配置和/或设置和扩展,旨在为您的开发容器启用新的开发能力。它们可以构建为与各种基础容器镜像一起工作。作为我们开放开发容器规范工作的一部分,我们对获取预创建功能的位置以及如何编写和分发您自己的功能进行了一些改进。

让我们看看有什么新功能,以及如何从任何支持开发容器的工具或服务(例如VS Code 开发容器扩展或GitHub Codespaces)开始使用这些功能!

向您的开发容器添加功能

Dev Container 功能提供了一种快速的方式,将开发容器元数据与一些安装步骤关联起来。您可以通过简单的引用将它们添加到您的开发容器中。

这些特性现在可以作为OCI Artifacts存储在任何支持的容器注册表中,这意味着你可以使用与引用容器镜像相同类型的标识符来引用它们。我们已经将一些早期在vscode-dev-containers仓库中的特性移动到一个新的devcontainers/features仓库中,并使用这种新方法发布它们。

从devcontainers/features仓库引用不同的特性就像在你的devcontainer.json中添加一个features属性一样简单。每个特性都有一个README.md,展示了如何引用该特性以及它有哪些可用的选项。

下面的示例安装了 godocker-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扩展中可用)来添加。

可用功能的规范站点列表

你甚至可以使用你最喜欢的CI系统中的功能与开发容器,通过使用开发容器CLI、GitHub Action或Azure DevOps任务。我们在devcontainers/ci仓库中提供了一个GitHub Action和Azure DevOps任务。开发容器CLI、GitHub Action或Azure DevOps任务也可以用于预构建镜像,这些镜像包含功能内容以加快启动时间。

如果您不仅想使用公开可用的功能,还想创建自己的私有或公共功能来分享,请继续阅读!

创作

开始创建自己的功能的一个好地方是新的功能模板仓库。除了包含一个给定功能内容的好模板外,该模板还包括一个GitHub Actions工作流,以便快速发布它们,使用GitHub容器注册表(GHCR)为您的账户,以尽可能快地启动和运行。我们稍后会详细讨论发布。

Feature 的源代码有两个组成部分:一个安装脚本(install.sh)和一个配置文件(devcontainer-feature.json)。

+-- feature
|    +-- devcontainer-feature.json
|    +-- install.sh
|    +-- (other files)

install.sh: 安装入口点脚本——它在概念上被添加为镜像Dockerfile的一层,并在构建时执行。此入口点脚本可以安装工具,例如语言(例如Ruby)和工具(GitHub CLI)。

devcontainer-feature.json: 这包含了关于Feature的元数据,一组可以在安装期间传递给Feature安装脚本的选项,以及将被合并到最终开发容器中的devcontainer.json的“片段”。例如,如果任何Feature在其配置中指示"privileged": true,则整个开发容器将以--privileged标志启动。

功能可以用多种语言编写,最直接的是shell脚本。如果功能是用其他语言编写的,应在元数据中包含相关信息,以便用户能够做出明智的选择。

注意: 虽然 install.sh 可以运行任何语言的 Features,但如果你用了一种解释型语言编写 Feature,而该语言在开发容器中不存在,代码将无法执行。请确保在 install.sh 中获取你所需的语言。

你应该确保你公开发布的功能除了功能本身外,还要检查和安装依赖项。

此外,公共特性很可能在arm64或x86_64机器上使用 - 因此请尽可能适应这一点。

您可以查看devcontainer-feature.json属性在规范中,以及在devcontainers/features仓库中的公共示例。

既然我们已经了解了如何创建一个特性,我该如何将其分发给其他人呢?

分布

功能以tarball形式分发。tarball包含功能子目录的全部内容,包括devcontainer-feature.jsoninstall.sh以及目录中的任何其他文件。

开放容器倡议 (OCI) 定义了容器和容器资源的行业标准。我们将特性视为OCI工件,并使用OCI注册表的概念来分发特性。

上述提到的Features模板仓库包含一个GitHub Actions工作流,用于自动化发布过程。它将Feature打包成一个tarball,并将资产作为OCI工件发布到GHCR。通过在GitHub仓库的Actions选项卡左侧选择模板仓库中的release.yaml工作流来触发它。GitHub Action将把Feature发布到GHCR的/命名空间下。只有当其devcontainer-feature.json中的版本属性更新时,Feature才会重新发布。

注意:使用GHCR时的一个手动步骤是将OCI 包标记为“公开”。每个功能只需执行一次此步骤。私有功能不需要此步骤,只要您已使用注册表的凭据登录到Docker CLI,就可以访问。

与社区分享你的功能

如果您希望您的贡献出现在VS Code的Dev ContainersGitHub Codespaces UI中以用于开发容器创建,您可以按照以下步骤操作:

合并后,您的更改将出现在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中提供了以下任何属性,则这些属性指示的顺序将被遵循(优先级递减)。

  1. 用户devcontainer.json中的overrideFeatureInstallOrder属性。允许用户控制其功能的执行顺序。
  2. installsAfter 属性定义为 Feature 的 devcontainer-feature.json 的一部分。

您可以阅读更多关于功能执行和安装顺序的信息 在规范中.

还有哪些新内容?

随着新功能仓库的推出,我们最近开源了一个新的devcontainers/images仓库,其中托管了一组之前在vscode-dev-containers仓库中的特定镜像。

我们正在为开发容器模板(我们在vscode-dev-containers中称之为“定义”)制定一个社区分发计划,我们预计这将类似于功能。我们一定会在vscode-dev-containers仓库中发布更新,就像我们在宣布新功能和镜像仓库时所做的那样。

我如何了解更多?

这篇文章只是触及了你可以用Features做什么的表面,我们很高兴你能尝试它们!

如上文所述,了解更多关于功能的内容以及如何分发它们的最佳地点是开发容器规范中的功能功能分发页面。

我们期待您在使用、创建和发布功能时提供反馈——我们很乐意听到它们在功能功能分发问题提案中的表现。

如果您有兴趣参与整个规范或连接另一个工具以利用它,请查看开发容器规范CLI仓库。

愉快的开发容器创建,编码愉快!

布里吉特·默托, @BrigitMurtaugh