如何配置 Docker + VS Code 开发环境

2025-06-08

如何配置 Docker + VS Code 开发环境

巴西葡萄牙语的用法:

在一些项目上使用 Docker 一段时间后,我找到了一种对我和我的同事都很有效的配置,所以我决定分享它,以便它对更多人有用。

请记住,本文基于我的个人经验,可能未必符合最佳实践或不适用于您的情况。欢迎提出改进建议!

我将向您展示如何从头开始设置一个完整的项目并在容器内完全隔离地运行它,因此无论您的主机操作系统如何,环境(希望)都可以重现并以相同的方式运行。

我日常使用 Ubuntu,所以本指南将从这个角度出发,但你应该能够轻松地将其适配到你选择的操作系统。在使用 WSL 2 的 Windows 中,它应该也能很好地运行。

目录

项目

为了说明所有步骤并尽可能真实,我们将使用以下堆栈创建一个基本的 Web 应用程序:

最后,我们将拥有一个一致的代码编辑环境:

  • 代码补全
  • 代码检查
  • 调试
  • 为项目中的所有工作人员提供明确定义的编辑器设置

堆栈选择基于我更熟悉的内容,但本指南中的所有概念都应适用于您选择的任何堆栈。

要求

我们需要在本地安装很少的东西:

这些代码扩展:

开始

为了方便您理解,我在 GitHub 上创建了一个代码仓库,其中包含所有代码,地址是https://github.com/davidrios/example-docker-project。我会列出具体的提交,以便您了解整个过程的结构。

这个项目是从零开始的,所以首先需要理清它的结构。我喜欢用下面这个结构:

  • 项目根目录
    • 一些 docker 的东西和 README
    • 会议
      • 项目 1
      • 项目 2
      • 杂项
      • ...
    • 代码
      • 项目 1
      • 项目 2
      • ...

接下来我们需要运行一些基本的容器来启动项目。根据项目描述,我们已经知道至少需要 3 个容器:后端、前端和数据库,因此我们需要创建一个基础容器docker-compose.yml。但在此之前,最有用的事情之一是创建一个.env文件,这样你就可以对容器进行一定程度的自定义。

让我们创建初始.env.template文件,每个用户将制作一个本地副本以进行自定义:

LOCAL_USER_ID=1000
POSTGRES_PASSWORD=password
Enter fullscreen mode Exit fullscreen mode

用于LOCAL_USER_ID解决 Linux 上的 Docker 权限问题(稍后会详细介绍)。您可以使用命令返回的本地计算机用户的 uid 来设置它id -u。如果您使用的是 Mac,则可以忽略它。

创建本地.env副本后,是时候定义基本内容了docker-compose.yml
https://github.com/davidrios/example-docker-project/blob/2aac6eb151104b205461d025ca07647c44bc5d36/docker-compose.yml

注意事项:

  • .env在第 3-5 行导入了该文件,并将所有定义的变量作为环境变量传递给所有其他容器。你可以在第 10-11 行、第 19-20 行等等处看到这一点。
  • 按照最佳实践,我使用的是 Alpine 镜像,并指定了主版本和次版本。您可能想要或需要使用基于 Ubuntu/Debian 等的镜像,但您应该始终选择特定的主版本/次版本。
  • 有一个为 PostgreSQL 数据定义的卷,因此该卷在运行之间得以保留。
  • 我以标准(非 root)用户身份在容器中运行所有项目,并且我喜欢在每个我预计登录并执行命令的容器上为该用户的主目录创建卷,这样我就可以在重建之间保留 shell 历史记录和其他内容。这是可选的,个人偏好设置,您可以直接删除这些卷。请注意,虽然使用这些卷很方便,但它们可能会对环境的整体可重复性产生细微的影响。
  • 对于后端和前端,由于它们尚未配置任何应用程序,但无论如何我都需要它们运行才能引导应用程序,所以我只是告诉它们运行一个很长的sleep

此时我们可以导航到主项目存储库并运行:

$ docker-compose up --build
Enter fullscreen mode Exit fullscreen mode

您应该会看到来自 PostgreSQL 的一些有关数据库初始化的消息,并且三个容器应该继续运行。

后端应用程序

