到目前为止,我们一直在使用单个容器应用程序。但是,我们现在想将 MySQL 添加到应用程序架构中。那么,问题来了:MySQL 将在哪里运行?是将它安装在同一容器中还是在另外的单独容器中运行?
一般来说,一个容器应该只做一件事。 原因:

  • 虽然你可以在开发阶段使用本地数据库,但是很可能要在生产环境中使用云数据库。所以,将数据库与应用程序运行在同一个容器中不太合适。
  • 一般情况下,一个容器应该仅启动一个进程,如果在同一个容器中运行多个进程会增加容器启动/关闭/服务运行情况监听的复杂性。

还有其他更多原因。因此,我们会新启动一个容器运行 MySQL 服务,结构如下:
多容器应用 Multi-Container Apps - 图1

容器 Networking

默认情况下,容器是独立运行的,并且对同一台主机上的其他进程或容器一无所知。So, how do we allow one container to talk to another? The answer is networking. Now, you don’t have to be a network engineer (hooray!). Simply remember this rule…那么,我们如何允许一个容器与另一个容器对话?答案是通过 networking - 网络。你不必具备网络工程师的知识,只需要记住一个最简单的规则就够用了:如果两个容器在同一个网络中,它们就可以相互通信。否则,就不行

启动 MySQL

