在 macOS 和 Windows 上支持开发容器(Dev Containers)功能

上个月,我们改进了 Podman 对 Windows 上开发容器(Dev Containers)的支持。开发容器是容器化开发的流行规范,正成为一种标准。在这篇博文中,我们将回顾开发容器中“功能(Feature)”的概念,以及 Podman 目前如何在不同操作系统上支持它。

开发容器

开发容器(https://containers.dev/),也称为 Dev Containers,是一种用于指定容器化开发环境的文件格式。通过 devcontainer.json 文件,团队可以声明构建和运行项目所需的工具和配置。它可以与项目的 Git 仓库一起进行版本控制,因此任何人都可以在几秒钟内启动开发环境。唯一的 H需求是 Docker 或 Podman 等容器引擎。

Microsoft 创建了该标准。Visual Studio Code、JetBrains IDE 和许多其他工具都支持它

开发容器镜像

Microsoft 维护着一份预构建的开发容器镜像列表。每个镜像都包含特定编程语言或技术栈的开发工具。Microsoft 预构建镜像的列表可以在 https://github.com/devcontainers/images 找到。例如,Go 开发的镜像就是 ghcr.io/devcontainers/templates/go。

{
    "image": "mcr.microsoft.com/devcontainers/go"
}

这些镜像为给定语言预定义了一组工具。然而,对于实际项目来说,这些工具还不够。可以使用自定义镜像代替 Microsoft 预构建的镜像,但是开发容器功能是自定义开发容器的流行方式。

开发容器功能

功能(Features)是可以包含在镜像之上的额外工具,可用功能列表(官方和社区支持的)发布在此处:https://containers.dev/features。在以下示例中,batspandocpre-commitshellcheckmodern-shell-utilsskopeo 等工具被添加到 Microsoft Go 镜像的工具中。

{
   "image": "mcr.microsoft.com/devcontainers/go",
   "features": {
       "ghcr.io/edouard-lopez/devcontainer-features/bats:0": {},
       "ghcr.io/rocker-org/devcontainer-features/pandoc:1": {},
       "ghcr.io/prulloac/devcontainer-features/pre-commit:1": {},
       "ghcr.io/marcozac/devcontainer-features/shellcheck:1": {},
       "ghcr.io/mikaello/devcontainer-features/modern-shell-utils:2": {},
       "ghcr.io/jsburckhardt/devcontainer-features/skopeo:1": {}
    }
}

功能也基于容器,与镜像不同,它们是在开发环境启动时构建的。

功能内部原理和额外构建上下文的使用

功能的安装方式很特殊;我们在此详细说明。这将有助于澄清 Podman 如何支持它们。
功能以类型为“application/vnd.devcontainers”的 OCI 工件分发。当开发容器启动时(例如使用 VisualStudio Code),OCI 工件被拉取,并且安装脚本被提取到一个本地文件夹中(这就是 skopeo 功能的安装脚本的样子)。

$ skopeo inspect --raw docker://ghcr.io/jsburckhardt/devcontainer-features/skopeo:1 | jq
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.devcontainers",
    "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "size": 0
  },
...
}

开发容器启动的下一步是构建自定义镜像。这是根据 devcontainer.json 镜像(上述示例中的 `mcr.microsoft.com/devcontainers/go`)即时完成的,并执行功能安装脚本。这是一个(简化)的生成 Dockerfile 示例,用于构建开发容器自定义镜像。

FROM mcr.microsoft.com/devcontainers/go

COPY --from=features-context /tmp/build-features/ /tmp/dev-container-features

RUN cd /tmp/dev-container-features/skopeo_3 \
 && chmod +x ./devcontainer-features-install.sh \
 && ./devcontainer-features-install.sh

这里有趣的部分是 `COPY --from` 指令。它使用了额外的构建上下文 `feature-context`,而不是默认的。文件夹 `tmp/build-features/` 存在于构建上下文 `feature-context` 中。这是一种优雅的方式,用于隔离开发容器镜像的构建上下文和功能文件夹,以避免冲突问题(例如,构建上下文子文件夹和功能文件夹可能具有相同的名称)。

