使用 Docker Compose
Docker Compose 提供了一种编排多个协同工作容器的方法。例如,处理请求的服务和前端网站,或者使用 Redis 缓存等辅助功能的服务。如果您在应用开发中使用微服务模型,则可以使用 Docker Compose 将应用代码分解为多个独立运行并通过 Web 请求进行通信的服务。本文旨在帮助您为应用(无论是 Node.js、Python 还是 .NET)启用 Docker Compose,并帮助您在 Visual Studio Code 中为这些场景配置调试。
此外,对于单容器场景,使用 Docker Compose 可以提供独立于工具的配置,而这是单个 Dockerfile 所无法做到的。诸如容器的卷挂载、端口映射和环境变量等配置设置,都可以在 docker-compose YML 文件中声明。
要在 VS Code 中通过 Container Tools 扩展使用 Docker Compose,您应该已经熟悉 Docker Compose 的基础知识。
为项目添加 Docker Compose 支持
如果您已经拥有一个或多个 Dockerfile,可以通过打开命令面板(⇧⌘P(Windows, Linux Ctrl+Shift+P)),并使用 Containers: Add Docker Compose Files to Workspace 命令来添加 Docker Compose 文件。请按照提示操作。
您可以在添加 Dockerfile 的同时,通过打开命令面板(⇧⌘P(Windows, Linux Ctrl+Shift+P))并使用 Containers: Add Docker Files to Workspace 命令,向工作区添加 Docker Compose 文件。系统会询问您是否要添加 Docker Compose 文件。如果您想保留现有的 Dockerfile,在提示覆盖 Dockerfile 时请选择否。
Container Tools 扩展会将 docker-compose.yml 文件添加到您的工作区。该文件包含按生产环境预期启动容器的配置。在某些情况下,还会生成一个 docker-compose.debug.yml 文件。该文件提供了一种简化的启动模式,用于启用调试器。

