使用 Docker 容器作为开发环境的温和入门

Avatar of Burke Holland
Burke Holland

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

讽刺声明:本文大部分内容都是讽刺。我不认为我代表了 Dylan Thomas,而且我永远不会鼓励你强迫那些不想要浅色主题的人使用它。无论他们有多么错误。

当 Dylan Thomas 写下“不要温柔地走进那个良夜”时,他指的是死亡。但如果他今天还活着,他可能指的是 Linux 容器。我们无法确定,因为他于 1953 年去世,但这是互联网,所以我非常有信心代表他发表权威言论。

我的信心来自于对自身技能和智力的完全高估,以及我最近尝试将 Docker 容器配置为我的开发环境的事实。我发现自己对即将消逝的光芒怒吼,因为 Docker 拒绝了我做出的每一次尝试,就像我是我,而它是詹姆斯国王,大喊“在我的房子里不行!”

痛苦是最好的老师。因为我关心你,并且没有其他不可告人的动机,我想利用这段经历为你提供一个“温和的”入门,介绍如何使用 Docker 容器作为开发环境。但首先,让我们谈谈你为什么要这样做。

为什么?

闭上眼睛,想象一下:一个打扮成狐狸的成年男子。

等等。不。场景错了。

相反,想象一个项目,它不仅包含你的源代码,还包含你的整个开发环境以及你的应用程序所需的所有依赖项和运行时。然后,你可以将该项目提供给任何地方的任何人(比如那个狐狸男),他们可以在没有任何配置更改的情况下运行你的项目。

这正是 Docker 容器所做的。Dockerfile 使用单个文件定义整个运行时环境。你只需要一种在该容器内进行开发的方法。

等等……

VS Code 和远程容器

VS Code 有一个名为 远程容器 的扩展,它允许你在 Docker 容器内加载项目并使用 VS Code 连接到它。这真是太神奇了。(他成功了吗?!护身符实际上从未停止旋转。)如果我们(我指的是“你”)实际操作一下,就会更容易理解。

将容器添加到项目

假设你有一台高端游戏电脑,你为你的孩子组装了它,然后决定自己留着。我的意思是,他们为什么要再买一台新电脑?哦,对了。他们不需要。他们甚至不能在周日把垃圾扔出去,即使你每周都告诉他们。

这是一台安装了 WSL2 和 Docker 的全新 Windows 机器,仅此而已。如果你尝试在这台机器上运行 Node.js 项目,Powershell 会告诉你它完全不知道你在说什么,也许你拼错了什么。公平地说,你的拼写确实很糟糕。还记得你四年级时在拼写比赛第一轮就被淘汰,因为你不会拼“fried”吗?FRYED?里面没有“Y”!

现在这不是什么大问题——你总是可以跳过并安装 Node.js。但假设你不想这样做,并且你很确定跳跃不是成年人该做的事情。

相反,我们可以将此项目配置为在已安装 Node.js 的容器中运行。如我所述,我根本不知道如何使用 Docker。我甚至几乎不会使用微波炉。幸运的是,VS Code 会为你配置项目——在某种程度上。

在命令面板中,有一个“添加开发容器配置文件…”命令。此命令查看你的项目并尝试添加正确的容器定义。

在本例中,VS Code 知道我这里有一个 Node 项目,所以我只需要选择 Node.js 14 即可。是的,我知道 12 目前是 LTS,但它将在 [查看手表] 一个月内变成 14,我是一个早期采用者,正如我目前在 2020 年对容器技术的兴趣所证明的那样。

这将添加一个包含一些资产的 .devcontainer 文件夹。一个文件是 Dockerfile,其中包含我们将使用的 Node.js 镜像,另一个文件是 devcontainer.json,其中包含一些项目级别的配置。

现在,在我们触碰任何东西并将其全部破坏之前(我们会做到这一点,相信我),我们可以从命令面板中选择“重新构建并在容器中重新打开”。这将重新启动 VS Code 并开始构建容器。一旦它完成(如果你的电脑不是你孩子永远不会知道的乐趣的高端游戏电脑,那么第一次构建可能需要一段时间),项目将在容器内打开。VS Code 连接到容器,你可以在左下角看到这一点。

现在,如果我们在 VS Code 中打开终端,Powershell 就会消失,因为我们不再使用 Windows 了,多萝西。我们现在在一个 Linux 容器中。我们既可以在这个神奇的国度中使用 npm install,也可以使用 npm start

