Save Load
GitHub 切换暗/亮/自动模式 切换暗/亮/自动模式 切换暗/亮/自动模式 返回首页

Docker Docs 3 立即开始

Docker Docs

转自:Docker 官网

链接:https://docs.docker.com/get-started/overview/

立即开始

第 1 部分:入门

方向和设置

欢迎!我们很高兴您想学习 Docker。

本页包含有关如何开始使用 Docker 的分步说明。在本教程中,您将学习如何:

  • 将映像作为容器生成和运行
  • 使用 Docker Hub 共享映像
  • 使用多个容器和一个数据库部署 Docker 应用程序
  • 使用 Docker Compose 运行应用程序

此外,您还将了解构建映像的最佳做法,包括有关如何扫描映像以查找安全漏洞的说明。

下载并安装 Docker

建议在 Linux 系统上安装 Docker。

开始教程

如果您已经运行了该命令以开始使用本教程,那么恭喜您!如果没有,请打开命令提示符或 bash 窗口,然后运行以下命令:

 docker run -d -p 80:80 docker/getting-started

您会注意到正在使用一些标志。以下是有关它们的更多信息:

  • -d- 在分离模式下运行容器(在后台)
  • -p 80:80- 将主机的端口 80 映射到容器中的端口 80
  • docker/getting-started- 要使用的图像

提示

您可以组合单个字符标志以缩短完整命令。例如,上面的命令可以写成:

 docker run -dp 80:80 docker/getting-started

Docker 仪表板 Docker Dashboard

在走得太远之前,我们想突出显示 Docker 仪表板,它让你可以快速查看计算机上运行的容器。Docker Dashboard 适用于 Mac 和 Windows。它使您可以快速访问容器日志,让您在容器内获得一个shell,并允许您轻松管理容器生命周期(停止,删除等)。

若要访问仪表板,请按照 Docker 桌面手册 中的说明进行操作。如果现在打开仪表板,您将看到本教程正在运行!容器名称(见下文)是随机创建的名称。因此,您很可能会有不同的名称。jolly_bouman

什么是容器? What is a container?

现在您已经运行了一个容器,什么是容器?简而言之,容器是计算机上的沙盒进程,与主机上的所有其他进程隔离。这种隔离利用了内核命名空间和 cgroups,这些功能已经在 Linux 中存在了很长时间。Docker 致力于使这些功能平易近人且易于使用。总而言之,容器:

  • 是映像的可运行实例。您可以使用 DockerAPI 或 CLI 创建、启动、停止、移动或删除容器。
  • 可以在本地机器、虚拟机上运行或部署到云中。
  • 可移植(可以在任何操作系统上运行)
  • 容器彼此隔离,并运行自己的软件、二进制文件和配置。

什么是容器映像? What is a container image?

运行容器时,它使用隔离的文件系统。此自定义文件系统由容器映像提供。由于映像包含容器的文件系统,因此它必须包含运行应用程序所需的一切 - 所有依赖项、配置、脚本、二进制文件等。该映像还包含容器的其他配置,例如环境变量、要运行的默认命令和其他元数据。

稍后我们将深入探讨图像,涵盖分层、最佳实践等主题。

第 2 部分:示例应用程序

在本教程的其余部分,我们将使用一个在 Node.js 中运行的简单待办事项列表管理器。如果您不熟悉 Node.js,请不要担心。不需要真正的 JavaScript 经验。

在这一点上,你的开发团队非常小,你只是在构建一个应用程序来证明你的MVP(最小可行产品)。你想展示它是如何工作的,以及它能够做什么,而不需要考虑它将如何为一个大型团队、多个开发人员等工作。

image

获取应用

在运行应用程序之前,我们需要将应用程序源代码获取到我们的计算机上。对于实际项目,通常会克隆存储库。但是,对于本教程,我们创建了一个包含该应用程序的 ZIP 文件。

  1. 下载应用程序内容。您可以拉取整个项目,也可以将其下载为zip并将其解压缩出应用程序文件夹以开始使用。
  2. 提取后,使用您喜欢的代码编辑器打开项目。如果你需要一个编辑器,你可以使用Visual Studio Code。您应该看到 和 两个子目录 ( 和 )。package.json src spec

生成应用的容器映像

为了构建应用程序,我们需要使用 .Dockerfile 只是一个基于文本的指令脚本,用于创建容器映像。如果您之前创建过 Dockerfile,您可能会在下面的 Dockerfile 中看到一些缺陷。但是,别担心。我们将介绍它们。Dockerfile

  1. 创建一个与该文件同名的文件,该文件与包含以下内容的文件位于同一文件夹中。
# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000

请检查文件没有像.某些编辑器可能会自动附加此文件扩展名,这将导致下一步中的错误。 Dockerfile.txt 2. 如果尚未执行此操作,请打开终端并转到包含 的目录。现在,使用命令生成容器映像。

 docker build -t getting-started .
此命令使用 Dockerfile 生成新的容器映像。您可能已经注意到下载了很多“图层”。这是因为我们指示构建者要从映像开始。但是,由于我们的计算机上没有该图像,因此需要下载该图像。`node:12-alpine`

下载映像后,我们在应用程序中复制并用于安装应用程序的依赖项。该指令指定从此映像启动容器时要运行的默认命令。yarn CMD

最后,标志标记我们的图像。只需将其视为最终图像的人类可读名称。由于我们命名了图像,因此我们可以在运行容器时引用该图像。-t getting-started

该命令的末尾指示 Docker 应在当前目录中查找 。. docker build Dockerfile

启动应用容器

现在我们有了一个映像,让我们运行该应用程序。为此,我们将使用该命令(还记得前面的命令吗? docker run

  1. 使用以下命令启动容器,并指定我们刚刚创建的映像的名称:docker run
     docker run -dp 3000:3000 getting-started
    
    还记得 和 标志吗?我们在“分离”模式下(在后台)运行新容器,并在主机的端口 3000 到容器的端口 3000 之间创建映射。如果没有端口映射,我们将无法访问该应用程序。-d -p
  2. 几秒钟后,打开 Web 浏览器以 http://localhost:3000。您应该会看到我们的应用程序。

第 3 部分:更新应用程序

作为一个小的功能请求,产品团队要求我们在没有任何待办事项列表项时更改“空文本”。他们希望将其更改为以下内容:

您还没有待办事项!在上面添加一个!

很简单,对吧?让我们进行更改。

更新源代码

  1. 在文件中,更新第 56 行以使用新的空文本。src/static/js/app.js
-                <p className="text-center">No items yet! Add one above!</p>
+                <p className="text-center">You have no todo items yet! Add one above!</p>
  1. 让我们使用之前使用的相同命令构建映像的更新版本。
 docker build -t getting-started .
  1. 让我们使用更新的代码启动一个新容器。
 docker run -dp 3000:3000 getting-started

哎呀!您可能看到过这样的错误(ID 会有所不同):

docker: Error response from daemon: driver failed programming external connectivity on endpoint laughing_burnell 
(bb242b2ca4d67eba76e79474fb36bb5125708ebdabd7f45c8eaf16caaabde9dd): Bind for 0.0.0.0:3000 failed: port is already allocated.

那么,发生了什么事呢?我们无法启动新容器,因为我们的旧容器仍在运行。这是因为容器正在使用主机的端口 3000,并且计算机上只有一个进程(包括容器)可以侦听特定端口。要解决此问题,我们需要删除旧容器。

更换旧容器

要删除容器,首先需要停止该容器。一旦停止,就可以将其删除。我们有两种方法可以删除旧容器。随意选择您最熟悉的路径。

使用 CLI 删除容器

  1. 使用命令获取容器的 ID。docker ps
docker ps
  1. 使用命令停止容器。docker stop
 Swap out <the-container-id> with the ID from docker ps
 docker stop <the-container-id>
  1. 容器停止后,可以使用该命令将其删除。docker rm
 docker rm <the-container-id>

注意

您可以通过向命令添加“force”标志来停止和删除单个命令中的容器。例如:

docker rmdocker rm -f <the-container-id>

使用 Docker 仪表板删除容器

如果打开 Docker 仪表板,只需单击两下即可删除容器!这当然比查找容器 ID 并将其删除要容易得多。

  1. 打开仪表板后,将鼠标悬停在应用容器上,你将看到右侧显示一组操作按钮。
  2. 单击垃圾桶图标以删除容器。
  3. 确认删除,您就完成了!

image

启动更新后的应用容器

  1. 现在,启动更新后的应用。
 docker run -dp 3000:3000 getting-started
  1. 在 http://localhost:3000 上刷新浏览器,您应该会看到更新的帮助文本!

回顾

虽然我们能够构建更新,但您可能已经注意到两件事:

  • 我们待办事项列表中的所有现有项目都不见了!这不是一个非常好的应用程序!我们很快就会讨论这个问题。
  • 对于如此小的更改,涉及很多步骤。在即将推出的部分中,我们将讨论如何查看代码更新,而无需在每次进行更改时重新生成和启动新容器。

在谈论持久性之前,我们将快速了解如何与他人共享这些图像。

第 4 部分:共享应用程序

现在我们已经构建了一个映像,让我们分享它!若要共享 Docker 映像,必须使用 Docker 注册表。默认注册表是 Docker Hub,是我们使用的所有映像的来源。

Docker ID

Docker ID 允许你访问 Docker Hub,它是世界上最大的容器映像库和社区。如果没有 Docker ID,请免费创建一个 Docker ID。

创建存储库

若要推送映像,我们首先需要在 Docker Hub 上创建一个存储库。

  1. 注册或登录到 Docker Hub。
  2. 单击创建存储库按钮。
  3. 对于存储库名称,请使用 。确保可见性为 。getting-startedPublic

私有仓库

您是否知道 Docker 提供了私有存储库,允许您将内容限制为特定用户或团队?查看 Docker 定价页上的详细信息。

  1. 点击创建按钮!

如果您查看下面的图像,可以看到一个示例 Docker 命令。此命令将推送到此存储库。

image

推送映像

  1. 在命令行中,尝试运行你在 Docker Hub 上看到的 push 命令。请注意,您的命令将使用您的命名空间,而不是“docker”。
    $ docker push docker/getting-started
    The push refers to repository [docker.io/docker/getting-started]
    An image does not exist locally with the tag: docker/getting-started
    
    为什么会失败?push 命令正在寻找一个名为 docker/getting-started 的映像,但没有找到一个。如果运行 ,则也不会看到一个。docker image ls
    要解决此问题,我们需要“标记”我们构建的现有映像,以便为其命名。
  2. 使用命令登录到 Docker 中心。docker login -u YOUR-USER-NAME
  3. 使用该命令为映像指定一个新名称。请务必使用您的 Docker ID 进行换出。docker tag getting-started YOUR-USER-NAME
    docker tag getting-started YOUR-USER-NAME/getting-started
    
  4. 现在再次尝试推送命令。如果要从 Docker Hub 复制值,则可以删除该部分,因为我们没有向映像名称添加标记。如果未指定标记,Docker 将使用名为 的标记。tagname latest
     docker push YOUR-USER-NAME/getting-started
    

在新实例上运行映像

现在,我们的映像已构建并推送到注册表中,让我们尝试在从未见过此容器映像的全新实例上运行我们的应用!为此,我们将使用 Play with Docker。

  1. 打开浏览器以使用 Docker 玩游戏
  2. 单击“登录”,然后从下拉列表中选择“docker”。
  3. 使用 Docker Hub 帐户进行连接。
  4. 登录后,单击左侧栏上的“添加新实例”选项。如果您没有看到它,请将浏览器放宽一点。几秒钟后,浏览器中将打开一个终端窗口。

image 5. 在终端中,启动新推送的应用。

 docker run -dp 3000:3000 YOUR-USER-NAME/getting-started

您应该看到图像被拉下并最终启动!

  1. 当它出现时,单击3000徽章,您应该会看到带有修改的应用程序!万岁!如果3000徽章未显示,您可以单击“打开端口”按钮并输入3000。

第 5 部分:保留数据库

如果您没有注意到,每次启动容器时,我们的待办事项列表都会被擦除干净。这是为什么呢?让我们深入了解容器的工作原理。

容器的文件系统

当容器运行时,它将映像中的各个层用于其文件系统。每个容器还拥有自己的“暂存空间”来创建/更新/删除文件。任何更改都不会在另一个容器中看到,即使它们使用相同的映像也是如此。

在实践中看到这一点

要查看其实际效果,我们将启动两个容器,并在每个容器中创建一个文件。您将看到的是,在一个容器中创建的文件在另一个容器中不可用。

  1. 启动一个容器,该容器将创建一个以随机数 1 到 10000 命名的文件。ubuntu /data.txt
      docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    
    如果您对该命令感到好奇,我们将启动一个 bash shell 并调用两个命令(为什么我们有 )。第一部分选取一个随机数并将其写入 。第二个命令是简单地监视文件以保持容器运行。&& /data.txt
  2. 验证我们是否可以通过放入容器来查看输出。为此,请打开仪表板,然后单击运行映像的容器的第一个操作。exec ubuntu
    您将看到一个在 ubuntu 容器中运行 shell 的终端。运行以下命令以查看文件的内容。之后再次关闭此终端。/data.txt
      cat /data.txt
    
    如果您更喜欢命令行,则可以使用该命令执行相同的操作。需要获取容器的 ID(用于获取它)并使用以下命令获取内容。
      docker exec <container-id> cat /data.txt
    
    您应该看到一个随机数!
  3. 现在,让我们启动另一个容器(相同的图像),我们将看到我们没有相同的文件。
      docker run -it ubuntu ls /
    
    瞧!那里没有文件!这是因为它只是被写入第一个容器的暂存空间。
  4. 继续使用该命令删除第一个容器。
      docker rm -f <container-id>
    

容器卷 Container volumes

在前面的实验中,我们看到每个容器每次启动时都从映像定义开始。虽然容器可以创建、更新和删除文件,但当删除容器并且所有更改都与该容器隔离时,这些更改将丢失。通过 volumes,我们可以改变这一切。

volumes 提供了将容器的特定文件系统路径连接回主机的功能。如果装载了容器中的某个目录,则该目录中的更改也会显示在主机上。如果我们在容器重启之间挂载相同的目录,我们将看到相同的文件。

卷主要有两种类型。我们最终将同时使用这两个卷,但我们将从命名卷开始。

保留待办事项数据

默认情况下,待办事项应用将其数据存储在容器文件系统的 SQLite 数据库中。如果您不熟悉 SQLite,不用担心!它只是一个关系数据库,其中所有数据存储在单个文件中。虽然这不是大型应用程序的最佳选择,但它适用于小型演示。稍后我们将讨论如何将其切换到其他数据库引擎。

由于数据库是单个文件,如果我们可以将该文件保留在主机上并使其可用于下一个容器,则它应该能够从最后一个容器中断的位置继续。通过创建一个卷并将其附加(通常称为“挂载”)到数据存储的目录,我们可以持久保存数据。当我们的容器写入文件时,它将持久保存到卷中的主机。

如前所述,我们将使用命名卷。将命名卷视为一个数据桶。Docker 维护磁盘上的物理位置,您只需要记住卷的名称即可。每次使用卷时,Docker 都会确保提供正确的数据。

  1. 使用该命令创建卷。

      docker volume create todo-db
    
  2. 在仪表板中再次停止并删除 todo 应用容器(或使用 ),因为它仍在运行,而无需使用持久卷。

      docker rm -f
    
  3. 启动 todo 应用容器,但添加标志以指定卷装入。我们将使用命名卷并将其装载到 ,这将捕获在路径中创建的所有文件。

      docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    
  4. 容器启动后,打开应用并将一些项目添加到待办事项列表。

  5. 停止并删除待办事项应用的容器。使用仪表板或获取 ID,然后将其删除。

      docker psdocker rm -f <id>
    
  6. 使用上面的相同命令启动新容器。

  7. 打开应用。您应该会看到您的项目仍在列表中!

  8. 签出列表后,请继续删除容器。

深入了解 volume

很多人经常问“当我使用命名卷时,Docker 实际上在哪里存储我的数据?如果您想知道,可以使用该命令。

docker volume inspect
docker volume inspect todo-db

是磁盘上存储数据的实际位置。请注意,在大多数计算机上,您需要具有 root 访问权限才能从主机访问此目录。但是,这就是它所在的地方!

第 6 部分:使用绑定装载 mounts

在上一章中,我们讨论并使用命名卷来持久保存数据库中的数据。如果我们只想存储数据,命名卷非常有用,因为我们不必担心数据存储的位置。

使用绑定挂载,我们控制主机上的确切挂载点。我们可以使用它来持久保存数据,但它通常用于向容器中提供其他数据。在处理应用程序时,我们可以使用绑定挂载将源代码挂载到容器中,让它看到代码更改,做出响应,并让我们立即看到更改。

对于基于节点的应用程序,nodemon是监视文件更改然后重新启动应用程序的绝佳工具。在大多数其他语言和框架中都有等效的工具。

快速卷类型比较

绑定装载和命名卷是 Docker 引擎附带的两种主要类型的卷。但是,其他卷驱动程序可用于支持其他用例(SFTP、Ceph、NetApp、S3 等)。

比较项 命名卷 绑定挂载
主机位置 Docker 选择 由您控制
装载示例(使用-v) my-volume:/usr/local/data /path/to/data:/usr/local/data
用容器内容填充新卷 Yes No
支持卷驱动程序 Yes No

启动开发模式容器

绑定装载和命名卷是 Docker 引擎附带的两种主要类型的卷。但是,其他卷驱动程序可用于支持其他用例(SFTP、Ceph、NetApp、S3 等)。

若要运行容器以支持开发工作流,我们将执行以下操作:

  • 将我们的源代码挂载到容器中
  • 安装所有依赖项,包括“开发”依赖项
  • 启动 nodemon 以监视文件系统更改

使用绑定挂载对于本地开发设置非常普遍。优点是开发计算机不需要安装所有生成工具和环境。只需一个命令,即可拉取开发环境并准备就绪。我们将在下一步中讨论 Docker Compose,因为这将有助于简化我们的命令(我们已经得到了很多标志)。

第 7 部分:多容器应用

到目前为止,我们一直在使用单容器应用。但是,我们现在想将MySQL添加到应用程序堆栈中。以下问题经常出现 - “MySQL将在哪里运行?将其安装在同一容器中还是单独运行?一般来说,每个容器都应该做一件事,并且做得很好。几个原因:

  • 很有可能您必须以不同于数据库的方式扩展 API 和前端
  • 单独的容器允许您隔离版本和更新版本
  • 虽然可以在本地对数据库使用容器,但可能需要在生产环境中对数据库使用托管服务。你不希望随应用一起提供数据库引擎。
  • 运行多个进程将需要一个进程管理器(容器仅启动一个进程),这增加了容器启动/关闭的复杂性

还有更多的原因。因此,我们将更新我们的应用程序,使其按如下方式工作:

image

容器网络

请记住,默认情况下,容器是独立运行的,对同一台计算机上的其他进程或容器一无所知。那么,我们如何允许一个容器与另一个容器通信呢?答案是网络。现在,您不必成为网络工程师(万岁!只要记住这个规则…

注意

如果两个容器位于同一网络上,它们可以相互通信。如果他们不是,他们就不能。

启动 MySQL

有两种方法可以将容器放在网络上:1) 在开始时分配它或 2) 连接现有容器。现在,我们将首先创建网络,并在启动时附加MySQL容器。

  1. 创建网络。

       docker network create todo-app
    
  2. 启动MySQL容器并将其附加到网络。我们还将定义一些数据库将用于初始化数据库的环境变量(请参阅MySQL Docker Hub列表中的“环境变量”部分)。

       docker run -d \
      --network todo-app --network-alias mysql \
      -v todo-mysql-data:/var/lib/mysql \
      -e MYSQL_ROOT_PASSWORD=secret \
      -e MYSQL_DATABASE=todos \
      mysql:5.7
    

    您还将看到我们指定了该标志。我们一会儿再回到这个问题上来。--network-alias

    提示

    您会注意到,我们正在使用此处命名的卷并将其挂载在 ,这是MySQL存储其数据的位置。但是,我们从未运行过命令。Docker 识别出我们想要使用命名卷,并自动为我们创建一个。 todo-mysql-data /var/lib/mysql docker volume create

  3. 要确认数据库已启动并运行,请连接到数据库并验证其是否连接。

       docker exec -it <mysql-container-id> mysql -u root -p
    

    出现密码提示时,请键入 secret。在 MySQL 外壳中,列出数据库并验证您是否看到了该数据库。

        SHOW DATABASES;
    

    您应看到如下所示的输出:

    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    | todos              |
    +--------------------+
    5 rows in set (0.00 sec)
    

    退出 MySQL shell 以返回到我们计算机上的 shell。

     exit
    

    万岁!我们有我们的数据库,它已经准备好供我们使用!

