💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 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)