这是一个 Express 应用程序,因此它应该在 3000 端口运行。但是,如果你尝试访问该端口,它将无法加载。这是因为我们需要将容器中的端口映射到本地主机的 3000 端口。就像人们通常做的那样。

幸运的是,这里有一个 UI。

远程容器扩展在操作栏中放置了一个“远程资源管理器”图标。对你来说它在左边,但对我来说它在右边。因为我移动了它,而且 你也应该这样做

这里有三个部分,但请查看底部一个名为“端口转发”的部分,我不是生菜最多的三明治,但我确信这就是我们想要的。你可以点击“转发端口”并输入“3000”,现在如果我们尝试从浏览器访问应用程序……

大多数情况下,“一切正常”。但配置也相当简单。让我们看看如何通过自动化项目本身的某些方面来开始自定义此设置。项目特定的配置在 devcontainer.json 文件中完成。

自动化项目配置

首先,我们可以通过添加 forwardPorts 变量并将 3000 指定为其值来自动化端口转发。我们还可以通过指定 postCreateCommand 属性来自动化 npm install 命令。而且,让我们面对现实,我们都可以减少至少一个 npm install 命令。

{
  // ...
  // Use 'forwardPorts' to make a list of ports inside the container available locally.
  "forwardPorts": [3000],
  // Use 'postCreateCommand' to run commands after the container is created.
  "postCreateCommand": "npm install",
  // ...
}

此外,我们还可以包含 VS Code 扩展。在 Docker 容器中运行的 VS Code 不会自动获取你安装的每个扩展。你必须在容器中安装它们,或者像我们在这里做的那样包含它们。

像 Prettier 和 ESLint 这样的扩展非常适合这种场景。我们还可以借此机会强迫每个人使用浅色主题,因为事实证明,深色主题 不利于阅读和理解。我觉得自己像个 先知

// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.128.0/containers/javascript-node-14
{
  // ...
  // Add the IDs of extensions you want installed when the container is created.
  "extensions": [
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "GitHub.github-vscode-theme"
  ]
  // ...
}

如果你想知道在哪里可以找到这些扩展 ID,如果你已安装它们,则它们会在智能感知 (Ctrl/Cmd + Shift) 中显示。如果没有,请搜索扩展市场,右键单击扩展并选择“复制扩展 ID”。或者更好的是,只需选择“添加到 devcontainer.json”。

默认情况下,VS Code 提供的 Node.js 容器已安装了 git 和 cURL 等工具。它没有安装的是“cowsay”,而我们不能没有 cowsay 就拥有 Linux 环境。这是在 Linux 章程中规定的(不是)。我不是制定规则的人。我们需要自定义此容器以添加它。

自动化环境配置

这正是我开始出轨的地方。为了向开发容器添加软件,你必须编辑 Dockerfile。Linux 不会容忍你的恶作剧或错误。

使用 VS Code 中的容器配置获得的基本 Docker 容器是 Debian Linux。Debian Linux 使用 apt-get 依赖项管理器。

apt-get install cowsay

我们可以将这段代码添加到 Dockerfile 的末尾。无论何时从 apt-get 安装内容,都要先运行一个apt-get update。此命令会更新软件包和软件包存储库的列表,以便您拥有最新缓存的列表。如果不这样做,容器构建将失败并提示您找不到“cowsay”。

# To fully customize the contents of this image, use the following Dockerfile instead:
# https://github.com/microsoft/vscode-dev-containers/tree/v0.128.0/containers/javascript-node-14/.devcontainer/Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-14
# ** Install additional packages **
RUN apt-get update \
  && apt-get -y install cowsay

这里需要注意几点…

  1. 那个RUN命令是 Docker 的一个功能,它会创建一个新的“层”。层是容器了解哪些内容已更改以及在重新构建容器时需要更新哪些内容的方式。它们有点像蛋糕层,除了您希望有很多层,因为巨大的蛋糕很棒。巨大的容器则不然。您应该尝试将相关的逻辑放在同一个RUN命令中,以避免创建不必要的层。
  2. 那个\表示在行尾换行。对于多行命令,您需要它。将其省略,您将体会到许多失败的 Docker 构建带来的痛苦。
  3. &&是您向RUN行添加其他命令的方式。看在上帝的份上,不要忘记上一行中的那个\
  4. -y标志很重要,因为默认情况下,apt-get 会提示您确保您确实想要安装刚刚尝试安装的内容。这将导致容器构建失败,因为没有人能够说YN-y标志是“不要用您愚蠢的确认提示打扰我”的简写。显然每个人都应该知道这一点。直到大约四个小时前我才知道。 

