多个容器应用
当我们希望 MySQL 应用在我们的应用程序中时,可能会联想这样的问题:“在哪儿运行 MySQL ?把它安装在同一容器中还是分开运行?”。总的来说,每个容器应当仅做一件事,并且把它做好,以下是理由:
- 你很有可能必须以与数据库不同的方式扩展 API 和前端
- 单独的容器可让您隔离版本和更新版本
- 虽然您可以在本地使用数据库的容器,但可能要在生产环境中使用数据库服务。同时,您不希望随应用程序一起提供数据库引擎
- 运行多个进程将需要一个进程管理器(容器仅启动一个进程),这增加了容器启动/关闭的复杂性 当然还有其他一些理由。
容器网络 Container Networking
默认情况下,每个容器都是相互独立的。那我们要怎么实现容器间通信呢?答案是网络。
如果两个容器在同一网络中,则它们能相互通信,否则不能。
着手 MySQL
有两种方法让容器置于同一网络之中:1) 在启动的时候分配或者 2) 连接一个已有的容器。现在,我们先创建一个网络,然后在启动时附加上 MySQL 容器。
- 创建网络。
docker network create todo-app
- 启动一个 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 能自动给我们创建一个命名挂载卷。
- 为了证实我们的数据库已经建立好并运行了,连接到数据库来验证以下。输入密码。在 MySQL shell 中,列出所有数据库,查看是否有
docker exec -it <mysql-container-id> mysql -p
todos
。你应该能看到这样的输出:mysql> SHOW DATABASES;
这说明我们的+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| todos |
+--------------------+
5 rows in set (0.00 sec)todos
数据库正常工作了。
连接到 MySQL
为了弄清楚这一点,我们将使用 nicolaka/netshoot 容器,该容器随附了许多工具,可用于对网络问题进行故障排除或调试。
用 nicolaka/netshoot 镜像启动一个新的容器。确保连接到了同一网络。
docker run -it --network todo-app nicolaka/netshoot
在容器中,我们将使用
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 不做任何支持这些环境变量的事情。您的应用需要知道如何寻找变量并获取文件内容。
现在启动一个开发模式的容器吧
- 明确所有的环境变量,连接到应用网络中。
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" - 如果我们看一下容器的日志的话,可以发现我们正在使用我们的 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 - 在浏览器中打开你的应用,然后添加一些事件在待办列表中。
- 连接我们的数据库,检验数据是否在数据库中。在 mysql shell 中,运行以下命令:
docker exec -it <mysql-container-id> mysql -p todos
很明显能看到数据存储下来了。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 |
+--------------------------------------+--------------------+-----------+