我喜欢以普通用户身份执行应用程序,为了避免 Linux 上的权限问题,该用户需要与我的本地计算机用户具有相同的 uid,这就是文件LOCAL_USER_ID中包含 的原因.env。为此,我们将添加一些在容器内使用的辅助脚本:

  • base.sh
  • run.sh
  • enter.sh

按照之前的决定,我们将把它们添加到一个目录中conf/backend/scripts。这些脚本只会在开发环境中执行,因此它们不会成为镜像的一部分。

我们还将Dockerfile为容器创建一个包含一些初始自定义配置的目录。Compose 文件将使用新镜像进行更新,并挂载代码和脚本。此外,code还将创建目录并再次执行 docker-compose:

检查项目如何变化:

https://github.com/davidrios/example-docker-project/commit/04ae1e95ca8dfe752e76a67fce5b9882847f2f8e

## *** stop docker-compose ***
$ mkdir code
$ docker-compose up --build
Enter fullscreen mode Exit fullscreen mode

现在容器已经运行,可以创建 Django 项目了。我们使用enter.sh脚本进入venv已激活并以正确用户身份登录的容器。现在,让我们创建项目:

## in another terminal at the root of the project folder:

## Create the database:
$ docker-compose exec -u postgres postgres-db createdb -T template0 -E utf8 backend

## Enter the backend container:
$ docker-compose exec backend /scripts/enter.sh

## Now inside the container:
$ cd /code
$ pip install --no-binary psycopg2 django psycopg2
$ django-admin startproject backend
$ cd backend

## Save the requirements:
$ pip freeze > requirements.txt

## *** Edit backend/settings.py to connect to the PostgreSQL db using the password from the environment and the host/dbname/user we already defined ***

$ python manage.py migrate
$ python manage.py createsuperuser
## *** insert desired login information ***

## Test the application runs:
$ python manage.py runserver

## *** Everything seems ok, stop the server and exit the container ***
Enter fullscreen mode Exit fullscreen mode

检查添加的文件,特别注意数据库配置:

https://github.com/davidrios/example-docker-project/commit/8f47c701e6a3984a32021da360ae0c49a2d68a95#diff-192a1d9e9543969133c5449ace7b1169de815b39d539bc55fc1d168f32eedb7bR76-R85

现在我们需要将应用程序设置为随容器自动运行,并公开端口,以便我们可以使用本地浏览器访问。应用程序包的安装也将添加到镜像中。

为了能够在我们选择的任何端口上本地运行并且不与其他正在运行的东西冲突,我们将该选项添加到.env.template文件中,并将同一行添加到我们的.env

APP_PORT=8000
Enter fullscreen mode Exit fullscreen mode

并更新docker相关内容。检查差异:

https://github.com/davidrios/example-docker-project/commit/c9276155e56f1f8c0168bb81902e5e0f22ed0dad

现在停止 docker-compose 并再次运行它,始终使用--build。应用程序现在应该正在运行,并且可以在http://localhost:8000 (或其他端口,如果你更改了) 上访问。

后端目前就到这里。VS Code 配置说明将在本指南的后面部分介绍。

前端应用程序

这个和后端非常相似。我们将创建相同的三个辅助脚本,并针对新环境进行一些特定的更改,并对镜像进行一些自定义。

这次我们还会添加一个.dockerignore文件,这样 Docker 每次构建镜像时就不用复制无用的东西了。如果你已经有一个大项目,这将大大加快构建速度。

看一下变化:

https://github.com/davidrios/example-docker-project/commit/bbb2c6180437c8e1ae5b3b0ca2121e78dc1250af

## *** We stop docker-compose and start it again ***
$ docker-compose up --build
Enter fullscreen mode Exit fullscreen mode

我们以相同的方式引导 Quasar 项目:

$ docker-compose exec frontend /scripts/enter.sh
$ yarn global add @quasar/cli
$ cd /code
$ ~/.yarn/bin/quasar create frontend
## *** choose quasar options ***
## Pick at least ESLint because it's used later.
## I'll also pick TypeScript to demonstrate the great VS Code support later
## And finally choose an ESLint preset you wanna use on your project. I'll stick with standard here
## Let Quasar install the packages with yarn and get out of the container
Enter fullscreen mode Exit fullscreen mode

现在我们将配置前端容器以自动以开发模式启动 Quasar 并导出端口以便我们可以访问它。

