尝试以扩展 VS Code 中的代理模式!

创建开发容器

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

创建开发容器的路径

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

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

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

注意:开发容器扩展有一个“开发容器:添加开发容器配置文件...”命令,允许你从列表中选择一个预定义的容器配置。如果你更喜欢立即拥有一个完整的开发容器,而不是逐步构建 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,你的开发容器就功能正常了,你可以连接到它并在其中开始开发。使用“开发容器:在容器中重新打开”命令进行尝试

Quick pick with list of Dev Containers commands

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

VS Code instance connected to dev container

其他开发容器场景

通过 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" 属性,从一组预定义的功能甚至你自己的功能中安装工具和语言。

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

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

有关详细信息,请参阅开发容器功能规范

重建

编辑 .devcontainer 文件夹内容时,需要重建才能使更改生效。使用“开发容器:重建容器”命令来更新你的容器。

但是,如果你重建容器,你将不得不重新安装所有手动安装的东西。为了避免这个问题,你可以使用 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"

像 NVM 这样的工具在不使用 -i 将 shell 置于交互模式的情况下将无法工作

"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)中选择“开发容器:添加开发容器配置文件...”命令将把所需文件添加到你的项目作为起点,你可以根据需要进一步自定义。

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

Add a dev container config

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

使用“开发容器:添加开发容器配置文件...”后,你将看到可用功能列表,这些功能是你可以轻松添加到开发容器中的工具和语言。“开发容器:配置容器功能”允许你更新现有配置。

Dev container features in Command Palette

你也可以重用现有 Dockerfile

Select Dockerfile

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

完整配置编辑循环

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

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

Container edit loop illustration

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

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

使用“开发容器:在容器卷中克隆仓库”命令时,你也可以迭代你的容器。

  1. 从命令面板(F1)中的“开发容器:在容器卷中克隆仓库”开始。如果你输入的仓库中没有 devcontainer.json,系统将要求你选择一个起点。
  2. 根据需要编辑 .devcontainer 文件夹的内容。
  3. 使用“开发容器:重建容器”进行尝试。
  4. 如果出现错误,请在出现的对话框中选择“在恢复容器中打开”。
  5. 在此“恢复容器”中根据需要编辑 .devcontainer 文件夹的内容。
  6. 使用“开发容器:在容器中重新打开”,如果仍然遇到问题则跳到第 4 步。

使用 Docker Compose

在某些情况下,单个容器环境是不够的。例如,你可能希望向配置中添加另一个复杂组件,例如数据库。你可以尝试直接将其添加到 Dockerfile,或者通过附加容器添加它。幸运的是,开发容器支持 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 中打开你想要工作的文件夹,并从命令面板(F1)运行“开发容器:添加开发容器配置文件...”命令。

系统将提示你从我们官方和社区索引中,选择一个预定义的容器配置,该配置在可筛选列表中根据你的文件夹内容排序。从 VS Code UI 中,你可以选择以下模板之一作为 Docker Compose 的起点

  • 现有 Docker Compose - 包含一组文件,你可以将其放入现有项目中,以重用项目根目录中的 docker-compose.yml 文件。
  • Node.js 和 MongoDB - 一个 Node.js 容器,连接到另一个容器中的 MongoDB 数据库。
  • Python 和 PostgreSQL - 一个 Python 容器,连接到另一个容器中的 PostgreSQL。
  • 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"
}

有关 workspaceFoldershutdownAction 等其他可用属性的信息,请参阅 devcontainer.json 参考

.devcontainer/devcontainer.json 文件添加到文件夹后,从命令面板(F1)运行“开发容器:在容器中重新打开”命令(如果你尚未在容器中,则运行“开发容器:在容器中打开文件夹...”)。

如果容器尚未运行,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 属性用于此目的。

有关安装软件的更多信息,请参阅安装其他软件;有关 postCreateCommand 属性的更多信息,请参阅devcontainer.json 参考

如果你的应用程序是使用 C++、Go 或 Rust,或另一种使用基于 ptrace 调试器的语言构建的,你还需要将以下设置添加到你的 Docker Compose 文件中

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

首次创建容器后,你需要运行“开发容器:重建容器”命令,以便 devcontainer.json、你的 Docker Compose 文件或相关 Dockerfile 的更新生效。

在 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"

同一个文件可以根据需要提供其他设置,例如端口映射。要使用它,除了 .devcontainer/docker-compose.extend.yml 之外,还需要以特定顺序引用你的原始 docker-compose.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 container configuration file reopen notification

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

添加“在开发容器中打开”徽章

你也可以在你的仓库中添加一个徽章或链接,以便用户可以轻松地在开发容器中打开你的项目。它将在必要时安装开发容器扩展,将仓库克隆到容器中,并启动开发容器。

例如,打开 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 repo”来找到该设置

Repository container folders setting

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

📁 github.com
    📁 devcontainers
        📁 templates
           📁 .devcontainer

配置到位后,使用任何开发容器命令时都将自动获取该配置。进入容器后,你还可以从命令面板(F1)中选择“开发容器:打开容器配置文件”以打开相关的 devcontainer.json 文件并进行进一步编辑。

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

后续步骤