VS Code Container Tools 扩展生成的开箱即用文件,您也可以根据自身场景进行自定义以实现优化。然后,您可以使用 Containers: Compose Up 命令(右键点击 docker-compose.yml 文件,或在命令面板中查找该命令)一次性启动所有服务。您也可以在 VS Code 的命令提示符或终端窗口中使用 docker-compose up 命令来启动容器。有关如何配置 Docker Compose 行为及可用命令行选项的详细信息,请参考 Docker Compose 文档。
使用 docker-compose 文件,现在可以直接在其中指定端口映射,而无需在 .json 配置文件中指定。有关示例,请参阅 Docker Compose 文档。
提示:使用 Docker Compose 时,请勿指定主机端口。相反,应让 Docker 随机选择一个可用端口,以自动避免端口冲突问题。
向项目中添加新容器
如果您想添加另一个应用或服务,可以再次运行 Containers: Add Docker Compose Files to Workspace,并选择覆盖现有的 docker-compose 文件;但请注意,这会导致这些文件中的自定义内容丢失。如果您想保留对 compose 文件的更改,可以手动修改 docker-compose.yml 文件以添加新服务。通常,您可以复制现有的服务部分,粘贴以创建新条目,并根据新服务相应地更改名称。
您可以再次运行 Containers: Add Docker Files to Workspace 命令,为新应用生成 Dockerfile。虽然每个应用或服务都有自己的 Dockerfile,但通常每个工作区只有一个 docker-compose.yml 和一个 docker-compose.debug.yml 文件。
在 Python 项目中,Dockerfile、.dockerignore 和 docker-compose*.yml 文件都位于工作区的根文件夹中。当您添加另一个应用或服务时,请将 Dockerfile 移动到该应用的文件夹中。
在 Node.js 项目中,Dockerfile 和 .dockerignore 文件将位于该服务的 package.json 旁边。
.NET 项目在创建 Docker Compose 文件时,文件夹结构已经配置为处理多个项目,.dockerignore 和 docker-compose*.yml 会被放置在工作区根目录中(例如,如果项目位于 src/project1,则文件位于 src 中)。因此,当您添加另一个服务时,可以在另一个文件夹(例如 project2)中创建项目,并按照前面所述重新创建或修改 docker-compose 文件。
调试
首先,请参阅目标平台的调试文档,以了解在 VS Code 中进行容器化调试的基础知识。
如果您想在 Docker Compose 中进行调试,请使用上一节所述的两个 Docker Compose 文件之一运行 Containers: Compose Up 命令,然后使用适当的 Attach(附加)启动配置进行附加。直接使用普通启动配置进行启动不会使用 Docker Compose。
创建一个 Attach 启动配置。这是 launch.json 中的一个部分。该过程主要是手动的,但在某些情况下,Container Tools 扩展可以通过添加预配置的启动配置来提供帮助,您可以将其用作模板并进行自定义。以下各节描述了针对每个平台(Node.js、Python 和 .NET)的过程。
Node.js
-
在调试选项卡上,选择配置下拉菜单,选择添加配置...,然后选择
Containers: Attach配置模板 Containers: Attach to Node。 -
在
docker-compose.debug.yml中配置调试端口。这是在创建文件时设置的,因此您可能不需要更改它。在下面的示例中,端口 9229 用于主机和容器上的调试。version: '3.4' services: node-hello: image: node-hello build: . environment: NODE_ENV: development ports: - 3000 - 9229:9229 command: node --inspect=0.0.0.0:9229 ./bin/www -
如果您有多个应用,则需要为其中一些应用更改端口,以便每个应用都有一个唯一的端口。您可以在
launch.json中指向正确的调试端口并保存文件。如果省略此步骤,端口将自动选择。以下是 Node.js 启动配置(附加)的示例:
"configurations": [ { "type": "node", "request": "attach", "name": "Containers: Attach to Node", "remoteRoot": "/usr/src/app", "port": 9229 // Optional; otherwise inferred from the docker-compose.debug.yml. }, // ... ] -
编辑完 Attach 配置后,保存
launch.json,并将您的新启动配置选为活动配置。在调试选项卡中,可以在配置下拉菜单中找到新配置。
-
右键点击
docker-compose.debug.yml文件并选择 Compose Up。 -
当您附加到一个公开 HTTP 端点并返回 HTML 的服务时,Web 浏览器不会自动打开。要在浏览器中打开应用,请在侧边栏中选择该容器,右键点击并选择 Open in Browser。如果配置了多个端口,系统会要求您选择端口。
-
以常规方式启动调试器。在调试选项卡中,选择绿色箭头(开始按钮)或使用 F5。
Python
对于使用 Docker Compose 进行 Python 调试,请遵循以下步骤:
-
在调试选项卡上,选择配置下拉菜单,选择添加配置...,选择 Python Debugger,并选择
Remote Attach配置模板。
-
系统会提示您选择用于调试的主机(例如 localhost)和端口。Python 的默认调试端口是 5678。如果您有多个应用,则需要为其中一个更改端口,以便每个应用都有一个唯一的端口。您可以在
launch.json中指向正确的调试端口并保存文件。如果省略此步骤,端口将自动选择。"configurations": [ { "name": "Python Debugger: Remote Attach", "type": "debugpy", "request": "attach", "port": 5678, "host": "localhost", "pathMappings": [ { "localRoot": "${workspaceFolder}", "remoteRoot": "/app" } ] } -
编辑完 Attach 配置后,保存
launch.json。导航到调试选项卡,并将 Python Debugger: Remote Attach 选为活动配置。 -
如果您已经有一个有效的 Dockerfile,建议运行 Containers: Add Docker Compose Files to Workspace 命令。这将创建一个
docker-compose.yml文件以及一个docker-compose.debug.yml文件,后者会对 Python 调试器进行卷映射并在容器中启动它。如果您还没有 Dockerfile,建议运行 Containers: Add Docker Files to Workspace 并选择是以包含 Docker Compose 文件。注意:默认情况下,使用 Containers: Add Docker Files to Workspace 时,选择 Django 和 Flask 选项将搭建一个为 Gunicorn 配置的 Dockerfile。在继续之前,请按照 Python 容器化快速入门中的说明确保其配置正确。
-
右键点击
docker-compose.debug.yml文件(如下例所示)并选择 Compose Up。version: '3.4' services: pythonsamplevscodedjangotutorial: image: pythonsamplevscodedjangotutorial build: context: . dockerfile: ./Dockerfile command: ["sh", "-c", "pip install debugpy -t /tmp && python /tmp/debugpy --wait-for-client --listen 0.0.0.0:5678 manage.py runserver 0.0.0.0:8000 --nothreading --noreload"] ports: - 8000:8000 - 5678:5678 -
容器构建并运行后,通过在选中 Python Debugger: Remote Attach 启动配置的情况下点击 F5 来附加调试器。

注意:如果您想将 Python 调试器导入到特定文件中,更多信息可以在 debugpy README 中找到。
-
当您附加到一个公开 HTTP 端点并返回 HTML 的服务时,Web 浏览器可能不会自动打开。要在浏览器中打开应用,请右键点击 Container Explorer 中的容器,并选择 Open in Browser。如果配置了多个端口,系统会要求您选择端口。

现在,您正在容器中调试运行中的应用程序。
.NET
-
在调试选项卡上,选择配置下拉菜单,选择添加配置...,并选择
Container Attach配置模板 Containers: .NET Attach (Preview)。 -
VS Code 会尝试使用默认路径将
vsdbg从主机复制到目标容器。您也可以在 Attach 配置中为现有vsdbg实例提供路径。"netCore": { "debuggerPath": "/remote_debugger/vsdbg" } -
编辑完 Attach 配置后,保存
launch.json,并将您的新启动配置选为活动配置。在调试选项卡中,可以在配置下拉菜单中找到新配置。 -
右键点击
docker-compose.debug.yml文件并选择 Compose Up。 -
当您附加到一个公开 HTTP 端点并返回 HTML 的服务时,Web 浏览器不会自动打开。要在浏览器中打开应用,请在侧边栏中选择该容器,右键点击并选择 Open in Browser。如果配置了多个端口,系统会要求您选择端口。
-
以常规方式启动调试器。在调试选项卡中,选择绿色箭头(开始按钮)或使用 F5。

-
如果您尝试附加到容器中运行的 .NET 应用,系统会提示您选择应用的容器。

要跳过此步骤,请在 launch.json 的 Attach 配置中指定容器名称。
"containerName": "Your ContainerName"接下来,系统会询问您是否要将调试器 (
vsdbg) 复制到容器中。请选择是。
如果配置正确,调试器应该已附加到您的 .NET 应用。

卷挂载 (Volume mounts)
默认情况下,Container Tools 扩展不会为调试组件执行任何卷挂载。在 .NET 或 Node.js 中不需要这样做,因为所需的组件已内置于运行时中。如果您的应用需要卷挂载,请通过使用 docker-compose*.yml 文件中的 volumes 标签来指定。
volumes:
- /host-folder-path:/container-folder-path
多 Docker Compose 文件配置
工作区可以包含多个 docker-compose 文件,以处理开发、测试和生产等不同环境。配置内容可以拆分到多个文件中。例如,一个定义所有环境通用信息的基础 compose 文件,以及定义特定环境信息的单独覆盖文件。当这些文件作为输入传递给 docker-compose 命令时,它会将这些文件合并为一个配置。默认情况下,Containers: Compose Up 命令将单个文件作为输入传递给 compose 命令,但您可以通过命令自定义来自定义 compose up 命令以传入多个文件。或者,您也可以使用自定义任务来使用所需的参数调用 docker-compose 命令。
注意:如果您的工作区有
docker-compose.yml和docker-compose.override.yml且没有其他 compose 文件,则在调用docker-compose命令时不传入输入文件,它会自动隐式使用这些文件。在这种情况下,无需进行自定义。
命令自定义
命令自定义提供了多种根据需求自定义 compose up 命令的方法。以下是 compose up 命令的一些自定义示例。
基础文件与覆盖文件
假设您的工作区有一个基础 compose 文件 (docker-compose.yml) 和每个环境的覆盖文件 (docker-compose.dev.yml, docker-compose.test.yml 和 docker-compose.prod.yml),并且您始终使用基础文件和一个覆盖文件来运行 docker compose up。在这种情况下,可以按以下示例自定义 compose up 命令。当调用 compose up 命令时,${configurationFile} 将被替换为所选文件。
"docker.commands.composeUp": [
{
"label": "override",
"template": "docker-compose -f docker-compose.yml ${configurationFile} up -d --build",
}
]
模板匹配
假设您为每个环境拥有一组不同的输入文件。您可以定义多个带有正则表达式匹配的模板,所选文件名将与此 match 属性进行匹配,并使用相应的模板。
"containers.commands.composeUp": [
{
"label": "dev-match",
"template": "docker-compose -f docker-compose.yml -f docker-compose.debug.yml -f docker-compose.dev.yml up -d --build",
"match": "dev"
},
{
"label": "test-match",
"template": "docker-compose -f docker-compose.yml -f docker-compose.debug.yml -f docker-compose.test.yml up -d --build",
"match": "test"
},
{
"label": "prod-match",
"template": "docker-compose -f docker-compose.yml -f docker-compose.release.yml -f docker-compose.prod.yml up -d --build",
"match": "prod"
}
]
在调用命令时选择模板
如果您在命令模板中省略了 match 属性,则每次调用 compose up 命令时都会询问使用哪个模板。例如:
"containers.commands.composeUp": [
{
"label": "dev",
"template": "docker-compose -f docker-compose.yml -f docker-compose.common.dev.yml ${configurationFile} up -d --build"
},
{
"label": "test",
"template": "docker-compose -f docker-compose.yml -f docker-compose.common.test.yml ${configurationFile} up -d --build"
},
{
"label": "prod",
"template": "docker-compose -f docker-compose.yml -f docker-compose.common.prod.yml ${configurationFile} up -d --build"
},
],
自定义任务
您可以定义一个如下所示的任务来调用 docker-compose 命令,而不是使用命令自定义。有关此选项的更多详细信息,请参阅自定义任务。
{
"type": "shell",
"label": "compose-up-dev",
"command": "docker-compose -f docker-compose.yml -f docker-compose.Common.yml -f docker-compose.dev.yml up -d --build",
"presentation": {
"reveal": "always",
"panel": "new"
}
}