接着就让我们将 todo-appMySQL 这两个容器放在同一个网络中:

  1. 创建网络

    1. docker network create todo-app
  2. 启动 MySQL 容器并为它指定网络。我们还设置了一些环境变量的值,MySQL 将使用这些变量来初始化数据库,它支持的所有环境变量可以查看 MySQL 镜像的说明

    1. docker run -d \
    2. --network todo-app --network-alias mysql \
    3. -v todo-mysql-data:/var/lib/mysql \
    4. -e MYSQL_ROOT_PASSWORD=secret \
    5. -e MYSQL_DATABASE=todos \
    6. mysql:5.7
  3. 如果使用的是 PowerShell,执行下面这条命令(只是 多行输入 的分隔符不一样而已)

    1. docker run -d `
    2. --network todo-app --network-alias mysql `
    3. -v todo-mysql-data:/var/lib/mysql `
    4. -e MYSQL_ROOT_PASSWORD=secret `
    5. -e MYSQL_DATABASE=todos `
    6. mysql:5.7
  4. 你应该看到了,我们增加了 --network-alias 标记,稍后再讨论它。

    备注: 这条命令还通过 -v 标记使用了名字为 todo-mysql-datavolume 并将它挂载到 /var/lib/mysql 路径下,也就是 MySQL 真正存储数据的地方。虽然我们没有先执行 docker volume create 命令提前创建 volume 但是 Docker 识别到我们要用 named volume,并自动为我们创建了一个。

  5. 为了确认数据库是否已经启动成功,可以使用以下命令连接到数据库容器,验证是否能连接成功。

    1. docker exec -it <mysql-container-id> mysql -p
  6. 出现输入密码的界面时,输入上一条命令指定的 MYSQL_ROOT_PASSWORD 环境变量的值 secret
    成功进入 MySQL 之后,执行以下命令列出所有数据库

    1. mysql> SHOW DATABASES;
  7. 你应该能看到如下所示的输出:

    1. +--------------------+
    2. | Database |
    3. +--------------------+
    4. | information_schema |
    5. | MySQL |
    6. | performance_schema |
    7. | sys |
    8. | todos |
    9. +--------------------+
    10. 5 rows in set (0.00 sec)

    连接到 MySQL

    MySQL 已启动成功,让我们使用它吧!但是,问题是…怎么用?如果我们在同一个网络上运行另一个容器,如何找到该容器?
    为了弄清楚这一点,我们将使用 nicolaka/netshoot 容器, 它内置大量可用于对网络问题进行故障排除或调试的工具。

  8. 使用 nicolaka/netshoot 镜像启动一个新容器。确保将它连接到同一个网络上

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

    1. dig mysql
  10. 将会看到类似如下的输出…

    1. ; <<>> DiG 9.14.1 <<>> mysql
    2. ;; global options: +cmd
    3. ;; Got answer:
    4. ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
    5. ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
    6. ;; QUESTION SECTION:
    7. ;mysql. IN A
    8. ;; ANSWER SECTION:
    9. mysql. 600 IN A 172.18.0.2
    10. ;; Query time: 0 msec
    11. ;; SERVER: 127.0.0.11#53(127.0.0.11)
    12. ;; WHEN: Wed Jan 13 05:21:49 UTC 2021
    13. ;; MSG SIZE rcvd: 44
  11. 在 “ANSWER SECTION” 应答部分,会看到 mysql 的 A 记录,解析为 172.18.0.2(你看到的 IP 地址很可能跟我不一样)。虽然 mysql 通常不是有效的主机名,但 Docker 能够将其解析为具有该网络别名的容器的 IP 地址(还记得我们之前在启动 MySQL 容器时使用的 --network-alias 标志吗?它的作用就是为这个容器主机取一个别名)。这意味着…我们的应用程序仅需连接到名为 mysql 的主机,就能跟数据库对话了,就是这么简单!

    使用 MySQL 运行我们的应用

    我们的 todo-app 应用程序支持一些环境变量的设置,以指定 MySQL 连接设置。它们是:

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

    重要提醒: 直接通过在命令行中设置环境变量的做法,在本地开发模式下没什么问题,但是 强烈反对 在生产环境中使用。Docker 前安全负责人 Diogo Monica,撰写了一篇精彩的博客文章 详细解释了反对的原因。 一种更安全的机制是使用 container orchestration framework 提供的安全支持。在大多数情况下,这些密码会作为文件挂载到正在运行的容器中。你会看到许多应用程序(包括 MySQL 镜像和 我们自己的 todo-app 应用程序)也支持带有 _FILE 后缀的环境变量以指向包含真正环境变量的文件。 例如,设置 MYSQL_PASSWORD_FILE 变量,这个变量指向的文件内容将被作为 MYSQL_PASSWORD 的值,即,真正的连接密码。Docker 默认不支持这种环境变量,你的应用需要自己清楚如何寻找变量并获取文件内容。

在解释了所有这些内容之后,让我们利用刚刚掌握的知识启动一个新容器:

  1. 我们将指定 todo-app 应用程序需要用到的几个环境变量,并将容器连接到我们的应用程序网络上

    1. # 确保在本机的 app 目录下执行以下命令
    2. docker run -dp 3000:3000 \
    3. -w /app \
    4. -v "$(pwd):/app" \
    5. --network todo-app \
    6. -e MYSQL_HOST=mysql \
    7. -e MYSQL_USER=root \
    8. -e MYSQL_PASSWORD=secret \
    9. -e MYSQL_DB=todos \
    10. node:12-alpine \
    11. sh -c "yarn install && yarn run dev"
  2. 如果使用的是 PowerShell,执行下面这条命令(只是 多行输入 的分隔符不一样而已)

    1. docker run -dp 3000:3000 `
    2. -w /app `
    3. -v "$(pwd):/app" `
    4. --network todo-app `
    5. -e MYSQL_HOST=mysql `
    6. -e MYSQL_USER=root `
    7. -e MYSQL_PASSWORD=secret `
    8. -e MYSQL_DB=todos `
    9. node:12-alpine `
    10. sh -c "yarn install && yarn run dev"
  3. 通过查看容器的日志(docker logs <container-id>),应该能看到类似 Connected to MySQL db at host mysql 的日志输出,代表我们的应用程序现在就是使用 MySQL 数据库来保存数据了。

    1. # 省略之前的日志消息 ...
    2. $ nodemon src/index.js
    3. [nodemon] 1.19.2
    4. [nodemon] to restart at any time, enter `rs`
    5. [nodemon] watching dir(s): *.*
    6. [nodemon] starting `node src/index.js`
    7. Connected to mysql db at host mysql
    8. Listening on port 3000
  4. 访问我们的应用程序,然后添加一些待办事项

  5. 连接到 MySQL 数据库并证明这些待办事项已被写入到 MySQL 数据库。记住,密码是 secret

    1. docker exec -it <mysql-container-id> mysql -p todos
  6. 进入 MySQL 之后执行以下命令:

    1. mysql> select * from todo_items;
    2. +--------------------------------------+--------------------+-----------+
    3. | id | name | completed |
    4. +--------------------------------------+--------------------+-----------+
    5. | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! | 0 |
    6. | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome! | 0 |
    7. +--------------------------------------+--------------------+-----------+
  7. 很显然,你的表数据跟我的不一样,因为它保存的是你刚刚输入的待办事项。但是,你应该能看到它们被存储在这里了!

如果你现在打开 Docker Dashboard 将会看到有两个正在运行的应用程序容器,其中一个是 MySQL 容器,另一个是我们的 todo-app 应用。但是,它们现在看起来好像没有任何关系。
多容器应用 Multi-Container Apps - 图2

回顾

至此,我们有了一个应用程序,它现在将其数据存储在单独运行的数据库容器中。
但是,要启动整个程序有点麻烦,我们必须创建一个网络,启动多个容器,指定所有环境变量,映射端口等等!这样复杂的步骤既不利于自己使用也不方便分享给其他人使用。
在下一节中,我们将讨论 Docker Compose。借助 Docker Compose 可以以一种更简单的方式分享我们的应用,并允许其他人只使用一个简单的命令就可以将它们运行起来!

原始资料:Multi-Container Applications

点击查看【bilibili】