连接到 MySQL

现在我们知道MySQL已经启动并运行,让我们使用它!但是,问题是…如何?如果我们在同一网络上运行另一个容器,我们如何找到容器(请记住每个容器都有自己的IP地址)?

为了解决这个问题,我们将使用nicolaka/netshoot容器,它附带了许多对故障排除或调试网络问题有用的工具。

  1. 使用 nicolaka/netshoot 映像启动新容器。确保将其连接到同一网络。

    docker run -it --network todo-app nicolaka/netshoot
    
  2. 在容器中,我们将使用该命令,这是一个有用的 DNS 工具。我们将查找主机名的IP地址。

     dig mysql
    

    你会得到这样的输出…

    ; <<>> DiG 9.14.1 <<>> mysql
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;mysql.				IN	A
    
    ;; ANSWER SECTION:
    mysql.			600	IN	A	172.23.0.2
    
    ;; Query time: 0 msec
    ;; SERVER: 127.0.0.11#53(127.0.0.11)
    ;; WHEN: Tue Oct 01 23:47:24 UTC 2019
    ;; MSG SIZE  rcvd: 44
    

    在“答案部分”中,您将看到解析为的记录(您的IP地址很可能具有不同的值)。虽然通常不是有效的主机名,但 Docker 能够将其解析为具有该网络别名的容器的 IP 地址(还记得我们之前使用的标志吗?)。

    A mysql 172.23.0.2 mysql --network-alias
    

    这意味着…我们的应用程序只需要连接到一个名为的主机,它就会与数据库通信!没有比这更简单的了