最终的改变如下:

https://github.com/davidrios/example-docker-project/commit/73e3b55df164ea2e1477cb51a8d591299c5f4643

请注意,我们刚刚将导出端口从后端移动到前端容器,我们稍后会修复它。

## *** We stop docker-compose and start it again ***
$ docker-compose up --build
Enter fullscreen mode Exit fullscreen mode

Quasar 应用程序现在应该正在运行并可通过http://localhost:8000(或其他端口,如果您更改了它)访问。

一个简单的 nginx 代理

为了让一切更轻松,我们将在一切之前创建一个简单但灵活的 nginx 代理。我们需要使用自定义配置文件,为了获得最大的灵活性,我们将在启动服务器之前处理配置并与环境变量进行插值。

这些是导致的变化:

https://github.com/davidrios/example-docker-project/commit/b07c83199fd8d4b1cd4dfce1e830a19e76b64543

我们现在可以通过http://localhost:8000访问前端应用程序,通过http://localhost:8000/admin 访问Django 管理员,并且 nginx 也会将所有请求代理/api*到后端应用程序。

VS Code 致胜

现在,我们已经将整个项目组织得井井有条,并且与本地计算机隔离运行,接下来需要开始编写代码了。如果我们在编写代码时也能拥有同样一致且愉悦的体验,那岂不是很棒?这时,VS Code 就能派上用场了!

我们将使用远程容器扩展直接在应用程序容器内运行 VS Code,这样它就可以在运行时访问环境,并且我们可以安装扩展而不影响我们的本地安装。

我们将使用一个名为 的目录code/vscode来存放相关内容,这样由于/code挂载,这些内容在容器内就已经可用了。您也可以将其放在其他目录中,但这样就需要为每个容器设置不同的挂载。

配置后端

首先创建目录code/vscode/backend并添加以下文件:

  • backend.code-workspace:用项目的真实名称替换后端,以便在导航时更容易区分代码窗口。
  • flake8.ini:我们在这里放置 Python 的 linter 设置。我们将使用 flake8 而不是 pylint,因为它速度更快,而且我更喜欢它 :)。
  • .devcontainer.json:告诉代码如何设置容器实例

还需要编辑容器base.sh脚本,以便它在每次重建后安装必要的软件包,并以调试模式启动 Python。此外,我们还将使用Watchdog将服务器配置为在任何文件更改时自动重新加载,因为 Python 的代码调试器扩展不支持 Django 的自动重新加载功能。此外,watchdog 比 Django 默认的自动重新加载功能占用更少的 CPU 资源。

我已经添加了我们的第一个测试 API。

一探究竟:

https://github.com/davidrios/example-docker-project/commit/33cb5d775b88d8b953ba690c17e1359dd5fa34a5

现在我们要做的就是停止 Compose 并再次运行它,一切都应该运行并准备就绪。要开始使用代码,我们打开一个新的代码窗口 ( Ctrl+Shift+N),然后转到File > Open Folder并打开<PROJECT>/code/vscode/backend目录。立即会出现一个通知,只需单击Reopen in Container按钮即可。您还可以打开命令面板 ( Ctrl+Shift+P) 并输入reopen in container并选择Remote-Containers: Reopen in container。然后,它会显示一条通知,提示正在安装代码服务器,片刻之后它会打开项目,状态栏上会显示您已进入容器内部,如下所示:

然后你将拥有所有编辑器的好东西,就像这样:

要进行调试,只需单击边线即可添加断点,或F9在所需的行处按下,如下所示:

然后转到Run侧面的视图,点击绿色的“播放”按钮。状态栏应该会变成橙色,表示调试器已连接。如果你像我这里一样设置了断点,并在浏览器中导航到新的 API,你会看到精彩的调试效果,如下所示:

每次更改某些文件时,应用程序也会重新加载,如下所示:

如果你需要运行任何 Django 管理命令,可以使用集成终端轻松完成。只需打开菜单Terminal > New Terminal( Ctrl+Shift+<BACKTICK>),如下所示:

如果您发现终端没有激活 venv,只需单击trash图标将其杀死并再次打开。

前端配置

