# 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 項目