0%

Docker多依赖关系的应用

真实的一个应用,不会像之前演示的那样,只有一个Tomcat,而是还有依赖的应用,比如数据库。

以大家熟知的WordPress博客软件为例,它需要连MySQL以存储博客。当然我们可以先启动mysql容器,然后启动WordPress容器,并且用--link参数让WordPress容器能够访问mysql容器。

但是Docker提供一种更简单,更自动化的方式来支撑多依赖关系的应用的整体运行,这一点对于微服务应用特别重要。这个机制就叫docker-compose,它的表现形式是写一个docker-compose.yml,里面描述好整个应用分多少个服务,每个服务间的依赖关系和每个服务自身配置。

docker-compose.yml

编辑一个docker-compose.yml的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

version: '3'

services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress

wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
volumes:
db_data:

语义表达的很明白,上述docker-compose.yml定义了两个services:一个是db,另一个是wordpress。并且wordpress依赖于db(配置中的depends_on节点),当然如果还有其他依赖,可以写多个。类比我们熟悉的maven,一个JAR可以依赖于多个其他的JAR,最终会形成一个依赖树。同样,service最终也会形成依赖树。

接着需要设置每个service的配置,还是那三项:端口映射、磁盘挂载和环境变量。

多服务整体运行

运行

1
$ docker-compose up -d

默认它会找当前目录的配置文件,它等效于docker-compose up -d -f docker-compose.yml。表示以后台形式(-d),启动(up)配置文件docker-compose.yml描述的多个服务。当服务间有依赖关系,docker-compose会先计算出依赖树,自己知道先运行哪个服务,再运行哪个服务。

  • 部分日志信息:
1
2
3
4
5
6
7
8
9
10
$ docker-compose up -d -f docker-compose.yml

Pulling db (mysql:5.7)...
5.7: Pulling from library/mysql

Pulling wordpress (wordpress:latest)...
latest: Pulling from library/wordpress

Creating docker_db_1 ... done
Creating docker_wordpress_1 ... done
  • 查看运行的容器:
1
2
3
4
5
6
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

bc04f0dea039 wordpress:latest "docker-entrypoint.s…" 11 minutes ago Up 11 minutes 0.0.0.0:8000->80/tcp docker_wordpress_1

a9b4763a9cf2 mysql:5.7 "docker-entrypoint.s…" 11 minutes ago Up 11 minutes 3306/tcp, 33060/tcp docker_db_1

访问

访问WordPress: 通过``ports: - “8000:80”端口映射到8000,访问 http://localhost:8000

image-20190926113740373

安装导航,进行网站初始化,填写诸如管理员账号、密码和邮箱之类的,确认后以管理员账号登录:

image-20190926114230411

当然我们换个浏览器,以访客身份浏览,则出现:

image-20190926114633245

###数据

博客的数据库的数据存哪了呢?应该看磁盘挂载。

  • 容器的磁盘挂载: 通过docker inspect $容器ID查看磁盘挂载信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

bc04f0dea039 wordpress:latest "docker-entrypoint.s…" 5 hours ago Up 5 hours 0.0.0.0:8000->80/tcp docker_wordpress_1
a9b4763a9cf2 mysql:5.7 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp, 33060/tcp docker_db_1


$ docker inspect docker_db_1 | grep -A 11 "Mounts"
"Mounts": [
{
"Type": "volume",
"Name": "docker_db_data",
"Source": "/var/lib/docker/volumes/docker_db_data/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "rw",
"RW": true,
"Propagation": ""
}
],

# Source 表示宿主机的磁盘挂载 '/var/lib/docker/volumes/docker_db_data/_data'
# Destination 表示容器内的磁盘路径 '/var/lib/mysql'
  • 进入数据库容器: 通过docker exec $容器ID进入数据库容器,并创建hello-*.txt文件。
1
2
3
4
5
6
7

$ docker exec -it docker_db_1 /bin/bash

root@a9b4763a9cf2:/# cd /var/lib/mysql
root@a9b4763a9cf2:/var/lib/mysql# echo "someting written in container" > hello-from-container.txt
root@a9b4763a9cf2:/var/lib/mysql# cat hello-from-container.txt
someting written in container