这里的配置与上面的类似。首先创建目录code/vscode/frontend并添加文件.devcontainer.json。我没有在这里配置项目文件,因为除了前端项目之外没有其他文件夹,并且还要使用Quasar在初始化项目时已经创建的设置文件。您可以像我们对后端所做的那样,将所有内容移动到项目文件中。

结果如下:

https://github.com/davidrios/example-docker-project/commit/644b7547322d903f7e4b33454b787e5d1d4c6f49

您甚至不需要重新运行 docker-compose,只需打开一个新的代码窗口,打开目录code/vscode/frontend并点击Reload in Container按钮即可。打开项目后,点击ESLint状态栏上的按钮,然后allow在出现对话框时点击。这样,您将拥有一个完整配置的编辑器,如下所示:

您甚至可以自动完成 Quasar 组件等内容!

如果您想使用 quasar-cli 来执行诸如创建组件之类的操作,只需单击菜单Terminal > New TerminalCtrl+Shift+<BACKTICK>)打开内置终端,如下所示:

现在是时候进行调试了!

为此,我们launch.json为 VS Code 创建了一个调试任务。由于以下原因,这并不那么简单:

  • VS Code 有两个 Chrome 调试器,一个是自带的新版本,另一个是可以从应用市场安装的旧版本。问题是,新版本更好用,但出于某种原因,我怀疑是远程容器的问题,它无法启动 Chrome 实例。旧版本可以启动实例,但性能不太好。所以,为了解决这个问题,我们可以两个都用!
  • 由于我们在文件中定义了一个自定义端口.env,我们需要某种方式将其传递给调试任务。遗憾的是,调试扩展程序运行在本地编辑器上,而不是远程编辑器上,因此它们无法访问环境变量。为了解决这个问题,我在运行任务时添加了一个小提示,询问应用程序在哪个端口上运行,为了方便起见,我使用了环境模板中的默认端口。

添加的任务文件:

https://github.com/davidrios/example-docker-project/blob/6e5195fd13d2aea79b1a31c265ddf60808d9a77e/code/frontend/.vscode/launch.json

我们有三个任务,您可以根据自己的浏览器使用它们:

我们还可以通过另一种方式来改善调试体验。默认的 webpack 配置不会生成详细的源码映射,这导致调试过程非常繁琐,而且几乎无法在转译后的文件中设置断点。我们通过devtool: 'eval-source-map'在默认设置中添加以下内容来解决这个问题。

即便如此,对于像.vue文件这样的转换文件,它会为每个中间结果生成具有不同哈希名称的同一文件的许多变体,这有点烦人,所以我们也通过定制output.devtoolModuleFilenameTemplate函数来解决这个问题。

由于该项目在 Quasar 中,我们按照预期的方式进行,通过编辑quasar.conf.js

最终看起来是这样的:

https://github.com/davidrios/example-docker-project/commit/1208e30bd280179df0780add22430af3a5​​4c6c30

现在您可以通过进入Run视图、选择一个任务并单击“播放”来运行调试器:

尝试添加一些console.log和/或设置断点,运行调试模式并使用该应用程序,然后您应该会看到类似这样的内容:

附录

我不太喜欢在代码编辑器里做太多事情,尤其是在终端里可以更灵活地完成的事情,这就是为什么我不使用 Code Docker 扩展来管理/运行 docker-compose 之类的程序。我还认为,当你的项目包含多个容器,每个容器都包含不同的应用程序时,这种方式会更好,就像本指南中那样。

由于我们手动管理撰写流程,有时 VS Code 会向您显示此通知:

我建议你直接点击Ignore。如果需要重建,只需停止终端上运行的 Compose,然后docker-compose up --build重新执行即可。

结论

有很多地方需要改进,你可以对各个部分做出不同的选择,这些是我做的,对我来说效果最好的。

请注意,整个配置更适合个人和小团队,对于较大的团队还有其他通常互相排斥的要求。

这一切都非常注重开发,因此容器映像不是可用于部署的真实映像,但只需稍加努力即可实现。

感谢阅读!

鏂囩珷鏉ユ簮锛�https://dev.to/davidrios/how-to-configure-a-docker-vs-code-development-environment-3jko
PREV
如何通过遵循一个简单的原则在 TypeScript 中设计更好的类型
NEXT
使用 express JS 生成和下载 CSV 文件