多容器应用程序
到目前为止,你一直在使用单容器应用程序。但是,现在你将向应用程序堆栈中添加 MySQL。经常会出现以下问题:“MySQL 将在哪里运行?是将其安装在同一个容器中,还是单独运行?”一般来说,每个容器都应该做好一件事。以下是单独运行容器的一些原因:
- 你很可能需要以不同于数据库的方式扩展 API 和前端。
- 分离的容器允许你独立地进行版本控制和更新版本。
- 虽然你可以在本地使用容器来运行数据库,但在生产环境中,你可能希望使用托管的数据库服务。那时你就不想将数据库引擎与你的应用程序一起发布了。
- 运行多个进程需要一个进程管理器(容器只启动一个进程),这会增加容器启动/关闭的复杂性。
还有更多原因。因此,如下图所示,最好在多个容器中运行您的应用程序。

容器网络
请记住,默认情况下,容器是隔离运行的,对同一台机器上的其他进程或容器一无所知。那么,你如何让一个容器与另一个容器通信呢?答案是网络。如果你将两个容器放在同一个网络上,它们就可以相互通信。
启动 MySQL
有两种方法可以将容器置于网络上:
- 在启动容器时分配网络。
- 将一个已在运行的容器连接到一个网络。
在接下来的步骤中,你将首先创建网络,然后在启动时附加 MySQL 容器。
创建网络。
$ docker network create todo-app启动一个 MySQL 容器并将其附加到网络上。你还将定义一些环境变量,数据库将使用这些变量来初始化数据库。要了解有关 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:8.0$ 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:8.0$ 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:8.0在前面的命令中,你可以看到
--network-alias标志。在后面的部分,你将学到更多关于这个标志的信息。提示你会注意到在上面的命令中有一个名为
todo-mysql-data的卷被挂载到/var/lib/mysql,这是 MySQL 存储其数据的地方。然而,你从未运行过docker volume create命令。Docker 识别出你想使用一个命名卷,并自动为你创建了一个。要确认数据库已启动并运行,请连接到数据库并验证其是否连接成功。
$ docker exec -it <mysql-container-id> mysql -u root -p当出现密码提示时,输入
secret。在 MySQL shell 中,列出数据库并验证你看到了todos数据库。mysql> SHOW DATABASES;你应该看到类似这样的输出:
+--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | todos | +--------------------+ 5 rows in set (0.00 sec)退出 MySQL shell 以返回到你机器上的 shell。
mysql> exit你现在有了一个
todos数据库,它已经准备好供你使用了。
连接到 MySQL
既然你知道 MySQL 已经启动并运行,你就可以使用它了。但是,你该如何使用它呢?如果你在同一个网络上运行另一个容器,你如何找到这个容器?记住,每个容器都有自己的 IP 地址。
为了回答上述问题并更好地理解容器网络,你将使用 nicolaka/netshoot 容器,该容器附带了许多用于故障排除或调试网络问题的有用工具。
使用 nicolaka/netshoot 镜像启动一个新容器。确保将其连接到同一个网络。
$ docker run -it --network todo-app nicolaka/netshoot在容器内部,你将使用
dig命令,这是一个有用的 DNS 工具。你将查找主机名mysql的 IP 地址。$ dig mysql你应该得到如下输出。
; <<>> DiG 9.18.8 <<>> 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”(应答部分)中,你会看到一条
A记录,其中mysql解析为172.23.0.2(你的 IP 地址很可能会有一个不同的值)。虽然mysql通常不是一个有效的主机名,但 Docker 能够将其解析为拥有该网络别名的容器的 IP 地址。记住,你之前使用了--network-alias。这意味着你的应用程序只需要连接到一个名为
mysql的主机,它就可以与数据库通信了。
使用 MySQL 运行你的应用程序
todo 应用程序支持设置一些环境变量来指定 MySQL 连接设置。它们是:
MYSQL_HOST- 正在运行的 MySQL 服务器的主机名MYSQL_USER- 用于连接的用户名MYSQL_PASSWORD- 用于连接的密码MYSQL_DB- 连接后使用的数据库
注意虽然使用环境变量来设置连接设置在开发中通常被接受,但在生产环境中运行应用程序时,这是非常不鼓励的。Docker 前安全主管 Diogo Monica 写了一篇很棒的博客文章,解释了其中的原因。
一个更安全的机制是使用你的容器编排框架提供的秘密(secret)支持。在大多数情况下,这些秘密会作为文件挂载到正在运行的容器中。你会看到许多应用程序(包括 MySQL 镜像和 todo 应用程序)也支持带有
_FILE后缀的环境变量,以指向包含该变量的文件。例如,设置
MYSQL_PASSWORD_FILE变量将使应用程序使用所引用文件的内容作为连接密码。Docker 不会对这些环境变量提供任何特殊支持。你的应用程序需要知道去查找该变量并获取文件内容。
你现在可以启动你的开发就绪容器了。
指定前面提到的每个环境变量,并将容器连接到你的应用网络。确保在运行此命令时,你位于
getting-started-app目录中。$ docker run -dp 127.0.0.1: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:18-alpine \ sh -c "yarn install && yarn run dev"在 Windows 中,请在 PowerShell 中运行此命令。
$ docker run -dp 127.0.0.1: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:18-alpine ` sh -c "yarn install && yarn run dev"在 Windows 中,在命令提示符中运行此命令。
$ docker run -dp 127.0.0.1:3000:3000 ^ -w /app -v "%cd%:/app" ^ --network todo-app ^ -e MYSQL_HOST=mysql ^ -e MYSQL_USER=root ^ -e MYSQL_PASSWORD=secret ^ -e MYSQL_DB=todos ^ node:18-alpine ^ sh -c "yarn install && yarn run dev"$ docker run -dp 127.0.0.1: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:18-alpine \ sh -c "yarn install && yarn run dev"如果你查看容器的日志(
docker logs -f <container-id>),你应该会看到一条类似下面的消息,表明它正在使用 mysql 数据库。$ nodemon src/index.js [nodemon] 2.0.20 [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在浏览器中打开该应用程序,并向你的待办事项列表中添加几个项目。
连接到 mysql 数据库,并证明这些项目正在被写入数据库。记住,密码是
secret。$ 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 | +--------------------------------------+--------------------+-----------+你的表格看起来会有所不同,因为它包含你的项目。但是,你应该看到它们存储在那里。
摘要
至此,你已经拥有一个应用程序,它现在将其数据存储在运行于独立容器中的外部数据库中。你还学习了一点关于容器网络和使用 DNS 进行服务发现的知识。
相关信息
后续步骤
你很可能开始对启动这个应用程序所需做的所有事情感到有些不知所措。你必须创建一个网络,启动容器,指定所有的环境变量,暴露端口等等。这需要记住的东西太多了,而且无疑使得将它传递给别人变得更加困难。
在下一节中,你将学习 Docker Compose。使用 Docker Compose,你可以更轻松地共享你的应用程序堆栈,并让其他人通过一个简单、单一的命令来启动它们。
使用 Docker Compose