Docker数据卷的使用
一、分层原理
在前面介绍过 ,镜像是一层层叠加而来,是只读不可修改的。最上面的一层称为“容器层”,是由镜像启动时,被添加的一层,我们的任何修改都会被记录在该容器层,而其它部分称为“镜像层”,都是只读不可修改的。镜像层的最下面我们称之为基础镜像(Base),大致的结构示意图如下所示。
镜像分层示意图分层的好处就是可以共享资源,比如多个镜像都由某个Base镜像构建而来,那么在下载这些镜像的时候,Base镜像就可以共享使用,只需要下载一份,只需要保存一份Base镜像即可。除此之外,分层所体现的最大特性就是Copy-on-Write
:
- 所有镜像的目录和文件会被组合成一个大的视图呈现在容器层,如果存在同名的,那么上层会覆盖下层的内容;
- 增加新的数据会被直接放在最上层的容器层进行保存,镜像层不会被修改;
- 尝试修改镜像层的数据会先从镜像层将数据copy到容器层,然后进行修改并保存在容器层,镜像层的数据不会被修改;
二、数据卷介绍
-
What?
Data Volume本质上是宿主机上的文件或者目录,我们可以把它挂载到容器内部的文件系统中,使得宿主机和容器能够共享数据卷中的内容。
-
Why?
容器中的数据在容器被销毁时也就消失了,在很多业务场景中这是不被允许的,比如数据库、日志系统、数据分析结果等,希望能将容器和存储分离开,容器即便销毁了,存储的数据依然存在。
多个容器之间也有数据共享的需求,只要还有一个容器存在,共享的数据就不应该被清除;
-
How?
常见的数据卷挂载主要有两种方式:命令挂载、使用DockerFile挂载,这两种方式都将在下面详细介绍。
三、命令挂载
3.1 指定路径挂载
所谓指定路径,是指同时指定了宿主机和容器内的挂载路径。
docker run -d -p 80:80 --name nginx01 -v E:\docker\nginx:/usr/nginx nginx
如上命令是在创建并启动容器的时候就进行目录的挂载,-d
表示后台启动,-p 80:80
表示绑定宿主机和容器的端口映射,--name nginx01
表示指定新建的容器名称指定为nginx01,-v local_path:container_path
表示挂载目录,将宿主机的路径local_path
和容器内的路径container_path
镜像挂载。
执行完成后,我们可以进入容器进行验证:
# pwd
/
# ls
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
boot docker-entrypoint.d etc lib media opt root sbin sys usr
# cd usr
# ls
bin games include lib libexec local nginx sbin share src
# cd nginx
# ls
#
可以看到,容器内部的/usr/nginx
目录已经存在,但是里面没有任何内容。现在我们在宿主机上或者在容器内的挂载目录内创建一个文件,在另外一边都能同步看到。
# pwd
/
# ls
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
boot docker-entrypoint.d etc lib media opt root sbin sys usr
# cd usr
# ls
bin games include lib libexec local nginx sbin share src
# cd nginx
# ls
# ls
readme.txt
# cat readme.txt
hello docker volume!#
我们此时将容器删除:docker rm nginx01
,然后在宿主机的挂载目录下发现,文件依然存在,并没有随着容器的移除而删除 。
需要注意的是:
- 在进行目录挂载的时候,要求宿主机中的文件或路径已经存在,而容器内则不作要求;
- 已经挂载的路径,本质上是容器把宿主机路径或者文件进步同步copy,而非引用的关系,所以同一目录或者文件是占用两倍存储空间;
- 可以在容器路径后加上
:ro
表示该路径容器只允许只读权限,或者加上:rw
表示该路径容器拥有读写权限(默认);
指定路径挂载当然存在一定的缺陷,比如我们指定了宿主机的挂载路径,就不利于容器的移植了 ,A宿主机有这个挂载路径,B宿主机也许就没有 。
3.2 匿名挂载
docker run -d -p 80:80 --name nginx2 -v /usr/nginx02 nginx
该命令与指定目录挂载的区别是-v /usr/nginx02
只是指定了容器内的挂载路径,并未指定宿主机的挂载路径。我们可以通过docker inspect name
来查看容器的详细信息得到具体宿主机的挂载路径:
"Mounts": [
{
"Type": "volume",
"Name": "592c56295f0200cb62b5f226cba1f53c77e45b48a95c77b6ce791c9b05696cc7",
"Source": "/var/lib/docker/volumes/592c56295f0200cb62b5f226cba1f53c77e45b48a95c77b6ce791c9b05696cc7/_data",
"Destination": "/usr/nginx02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
如上Source中的路径就是宿主机上的挂载路径,Destination是容器内的挂载路径。我们注意到卷名目录是一串无意义的数字字母组合,因此称为匿名挂载。
问题:以上挂载路径在windows系统的哪里尚未找到,有知道的可以帮忙补充下。
3.3 具名挂载
所谓具名就是指给无意义的数据卷起名字,挂载的格式和指定路径挂载一致 :
docker run -d -p 80:80 --name nginx3 -v nginx_volume:/usr/nginx03 nginx
此时 ,数据卷的名称就是我们指定的nginx_volume
,而不是无意义的数字字母组合了。
注意:
- 数据卷如果被容器使用中是不能被删除的,需要先删除容器后,才可以删除数据卷;
- 可以通过多个
-v
同时指定多个挂载路径;
四、共享数据
在前面的例子中,我们已经实现了宿主机挂载路径和容器内挂载路径的数据共享,下面就数据共享进行更深一步地探讨。
场景一:多个容器组成的集群需要共享宿主机的数据。
按照如上的使用说明,我们同时为多个容器挂载到宿主机的同一目录或者文件即可:
docker run -d -p 80:80 --name nginx01 -v E:\docker\nginx:/usr/nginx nginx
docker run -d -p 81:81 --name nginx02 -v E:\docker\nginx:/usr/nginx nginx
docker run -d -p 82:82 --name nginx03 -v E:\docker\nginx:/usr/nginx nginx
但是有一个缺点,一旦我们要修改宿主机的挂载目录,需要对每一个容器的挂载关系进行修改,显然不方便。此时我们就需要抽象一层容器出来,使得只有该容器和宿主机的挂载点绑定,其它容器和该容器绑定,这种容器我们称之为“数据卷容器”,Volume Container,简称VC容器。
docker create --name vc_data -v volume_name:container_path image_name
docker run -d -p 80:80 --name nginx01 --volumes-from vc_data nginx
docker run -d -p 81:81 --name nginx02 --volumes-from vc_data nginx
docker run -d -p 82:82 --name nginx03 --volumes-from vc_data nginx
如此,nginx01、nginx02、nginx03这三个容器就继承了vc_data数据卷容器的数据卷信息,使得它们和宿主机的挂载路径绑定了对应关系。随后只要修改vc_data的挂载关系,nginx01~nginx03就能自动更改,体现了编程领域的继承和解耦的优势。
场景二:多个容器组成的集群相互之间共享数据
这种场景就是不涉及宿主机的文件了,容器之间的数据不需要和宿主机进行共享。我们在创建镜像的时候就把数据封装到了镜像里面,并且建立了数据卷。
FROM busybox:latest
ADD readme.md /usr/file
VOLUME /usr/file
如上的dockerfile指定了数据卷就是容器的usr/file
目录,然后基于该dockerfile构建镜像并创建容器:
docker build -t new_image .
docker create --name vc_data new_image
如此新创建的vc_data就是容器卷镜像,它不需要运行,只是共享数据而已。
docker run -d -p 80:80 --name nginx01 --volumes-from vc_data nginx
docker run -d -p 81:81 --name nginx02 --volumes-from vc_data nginx
docker run -d -p 82:82 --name nginx03 --volumes-from vc_data nginx
如此,就能实现容器之间数据卷的共享。需要注意的是,只要任何一个容器仍然存在,那么该数据卷就不会被删除,只有当所有使用该数据卷的容器都删除了,数据卷才会被删除。
五、DockerFile挂载
在后面的内容中我们会介绍Dockerfile的使用,其中也可以在新镜像的制作时就进行数据卷的挂载,比如:
FROM java:8
VOLUME ["/tmp","/home/docker/demo"]
...
其中,VOLUME指令就是进行数据卷挂载,后面的参数是容器中需要挂载的路径,但是只能进行匿名挂载。