使用命令提示符选择“重新构建容器”…

就这样…

它不起作用。

这是我对“Linux 眩晕症”的第一个教训。Linux 有如此之多的发行版,而且它们并不都以相同的方式处理事物。可能很难弄清楚为什么某些东西在一个地方(Mac、WSL2)可以工作而在其他地方却不能工作。 “cowsay”不可用的原因是 Debian 将“cowsay”放在/usr/games中,该目录未包含在PATH环境变量中。

一种解决方案是将其添加到 Dockerfile 中的PATH。如下所示…

FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-14
RUN apt-get update \
  && apt-get -y install cowsay
ENV PATH="/usr/games:${PATH}"

太棒了。伙计们,我们正在解决实际问题。人们喜欢奶牛单行命令。我相信我在某处听说过

概括地说,项目配置(转发端口、安装项目依赖项等)在devcontainer.json中完成,环境配置(安装软件)在“Dockerfile”中完成。现在让我们大胆尝试一些更前沿的东西。

高级配置

假设您有一个华丽的、经过精心设置的终端,您也希望将其放入容器中。我的意思是,仅仅因为您在容器中开发并不意味着您的终端必须很无聊。但您也不希望为打开的每个项目重新配置您自命不凡的 zsh 设置。我们也可以自动化吗?让我们拭目以待。

幸运的是,您获得的镜像中已经安装了 zsh。唯一的问题是,当容器打开时,它不是默认的 shell。在正常的 Docker 场景中,您可以通过多种方法将 zsh 设为默认 shell,但此处没有一种方法有效。这是因为您无法控制容器的构建方式。

相反,请再次查看可靠的devcontainer.json文件。在其中,有一个"settings"块。实际上,那里已经有一行向您显示默认终端设置为"/bin/bash"。将其更改为"/bin/zsh"

// Set *default* container specific settings.json values on container create.
"settings": {
  "terminal.integrated.shell.linux": "/bin/zsh"
}

顺便说一句,您可以在那里设置任何 VS Code 设置。比如,将侧边栏移到右侧。瞧——我已经为您修复了。

// Set default container specific settings.json values on container create.
"settings": {
  "terminal.integrated.shell.linux": "/bin/zsh",
  "workbench.sideBar.location": "right"
},

那些让您比其他人更优秀的自命不凡的插件呢?对于这些插件,您将需要您的.zshrc文件。容器中已经有了 oh-my-zsh,它位于“根”文件夹中。您只需要确保在.zshrc顶部设置了指向ZSH的路径,以便它指向根目录。如下所示…

# Path to your oh-my-zsh installation.
export ZSH="/root/.oh-my-zsh"


# Set name of the theme to load --- if set to "random", it will
# load a random theme each time oh-my-zsh is loaded, in which case,
# to know which specific one was loaded, run: echo $RANDOM_THEME
# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes
ZSH_THEME="cloud"


# Which plugins would you like to load?
plugins=(zsh-autosuggestions nvm git)


source $ZSH/oh-my-zsh.sh

然后,您可以将那个性感的.zshrc文件复制到 Dockerfile 中的根文件夹。我将该.zshrc文件放在项目中的.devcontainer文件夹中。

COPY .zshrc /root/.zshrc

如果您需要在安装插件之前下载插件,请使用RUN命令在 Dockerfile 中执行此操作。请记住将所有这些组合到一个命令中,因为每个RUN都是一个新的层。您现在几乎是容器专家了。下一步是撰写一篇关于它的博文,并像您发明了这个东西一样指导人们使用 Docker 的方法。

RUN git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions

看看这个美丽的终端!注意这些颜色!git 插件会告诉您分支并添加闪电表情符号!没有什么比自定义终端更能体现“我知道自己在做什么”了。我喜欢将我的终端带到星巴克,让人们看到它在运行,并想知道我是否是个名人。

温和地前进

希望您能坚持到这一步,并认为:“天哪,这个人反应过度了。这并不难。”如果是这样,我就成功地拯救了您。欢迎。不用感谢我。是的,我确实有一个亚马逊愿望清单。

有关远程容器的更多信息,包括如何执行添加数据库或使用 Docker Compose 等操作,请查看官方的远程容器文档,其中提供了更多清晰度,并且神经质的评论减少了 100%。