这意味着要支持开发容器功能,容器引擎需要支持额外的构建上下文。

Podman 对额外构建上下文的支持

额外构建上下文是最近才出现的功能。它们在 Podman v4.2 和 Buildah v1.26 中引入。

podman build \
     --build-context context=./alt-ctx \
     ./main-ctx

当前额外构建上下文的实现在 Podman 本地运行时效果很好。但当 Podman 在远程机器上运行时则不然。事实上,与主要构建上下文不同,额外上下文不会发送到远程机器,并且由于额外构建上下文的本地路径在远程机器中不存在,因此构建失败。

这是一个令人恼火的限制,有望很快得到解决。但目前,Podman Machine 可以通过一些默认约定来支持额外的构建上下文(和开发容器功能)。

Podman Machine 支持额外构建上下文的默认约定

首先,Podman Machine 将一些默认文件夹挂载到客户操作系统中。

LinuxmacOSWindows
主机路径客户机路径主机路径客户机路径主机路径客户机路径
$HOME$HOME/Users/UsersC:\/mnt/C
/private/private
这意味着如果一个额外的构建上下文位于已挂载的主机路径中,那么它也存在于 Podman Machine 中 🎉。

其次,Windows 主机路径(例如 C:/Users/user1)会自动转换为 Linux 客户机路径(例如 /mnt/c/Users/user1)。Podman 客户端在挂载卷时也使用此技巧(例如 podman run -v C:/Users/user1)。

📝 从 Podman 5.3.0 开始,这些约定在 Windows WSL 上受支持(Hyper-V 有一个单独的问题)。

下表总结了目前不同操作系统对开发容器功能(和额外构建上下文)的支持情况。

Linux 本地Linux 远程macOS (vfkit)macOS (libkrun)Windows (WSLv2)Windows (Hyper-V)
🟢🟠🟠🟠
开发容器功能支持

🟢 可用

🟠 使用默认 Podman 机器配置可用

暂不可用

起源:使用 Podman 支持的开发容器构建 Podman

现在 Podman 对 Dev Containers 功能提供了不错的支持,让我们充分利用它。让我们从源代码构建 Podman 本身!唯一的要求应该是在本地安装了:

  • Podman
  • devcontainer CLI(带有 Dev Containers 扩展的 VisualStudio Code 也可以)。

安装这两个必要条件后,这个简单的 `devcontainer.json` 和 `Dockerfile` 就足以构建 Podman。

{
	"name": "podman-devcontainer",
	"build": {
		"dockerfile": "Dockerfile",
		"context": ".."
	},
	"features": {
		"ghcr.io/devcontainers/features/go:1": {}
	},

	"containerEnv": {
		"HOME": "/home/vscode"
	}
}
FROM quay.io/podman/stable
COPY /rpm/podman.spec /rpm/podman.spec
RUN dnf update -y && dnf -y install 'dnf-command(builddep)'
RUN dnf -y \
    builddep /rpm/podman.spec

这两个文件应该被复制到一个名为 `.devcontainer` 的新文件夹中,该文件夹位于 podman 仓库的根目录(或者查看 我的 podman fork 中名为 inception 的分支)。

在 Podman 仓库根目录的终端中运行以下命令,启动开发容器。

$ devcontainer up --docker-path podman --workspace-folder .

然后,可以在开发容器中执行 `make podman` 命令。

$ devcontainer exec --workspace-folder . --docker-path podman \
    make podman

或者,也可以使用 VisualStudio Code 启动相同的开发容器。

截图

📝 运行 Podman Makefile 的其他 make 目标可能会失败。这可能与开发容器中缺少某些工具或 Podman Machine 资源不足(RAM 和 CPU)有关。无论如何,这个 `devcontainer.json` 应该很容易改进。

发表评论

订阅

输入您的电子邮件地址以接收来自本网站的电子邮件更新。

返回

您的消息已发送

警告
警告
警告。

分类


搜索