跳到主要内容

多个容器应用

原页面

当我们希望 MySQL 应用在我们的应用程序中时,可能会联想这样的问题:“在哪儿运行 MySQL ?把它安装在同一容器中还是分开运行?”。总的来说,每个容器应当仅做一件事,并且把它做好,以下是理由:

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

容器网络 Container Networking

默认情况下,每个容器都是相互独立的。那我们要怎么实现容器间通信呢?答案是网络

如果两个容器在同一网络中,则它们能相互通信,否则不能。

着手 MySQL

有两种方法让容器置于同一网络之中:1) 在启动的时候分配或者 2) 连接一个已有的容器。现在,我们先创建一个网络,然后在启动时附加上 MySQL 容器。

  1. 创建网络。
    docker network create todo-app
  2. 启动一个 MySQL 容器,并将它附加在网络中。我们还需要添加一些环境变量来初始化数据库(可以在 MySQL Docker Hub listing 中查看“Environment Variables”)。
    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 修饰符。

这里使用了一个叫 todo-mysql-data 的挂载卷,并装载在 /var/lib/mysql 上,MySQL 将在这上面存放数据。但是,我们从未运行过 docker volume create 命令。Docker 能自动给我们创建一个命名挂载卷。

  1. 为了证实我们的数据库已经建立好并运行了,连接到数据库来验证以下。
    docker exec -it <mysql-container-id> mysql -p
    输入密码。在 MySQL shell 中,列出所有数据库,查看是否有 todos
    mysql> SHOW DATABASES;
    你应该能看到这样的输出:
    +--------------------+
    | Database |
    +--------------------+
    | information_schema |
    | mysql |
    | performance_schema |
    | sys |
    | todos |
    +--------------------+
    5 rows in set (0.00 sec)
    这说明我们的 todos 数据库正常工作了。

连接到 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.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

    在“ANSWER SECTION”中,可以看到解析的地址 173.23.0.2 (你的地址应该和这个不一样)。虽然 mysql 通常不是个有效的主机名,但 Docker 能够将其解析为具有该网络别名的容器的 IP 地址(还记得我们之前使用的 --network-alias 标志吗?)。

    这意味着,我们的应用只需要连接到一个名为 mysql 的主机,然后就能与数据库通信了。

运行我们的应用

待办应用支持设定一些环境变量来明确 MySQL 连接设置。有:

  • MYSQL_HOST MySQL 的主机名
  • MYSQL_USER 要连接的用户名
  • MYSQL_PASSWORD 密码
  • MYSQL_DB 要连接的数据库

注意,通过环境变量设置数据库连接在开发环境中是可以的,但是在生产环境中是非常不建议的,具体理由可以看 这个帖子

一种更安全的机制是使用容器编排框架提供的保密支持。在大多数情况下,这些机密会作为文件安装在正在运行的容器中。 您会看到许多应用程序(包括 MySQL 镜像和待办应用)也支持带有 _FILE 后缀的环境变量,以指向包含该变量的文件。

例如,设置 MYSQL_PASSWORD_FILE 变量将使应用程序将引用文件的内容用作连接密码。 Docker 不做任何支持这些环境变量的事情。您的应用需要知道如何寻找变量并获取文件内容。

现在启动一个开发模式的容器吧

  1. 明确所有的环境变量,连接到应用网络中。
    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. 如果我们看一下容器的日志的话,可以发现我们正在使用我们的 mysql 数据库。
    # Previous log messages omitted
    $ 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. 连接我们的数据库,检验数据是否在数据库中。
    docker exec -it <mysql-container-id> mysql -p todos
    在 mysql shell 中,运行以下命令:
    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 |
    +--------------------------------------+--------------------+-----------+
    很明显能看到数据存储下来了。