菜单

Juning
发布于 2020-05-27 / 966 阅读
2
0

Docker (四) 容器数据卷

什么是容器数据卷

数据卷是一个可供容器使用的特殊目录,它将主机操作系统目录直接映射进容器,类似于Linux中的mount(挂载)操作。

数据卷可以提供很多有用的特性,如下所示:

  1. 数据卷可以在容器之间共享和重用,容器间传递数据将变得高效方便;

  2. 对数据卷内数据的修改会立马生效,无论是容器内操作还是本地操作;

  3. 对数据卷的更新不会影响镜像,解耦了应用和数据;

  4. 卷会一直存在,直到没有容器使用,可以安全地卸载它。

也就是将容器数据持久化,比如我们创建一个MySQL的容器,然后将这个容器删除,在删除前将数据类容挂载到宿主机,那么即使容器被删除,那么数据也会持久化到宿主机上

使用数据卷

方式一:直接使用命令来挂载 -v

docker run -v 主机目录:容器目录

# 测试
juning@chengjiajundeMacBook-Pro ~ % docker run -it -v /Users/juning/dev/docker_app_data:/home centos /bin/bash

# 启动起来之后可以使用 docker inspect 容器ID 查看是否挂载成功
juning@chengjiajundeMacBook-Pro ~ % docker inspect 917f94c6838d

docker数据卷1.png

我们重新打开一个终端窗口,测试文件的同步:

docker数据卷2.png

再来测试!

  1. 停止容器
  2. 在宿主机上修改文件
  3. 启动容器
  4. 容器内部的数据依旧是同步的

docker数据卷3.png

这样做的好处就是我们以后的修改只需要在本地做修改即可,容器内会自动同步数据

实战:安装MySQL

思考:MySQL的数据持久化的问题

# 获取MySQL镜像
docker pull mysql:5.7
# 运行容器,需要做数据挂载
# 安装启动MySQL需要配置密码,这是需要注意的一个点
# 官方的测试:docker run --name some-mysql -v /my/custom:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 启动
# -d 后台运行
# -p 端口映射
# -v 卷挂载
# -e 环境变量
# --name 容器名字
docker run -d -p 3310:3306 -v /Users/juning/dev/docker_app_data/mysql/conf:/etc/mysql/cong.d -v /Users/juning/dev/docker_app_data/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7

启动完之后用Navicat连接测试:
docker数据卷4测试连接.png

连接成功

假设我们将容器删除:

docker数据卷5.png

发现挂载到本地的数据没有丢失,这就实现了容器数据持久化的功能。

具名和匿名挂载

# 匿名挂载
-v 容器内路径!
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 查看所有卷的情况
juning@chengjiajundeMacBook-Pro data % docker volume ls
DRIVER              VOLUME NAME
local               1c88bd0ae8b51f3efa366ea7f165cce99f68aeb9a583491a6503bc8f1c0f6434
local               12ce0976bd5ec237ec8fdff01857e6ea864dbb5eba075dc0f57cd619ad0152b4
local               8994961f4ecc6622cad6b421cb24ea40a9892c1d8dc676425f18e8200fb99d6a
local               d7423b28fbb062d15d3a3f4001f2f0aa210fe695dd50acc5ec5ff8fa79f39a5e
# 这里发现VOLUME NAME全部是一段she码,因为的-v只写了容器内部的路径,没有写容器外的路径


# 具名挂载
docker run -d -P --name nginx02 -v ju_ming_gua_zai:/etc/nginx nginx
juning@chengjiajundeMacBook-Pro data % docker volume ls                                                   
DRIVER              VOLUME NAME
local               1c88bd0ae8b51f3efa366ea7f165cce99f68aeb9a583491a6503bc8f1c0f6434
local               12ce0976bd5ec237ec8fdff01857e6ea864dbb5eba075dc0f57cd619ad0152b4
local               8994961f4ecc6622cad6b421cb24ea40a9892c1d8dc676425f18e8200fb99d6a
local               d7423b28fbb062d15d3a3f4001f2f0aa210fe695dd50acc5ec5ff8fa79f39a5e
local               ju_ming_gua_zai
# 可以看到VOLUME NAME有一个刚才指定的ju_ming_gua_zai(ju_ming_gua_zai前不能加'/',不然就会被认为是路径)

