# 10. docker 部署深入理解
#### 1. 介绍
上一篇:[使用compose部署Rocket.Chat应用(九)](https://www.rails365.net/articles/docker-bu-shu-shen-ru-li-jie-zhi-shu-ju-ku-shi)
之所以来介绍关于数据库的部署,是因为数据库很重要,经常会被用到,也是为了下面两篇文章作铺垫。
应该说最主要是为了深入理解docker的几个概念,比如匿名卷,数据卷,网络,还有`docker-compose`的写法。
学了这些,和理解了这些知识,应该说,以后部署一个别人给你的,或网络上存在的成熟应用,是没有问题的。
#### 2. 端口映射
数据库会以mysql作为例子,postgresql等数据库是一样的。
<https://github.com/docker-library/docs/tree/master/mysql>
这里有它的使用说明。
可以先看看的。
首先来思考一下,要运行一个mysql服务最基本的需求,可能是要先设置一个账号名和密码,不然如何使用呢?
所以:
运行一个最简单的mysql镜像,可以这样:
```
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql
```
然后用`docker ps`查看一下,是这样的:
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6679faa19a49 mysql "docker-entrypoint..." 3 seconds ago Up 3 seconds 3306/tcp some-mysql
```
以`mysql`镜像为基础,创建了一个容器,名为`some-mysql`,并设置`root`密码为`my-secret-pw`。
但是这个容器是没有暴露接口的,没有暴露接口的容器也是能登录的,不过需要特殊的手段,没有暴露接口,你就不能用外部的mysql等客户端来登录。
如果要暴露接口可以这么做:
```
docker run --name some-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql
```
**注意,可以使用`docker stop some-mysql && docker rm -f $(docker ps -a | grep Exit | awk '{ print $1 }')`来停止使用这个容器**
现在使用`docker ps`来查看一下。
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
feab724df3b6 mysql "docker-entrypoint..." 3 seconds ago Up 2 seconds 0.0.0.0:3306->3306/tcp some-mysql
```
可见,端口那个部分由`3306/tcp`变成了`0.0.0.0:3306->3306/tcp`。
`0.0.0.0`表示外部的主机能访问到。
#### 3. 环境变量
之前有说过,启动这个mysql容器的时候,已经设置了root的密码,是通过环境变量的方式设置的,也就是一个参数`-e`。
除此之外,你还可以设置其他变量:
- MYSQL\_DATABASE 指定使用的数据库,默认会创建
- MYSQL\_USER 如果不想使用root账号,可以新建一个账号来使用
- MYSQL\_PASSWORD 账号的密码
也就是说,mysql镜像会利用这些传过来的变量,来做一些操作,比如说创建数据库,创建root密码等。这样你才能去连接这个mysql服务。
#### 4. 匿名卷
我们有一个重要的功能需要注意,就是把数据库的数据保存下来,不会因为container停止了,数据就没了。
这个要用到一个叫`匿名卷`的问题。
```
# https://github.com/docker-library/mysql/blob/ee989d0666458d08dd79c55f7abb62be29a9cb3d/5.5/Dockerfile
VOLUME /var/lib/mysql
```
这个`mysql`镜像已经做好了匿名卷,就是`/var/lib/mysql`这个目录,意思就是说任何向 `/var/lib/mysql` 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。
**注意:/var/lib/mysql是容器中的路径**
当然,我们可以使用自己的目录来覆盖这个挂载设置。
使用`-v`参数即可:
```
docker run --name some-mysql -p 3306:3306 -v /home/ubuntu/owncloud/mysql_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql
```
当然你可以挂载你任何想持久化的目录和文件。
#### 5. 深入环境变量
开启了mysql服务,总要被连接或使用吧。
比如我要进去把数据导出来,可以这么做:
```
$ docker exec some-mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /some/path/on/your/host/all-databases.sql
```
`$MYSQL_ROOT_PASSWORD`表示引用容器内的变量。
`MYSQL_ROOT_PASSWORD`这个变量之前有提过,在Dockerfile或一些其他启动脚本就可以先定义好,你再传进去就好了。
整条命令,表示进入容器中执行`mysqldump`命令,这个命令使用的mysql用户是`root`,连接密码,是使用是容器内部的变量`MYSQL_ROOT_PASSWORD`。
那如何来证明呢?
可以这么做:
```
$ docker exec some-mysql sh -c 'echo "$MYSQL_ROOT_PASSWORD"'
my-secret-pw
```
它会输出你之前启动mysql服务器端容器时传过去的密码:`my-secret-pw`。
我们再传一个自己的变量,然后重启开启容器:
```
$ docker run --name some-mysql -p 3306:3306 -v /home/ubuntu/owncloud/mysql_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -e MYENV=MYVALUE -d mysql
```
传的变量名是`MYENV`,值是`MYVALUE`。
结果输出如下:
```
$ docker exec some-mysql sh -c 'echo "$MYENV"'
MYVALUE
```
证明我们的猜想是正确的:**所以传给容器的变量都被保存着,进入容器都能使用**。
除此之外,我们还可以开启一个客户端的容器进程去连接服务器端的容器进程。
```
$ docker run -it --link some-mysql:mysql --rm mysql sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'
```
你会发现,开启一个客户端,并且进入其中了。
这部分`--link some-mysql:mysql`先不看,我们先来看看后面的环境变量。
怎么又多出了几个变量,不过这些变量都能查看到:
```
$ docker run -it --link some-mysql:mysql --rm mysql sh -c 'env'
```
输出如下:
```
MYSQL_ENV_MYENV=MYVALUE
HOSTNAME=811b319ce670
MYSQL_MAJOR=5.7
SHLVL=0
HOME=/root
MYSQL_ENV_MYSQL_MAJOR=5.7
TERM=xterm
MYSQL_PORT_3306_TCP_ADDR=172.17.0.2
MYSQL_ENV_MYSQL_ROOT_PASSWORD=my-secret-pw
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MYSQL_VERSION=5.7.17-1debian8
MYSQL_ENV_GOSU_VERSION=1.7
MYSQL_PORT_3306_TCP_PORT=3306
MYSQL_PORT_3306_TCP_PROTO=tcp
MYSQL_PORT=tcp://172.17.0.2:3306
MYSQL_PORT_3306_TCP=tcp://172.17.0.2:3306
MYSQL_ENV_MYSQL_VERSION=5.7.17-1debian8
GOSU_VERSION=1.7
PWD=/
MYSQL_NAME=/competent_lamport/mysql
```
之前使用`MYSQL_ROOT_PASSWORD`这种变量好好的,为何又要使用`MYSQL_ENV_MYSQL_ROOT_PASSWORD`这个变量呢,原因是因为用了`--link`。
把`--link`这部分去掉再来试试。
```
$ docker run -it --rm mysql sh -c 'env'
HOSTNAME=744912e7a7f6
MYSQL_MAJOR=5.7
SHLVL=0
HOME=/root
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MYSQL_VERSION=5.7.17-1debian8
GOSU_VERSION=1.7
PWD=/
```
看到没?少了好多变量。
想要用什么,拿来用就好了。
其中有一个变量叫`MYSQL_PORT_3306_TCP_ADDR`。
这个变量跟docker的网络接口有关。
#### 6. link和网络接口
先来看看`--link some-mysql:mysql`这个参数。
以`:`分隔分为两部分,第一部分是容器的名称,第二部是网络连接接口的别名。
表示要连接`some-mysql`这个容器,这个容器是一个mysql服务器程序,并且取了一个别名叫`mysql`,这个别名有点类似于`localhost`,有点儿像ip地址的作用,只是在容器或容器之间用。
**这个别名会发挥着很大的作用的,连接这个mysql服务器(some-mysql)的容器,都可以使用这个别名来找到这台mysql服务器的ip地址。**
下面的章节我们会介绍这个别名的使用。
我们先把这个别名改一下。
```
$ docker run -it --link some-mysql:db --rm mysql sh -c 'exec env'
HOSTNAME=ab6048fd4ccd
DB_PORT=tcp://172.17.0.2:3306
MYSQL_MAJOR=5.7
SHLVL=0
DB_PORT_3306_TCP=tcp://172.17.0.2:3306
DB_ENV_MYSQL_VERSION=5.7.17-1debian8
HOME=/root
DB_NAME=/clever_goldberg/db
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MYSQL_VERSION=5.7.17-1debian8
DB_ENV_MYSQL_MAJOR=5.7
DB_PORT_3306_TCP_ADDR=172.17.0.2
GOSU_VERSION=1.7
DB_ENV_MYSQL_ROOT_PASSWORD=my-secret-pw
PWD=/
DB_ENV_GOSU_VERSION=1.7
DB_PORT_3306_TCP_PORT=3306
DB_PORT_3306_TCP_PROTO=tcp
```
看下输出,之前类似以`MYSQL`开头的变量`MYSQL_PORT_3306_TCP_ADDR`的变量都变成了以`DB`开头的变量`DB_PORT_3306_TCP_ADDR`。
`DB_PORT_3306_TCP_ADDR`是个ip地址,它代表的是这台被连接的mysql服务器的容器的ip地址。
其实用一台命令也可以查出这个ip地址。
```
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id
```
`container_name_or_id`表示容器的hash值,可以写上mysql服务器那个容器的名称。
比如:
```
$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' some-mysql
172.17.0.2
```
果然这个地址一样的。
我们来验证一下。
进入`some-mysql`那个容器:
```
$ docker exec -it some-mysql bash
```
然后:
```
$ cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 9acca0e7b7f8
```
看最后一行`172.17.0.2 9acca0e7b7f8`。`9acca0e7b7f8`是`some-mysql`这个容器的hash值。
总结一下,也就是说,**如果容器要被外部使用,就要暴露接口,如果容器间使用就可以不必要暴露接口**。
下一篇文章会写关于在`docker-compose`中使用`--link`和`docker network`这个关于网络连接的指令。
完结。
下一篇:[部署owncloud与phpMyAdmin(十一)](https://www.rails365.net/articles/bu-shu-owncloud-yu-phpmyadmin-shi-yi)
- 0. 介绍
- 1. 安装 docker
- 2. docker 的镜像和镜像源加速
- 3. docker 的容器
- 4. 理解 docker 镜像的层叠结构
- 5. 使用 Dockerfile 文件
- 6. docker 的数据卷
- 7. Docker Compose 的介绍与安装
- 8. 使用 compose 部署 GitLab 应用
- 9. 使用 compose 部署 Rocket.Chat 应用
- 10. docker 部署深入理解
- 11. 部署 owncloud 与 phpMyAdmin
- 12. 让 php-fpm 跑的 owncloud 应用 docker 化
- 13. docker 迁移 GitLab 项目