当然我们还可以在容器内,访问数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

$ docker exec -it docker_db_1 /bin/bash

root@de7ea06d95c5:/# mysql -u wordpress -pwordpress -h localhost -P 3306
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.27 MySQL Community Server (GPL)

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| wordpress |
+--------------------+
2 rows in set (0.02 sec)
  • 在宿主机查看文件: 在容器中创建的hello-*.txt文件,能在宿主机找到吗?

在Docker-for-mac里比较奇怪,居然没有 ‘/var/lib/docker/volumes/docker_db_data/_data’这个目录,连’/var/lib/docker’都没有。在“Docker->preference”查看:

image-20190926165507553

解决的办法是:换个配置,在volumes节点写绝对地址。当然为了在宿主机能直接访问数据库,我们顺便把MySQL的端口也映射出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

version: '3'

services:
db:
image: mysql:5.7
ports:
- "13306:3306" # 把MySQL端口也映射出来
volumes: # 磁盘挂载填写宿主机的绝对路径
- ~/tmp/docker-compose-data/db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress

wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress

重启下 docker-compose.yml 就能在宿主机目录查看到新文件 hello-*.txt了:

1
2
宿主机 $ cat ~/tmp/docker-compose-data/db_data/hello-from-container.txt
someting written in container

另外从宿主机访问MySQL,端口号是宿主机映射的 13306,不是容器内的3306:

1
2
3
4
$ mysql -u wordpress -pwordpress -h 127.0.0.1 -P 13306
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5
mysql>

关闭

1
2
3
4
5
6
7

$ docker-compose down
Stopping docker_wordpress_1 ... done
Stopping docker_db_1 ... done
Removing docker_wordpress_1 ... done
Removing docker_db_1 ... done
Removing network docker_default

手动通过link运行

为了方便读者把docker-compose.yml与命令行对应起来,文章最后还是附上命令行模式:先运行mysql镜像,然后运行wordpress,并通过--link参数让wordpress容器能访问mysql容器。

  • MySQL容器
1
2
3
4
5
6
7

$ docker run --name blog_database -p 13306:3306 -v ~/tmp/docker-compose-data/db_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=wordpress -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=wordpress -d --rm mysql:5.7

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5045996bb69c mysql:5.7 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 33060/tcp, 0.0.0.0:13306->3306/tcp blog_database
# 运行后的容器ID是 '5045996bb69c',容器名是指定的 'blog_database'
  • WordPress容器
1
2
3
4
5
6
7

$ docker run --name blog_wordpress --link blog_database:wpdb -p 8000:80 -e WORDPRESS_DB_HOST=wpdb:3306 -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=wordpress -d --rm wordpress:latest

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3d468261204 wordpress:latest "docker-entrypoint.s…" 21 seconds ago Up 20 seconds 0.0.0.0:8000->80/tcp blog_wordpress
# 运行后的容器ID是 'a3d468261204',容器名是指定的 'blog_wordpress'
  • 参数简析
    • --name blog_database: 给运行后的容器指定唯一的名字。以便其他容器,能够通过--link参数来连接它。
    • -p 13306:3306: 端口映射,前者是宿主机的端口号,后者是容器内的端口号。
    • -v ~/tmp/docker-compose-data/db_data:/var/lib/mysql: 磁盘挂载,前者是宿主机的磁盘路径,后者是容器内的磁盘路径。
    • -e MYSQL_ROOT_PASSWORD=wordpress: 环境变量,形式是key=value。如果多个,可以-e k1=v1 -e k2=v2 -e k3=v3如此设置。
    • -d 表示后台形式运行。前台则用-it参数。
    • --rm 表示如果名字叫--name blog_database的容器已经存在,则自动销毁后重建。否则,报告重名的容器。有点像给某个进程restart的语义。
    • --link blog_database:wpdb 表示容器的引用。前者表示容器在宿主机范围的名称,后者表示容器内的别称。后面对数据库的引用,则用别称,比如-e WORDPRESS_DB_HOST=wpdb:3306,其中的wpdb就是这个别称。