可以看到,在没有指定目录的情况下匿名挂载的VOLUME NAME是一串hash码,就算给他指定了名称,那也只是一个我们自己能看的懂的字符而已。

那么在没有指定目录的情况下如何知道挂载在主机的哪一个目录下呢?

# 查看卷
juning@chengjiajundeMacBook-Pro data % docker volume inspect ju_ming_gua_zai
[
    {
        "CreatedAt": "2020-05-26T13:54:30Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/ju_ming_gua_zai/_data",
        "Name": "ju_ming_gua_zai",
        "Options": null,
        "Scope": "local"
    }
]

docker下所有容器内的卷,在没有指定目录的情况下,都是在“/var/lib/docker/volumes/xxxxx/_data”下

注意:在Mac环境下会有一个坑,具体细节在Docker (四点五) MacOS下/var/lib/docker 的存放位置解决

我们通过具名挂载可以方便的找到我们的卷,建议不要使用匿名挂载

# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载
-v 容器内路径							# 匿名挂载
-v 卷名:容器内路径					# 具名挂载
-v /宿主机路径:容器内路径		# 指定路径挂载

拓展:

# 通过 -v 容器内路径:or或者rw  改变读写权限
ro readonly			# 只读
rw readwrite		# 可读可写
# 一旦设置了容器权限,容器对我们挂载出来的内容就有限制了
docker run -d -P --name nginx02 -v ju_ming_gua_zai:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v ju_ming_gua_zai:/etc/nginx:rw nginx
# 只要看到or就说明这个路径只能通过宿主机来操作,容器内部是无法修改的,默认rw可读可写

初识Dockerfile

Dockerfile就是用来构建docker镜像的构建文件,其实就只是一串脚本

通过脚本可以生成镜像,镜像是一层一层的,脚本的一个个的命令,每个命令都是一层

# 创建一个dockerfile文件
# 文件的内容:指令(大写) 参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "-------end-----"
CMD /bin/bash
# 这里的每个命令就是镜像的一层

docker数据卷6dockerfile.png

启动刚刚创建的容器

docker数据卷7.png

那么这两个卷在主机的哪里的?

docker数据卷8.png

查看一下卷挂载的路径:

docker inspect dd12e56a09d8

docker数据卷9.png

查看数据是否同步:

docker数据卷10.png

上面的这种方式我们未来使用的会非常多,因为我们通常会构建自己的镜像,假设构建镜像的时候没有可挂载的卷,那么就需要手动挂载 -v 容器内路径

数据卷容器

比如两个MySQL同步数据,说白了也就是容器跟容器之间的数据同步

docker数据卷11.png

# 启动容器  并找到数据卷
docker run -it --name docker01 juning/centos:1.0
# 启动容器  并挂载docker01
docker run -it --name docker02 --volumes-from docker01 juning/centos:1.0

然后在各自的控制台创建文件:

docker数据卷12.png

可以看到docker01和docker02之间的文件已经共享

当然,容器间的数据共享可以无限套娃,比如docker01被docker02挂载 docker02被docker03挂载。。。。。

而且就算docker01被删除,docker02,docker03也仍可以看到共享的数据

因为docker之间相互独立,所以docker01、2、3之间属于一种相互拷贝的概念,当前也可以理解为是读取宿主机上的挂载卷

那么多个MySQL实现数据共享可以这么做:

$ docker run -d -p 3306:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql
$ docker run -d -p 3306:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql

结论

容器之间配置信息的传递,数据卷容器的生命周期会一直持续到没有容器使用为止(去中心化的概念)

但是一旦数据持久化到了本地,那么本地的数据是不会被删除的,即使没有容器去使用它(所有数据卷容器结束使用)


评论