创建一个开发容器

Visual Studio Code Dev Containers 扩展允许您使用 Docker 容器作为全功能的开发环境。它允许您在容器内打开任何文件夹或仓库,并充分利用 Visual Studio Code 的全部功能集。项目中的 devcontainer.json 文件告诉 VS Code 如何访问(或创建)一个具有明确定义的工具和运行时堆栈的开发容器。此容器可用于运行应用程序或提供与代码库工作所需的独立工具、库或运行时。

创建开发容器的路径

在本文档中,我们将介绍在VS Code中创建开发(dev)容器的步骤:

  1. 创建一个devcontainer.json,它描述了VS Code应该如何启动容器以及连接后应该做什么。
  2. 通过使用Dockerfile,对开发容器进行更改并持久化,例如安装新软件。
  3. 通过Docker Compose配置多个容器。
  4. 在进行更改时,构建您的开发容器以确保更改生效。

完成上述任何步骤后,您将拥有一个功能齐全的开发容器,您可以继续本教程的下一步以添加更多功能,或者停止并开始在您当前拥有的开发环境中工作。

注意:Dev Containers 扩展有一个 Dev Containers: 添加 Dev Container 配置文件... 命令,允许你从列表中选择一个预定义的容器配置。如果你希望立即拥有一个完整的开发容器,而不是逐步构建 devcontainer.json 和 Dockerfile,你可以直接跳转到 自动化开发容器创建

创建 devcontainer.json 文件

VS Code 的容器配置存储在 devcontainer.json 文件中。该文件类似于用于调试配置的 launch.json 文件,但用于启动(或附加到)您的开发容器。开发容器配置位于 .devcontainer/devcontainer.json 下,或作为 .devcontainer.json 文件(注意点前缀)存储在项目的根目录中。

你可以使用一个镜像作为你的devcontainer.json的起点。镜像就像一个预装了各种工具和操作系统的小型磁盘驱动器。你可以从容器注册表中拉取镜像,容器注册表是存储镜像的仓库集合。这里是一个简单的devcontainer.json示例,它使用了一个预构建的TypeScript和Node.js VS Code开发容器镜像

{
  "image": "mcr.microsoft.com/devcontainers/typescript-node:0-18"
}

您可以更改您的配置以执行以下操作:

对于这个例子,如果你想将Code Spell Checker扩展安装到你的容器中并自动转发端口3000,你的devcontainer.json文件将如下所示:

{
  "image": "mcr.microsoft.com/devcontainers/typescript-node",

  "customizations": {
    "vscode": {
      "extensions": ["streetsidesoftware.code-spell-checker"]
    }
  },
  "forwardPorts": [3000]
}

注意: 根据基础镜像中的内容,容器中已经添加了额外的配置。例如,我们在上面添加了 streetsidesoftware.code-spell-checker 扩展,容器还将包括 "dbaeumer.vscode-eslint" 作为 mcr.microsoft.com/devcontainers/typescript-node 的一部分。在使用 devcontainer.json 进行预构建时,这会自动发生,您可以在 预构建部分 中了解更多信息。

使用上述的devcontainer.json,您的开发容器已经可以正常工作,您可以连接并开始在其中进行开发。尝试使用Dev Containers: Reopen in Container命令来体验:

快速选择Dev Containers命令列表

运行此命令后,当 VS Code 重新启动时,您现在处于一个 Node.js 和 TypeScript 开发容器中,端口 3000 已转发并且安装了 ESLint 扩展。连接后,请注意状态栏左侧的绿色远程指示器,以显示您已连接到开发容器:

VS Code 实例连接到开发容器

额外的开发容器场景

通过一个devcontainer.json文件,你可以:

如果devcontainer.json支持的工作流不符合您的需求,您也可以附加到已经运行的容器

提示: 想要使用远程Docker主机吗?请参阅在远程Docker主机上开发文章以获取设置详情。

安装额外软件

您可能希望在开发容器中安装额外的软件。一旦VS Code连接到容器,您可以打开VS Code终端并在容器内的操作系统上执行任何命令。这使您能够安装新的命令行工具并从Linux容器内部启动数据库或应用程序服务。

大多数容器镜像基于Debian或Ubuntu,其中使用aptapt-get命令来安装新软件包。您可以在Ubuntu的文档中了解更多关于该命令的信息。Alpine镜像包含一个类似的apk命令,而CentOS / RHEL / Oracle SE / Fedora镜像使用yum最近使用dnf

您想要安装的软件的文档通常会提供具体的说明,但如果您在容器中以root身份运行,则可能不需要在命令前加上sudo

