前一篇随笔中咱们了解了docker的网络相关说明,回顾请参考http://www.javashuo.com/article/p-upglxrrl-hs.html;今天咱们来聊一聊docker的数据管理相关说明;html
在前面的博客中咱们有强调过docker的镜像是分层构建的,把镜像启动为容器后,docker会加载只读层镜像并在镜像最顶层添加一个读写层;咱们还说过该读写层不属于镜像属于容器;若是该容器宕掉了,那么随之镜像最顶层的读写层也随之消失;那么问题来了,若是以前运行的容器,在宕掉前在可写层生成的数据怎么保存下来呢?为了解决这个问题,咱们先来看看docker的COW机制吧;node
如上图所示,docker镜像是分层构建的,在最下面一层的数据到第二层或更高层有相同数据时,在上面一层的数据就会覆盖下面一层相同的数据;从而使得在上面一层和下面一层相同的数据只会存在一份,不一样的数据都会存在;一样的道理,docker镜像经过层层镜像构建,在docker镜像最顶层看到的数据就应该是下面若干层重叠之后,相同的数据在最上层只会看到一份,而其余数据则是由下面若干层不一样数据的叠加构成;在运行层容器时,容器内部看到的数据就是全部层镜像不一样数据的集合和相同数据留一份的结果;若是运行过程当中容器修改了现有文件,那么该文件就从读写层下面去复制一份到读写层,该文件的原来在只读层的文件仍是存在,只是已经被读写层把该文件的副本隐藏了,这就叫docker的写时复制(cow)机制;linux
从上面的信息咱们了解到docker容器在产生数据是在可写层,在修改数据时,会把原来的数据从原来只读层复制到可写层,从而隐藏原来的只读层的,可是只读层的数据还在;因此容器删除后,咱们修改的数据不会保存到镜像,而是随容器的删除而删除;关闭并重启容器对于数据不受什么影响;咱们如今有没有一种办法让读写层的数据保存起来,即使容器被删除后数据还依然可存在呢?nginx
先来讲说nfs数据共享吧;nfs共享是经过把一个目录经过网络的形式挂载到另一个目录;从而实现往一个目录写数据时,就至关于把数据写到远端的目录的文件中;在docker容器中咱们把这种相似的方式叫作卷;所谓卷就是容器上的一个或多个目录,此类目录可绕过联合文件系统,与宿主机上的某目录产生关联关系(绑定,相似nfs里的挂载);volume于容器初始化之时就会建立,由base image提供的卷中的数据会在此期间完成复制;volume的初衷是独立于容器的生命周期实现数据持久化,所以删除容器时,既不会删除卷,也不会对那些没有被引用的卷做垃圾回收操做;web
卷为docker提供了独立于容器的数据管理机制;咱们能够把镜像想象成静态文件,例如程序,把卷类比成动态内容,好比数据,因而镜像能够复用,而卷能够共享;卷实现了程序(镜像)和数据(卷)的分离;实现了容器之间的数据共享和复用,使得容器间传递数据变得高效方便;对数据卷内数据的修改会立马生效,不管是在容器中修改仍是在本地操做;docker有两种类型的卷,每种类型都在容器中存在一个挂载点,但其在宿主机上的位置有所不一样而已;bind mount volume这种卷是由用户指定目录把存储上的一个目录挂载到容器内部的某个目录;docekr-managed volume这种卷是docker本身管理的卷,一般表现形式上把宿主机上的/var/lib/docker/vfs/dir/某个卷的ID 挂载到容器内部某个目录下;docker
示例:在的docker容器中使用docker-managed 类型的volumecentos
[root@node1 ~]# docker run --name m2 -it --rm -v /mydata linux1874/myimg:v0.1 /bin/sh / # ls / bin dev etc home mydata proc root sys tmp usr var / # cd /mydata/ /mydata # ls /mydata # [root@node1 ~]# docker container inspect m2 -f {{.Mounts}} [{volume 65db95bc3010530381e2bcc20fdd216329a502527e17a1e70c1cb7f6e2d2a422 /var/lib/docker/volumes/65db95bc3010530381e2bcc20fdd216329a502527e17a1e70c1cb7f6e2d2a422/_data /mydata local true }] [root@node1 ~]# ll /var/lib/docker/volumes/65db95bc3010530381e2bcc20fdd216329a502527e17a1e70c1cb7f6e2d2a422/_data total 0 [root@node1 ~]# echo "hello world" > /var/lib/docker/volumes/65db95bc3010530381e2bcc20fdd216329a502527e17a1e70c1cb7f6e2d2a422/_data/aa.txt [root@node1 ~]# cat /var/lib/docker/volumes/65db95bc3010530381e2bcc20fdd216329a502527e17a1e70c1cb7f6e2d2a422/_data/aa.txt hello world [root@node1 ~]# docker attach m2 /mydata # ls aa.txt /mydata # cat aa.txt hello world /mydata #
提示:-v只指定了一个目录表示指定容器内部的目录,它会经过docker daemon 自动在宿主机上生成挂载目录;像这种方式的挂载就叫作docker -managed类型的数据卷;从上面的信息能够看到咱们能够用docker container inspect -f {{.Mounts}} 容器名称,来查看容器的卷、标识符以及挂载点主机目录信息;咱们在生成的挂载目录下建立一个aa.txt的文件,而后在容器里/mydata里可以看到该文件和文件内容;bash
测试:咱们把容器停掉看看数据卷是否会被删除呢?网络
[root@node1 ~]# cat /var/lib/docker/volumes/65db95bc3010530381e2bcc20fdd216329a502527e17a1e70c1cb7f6e2d2a422/_data/aa.txt hello world [root@node1 ~]# docker attach m2 /mydata # ls aa.txt /mydata # cat aa.txt hello world /mydata # exit [root@node1 ~]# [root@node1 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 33c6f135eb86 linux1874/myimg:v0.1 "/bin/sh -c '/bin/ht…" 20 minutes ago Up 20 minutes m1 [root@node1 ~]# cat /var/lib/docker/volumes/65db95bc3010530381e2bcc20fdd216329a502527e17a1e70c1cb7f6e2d2a422/_data/aa.txt cat: /var/lib/docker/volumes/65db95bc3010530381e2bcc20fdd216329a502527e17a1e70c1cb7f6e2d2a422/_data/aa.txt: No such file or directory [root@node1 ~]#
提示:在运行容器时若是使用了--rm选项,但容器停掉之后,对应挂载点数据卷会随之删除;curl
测试:运行容器不使用--rm选项,看看容器停掉后,是否还会删除数据卷?
[root@node1 ~]# docker run --name m2 -v /mydata -it linux1874/myimg:v0.1 /bin/sh / # ls / bin dev etc home mydata proc root sys tmp usr var / # cd mydata/ /mydata # ls /mydata # [root@node1 ~]# docker container inspect -f {{.Mounts}} m2 [{volume 28c477840d926c2fc33ebfacd607b84cfd5a272d279e1f966a65b7cb2f1d9a2e /var/lib/docker/volumes/28c477840d926c2fc33ebfacd607b84cfd5a272d279e1f966a65b7cb2f1d9a2e/_data /mydata local true }] [root@node1 ~]# cd /var/lib/docker/volumes/28c477840d926c2fc33ebfacd607b84cfd5a272d279e1f966a65b7cb2f1d9a2e/_data [root@node1 _data]# ls [root@node1 _data]# echo "hello tom" > aa.txt [root@node1 _data]# cat aa.txt hello tom [root@node1 _data]# docker attach m2 /mydata # ls aa.txt /mydata # cat aa.txt hello tom /mydata # exit [root@node1 _data]#docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e6560584d41e linux1874/myimg:v0.1 "/bin/sh" About a minute ago Exited (0) 9 seconds ago m2 33c6f135eb86 linux1874/myimg:v0.1 "/bin/sh -c '/bin/ht…" 32 minutes ago Up 32 minutes m1 [root@node1 _data]# docker container rm m2 m2 [root@node1 _data]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 33c6f135eb86 linux1874/myimg:v0.1 "/bin/sh -c '/bin/ht…" 35 minutes ago Up 35 minutes m1 [root@node1 _data]# cd [root@node1 ~]# cd - /var/lib/docker/volumes/28c477840d926c2fc33ebfacd607b84cfd5a272d279e1f966a65b7cb2f1d9a2e/_data [root@node1 _data]# ls aa.txt [root@node1 _data]# cat aa.txt hello tom [root@node1 _data]#
提示:在不使用--rm选项运行容器,容器中止后删除容器,对于数据卷是不会被删除的;
示例:在docker容器中使用bind-mount 类型volume
[root@node1 ~]# mkdir /hostdir/v1 -p [root@node1 ~]# docker run --name m3 -it -v /hostdir/v1/:/mydata linux1874/myimg:v0.1 /bin/sh / # ls / bin dev etc home mydata proc root sys tmp usr var / # cd /mydata/ /mydata # ls /mydata # [root@node1 ~]# docker container inspect -f {{.Mounts}} m3 [{bind /hostdir/v1 /mydata true rprivate}] [root@node1 ~]# cd /hostdir/v1/ [root@node1 v1]# ls [root@node1 v1]# echo "hello jerry" > aa.sh [root@node1 v1]# cat aa.sh hello jerry [root@node1 v1]# docker attach m3 /mydata # ls aa.sh /mydata # cat aa.sh hello jerry /mydata # exit [root@node1 v1]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 62ddbdc11a62 linux1874/myimg:v0.1 "/bin/sh" About a minute ago Exited (0) 4 seconds ago m3 33c6f135eb86 linux1874/myimg:v0.1 "/bin/sh -c '/bin/ht…" 41 minutes ago Up 41 minutes m1 [root@node1 v1]# docker container rm m3 m3 [root@node1 v1]# cd [root@node1 ~]# cd - /hostdir/v1 [root@node1 v1]# ls aa.sh [root@node1 v1]# cat aa.sh hello jerry [root@node1 v1]#
提示:-v指定宿主机目录:容器目录表示把宿主机目录挂载到容器的某个目录;容器里的目录能够是不存在的目录,它会自动建立;这种数据卷咱们叫作bind-mount类型的数据卷;一般表现形式就是用户本身定义把宿主机的那个目录看成数据卷挂载到容器里的某个目录;和上面同样咱们在宿主机上的对应目录下建立文件,在容器对应目录是能够正常访问到该文件的;-v可以使用屡次来指定不一样的数据卷挂载关系;一般bind-mount类型的卷用的比较多;
示例:多个容器的卷使用同一宿主机目录
[root@node1 ~]# ll /hostdir/v1/aa.sh -rw-r--r-- 1 root root 12 May 24 13:59 /hostdir/v1/aa.sh [root@node1 ~]# cat /hostdir/v1/aa.sh hello jerry [root@node1 ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@node1 ~]# docker run --name m1 -d -v /hostdir/v1/:/var/www/web/html linux1874/myimg:v0.1 9eb3287e4007f0656b1f099aac9504724307679ac41689774059388a96d75cf0 [root@node1 ~]# docker container inspect -f {{.Mounts}} m1 [{bind /hostdir/v1 /var/www/web/html true rprivate}] [root@node1 ~]# docker container inspect -f {{.NetworkSettings.IPAddress}} m1 172.17.0.2 [root@node1 ~]# curl http://172.17.0.2 <HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD> <BODY><H1>404 Not Found</H1> The requested URL was not found </BODY></HTML> [root@node1 ~]# curl http://172.17.0.2/aa.sh hello jerry [root@node1 ~]# docker run --name m2 -d -v /hostdir/v1/:/var/www/web/html linux1874/myimg:v0.1 ff877e29d10c55b355b5270218d486894958880058e2eb7bef9bdd1c7c531f0f [root@node1 ~]# docker container inspect -f {{.Mounts}} m2 [{bind /hostdir/v1 /var/www/web/html true rprivate}] [root@node1 ~]# docker container inspect -f {{.NetworkSettings.IPAddress}} m2 172.17.0.3 [root@node1 ~]# curl http://172.17.0.3 <HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD> <BODY><H1>404 Not Found</H1> The requested URL was not found </BODY></HTML> [root@node1 ~]# curl http://172.17.0.3/aa.sh hello jerry [root@node1 ~]#
提示:数据卷挂载到容器会覆盖容器原有文件;这个同mount挂载没有本质的不一样;从上面信息能够看到aa.sh就把原有的index.html给覆盖了;除了以上方式指定挂载同一宿主机目录外,咱们还可使用--volumes-from选项来指定从那个容器里复制数据挂载到本容器;以下
[root@node1 ~]# docker run --name m3 -d --volumes-from m1 linux1874/myimg:v0.1 61a1fbdc9c559a594870dc1e2bbf505a4eb588fc03e5ab50233b55b907beb9b0 [root@node1 ~]# docker container inspect -f {{.Mounts}} m3 [{bind /hostdir/v1 /var/www/web/html true rprivate}] [root@node1 ~]# docker container inspect -f {{.NetworkSettings.IPAddress}} m3 172.17.0.4 [root@node1 ~]# curl http://172.17.0.4/aa.sh hello jerry [root@node1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE linux1874/myimg v0.1 e408b1c6e04f 38 hours ago 1.22MB busybox latest 78096d0a5478 10 days ago 1.22MB centos 7 b5b4d78bc90c 2 weeks ago 203MB nginx stable-alpine ab94f84cc474 4 weeks ago 21.3MB [root@node1 ~]# docker run --name c1 -it --volumes-from m1 centos:7 /bin/sh sh-4.2# ls / anaconda-post.log dev home lib64 mnt proc run srv tmp var bin etc lib media opt root sbin sys usr sh-4.2# cat /var/www/web/html/aa.sh hello jerry sh-4.2#
示例:以只读方式挂载数据卷
[root@node1 ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@node1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE linux1874/myimg v0.1 e408b1c6e04f 38 hours ago 1.22MB busybox latest 78096d0a5478 10 days ago 1.22MB centos 7 b5b4d78bc90c 2 weeks ago 203MB nginx stable-alpine ab94f84cc474 4 weeks ago 21.3MB [root@node1 ~]# docker run --name c1 -it -v /hostdir/v1/:/mydata:ro centos:7 /bin/sh sh-4.2# ls / anaconda-post.log dev home lib64 mnt opt root sbin sys usr bin etc lib media mydata proc run srv tmp var sh-4.2# cd mydata/ sh-4.2# ls aa.sh sh-4.2# echo "hello tom" > aa.sh sh: aa.sh: Read-only file system sh-4.2# exit exit [root@node1 ~]# docker run --name c2 -it -v /hostdir/v1/:/mydata centos:7 /bin/sh sh-4.2# cd /mydata/ sh-4.2# ls aa.sh sh-4.2# echo "hello tom" > aa.sh sh-4.2# cat aa.sh hello tom sh-4.2# exit exit [root@node1 ~]#
提示:咱们挂载数据卷不指定权限默认是rw,以读写方式挂载;指定权限为ro(只读)挂载数据卷后,在容器内部就不能修改数据卷里的文件内容了;
示例:利用容器备份另外一容器的数据
[root@node1 ~]# ll /hostdir/v1/ total 4 -rw-r--r-- 1 root root 10 May 24 14:42 aa.sh [root@node1 ~]# echo "hello world" > /hostdir/v1/bb.sh [root@node1 ~]# echo "hello world,hello tom" > /hostdir/v1/cc.sh [root@node1 ~]# ls /hostdir/v1/ aa.sh bb.sh cc.sh [root@node1 ~]# docker run --name m1 -d -v /hostdir/v1/:/var/www/web/html linux1874/myimg:v0.1 5959e832c01f68c24e2542138a95eb7bfeabcb7608b070ef1b536c625ddfd612 [root@node1 ~]# docker run --name c1 --volumes-from m1 -v $(pwd):/backup centos:7 tar cvf /backup/backup.tar.gz /var/www/web/html tar: Removing leading `/' from member names /var/www/web/html/ /var/www/web/html/aa.sh /var/www/web/html/bb.sh /var/www/web/html/cc.sh [root@node1 ~]# ls backup.tar.gz [root@node1 ~]# tar xf backup.tar.gz [root@node1 ~]# ls backup.tar.gz var [root@node1 ~]# cd var/www/web/html/ [root@node1 html]# ls aa.sh bb.sh cc.sh [root@node1 html]# cat aa.sh bb.sh cc.sh hello tom hello world hello world,hello tom [root@node1 html]#
提示:解释下上面利用容器备份另外一容器里的数据;首先经过--volumes-from来克隆m1的数据卷,而后在经过-v来指定把当前路径挂载到容器内部的/backup目录下,而后经过启动容器执行tar cvf /backup/backup.tar.gz /var/www/web/html来把容器里的/var/www/web/html的文件打包到/backup/backup.tar.gz(容器内部的目录);可以在当前目录看到打包的文件缘由是由于咱们把当前路径挂载到容器里的/backup目录,因此咱们在当前宿主机目录可以看到打包好的文件;
示例:利用容器恢复刚才打包的数据文件
首先运行一个容器
[root@node1 ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5959e832c01f linux1874/myimg:v0.1 "/bin/sh -c '/bin/ht…" 15 minutes ago Up 15 minutes m1 [root@node1 ~]# docker run --name m2 -it -v /var/www/web/html linux1874/myimg:v0.1 /bin/sh / # ls /var/www/web/html/ index.html / # [root@node1 ~]#
而后在来一容器克隆以前容器的数据卷,经过挂载当前目录为数据卷,在执执行 tar xf命令来解包
[root@node1 ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6462f22e0d7f linux1874/myimg:v0.1 "/bin/sh" 2 minutes ago Up 2 minutes m2 5959e832c01f linux1874/myimg:v0.1 "/bin/sh -c '/bin/ht…" 22 minutes ago Up 21 minutes m1 [root@node1 ~]# docker run --name c2 --volumes-from m2 -v $(pwd):/backup centos:7 tar xf /backup/backup.tar.gz [root@node1 ~]# docker attach m2 / # ls bin dev etc home proc root sys tmp usr var / # ls /var/www/web/html/ aa.sh bb.sh cc.sh index.html / # cd /var/www/web/html/ /var/www/web/html # cat aa.sh bb.sh cc.sh hello tom hello world hello world,hello tom /var/www/web/html #
提示:以上命令的思想是运行容器C2 把m2的数据卷挂载先克隆到c2上,而后经过挂载当前宿主机目录到容器的/backup,而后再执行解包操做便可;这里还须要注意一点,若是容器的工做目录不是根目录或者是其余特殊目录,解压命令能够经过-C来指定解压到那个目录;