多容器应用 Multi-Container Apps

未匹配的标注

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

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

还有其他更多原因。因此,我们会新启动一个容器运行 MySQL 服务,结构如下:

Todo App connected to MySQL container

容器 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. 创建网络

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

    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

    如果使用的是 PowerShell,执行下面这条命令(只是 多行输入 的分隔符不一样而已)

    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 标记,稍后再讨论它。

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

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

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

    出现输入密码的界面时,输入上一条命令指定的 MYSQL_ROOT_PASSWORD 环境变量的值 secret
    成功进入 MySQL 之后,执行以下命令列出所有数据库

    mysql> SHOW DATABASES;

    你应该能看到如下所示的输出:

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

连接到 MySQL

MySQL 已启动成功,让我们使用它吧!但是,问题是…怎么用?如果我们在同一个网络上运行另一个容器,如何找到该容器?

为了弄清楚这一点,我们将使用 nicolaka/netshoot 容器, 它内置大量可用于对网络问题进行故障排除或调试的工具。

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

    docker run -it --network todo-app nicolaka/netshoot
  2. 在容器内部,我们将使用 dig 命令,这是一个有用的 DNS 工具。我们将查找主机名 mysql 的 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.18.0.2
    
    ;; Query time: 0 msec
    ;; SERVER: 127.0.0.11#53(127.0.0.11)
    ;; WHEN: Wed Jan 13 05:21:49 UTC 2021
    ;; MSG SIZE  rcvd: 44

    在 “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 应用程序需要用到的几个环境变量,并将容器连接到我们的应用程序网络上

    # 确保在本机的 app 目录下执行以下命令
    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"

    如果使用的是 PowerShell,执行下面这条命令(只是 多行输入 的分隔符不一样而已)

    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"
  2. 通过查看容器的日志(docker logs <container-id>),应该能看到类似 Connected to MySQL db at host mysql 的日志输出,代表我们的应用程序现在就是使用 MySQL 数据库来保存数据了。

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

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

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

    进入 MySQL 之后执行以下命令:

    mysql> select * from todo_items;
    +--------------------------------------+--------------------+-----------+
    | id                                   | name               | completed |
    +--------------------------------------+--------------------+-----------+
    | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! |         0 |
    | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome!        |         0 |
    +--------------------------------------+--------------------+-----------+

    很显然,你的表数据跟我的不一样,因为它保存的是你刚刚输入的待办事项。但是,你应该能看到它们被存储在这里了!

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

Docker Dashboard showing two ungrouped app containers

回顾

至此,我们有了一个应用程序,它现在将其数据存储在单独运行的数据库容器中。

但是,要启动整个程序有点麻烦,我们必须创建一个网络,启动多个容器,指定所有环境变量,映射端口等等!这样复杂的步骤既不利于自己使用也不方便分享给其他人使用。

在下一节中,我们将讨论 Docker Compose。借助 Docker Compose 可以以一种更简单的方式分享我们的应用,并允许其他人只使用一个简单的命令就可以将它们运行起来!

原始资料:Multi-Container Applications

配套视频
www.bilibili.com/video/BV1SX4y137q...

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~