例如:

# If running as root
apt-get update
apt-get install <package>

如果您以root身份运行,只要在您的容器中配置了sudo,您就可以安装软件。所有预定义的容器都已设置好sudo,但向容器添加非root用户文章可以帮助您为自己的容器设置此功能。无论如何,如果您安装并配置了sudo,您将能够在以任何用户(包括root)身份运行时使用它。

# If sudo is installed and configured
sudo apt-get update
sudo apt-get install <package>

假设你想安装Git。你可以在VS Code的集成终端中运行以下命令:

# If sudo is installed and configured
sudo apt-get update
# Install Git
sudo apt-get install git

你也可以使用devcontainer.json中的"features"属性来从预定义的Features集合中安装工具和语言,甚至是你自己的。

例如,您可以使用以下命令安装最新版本的 Azure CLI:

"features": {
    "ghcr.io/devcontainers/features/azure-cli:1": {
        "version": "latest"
    }
  }

查看Dev Container Features 规范以获取更多详细信息。

重建

当编辑.devcontainer文件夹的内容时,您需要重新构建以使更改生效。使用Dev Containers: Rebuild Container命令来更新您的容器。

然而,如果你重建容器,你将不得不重新安装你手动安装的任何东西。为了避免这个问题,你可以在devcontainer.json中使用postCreateCommand属性或自定义的Dockerfile

自定义的Dockerfile将受益于Docker的构建缓存,并且比postCreateCommand更快地重建。然而,Dockerfile在开发容器创建之前运行,并且工作区文件夹尚未挂载,因此无法访问工作区文件夹中的文件。Dockerfile最适合用于安装与工作区文件无关的软件包和工具。

postCreateCommand 操作在容器创建后运行,因此您也可以使用该属性来运行诸如 npm install 之类的命令,或者在您的源代码树中执行一个 shell 脚本(如果您已经挂载了它)。

"postCreateCommand": "bash scripts/install-dependencies.sh"

你也可以使用一个交互式的bash shell,这样你的.bashrc就会被加载,自动为你的环境定制你的shell:

"postCreateCommand": "bash -i scripts/install-dependencies.sh"

如果不使用-i将shell置于交互模式,像NVM这样的工具将无法工作:

"postCreateCommand": "bash -i -c 'nvm install --lts'"

命令需要退出,否则容器将无法启动。例如,如果您将应用程序启动添加到postCreateCommand,该命令将不会退出。

还有一个postStartCommand,每次容器启动时都会执行。参数的行为与postCreateCommand完全相同,但命令是在启动时执行,而不是在创建时执行。

与其直接在devcontainer.json中引用镜像或通过postCreateCommandpostStartCommand安装软件,更高效的做法是使用Dockerfile。

Dockerfile

Dockerfile 也将位于 .devcontainer 文件夹中。您可以将 devcontainer.json 中的 image 属性替换为 dockerfile

{
  "build": { "dockerfile": "Dockerfile" },

  "customizations": {
    "vscode": {
      "extensions": ["dbaeumer.vscode-eslint"]
    }
  },

  "forwardPorts": [3000]
}

当你进行诸如安装新软件等更改时,Dockerfile中所做的更改即使在重新构建开发容器后也会保留。

在你的Dockerfile中,使用FROM来指定镜像,并使用RUN指令来安装任何软件。你可以使用&&来将多个命令串联在一起。

FROM mcr.microsoft.com/devcontainers/javascript-node:0-18
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
    && apt-get -y install git

注意:DEBIAN_FRONTEND 导出可以避免在继续使用容器时出现警告。

自动化开发容器创建

与其手动创建.devcontainer,不如从命令面板(F1)中选择Dev Containers: Add Dev Container Configuration Files...命令,这将为您的项目添加所需的文件作为起点,您可以根据自己的需求进一步自定义这些文件。

该命令允许您根据文件夹的内容从列表中选择预定义的容器配置:

添加开发容器配置

您可以选择的预定义容器配置来自我们的第一方和社区索引,这是Dev Container 规范的一部分。我们托管了一组模板作为规范的一部分,位于devcontainers/templates 仓库中。您可以浏览该仓库的src文件夹以查看每个模板的内容。

在使用Dev Containers: Add Dev Container Configuration Files...结束时,您将看到可用功能的列表,这些功能是可以轻松添加到您的开发容器中的工具和语言。Dev Containers: Configure Container Features允许您更新现有配置。

命令面板中的开发容器功能

你也可以重复使用现有的Dockerfile:

选择 Dockerfile

