Docker-06-数据卷技术
1. 什么是数据卷?
数据卷是一个可供容器使用的特殊目录,它绕过文件系统,可以提供很多有用的特性:
- 数据卷可以在容器之间共享和重用。
- 对数据卷的更改会立即生效。
- 对数据卷的更新不会影响镜像。
- 数据卷会一直存在,直到没有容器使用。
数据卷的使用,类似于 linux 下对目录或文件进行 mount 操作。
2. 使用数据卷的场景
在多个容器之间共享数据,多个容器可以同时以只读或者读写的方式挂载同一个数据卷,从而共享数据卷中的数据。
当宿主机不能保证一定存在某个目录或一些固定路径的文件时,使用数据卷可以规避这种限制带来的问题。
当你想把容器中的数据存储在宿主机之外的地方时,比如远程主机上或云存储上。
当你需要把容器数据在不同的宿主机之间备份、恢复或迁移时,数据卷是很好的选择。
3. 数据卷原理
下图描述了 docker 容器挂载数据的三种方式:
docker数据卷的本质是容器中一个特殊目录。在容器创建过程中,docker会将宿主机上的指定目录(一个以数据卷ID为名称的目录)挂在到容器指定的目录上。这里使用的挂载方式是(bind mount),所以挂载完成后的宿主机目录和目标目录表现一致。
比如我们执行下面的命令创建数据卷 hello,并挂载到容器 testcon 的 /world 目录:
1 | docker volume create hello |
实际上在容器的创建过程中,类似于在容器中执行了下面的代码:
1 | // 将数据卷 hello 在宿主机上的目录绑定挂载到 rootfs 中指定的挂载点 /world 上 |
4. 具名挂载 匿名挂载
1 | # 如何确定是具名挂载还是匿名挂载,还是指定路径挂载 |
5. 数据卷的创建,挂载
- 创建随机名字的volume,并挂载到容器的/data目录(匿名挂载)
1 | docker run -it -d --name u1 -v /data ubuntu /bin/bash |
说明: 在用 docker run 命令的时候,使用 -v 标记可以在容器内创建一个数据卷。多次使用 -v 标记可以创建多个数据卷
- 创建命名的数据卷并挂载(具名挂载)
1 | 法1: |
- 将宿主机目录 挂载到容器(指定路径挂载)
1 | docker run -it -d --name u1 -v /tmp:/opt ubuntu /bin/bash |
- 将单个文件作为volume挂载到容器中.
1 | #将当前目录下的t.txt 文件挂载为 容器的/opt/t.txt |
- 挂载数据卷为只读 ro
1 | docker run -it -d --name u1 -v u1_vol:/data:ro ubuntu |
- 使用Dockerfile 添加volume
1 | #使用VOLUME 指令向容器添加volume,与上面(1)相同 |
在使用 docker build 命令生成镜像并且以该镜像启动容器时会挂载一个数据卷到 /data 目录。根据我们已知的数据覆盖规则,如果镜像中存在 /data 目录,这个目录中的内容将全部被复制到宿主机中对应的目录中,并且根据容器中的文件设置合适的权限和所有者。
注意:
VOLUME 指定不能挂载主机中的指定目录。这是为了保证DockerFile的一致性,因为不能保证所有的宿主机都有对应的目录。
在实际的使用中,还有一个陷阱需要大家注意:在Dockerfile使用VOLUME指令之后的代码,如果尝试对这个数据卷进行修改,这些修改都不会生效
下面是一个这样的例子:
1 | FROM ubuntu |
通过这个 Dockerfile 创建镜像并启动容器后,该容器中存在用户 nick,并且能够看到 /data 目录挂载的数据卷。但是 /data 目录内并没有文件 test.txt,更别说 test.txt 文件的所有者属性了。要解释这个现象需要我们了解通过 Dockerfile 创建镜像的过程:
Dockerfile 中除了 FROM 指令的每一行都是基于上一行生成的临时镜像运行一个容器,执行一条指令并执行类似 docker commit 的命令得到一个新的镜像。这条类似 docker commit 的命令不会对挂载的数据卷进行保存。
所以上面的 Dockerfile 最后两行执行时,都会在一个临时的容器上挂载 /data,并对这个临时的数据卷进行操作,但是这一行指令执行并提交后,这个临时的数据卷并没有被保存。因而我们最终通过镜像创建的容器所挂载的数据卷是没有被最后两条指令操作过的。我们姑且叫它 “Dockerfile 中数据卷的初始化问题”。
下面的写法可以解决 Dockerfile 中数据卷的初始化问题:
1 | FROM ubuntu |
通过这个 Dockerfile 创建镜像并启动容器后,数据卷的初始化是符合预期的。这是由于在挂载数据卷时,/data 已经存在,/data 中的文件以及它们的权限和所有者设置会被复制到数据卷中。
还有另外一种方法可以解决 Dockerfile 中数据卷的初始化问题。就是利用 CMD 指令和 ENTRYPOINT 指令的执行特点:与 RUN 指令在镜像构建过程中执行不同,CMD 指令和 ENTRYPOINT 指令是在容器启动时执行。因此使用下面的 Dockerfile 也可以达到对数据卷的初始化目的:
1 | FROM ubuntu |
6. 数据的覆盖问题
- 如果挂载一个空的数据卷到容器的一个非空目录中,那么这个目录下的文件会被复制到数据卷中
- 如果挂载一个非空的数据卷到容器的一个目录中,那么容器中的目录会显示数据卷的数据。如果原来容器中的目录有数据,那么这些原始数据会被隐藏掉
总结:
灵活利用第一个规则可以帮助我们初始化数据卷中的内容。
掌握第二条规则可以保证挂载数据卷后的数据总是你期望的数据。
7. 共享数据卷与数据卷容器
数据卷的共享有两种方式:
7.1 两个docker之间直接共享数据卷
--volumes-from
:相当于继承的概念
1 | root@ubuntu:~# docker run -it -d --name u1 -v share_vol:/opt ubuntu:14.04 /bin/bash #将数据卷share_vol挂载到容器的/opt |
7.2 单独创建一个数据容器,其他容器与之共享volume,推荐使用这种方式
--volumes-from
:相当于继承的概念
如果用户需要在容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。数据卷容器其实就是一个普通的容器,专门用它提供数据卷供其他容器挂载。
一个容器挂载了一个volume,即使这个容器停止运行,该volume仍然存在,其他容器也可以使用
--volumes-from
与这个容器共享卷
- 创建数据卷容器,给容器挂载一个 volume后容器停止,好节约资源.
1 | docker run --name share_docker -v share_vol:/data ubuntu /bin/bash |
- 然后再其他容器中使用
--volumes-from
来挂在 share_docker 容器中的数据卷
1 | root@ubuntu:~# docker run -it -d --name u1 --volumes-from share_docker ubuntu /bin/bash |
8. 备份,恢复或者迁移 volume
volume作为数据的载体,在很多情况下需要对其中的数据进行备份迁移。
一个很容易想到的办法是使用 docker inspect 查找到volume 在宿主机上对应的文件夹的位置,然后复制其中的内容或者打包。这种做法不推荐,推荐使用 –volumes-from来实现.
8.1 备份
1 | root@ubuntu:~# docker run --rm --volumes-from share_docker -v $(pwd):/backup ubuntu tar cvf /backup/data.tar /data |
8.2 恢复
1 | 创建需要恢复数据的目标容器: |
9. 使用mount语法挂载数据卷
之前我们使用 –volume(-v) 选项来挂载数据卷,现在docker提供了更强大的 –mount 选项来管理数据卷。
mount 选项可以通过逗号分割多个键值对一次提供多个配置项,因此mount 选项可以提供比volume 选项更详细的配置
使用 mount 选项的常用配置如下:
- type 指定挂载方式,我们这里用到的是 volume,其实还可以有 bind 和 tmpfs。
- volume-driver 指定挂载数据卷的驱动程序,默认值是 local。
- source 指定挂载的源,对于一个命名的数据卷,这里应该指定这个数据卷的名称。在使用时可以写 source,也可以简写为 src。
- destination 指定挂载的数据在容器中的路径。在使用时可以写 destination,也可以简写为 dst 或 target。
- readonly 指定挂载的数据为只读。
- volume-opt 可以指定多次,用来提高更多的 mount 相关的配置。
下面我们来看具体的例子:
1 | $ docker volume create hello |
我们创建了名称为 hello 的数据卷,然后把它挂在到容器中的 /world 目录。通过 inspect 命令查看容器的详情中的 “Mounts” 信息可以验证实际的数据卷挂载结果.
- 使用volume driver 把数据存储到别的地方(远程)
除了默认的把数据卷中的数据存储在宿主机,docker 还允许我们通过指定 volume driver 的方式把数据卷中的数据存储在其它的地方,比如 Azrue Storge 或 AWS 的 S3。
简单起见,我们接下来的 demo 演示如何通过 vieux/sshfs 驱动把数据卷的存储在其它的主机上。
- docker 默认是不安装 vieux/sshfs 插件的,我们可以通过下面的命令进行安装:
1 | docker plugin install --grant-all-permissions vieux/sshfs |
- 然后通过 vieux/sshfs 驱动创建数据卷,并指定远程主机的登录用户名、密码和数据存放目录:
1 | docker volume create --driver vieux/sshfs \ |
注意,请确保你指定的远程主机上的挂载点目录是存在的(demo 中是 /home/nick/sshvolume 目录),否则在启动容器时会报错。
- 最后在启动容器的时候指定挂载这个数据卷:
1 | docker run -id \ |
你在容器中 /world 目录下操作的文件都存储在远程主机的 /home/nick/sshvolume 目录中。进入容器testcon然后在 /world 目录下创建一个文件,然后打开远程主机/home/nick/sshvolume 目录进行查看,你新建的文件看看是不是已经在那里了!
- docker volume 使用NFS存储
1 | NFS服务端,配置nfs共享 |
1 | nfs客户端 |
1 | 创建volume 连接 39.106.171.56:/data/nfs |
1 | 查看 |
1 | 容器使用volume-nfs |
1 | 查看 |
1 | 容器创文件测试 |