使用 MySQL🔗 运行您的应用

待办事项应用程序支持设置一些环境变量来指定MySQL连接设置。它们是:

  • MYSQL_HOST- 正在运行的 MySQL 服务器的主机名
  • MYSQL_USER- 用于连接的用户名
  • MYSQL_PASSWORD- 用于连接的密码
  • MYSQL_DB- 连接后要使用的数据库

通过 Env Vars 设置连接设置 虽然使用 env vars 设置连接设置通常适用于开发,但在生产环境中运行应用程序时,强烈建议不要这样做。Docker的前安全主管Diogo Monica写了一篇精彩的博客文章,解释了原因。

更安全的机制是使用容器业务流程框架提供的机密支持。在大多数情况下,这些机密作为文件装载到正在运行的容器中。你会看到许多应用程序(包括MySQL图像和todo应用程序)也支持带有后缀的env vars,以指向包含变量的文件。

例如,设置 var 将导致应用使用引用文件的内容作为连接密码。Docker 不做任何事情来支持这些 env vars。你的应用需要知道查找变量并获取文件内容。

解释完所有这些,让我们开始我们的开发就绪容器!

  1. 注意:对于MySQL版本8.0及更高版本,请确保在 中包含以下命令

     ALTER USER 'root' IDENTIFIED WITH mysql_native_password BY 'secret';
    flush privileges;
    
  2. 我们将指定上述每个环境变量,并将容器连接到我们的应用网络。

      docker run -dp 3000:3000 \
    -w /app -v "$(pwd):/app" \
    --network todo-app \
    -e MYSQL_HOST=mysql \
    -e MYSQL_USER=root \
    -e MYSQL_PASSWORD=secret \
    -e MYSQL_DB=todos \
    node:12-alpine \
    sh -c "yarn install && yarn run dev"
    

    如果您使用的是Windows,请在PowerShell中使用此命令。

     PS> docker run -dp 3000:3000 `
    -w /app -v "$(pwd):/app" `
    --network todo-app `
    -e MYSQL_HOST=mysql `
    -e MYSQL_USER=root `
    -e MYSQL_PASSWORD=secret `
    -e MYSQL_DB=todos `
    node:12-alpine `
    sh -c "yarn install && yarn run dev"
    
  3. 如果我们查看容器的日志(),我们应该看到一条消息,指示它正在使用mysql数据库。docker logs

    nodemon src/index.js
    
  4. 在浏览器中打开应用程序,并将一些项目添加到待办事项列表中。

  5. 连接到 mysql 数据库并证明项目正在写入数据库。请记住,密码是秘密的。

    docker exec -it <mysql-container-id> mysql -p todos
    

    在 mysql shell 中,运行以下命令:

    select * from todo_items;
    

    显然,您的桌子看起来会有所不同,因为它有您的物品。但是,您应该看到它们存储在那里!

如果你快速浏览一下 Docker 仪表板,就会发现我们有两个应用容器正在运行。但是,没有真正的迹象表明它们在单个应用程序中组合在一起。我们很快就会看到如何让它变得更好!

使用 Docker Compose

Docker Compose 是一个工具,旨在帮助定义和共享多容器应用程序。使用Compose,我们可以创建一个YAML文件来定义服务,并且使用单个命令,可以启动所有内容或将其全部拆除。

使用 Compose 的最大优点是,您可以在文件中定义应用程序堆栈,将其保留在项目存储库的根目录下(现在受版本控制),并轻松使其他人能够为您的项目做出贡献。有人只需要克隆你的存储库并启动撰写应用。事实上,你可能会看到GitHub/GitLab上有相当多的项目正在这样做。

那么,我们该如何开始呢?