现在你已经有了一个devcontainer.json和Dockerfile,让我们来看看编辑容器配置文件的通用流程。

完整配置编辑循环

编辑您的容器配置很容易。由于重建容器会将其“重置”为初始内容(除了您的本地源代码),如果您编辑容器配置文件(devcontainer.jsonDockerfiledocker-compose.yml),VS Code 不会自动重建。相反,有几个命令可以使编辑配置更容易。

以下是使用这些命令的典型编辑循环:

容器编辑循环示意图

  1. 从命令面板中的Dev Containers: 添加开发容器配置文件...开始(F1)。
  2. 根据需要编辑.devcontainer文件夹的内容。
  3. 尝试使用Dev Containers: 在容器中重新打开
  4. 如果看到错误,请在出现的对话框中选择在本地打开文件夹
  5. 窗口重新加载后,构建日志的副本将出现在控制台中,以便您可以调查问题。根据需要编辑.devcontainer文件夹的内容。(如果您关闭了日志,也可以使用Dev Containers: Show Container Log命令再次查看日志。)
  6. 运行Dev Containers: Rebuild and Reopen in Container,如果需要,跳转到步骤4。

如果您已经成功构建,您仍然可以在连接到容器时根据需要编辑.devcontainer文件夹的内容,然后在命令面板(F1)中选择Dev Containers: Rebuild Container以使更改生效。

您还可以在使用Dev Containers: Clone Repository in Container Volume命令时对容器进行迭代。

  1. 从命令面板中的Dev Containers: Clone Repository in Container Volume开始(F1)。如果您输入的仓库中没有devcontainer.json文件,系统会要求您选择一个起点。
  2. 根据需要编辑.devcontainer文件夹的内容。
  3. 尝试使用Dev Containers: Rebuild Container
  4. 如果您看到错误,请在出现的对话框中选择在恢复容器中打开
  5. 根据需要在此“恢复容器”中编辑.devcontainer文件夹的内容。
  6. 如果仍然遇到问题,请使用Dev Containers: Reopen in Container并跳转到步骤4。

使用 Docker Compose

在某些情况下,单个容器环境是不够的。假设您想在配置中添加另一个复杂的组件,比如数据库。您可以尝试直接将其添加到Dockerfile中,或者通过另一个容器添加。幸运的是,Dev Containers支持Docker Compose管理的多容器配置。

你可以选择:

  1. 使用在现有且未修改的docker-compose.yml中定义的服务。
  2. 创建一个新的docker-compose.yml(或复制现有的一个),用于开发服务。
  3. 扩展您现有的Docker Compose配置以开发服务。
  4. 使用单独的VS Code窗口同时处理多个由Docker Compose定义的服务

注意: 使用Alpine Linux容器时,由于扩展中的本地代码依赖glibc,某些扩展可能无法正常工作。

VS Code 可以配置为自动启动 Docker Compose 文件中特定服务所需的任何容器。如果您已经使用命令行启动了配置的容器,VS Code 将附加到您指定的正在运行的服务。这为您的多容器工作流程提供了与上述 Docker 镜像和 Dockerfile 工作流程相同的快速设置优势,同时仍然允许您使用命令行(如果您更喜欢)。

要快速开始,打开文件夹在VS Code中你想要工作的文件夹,并在命令面板中运行Dev Containers: Add Dev Container Configuration Files...命令(F1)。

您将被提示从我们的第一方和社区索引中选择一个预定义的容器配置,该列表根据您文件夹的内容进行排序。从VS Code界面中,您可以选择以下模板之一作为Docker Compose的起点:

  • Existing Docker Compose - 包含一组文件,您可以将这些文件放入现有项目中,这些文件将重用项目根目录中的docker-compose.yml文件。
  • Node.js & MongoDB - 一个连接到不同容器中的MongoDB数据库的Node.js容器。
  • Python & PostgreSQL - 一个连接到不同容器中的PostgreSQL的Python容器。
  • Docker-Outside-of-Docker Compose - 包含 Docker CLI,并展示了如何通过挂载 Docker Unix 套接字从开发容器内部访问本地 Docker 安装。

在你做出选择后,VS Code 将会在文件夹中添加相应的 .devcontainer/devcontainer.json(或 .devcontainer.json)文件。

你也可以手动创建你的配置。要重复使用未修改的Docker Compose文件,你可以在.devcontainer/devcontainer.json中使用dockerComposeFileservice属性。

例如:

