规范
Dev Container Features 参考
开发容器功能(Development Container Features)是自包含、可共享的安装代码单元和开发容器配置。该名称源于这样一个理念:引用其中一个功能,就可以快速且轻松地将更多的工具、运行时或库“功能”添加到你的开发容器中,供你或你的协作者使用。
功能元数据由功能根文件夹中的 devcontainer-feature.json 文件捕获。
注意:虽然功能可以安装在任何基础镜像之上,但功能的实现可能会将其限制在可能的子集基础镜像中。例如,某些功能的编写可能仅适用于特定的 Linux 发行版(例如使用
apt包管理器的 debian 系镜像)。
文件夹结构
功能是一个自包含实体,位于一个文件夹中,至少包含一个 devcontainer-feature.json 和 install.sh 入口脚本。允许存在其他文件,并与必需文件一起打包。
+-- feature
| +-- devcontainer-feature.json
| +-- install.sh
| +-- (other files)
devcontainer-feature.json 属性
devcontainer-feature.json 文件定义了关于给定功能的元数据。
除 id、version 和 name 外,所有属性均为可选。
devContainerFeature.schema.json 定义了 devcontainer-feature.json 文件的架构。
文件的属性如下所示:
| 属性 | 类型 | 描述 |
|---|---|---|
id |
string | 必需:功能的标识符。在功能所在的仓库上下文中必须是唯一的,且必须与 devcontainer-feature.json 所在目录的名称匹配。 |
version |
string | 必需:功能的语义版本(例如:1.0.0)。 |
name |
string | 必需:功能的“对人类友好”的显示名称。 |
description |
string | 功能的描述。 |
documentationURL |
string | 指向功能文档的 URL。 |
licenseURL |
string | 指向功能许可协议的 URL。 |
keywords |
数组 | 与搜索此定义/功能的用户相关的字符串列表。 |
options |
对象 | 选项映射,这些选项将作为环境变量传递给脚本的执行。 |
containerEnv |
对象 | 一组键值对,用于设置或覆盖环境变量。 |
privileged |
boolean | 在使用该功能时,为容器设置 特权模式(docker-in-docker 等功能需要此项)。 |
init |
boolean | 在使用该功能时,向容器添加 tiny init 进程(--init)。 |
capAdd |
数组 | 在使用该功能时,添加容器 能力(capabilities)。 |
securityOpt |
数组 | 在使用该功能时,设置容器安全选项(例如更新 seccomp 配置文件)。 |
entrypoint |
string | 如果功能需要一个在容器启动时触发的“入口点”脚本,则进行设置。 |
customizations |
对象 | 特定于产品的属性,customizations 下的每个命名空间都被视为一组单独的属性。对于每一组属性,对象会被解析,值会被替换,而数组则会合并为并集。 |
dependsOn |
对象 | 在安装此功能之前必须满足的功能依赖对象(**)。元素遵循与 devcontainer.json 中 features 对象相同的语义。更多信息请参阅安装顺序。 |
installsAfter |
数组 | 在当前功能之前执行的功能 ID 数组(省略版本标签)。允许功能作者控制不同功能之间的软依赖。更多信息请参阅安装顺序。 |
legacyIds |
数组 | 用于发布此功能的旧 ID 数组。此属性对于在单个命名空间内重命名已发布的功能很有用。 |
deprecated |
boolean | 指示该功能已弃用,且将不会再收到任何更新/支持。此属性旨在供支持工具使用,用于高亮显示功能的弃用状态。 |
mounts |
对象 | 默认为未设置。这是跨编排器向容器添加额外挂载的方式。每个值都是一个接受与 Docker CLI --mount 标志相同值的对象。值中可以引用预定义的 devcontainerId 变量。例如:"mounts": [{ "source": "dind-var-lib-docker", "target": "/var/lib/docker", "type": "volume" }] |
(**) ID 必须指向 (1) 发布到 OCI 注册表的功能,(2) 功能 Tgz URI,或 (3) 本地文件树中的功能。不支持已弃用的功能标识符(即 GitHub Release),并且存在此属性可能被视为致命错误或被忽略。对于 本地功能(即:开发期间),你可以通过提供相对于包含活动 devcontainer.json 的文件夹的相对路径来依赖其他本地功能。此属性内功能的行为再次镜像了 devcontainer.json 中的 features 对象。
生命周期钩子
以下生命周期钩子可以声明为 devcontainer-feature.json 的属性。
| 属性 | 类型 |
|---|---|
onCreateCommand |
字符串、数组、对象 |
updateContentCommand |
字符串、数组、对象 |
postCreateCommand |
字符串、数组、对象 |
postStartCommand |
字符串、数组、对象 |
postAttachCommand |
字符串、数组、对象 |
行为
每个属性都镜像了 devcontainer.json 中匹配属性的行为,包括命令在 项目工作区文件夹 上下文中执行的行为。
对于每个生命周期钩子(按 功能安装顺序),功能贡献的每个命令按顺序执行(阻止下一个命令执行)。功能提供的命令总是在任何用户提供的生命周期命令(即 devcontainer.json 中的命令)之前执行。
如果功能使用 对象语法 提供给定命令,则该组中的所有命令将并行执行,但仍会阻止后续功能和/或 devcontainer.json 中的命令。
注意:这些属性存储在 镜像元数据 中。
将脚本写入已知的容器路径
功能将脚本写入容器内的已知持久路径可能会有所帮助(例如,供后续在特定生命周期钩子中使用)。
以 git-lfs 功能为例,它在安装期间 编写了一个脚本 到 /usr/local/share/pull-git-lfs-artifacts.sh。
install.sh
PULL_GIT_LFS_SCRIPT_PATH="/usr/local/share/pull-git-lfs-artifacts.sh"
tee "$PULL_GIT_LFS_SCRIPT_PATH" > /dev/null \
<< EOF
#!/bin/sh
set -e
<...truncated...>
EOF
该脚本随后在 postCreateCommand 生命周期钩子 期间执行。
devcontainer-feature.json
{
"id": "git-lfs",
"version": "1.1.0",
"name": "Git Large File Support (LFS)",
// <...truncated...>
"postCreateCommand": "/usr/local/share/pull-git-lfs-artifacts.sh",
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
}
options 属性
options 属性包含一个选项 ID 及其相关配置设置的映射。ID 会变成全大写的环境变量名。更多详细信息请参阅 选项解析。例如:
{
"options": {
"optionIdGoesHere": {
"type": "string",
"description": "Description of the option",
"proposals": ["value1", "value2"],
"default": "value1"
}
}
}
| 属性 | 类型 | 描述 |
|---|---|---|
optionId |
string | 选项 ID,它被转换为全大写的环境变量,并包含选定的值。 |
optionId.type |
string | 选项类型。目前有效的类型为:boolean, string |
optionId.proposals |
数组 | 建议的字符串值列表。允许使用自由格式值。使用 optionId.enum 时请省略此项。 |
optionId.enum |
数组 | 严格的允许字符串值列表。不允许使用自由格式值。使用 optionId.proposals 时请省略此项。 |
optionId.default |
字符串或布尔值 | 选项的默认值。 |
optionId.description |
string | 选项的描述。 |
用户环境变量
功能脚本以 root 用户身份运行,有时需要知道开发容器将使用哪个用户帐户。
_REMOTE_USER 和 _CONTAINER_USER 环境变量会被传递给功能脚本,其中 _CONTAINER_USER 是容器的用户,_REMOTE_USER 是配置的 remoteUser。如果没有配置 remoteUser,_REMOTE_USER 将被设置为与 _CONTAINER_USER 相同的值。
此外,这两个用户的主文件夹会作为 _REMOTE_USER_HOME 和 _CONTAINER_USER_HOME 环境变量传递给功能脚本。
容器用户可以通过 devcontainer.json 和镜像元数据中的 containerUser、docker-compose.yml 中的 user、Dockerfile 中的 USER 进行设置,也可以从基础镜像继承。
开发容器 ID
标识符在 devcontainer.json 和功能元数据中被称为 ${devcontainerId},它将被替换为开发容器的 ID。它应该仅用于构建镜像时不需要的配置和元数据部分,因为否则将导致无法在开发容器 ID 未知时预构建镜像。除了布尔值、数字和枚举属性外,支持 ${devcontainerId} 的功能元数据属性包括:entrypoint, mounts, customizations。
实现者可以选择如何计算此标识符。他们必须确保它在同一 Docker 主机上的其他开发容器中是唯一的,并且在开发容器重建时是稳定的。标识符必须仅包含字母数字字符。我们在下面描述一种实现方法。
基于标签的实现
以下假设可以通过容器上的一组标签在同一 Docker 主机上的其他开发容器中识别开发容器。实现者可以选择遵循此方法。
标识符派生自唯一标识开发容器的容器标签集。由实现者选择这些标签。例如,如果开发容器基于本地文件夹,则标签可以命名为 devcontainer.local_folder,其值为本地文件夹的路径。
例如,ghcr.io/devcontainers/features/docker-in-docker 功能 可以使用开发容器 ID,如下所示:
{
"id": "docker-in-docker",
"version": "1.0.4",
// ...
"mounts": [
{
"source": "dind-var-lib-docker-${devcontainerId}",
"target": "/var/lib/docker",
"type": "volume"
}
]
}
基于标签的计算
- 将标签作为 JSON 对象输入,对象的键为标签名称,对象的值为标签值。
- 为了确保实现者得到相同的结果,对象键必须进行排序,并且键和值之外的任何可选空格必须被删除。
- 从 UTF-8 编码的输入字符串计算 SHA-256 哈希。
- 使用以“0”左填充至 52 个字符的 base-32 编码表示作为结果。
JavaScript 实现,将带有标签的对象作为参数,并返回字符串作为结果
const crypto = require('crypto');
function uniqueIdForLabels(idLabels) {
const stringInput = JSON.stringify(idLabels, Object.keys(idLabels).sort()); // sort properties
const bufferInput = Buffer.from(stringInput, 'utf-8');
const hash = crypto.createHash('sha256')
.update(bufferInput)
.digest();
const uniqueId = BigInt(`0x${hash.toString('hex')}`)
.toString(32)
.padStart(52, '0');
return uniqueId;
}
devcontainer.json 属性
功能在用户的 devcontainer.json 的顶级 features 对象下进行引用。
用户可以指定任意数量的功能。在构建时,这些功能将按照由 安装顺序规则和实现 组合定义的顺序进行安装。
单个功能以键值对形式提供,其中键是功能标识符,值是包含“选项”的对象(对于“默认值”可以为空)。功能对象中的每个键都必须是唯一的。
这些选项在构建时作为环境变量获取,如 选项解析 中所述。
下面是一个有效的 features 对象示例。
"features": {
"ghcr.io/user/repo/go": {},
"ghcr.io/user/repo1/go:1": {},
"ghcr.io/user/repo2/go:latest": {},
"https://github.com/user/repo/releases/devcontainer-feature-go.tgz": {
"optionA": "value"
},
"./myGoFeature": {
"optionA": true,
"optionB": "hello",
"version" : "1.0.0"
}
}
注意:如果省略
:latest版本注解,它会被隐式添加。要固定到特定的包版本(示例),请将其附加到功能的末尾。
选项的值可以作为 string 或 boolean 提供,并且应与 devcontainer-feature.json 文件中功能预期的内容匹配。
作为简写,features 属性的值可以作为单个字符串提供。此字符串映射到一个名为 version 的选项。在下面的示例中,两个示例是等效的。
"features": {
"ghcr.io/owner/repo/go": "1.18"
}
"features": {
"ghcr.io/owner/repo/go": {
"version": "1.18"
}
}
引用功能
指定的 id 格式决定了支持工具如何定位和下载给定的功能。id 是以下之一:
| 类型 | 描述 | 示例 |
|---|---|---|
<oci-registry>/<namespace>/<feature>[:<semantic-version>] |
引用 OCI 注册表中的功能(*) | ghcr.io/user/repo/go ghcr.io/user/repo/go:1 ghcr.io/user/repo/go:latest |
https://<uri-to-feature-tgz> |
指向 tarball 的直接 HTTPS URI。 | https://github.com/user/repo/releases/devcontainer-feature-go.tgz |
./<path-to-feature-dir> |
相对于包含 devcontainer-feature.json 的文件夹的目录(**)。 | ./myGoFeature |
(*) OCI 注册表必须实现 OCI Artifact Distribution Specification。一些实现者可以在 这里找到。
(**) 提供的路径总是相对于包含 devcontainer.json 的文件夹。进一步要求在 本地引用附录 中概述。
版本控制
每个功能都根据 semver 规范单独进行版本控制。各自 devcontainer-feature.json 文件中的 version 属性会进行更新以递增功能版本。
处理功能发布的基础设施工具在确切版本已发布的情况下不会重新发布功能;但是,基础设施必须按照 semver 规范重新发布主版本和次版本。
创作
功能可以用多种语言编写,最直接的是 bash 脚本。如果功能是用不同的语言编写的,关于它的信息应该包含在元数据中,以便用户可以做出明智的选择。
执行功能所需的应用程序的相关参考信息应包含在 devcontainer-feature.json 的元数据部分中。
对于不包含此信息的开发功能,应用程序应默认为 /bin/sh。
如果功能作为包含 devcontainer.json 的仓库的一部分包含在文件夹中,则无需其他步骤。
发布
有关分发功能的信息,请参阅 功能分发页面。
执行
调用 install.sh
每个功能的 install.sh 脚本应在容器镜像构建期间以 root 身份执行。这允许脚本添加 OS 依赖项或设置,否则这些内容将无法被修改。这也允许脚本使用 su 命令切换到另一个用户的上下文(例如:su ${USERNAME} -c "command-goes-here")。结合使用,这允许在基础镜像中出于安全原因没有 sudo 的情况下,也能进行 root 和非 root 镜像修改。
为了确保使用正确的 shell,应在 install.sh 上设置执行位并直接调用该文件(例如 chmod +x install.sh && ./install.sh)。
注意:建议功能作者使用其支持的发行版默认提供的 shell 编写
install.sh(例如,Debian/Ubuntu 或 Fedora 中的bash,Alpine 中的sh)。如果需要不同的 shell(例如fish),install.sh可用于引导:检查所需 shell 的存在,必要时安装它,然后使用该 shell 调用辅助脚本。
install.sh文件同样可以用于引导用 Go 等编译语言编写的内容。鉴于功能需要在 x86_64 和基于 arm64 的设备(例如 Apple Silicon Mac)上工作的可能性日益增加,install.sh可以检测当前的架构(例如,使用uname -m或dpkg --print-architecture之类的方法),然后为该架构调用正确的执行文件。
安装顺序
默认情况下,功能安装在基础镜像之上,安装顺序由实现工具确定为最佳。
如果功能的 devcontainer-feature.json 或用户的 devcontainer.json 中提供了以下任何属性,则会尊重这些属性指示的顺序。
- 在功能的
devcontainer-feature.json中定义的dependsOn属性。 - 在功能的
devcontainer-feature.json中定义的installsAfter属性。 - 用户
devcontainer.json中的overrideFeatureInstallOrder属性。允许用户控制其功能的执行顺序。
dependsOn
可选的 dependsOn 属性指示给定功能的一组必需的“硬”依赖项。
dependsOn 属性在功能的 devcontainer-feature.json 元数据文件中声明。此属性的元素镜像了 devcontainer.json 中 features 对象的语义。因此,所有依赖项都可以提供相关的选项,如果功能默认选项已足够,则可以使用空对象(例如:"bar:123": {})。提供不同选项的相同功能被视为不同的功能(有关详细信息,请参见 功能相等性)。
dependsOn 属性中指示的所有功能必须在给定功能被安装之前得到满足(安装顺序中存在一个与每个依赖项 相等 的功能)。如果 dependsOn 属性中指示的任何功能无法安装(例如由于循环依赖、无法解析功能等),则整个开发容器创建应失败。
dependsOn 属性必须递归评估。因此,如果功能依赖项有自己的 dependsOn 属性,则该功能的依赖项也必须在给定功能安装之前得到满足。
{
"name": "My Feature",
"id": "myFeature",
"version": "1.0.0",
"dependsOn": {
"foo:1": {
"flag": true
},
"bar:1.2.3": {},
"baz@sha256:a4cdc44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {},
}
}
在上面的代码片段中,myfeature 必须在 foo、bar 和 baz 之后安装。如果通过 dependsOn 属性提供的功能声明了它们自己的依赖项,这些依赖项也必须在功能安装之前得到满足。
installsAfter
installsAfter 属性指示影响已排队安装的功能安装顺序的“软依赖项”。此属性的有效行为与 dependsOn 相同,但有以下差异:
installsAfter不递归评估。installsAfter仅影响已设置为安装的功能的安装顺序。任何未在 (1) 解析dependsOn依赖树后或 (2) 用户devcontainer.json中指示的功能,都不应添加到安装列表中。installsAfter指示的功能不能提供选项,也不能固定到特定的版本标签或摘要。仍应执行到规范名称的解析(例如:如果功能已被 重命名)。
{
"name": "My Feature",
"id": "myFeature",
"version": "1.0.0",
"installsAfter": [
"foo",
"bar"
]
}
在上面的代码片段中,myfeature 必须在 foo 和 bar 之后安装(如果该功能已排队安装)。如果 second 和 third 未排队安装,则应忽略此依赖关系。
overrideFeatureInstallOrder
devcontainer.json 的 overrideFeatureInstallOrder 属性是一个功能 ID 数组,一旦其上述依赖项安装完成,这些功能将按优先级降序安装。
此属性不得指示与解析后的依赖图(请参阅 依赖算法)不一致的安装顺序。如果
overrideFeatureInstallOrder属性与依赖图不一致,实现工具应使依赖解析步骤失败。
此评估是通过为属性中存在的所有匹配功能标识符(省略版本)的节点分配一个 roundPriority 来执行的。
例如,给定 overrideFeatureInstallOrder 数组中的 n 个功能,编排工具应为每个功能分配一个 roundPriority 为 n - idx,其中 idx 是功能在数组中从零开始的索引。
例如:
overrideFeatureInstallOrder = [
"foo",
"bar",
"baz"
]
将导致以下 roundPriority 分配:
const roundPriority = {
"foo": 3,
"bar": 2,
"baz": 1
}
此属性不得影响依赖图定义的依赖关系(请参阅 依赖图),并且仅在基于轮次的排序步骤中进行评估(请参阅 轮次排序)。换句话说,此属性不能在功能的所有依赖项(软依赖和硬依赖)都安装之前“提前”功能。在功能依赖项在其他轮次中安装后,此属性应尽可能早地“提前”每个功能(给定数组中标识符的顺序)。
与 installsAfter 类似,此属性的成员不得提供选项,也不能固定到特定的版本标签或摘要。
如果功能在 overrideFeatureInstallOrder 中指示,但不是依赖图的成员(它未排队安装),编排工具可能会使依赖解析步骤失败。
定义
定义:功能相等性
本规范定义两个功能相等,如果两个功能指向完全相同的内容,并以相同的选项执行。
对于发布到 OCI 注册表的功能,如果它们的清单摘要相等,且针对该功能执行的选项相等(逐值比较),则两个功能是相同的。相同的清单摘要意味着功能的 tgz 内容及其整个
devcontainer-feature.json是相同的。如果不满足这些条件中的任何一个,则认为功能不相等。对于通过 HTTPS URI 获取的功能,如果 tgz 的内容相同(哈希为相同值),且针对该功能执行的选项相等(逐值比较),则两个功能是相同的。如果不满足这些条件中的任何一个,则认为功能不相等。
对于本地功能,每个功能都被认为是唯一的,不与任何其他本地功能相等。
定义:轮次稳定排序
为了防止非确定性行为,算法将根据以下规则对每一轮进行排序:
- 按其完全限定资源名称(对于 OCI 发布的功能,这意味着没有版本或摘要的 ID)对每个功能进行字典序比较和排序。如果比较结果相等,
- 将每个功能从旧标签到新标签进行比较和排序(
latest为“最新”)。如果比较结果相等,- 按选项比较和排序每个功能:
- 用户定义的选项数量最多(请注意,省略选项将默认该值为功能的默认值,不被视为用户定义的选项)。如果比较结果相等,
- 按字典序排序提供的选项键。如果比较结果相等,
- 按字典序排序提供的选项值。如果比较结果相等,
- 按其规范名称排序功能(对于 OCI 发布的功能,解析为摘要哈希的功能 ID)。
如果根据这些比较规则没有差异,则认为功能相等。
依赖安装顺序算法
实现工具负责计算功能安装顺序(或在无法解析有效的安装顺序时提供错误)。要安装的功能集是用户定义功能(那些直接在用户的
devcontainer.json中指示的功能)及其依赖项(那些由dependsOn或installsAfter属性指示的功能,并考虑用户开发容器的overrideFeatureInstallOrder属性)的并集。实现工具将执行以下步骤:(1) 构建依赖图
从用户定义的功能中,编排工具将构建一个依赖图。图将通过遍历每个功能的
dependsOn和installsAfter属性来构建。然后获取每个依赖项的元数据,并将节点添加为依赖功能的边。对于dependsOn依赖项,该依赖项将反馈到工作列表中进行递归解析。维护一个累加器,包含所有唯一发现和用户提供的功能,每个功能都包含对其依赖项的引用。如果完全相同的功能(请参阅 功能相等性)已添加到累加器中,则不会再次添加。功能树解析后,累加器将馈送到 (B3)。
该图可以作为具有两种边的邻接表存储:(1)
dependsOn边或“硬依赖”,以及 (2)installsAfter边或“软依赖”。(2) 分配轮次优先级
图中的每个节点都有一个隐式的默认
roundPriority0。为了在全局影响安装顺序,同时仍遵守在 (1) 中构建的依赖图,可以调整每个功能的
roundPriority值。在 (3) 中计算每一轮时,仅提交等于该集合最大roundPriority的功能(其余的将未提交并在后续轮次中重新评估)。在以下情况下,
roundPriority被设置为非零值:(3) 轮次排序
按轮次对 (1) 的结果执行排序。此排序将重新排列功能,生成要安装的功能的排序列表。排序将如下执行:
从 (2) 中的所有元素开始放入
worklist,并创建一个空列表installationOrder。当worklist不为空时,遍历worklist中的每个元素,并检查其所有依赖项(如果有)是否已经是installationOrder的成员。如果检查为真,则将其添加到中间列表round中;如果不是,则跳过它。相等性由 功能相等性 确定。然后对于每个中间
round列表,仅向installationOrder提交那些共享最大roundPriority的节点。将round中所有roundPriority严格较低的节点返回到worklist中,以便在后续迭代中重新处理。如果有多个节点具有相同的roundPriority,则根据 轮次稳定排序 进行最终排序后提交给installationOrder。重复进行所需的轮次,直到
worklist为空。如果出现没有元素添加到installationOrder的轮次,则算法应终止并返回错误。这表明依赖图中存在循环依赖或其他致命错误。实现者应尝试向用户提供有关错误的信息和可能的缓解策略。注释
从实现角度来看,
installsAfter节点可以作为一组单独的有向边添加,就像dependsOn节点作为有向边添加一样(请参阅 (1))。在基于轮次的安装和排序 (3) 之前,编排工具应删除所有与worklist中未设置为安装的功能不对应的installsAfter有向边。在每一轮中,如果功能的所有要求(dependsOn和installsAfter依赖项)都在以前的轮次中得到满足,则可以安装该功能。如果对
installsAfter属性的评估导致不一致的状态(例如:循环依赖),实现应使依赖解析步骤失败。
选项解析
功能选项(在用户的 devcontainer.json 中指定为单个功能键/值对的值)作为环境变量传递给功能。
支持工具将解析用户提供的 options 对象。如果为某个功能提供了值,它将按照 <OPTION_NAME>=<value> 的格式发出到名为 devcontainer-features.env 的文件中。
为了确保选项作为环境变量有效,将执行以下替换:
(str: string) => str
.replace(/[^\w_]/g, '_')
.replace(/^[\d_]+/g, '_')
.toUpperCase();
此文件在构建时被功能 install.sh 入口脚本加载以进行处理。
功能 devcontainer-feature.json 定义的任何在用户 devcontainer.json 中省略的选项,都将隐式导出为其默认值。
选项解析示例
假设 python 功能在 devcontainer-feature.json 文件中声明了以下 options 参数:
// ...
"options": {
"version": {
"type": "string",
"enum": ["latest", "3.10", "3.9", "3.8", "3.7", "3.6"],
"default": "latest",
"description": "Select a Python version to install."
},
"pip": {
"type": "boolean",
"default": true,
"description": "Installs pip"
},
"optimize": {
"type": "boolean",
"default": true,
"description": "Optimize python installation"
}
}
用户的 devcontainer.json 这样声明 python 功能:
"features": {
"ghcr.io/devcontainers/features/python:1": {
"version": "3.10",
"pip": false
}
}
发出的环境变量将是:
VERSION="3.10"
PIP="false"
OPTIMIZE="true"
这些将被加载并对 install.sh 入口脚本可见。以下 install.sh……
#!/usr/bin/env bash
echo "Version is $VERSION"
echo "Pip? $PIP"
echo "Optimize? $OPTIMIZE"
……输出以下内容:
Version is 3.10
Pip? false
Optimize? true
重命名功能的步骤
- 更新功能 源代码 文件夹和 devcontainer-feature.json 属性 中的
id属性以反映新的id。在此步骤中,其他属性(name,documentationUrl等)可以根据需要更新。 - 添加或更新功能的
legacyIds属性,包括之前使用的id。 - 提升功能的语义版本。
- 重新运行
devcontainer features publish命令或实现 功能分发规范 的等效工具。
示例:重命名功能
假设我们目前有一个 docker-from-docker 功能 👇
当前 devcontainer-feature.json
{
"id": "docker-from-docker",
"version": "2.0.1",
"name": "Docker (Docker-from-Docker)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-from-docker",
....
}
我们想将此功能重命名为 docker-outside-of-docker。功能的源代码文件夹将更新为 docker-outside-of-docker,更新后的 devcontainer-feature.json 将如下所示 👇
{
"id": "docker-outside-of-docker",
"version": "2.0.2",
"name": "Docker (Docker-outside-of-Docker)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-outside-of-docker",
"legacyIds": [
"docker-from-docker"
]
....
}
注意 - 由 version 属性定义的功能的语义版本应继续,不应从 1.0.0 重新开始。
实现说明
对于实现功能的应用程序,有几件事需要牢记:
- 功能的执行顺序由应用程序决定,基于功能作者使用的
installsAfter属性。如果需要,用户可以使用devcontainer.json中的overrideFeatureInstallOrder来覆盖它。 - 功能用于创建可用于创建容器或不创建容器的镜像。
- 如果只有 1 个功能需要
privileged、init等参数,则包含这些参数。 - 像
capAdd、securityOp这样的参数被连接在一起。 containerEnv在功能以ENV命令在 Dockerfile 中执行之前添加。- 每个功能脚本作为其自己的层执行,以帮助缓存和重建。