{
  "name": "[Optional] Your project name here",
  "dockerComposeFile": "../docker-compose.yml",
  "service": "the-name-of-the-service-you-want-to-work-with-in-vscode",
  "workspaceFolder": "/default/workspace/path/in/container/to/open",
  "shutdownAction": "stopCompose"
}

查看devcontainer.json 参考以获取其他可用属性的信息,例如workspaceFoldershutdownAction

一旦你在文件夹中添加了一个.devcontainer/devcontainer.json文件,从命令面板(F1)运行Dev Containers: Reopen in Container命令(或者如果你还没有在容器中,运行Dev Containers: Open Folder in Container...)。

如果容器尚未运行,VS Code 将在此示例中调用 docker-compose -f ../docker-compose.yml upservice 属性指示 VS Code 应连接到 Docker Compose 文件中的哪个服务,而不是应启动哪个服务。如果您手动启动它们,VS Code 将附加到您指定的服务。

你也可以创建你的Docker Compose文件的开发副本。例如,如果你有.devcontainer/docker-compose.devcontainer.yml,你只需在devcontainer.json中更改以下行:

"dockerComposeFile": "docker-compose.devcontainer.yml"

然而,更好的方法通常是避免复制你的Docker Compose文件,而是通过用另一个文件扩展它。我们将在下一节中介绍扩展Docker Compose文件

为了避免在默认容器命令失败或退出时容器关闭,您可以按照以下方式修改您在devcontainer.json中指定的服务的Docker Compose文件:

# Overrides default command so things don't shut down after the process ends.
command: /bin/sh -c "while sleep 1000; do :; done"

如果尚未这样做,您可以使用Docker Compose文件中的卷列表将本地源代码"绑定"挂载到容器中。

例如:

volumes:
  # Mounts the project folder to '/workspace'. The target path inside the container
  # should match what your application expects. In this case, the compose file is
  # in a sub-folder, so you will mount '..'. You would then reference this path as the
  # 'workspaceFolder' in '.devcontainer/devcontainer.json' so VS Code starts here.
  - ..:/workspace:cached

然而,在Linux上,使用绑定挂载时,您可能需要设置并指定一个非root用户,否则您创建的任何文件都将属于root。详情请参见向您的开发容器添加非root用户。要让VS Code以不同的用户身份运行,请将此添加到devcontainer.json中:

"remoteUser": "your-user-name-here"

如果您希望所有进程以不同的用户身份运行,请将此添加到您的Docker Compose文件中的相应服务中:

user: your-user-name-here

如果您没有为开发创建自定义的Dockerfile,您可能希望在服务的容器内安装额外的开发者工具,例如curl。虽然将这些工具添加到容器镜像中效率较低,但您也可以使用postCreateCommand属性来实现此目的。

请参阅安装额外软件以获取有关安装软件的更多信息,以及devcontainer.json 参考以获取有关postCreateCommand属性的更多信息。

如果您的应用程序是使用C++、Go、Rust或其他使用基于ptrace的调试器的语言构建的,您还需要在Docker Compose文件中添加以下设置:

# Required for ptrace-based debuggers like C++, Go, and Rust
cap_add:
- SYS_PTRACE
security_opt:
- seccomp:unconfined

首次创建容器后,您需要运行Dev Containers: Rebuild Container命令,以使对devcontainer.json、您的Docker Compose文件或相关Dockerfiles的更新生效。

在 Docker Compose 中使用 localhost

您可以按照Docker的文档中描述的方式向您的docker-compose.yml文件添加其他服务。但是,如果您希望此服务中运行的任何内容在容器中的localhost上可用,或者希望本地转发该服务,请确保将此行添加到服务配置中:

# Runs the service on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
network_mode: service:db

你可以在Node.js 和 MongoDB 示例开发容器中看到一个network_mode: service:db的示例。

扩展您的Docker Compose文件以进行开发

引用现有的部署/非开发重点的docker-compose.yml有一些潜在的缺点。

例如:

  • 如果容器的入口点关闭,Docker Compose 将关闭该容器。这对于您正在调试并需要重复重启应用程序的情况来说是有问题的。
  • 您可能也没有将本地文件系统映射到容器中,或者将端口暴露给您想要访问的其他资源,如数据库。
  • 您可能希望将本地.ssh文件夹的内容复制到容器中,或者在使用Docker Compose中设置上述的ptrace选项。

您可以通过使用多个docker-compose.yml文件来扩展整个Docker Compose配置,从而解决这些及其他类似问题,这些文件可以覆盖或补充您的主要配置文件。

例如,考虑这个额外的 .devcontainer/docker-compose.extend.yml 文件:

version: '3'
services:
  your-service-name-here:
    volumes:
      # Mounts the project folder to '/workspace'. While this file is in .devcontainer,
      # mounts are relative to the first file in the list, which is a level up.
      - .:/workspace:cached

    # [Optional] Required for ptrace-based debuggers like C++, Go, and Rust
    cap_add:
      - SYS_PTRACE
    security_opt:
      - seccomp:unconfined

    # Overrides default command so things don't shut down after the process ends.
    command: /bin/sh -c "while sleep 1000; do :; done"

同一个文件可以根据需要提供额外的设置,例如端口映射。要使用它,请按特定顺序引用原始的docker-compose.yml文件以及.devcontainer/docker-compose.extend.yml

{
  "name": "[Optional] Your project name here",

  // The order of the files is important since later files override previous ones
  "dockerComposeFile": ["../docker-compose.yml", "docker-compose.extend.yml"],

  "service": "your-service-name-here",
  "workspaceFolder": "/workspace",
  "shutdownAction": "stopCompose"
}

VS Code 将在启动任何容器时自动使用这两个文件。你也可以从命令行自行启动它们,如下所示:

docker-compose -f docker-compose.yml -f .devcontainer/docker-compose.extend.yml up

虽然postCreateCommand属性允许你在容器内安装额外的工具,但在某些情况下,你可能希望为开发准备一个特定的Dockerfile。你也可以使用相同的方法来引用一个专门用于开发的自定义Dockerfile,而无需修改现有的Docker Compose文件。例如,你可以如下更新.devcontainer/docker-compose.extend.yml

version: '3'
services:
  your-service-name-here:
      # Note that the path of the Dockerfile and context is relative to the *primary*
      # docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile"
      # array). The sample below assumes your primary file is in the root of your project.
      build:
        context: .
        dockerfile: .devcontainer/Dockerfile
      volumes:
        - .:/workspace:cached
      command: /bin/sh -c "while sleep 1000; do :; done"

恭喜!您已在Visual Studio Code中配置了一个开发容器。继续阅读以了解如何在团队成员和各种项目之间共享容器配置。

将配置文件添加到仓库

你可以通过将devcontainer.json文件添加到源代码控制中,轻松地为你的项目共享一个定制的开发容器模板。通过在你的仓库中包含这些文件,任何在VS Code中打开你的仓库本地副本的人,只要他们安装了开发容器扩展,就会自动提示在容器中重新打开文件夹。

开发容器配置文件重新打开通知

除了让你的团队使用一致的环境和工具链的优势之外,这也使得新的贡献者或团队成员能够更快地提高生产力。首次贡献者将需要更少的指导,并减少与环境设置相关的问题。

添加一个在开发容器中打开的徽章

您还可以在您的仓库中添加徽章或链接,以便用户可以轻松地在Dev Containers中打开您的项目。如果需要,它将安装Dev Containers扩展,将仓库克隆到容器中,并启动开发容器。

例如,一个用于打开 https://github.com/microsoft/vscode-remote-try-java 的徽章看起来像这样:

[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode-remote-try-java)

你也可以直接包含一个open in dev container链接:

If you already have VS Code and Docker installed, you can click the badge above or [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode-remote-try-java) to get started. Clicking these links will cause VS Code to automatically install the Dev Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use.

替代方案:仓库配置文件夹

在某些情况下,您可能希望为您不控制的存储库或您不希望存储库本身包含配置的存储库创建配置。为了处理这种情况,您可以在本地文件系统上配置一个位置来存储配置文件,这些文件将根据存储库自动被拾取。

首先,更新Dev > Containers: Repository Configuration Paths 用户设置,使用您想要用于存储仓库容器配置文件的本地文件夹。

在设置编辑器中,您可以搜索 'dev containers repo' 来找到该设置:

仓库容器文件夹设置

接下来,将您的.devcontainer/devcontainer.json(及相关文件)放置在与远程仓库位置镜像的子文件夹中。例如,如果您想为github.com/devcontainers/templates创建配置,您将创建以下文件夹结构:

📁 github.com
    📁 devcontainers
        📁 templates
           📁 .devcontainer

一旦配置到位,使用任何Dev Containers命令时,配置将自动被应用。进入容器后,您还可以从命令面板(F1)中选择Dev Containers: 打开容器配置文件来打开相关的devcontainer.json文件并进行进一步的编辑。

用于查找配置的路径是从git remote -v的输出中派生的。如果您尝试在容器中重新打开文件夹时未找到配置,请检查命令面板(F1)中的日志Dev Containers: 显示容器日志以查看已检查的路径列表。

下一步