环境说明:

主机名 操作系统版本 IP地址 docker版本 说明
ubuntu1604 Ubuntu 16.04.5 172.27.9.31 18.09.2 docker主机
centos7 centos7.3 172.27.9.181 / NFS服务器

ubuntu安装详见:Ubuntu16.04.5以lvm方式安装全记录
docker安装详见:Ubuntu16.04安装Docker
 在Linux上运行的Docker有三种不同的方式将数据从 Docker Host挂载到 Docker 容器,并实现数据的读取和存储:volumes、bind mounts、tmpfs。  
三者的区别在于数据存储在docker主机的位置不同。

Docker实践(三):数据持久化及共享_第1张图片

  • Volumes(又称docker managed volume)储在主机文件系统的中,由docker管理(在Linux上默认位置为/var/lib/docker/volumes/),只有Docker进程能修改该位置,Volumes是在Docker中保存数据的最佳方式。
  • Bind mounts可以存储在主机系统的任何位置,可能是重要的系统文件或目录,Docker主机或Docker容器上的非Docker进程可以随时修改它们。
  • tmpfs挂载仅存储在主机系统的内存中,不写入主机系统的文件系统。 

一、Volumes

简介

volumes(也被称为Docker-managed volumes)是保存Docker容器生成和使用的数据的首选机制。相较于bind mounts依赖于主机的目录结构,volumes完全由Docker管理。与bind mounts相比,volumes有几个优势:

  • 与bind mounts相比,volumes更容易备份或迁移。
  • 可以使用docker cli命令或docker api管理volumes。
  • volumes可以在Linux和Windows容器上工作。
  • 可以更安全地在多个容器之间共享volumes。
  • Volume drivers可以实现在远程主机或云主机存储数据以供加密卷的内容,或添加其他功能。
  • 新的volumes可以通过容器预先填充其内容。

此外,volumes通常比在容器的可写层中保存数据更好,因为volumes不会增加使用它的容器的大小,并且volumes的内容存在于给定容器的生命周期之外(即使容器被销毁volumes也会保存在docker host的文件系统中)
volumes在docker host的位置:
Docker实践(三):数据持久化及共享_第2张图片

Volumes测试

1.新建Volume

root@ubuntu1604:~# docker volume create my-vol
my-vol

2.列出Volumes

root@ubuntu1604:~# docker volume ls
DRIVER              VOLUME NAME
local               my-vol

3.查看volume详情

root@ubuntu1604:~# docker volume inspect my-vol
[
    {
        "CreatedAt": "2019-03-25T15:02:16+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

volume 在host上的默认目录为:/var/lib/docker/volumes/$(VOLUME NAME)/_data
新建的volume内容为空

root@ubuntu1604:~# ll /var/lib/docker/volumes/my-vol/_data
total 8
drwxr-xr-x 2 root root 4096 Mar 25 15:02 ./
drwxr-xr-x 3 root root 4096 Mar 25 15:02 ../

4.使用volume

4.1新建container

新建container myweb01并使用volume my-vol

root@ubuntu1604:~# docker run -d -p 80:80 -v my-vol:/usr/local/apache2/htdocs --name myweb01 httpd

容器myweb01的80端口被映射为主机的80端口,my-vol被挂载到apache server存放静态文件的目录/usr/local/apache2/htdocs  

4.2访问myweb01

root@ubuntu1604:~# curl 172.27.9.31:80

It works!

4.3再次查看volume内容

root@ubuntu1604:~# ll /var/lib/docker/volumes/my-vol/_data                                        
total 12
drwxr-sr-x 2 root www-data 4096 Mar 25 15:30 ./
drwxr-xr-x 3 root root     4096 Mar 25 15:02 ../
-rw-r--r-- 1 root src        45 Jun 12  2007 index.html
root@ubuntu1604:~# more /var/lib/docker/volumes/my-vol/_data/index.html 

It works!

发现容器myweb01的内容index.html被复制到volume中,这也印证了前面所讲的volumes的特点之一:“新的volumes可以通过容器预先填充其内容” 

4.4更新数据并访问

root@ubuntu1604:~# echo "update volumes:loong576" >/var/lib/docker/volumes/my-vol/_data/index.html 
root@ubuntu1604:~# curl 172.27.9.31:80                                                             
update volumes:loong576

更新volume中的index.html,发现容器myweb01的访问内容也一并更新。  

4.5销毁volume

root@ubuntu1604:~# docker rm -f -v myweb01
myweb01
root@ubuntu1604:~# more /var/lib/docker/volumes/my-vol/_data/index.html
update volumes:loong576
root@ubuntu1604:~# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
root@ubuntu1604:~# docker volume ls
DRIVER              VOLUME NAME
local               my-vol
root@ubuntu1604:~# docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
my-vol

Total reclaimed space: 24B
root@ubuntu1604:~# docker volume ls   
DRIVER              VOLUME NAME
root@ubuntu1604:~# more /var/lib/docker/volumes/my-vol/_data/index.html
more: stat of /var/lib/docker/volumes/my-vol/_data/index.html failed: No such file or directory

当销毁容器时,volume没有被同时消除,留下孤儿volume,通过docker volume prune即可删除   

二、Bind mounts  

简介

 Docker早期就有了Bind mounts。与volumes相比,Bind mounts的功能有限。使用Bind mounts时,主机上的文件或目录将装载到容器中。文件或目录由其在主机上的完整路径或相对路径引用。相反,使用卷时,会在主机上Docker的存储目录中创建一个新目录,Docker管理该目录的内容。

 对于Bind mounts,文件或目录不需要已经存在于Docker主机上。如果它还不存在,则按需创建。Bind mounts的性能非常好,但它们依赖于主机的文件系统,该文件系统具有特定的可用目录结构。如果您正在开发新的Docker应用程序,请考虑改用volumes。另外不能使用docker cli命令直接管理绑定装载。
Bind mounts在docker host的位置:
Docker实践(三):数据持久化及共享_第3张图片

Bind mounts测试

本文使用NFS作为Bind mounts的挂载点
NFS搭建配置详见:Centos7下NFS服务器搭建及客户端连接配置

1.挂载nfs并新建index.html

root@ubuntu1604:~# mount -t nfs 172.27.9.181:/nfs /mywebtest 
root@ubuntu1604:~# df -h
Filesystem               Size  Used Avail Use% Mounted on
udev                     467M     0  467M   0% /dev
tmpfs                     98M  5.9M   92M   7% /run
/dev/mapper/rootvg-root  4.6G  539M  3.8G  13% /
/dev/mapper/rootvg-usr   4.6G  921M  3.5G  21% /usr
tmpfs                    488M     0  488M   0% /dev/shm
tmpfs                    5.0M     0  5.0M   0% /run/lock
tmpfs                    488M     0  488M   0% /sys/fs/cgroup
/dev/sda1                453M   59M  367M  14% /boot
/dev/mapper/rootvg-tmp   4.6G  9.6M  4.4G   1% /tmp
/dev/mapper/rootvg-opt   4.6G  9.6M  4.4G   1% /opt
/dev/mapper/rootvg-var   4.6G  470M  3.9G  11% /var
/dev/mapper/rootvg-home  4.6G  9.6M  4.4G   1% /home
cgmfs                    100K     0  100K   0% /run/cgmanager/fs
tmpfs                     98M     0   98M   0% /run/user/0
172.27.9.181:/nfs        6.0G   97M  5.9G   2% /mywebtest
root@ubuntu1604:~# cd /mywebtest/ && touch index.html && echo "bind mount webtest:loong576" > index.html
root@ubuntu1604:/mywebtest# more index.html 
bind mount webtest:loong576

/mywebtest被挂载到远程的nfs服务器172.27.9.181的/nfs目录,并在/mywebtest目录新建index.html文件

2.新建container myweb02

root@ubuntu1604:~# docker run -d -p 81:80 -v /mywebtest/:/usr/local/apache2/htdocs --name myweb02 httpd
749a107648450a627a03fadc29680a0cdaff1e68ba10421ddebb52e2f7f7baf4

新建容器myweb02,将 /mywebtest挂载到容器/usr/local/apache2/htdocs,容器的80端口映射为主机的81端口。

3.访问myweb02

root@ubuntu1604:~# curl 172.27.9.31:81
bind mount webtest:loong576

发现访问内容为刚刚新建的index.html,bind mounts的内容覆盖了了容器的index.html。

4.更新index.html

root@ubuntu1604:~# echo "update bind mount webtest:loong576 02" >/mywebtest/index.html 
root@ubuntu1604:~# curl 172.27.9.31:81                                                 
update bind mount webtest:loong576 02

更新index.html,访问myweb02,发现一并更新

5.销毁容器

root@ubuntu1604:~# docker rm -f myweb02
myweb02
root@ubuntu1604:~# docker ps -a        
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
root@ubuntu1604:~# more /mywebtest/index.html 
update bind mount webtest:loong576 02

容器myweb02被销毁,但是Bind mounts继续存在。
volumes与bind mounts异同:

不同点 volumes bind mounts
Source位置 /var/lib/docker/volumes/... 可以任意指定
对已有挂载点影响 容器内数据复制到volume 覆盖掉容器的内容
是否支持单个文件 不支持,只能是目录 支持
权限控制 读写或者只读 读写或者只读
移植性 强,无需指定host目录 弱,与host path绑定

三、tmpfs mounts

简介

 volumes和bind mounts允许您在主机和容器之间共享文件,这样即使容器停止,您也可以保留数据。
 如果您在Linux上运行docker,则有第三个选项:tmpfs mounts。当使用tmpfs装载创建容器时,容器可以在容器的可写层之外创建文件。
 与volumes和bind mounts不同,tmpfs挂载是临时的,并且只持久存在于主机内存中。当容器停止时,tmpfs挂载将被删除,在那里写入的文件将不会被持久化。
tmpfs mounts在docker host的位置:
Docker实践(三):数据持久化及共享_第4张图片

tmpfs mounts测试

1.新建container myweb03

root@ubuntu1604:~# docker run -d -p 82:80 --tmpfs /web03 --name myweb03 httpd 

新建容器myweb03,容器对目录/web03所有的读写操作都在内存中。

2.新建test.txt

root@ubuntu1604:~# docker exec -it myweb03 bash                              
root@f91161779960:/usr/local/apache2# cd /web03/ && echo tmpfs-test > test.txt
root@f91161779960:/web03# more test.txt 
tmpfs-test

在/web03中新建测试文件test.txt,重启容器后观察该文件是否存在。

3.重启myweb03

root@ubuntu1604:~# docker stop myweb03
myweb03
root@ubuntu1604:~# docker start myweb03
myweb03
root@ubuntu1604:~# docker exec -it myweb03 bash
root@f91161779960:/usr/local/apache2# cd /web03/
root@f91161779960:/web03# ls -l
total 0

重启容器,发现测试文件test.txt消失。

四、容器间的数据共享

bind mounts方式

 该方式为依赖于主机的共享,多个容器通过 Volume 绑定到主机上的相同位置

1.新建index.html

root@ubuntu1604:~# echo "container datas share : bind mounts" > /mywebtest/index.html

2.新建container web01 web02 web03

root@ubuntu1604:~# docker run -d -p 7701:80 -v /mywebtest/:/usr/local/apache2/htdocs --name web01 httpd       
cfa46a13458aa650c2d3243460f38ef0a53d4f8d4643633b34edb1c257fa56f6
root@ubuntu1604:~# docker run -d -p 7702:80 -v /mywebtest/:/usr/local/apache2/htdocs --name web02 httpd 
4655998a69590653af9e41eaa92e48a3798666e79a70f55f9ca33d5c1e60ea05
root@ubuntu1604:~# docker run -d -p 7703:80 -v /mywebtest/:/usr/local/apache2/htdocs --name web03 httpd 
69e917fbbb20f8a5b886b14ffa05ee93d28461e551eaf4caf9abc0610f60943b

新建容器web01/web02/web03,将主机的/mywebtst分别挂载到容器的usr/local/apache2/htdocs目录。

3.访问web

root@ubuntu1604:~# curl 172.27.9.31:7701
container datas share : bind mounts
root@ubuntu1604:~# curl 172.27.9.31:7702
container datas share : bind mounts
root@ubuntu1604:~# curl 172.27.9.31:7703
container datas share : bind mounts

在/web03中新建测试文件test.txt,重启容器后观察该文件是否存在。

4.更新index.html

root@ubuntu1604:~# echo "container datas share 02 : bind mounts" > /mywebtest/index.html 
root@ubuntu1604:~# curl 172.27.9.31:7701                                                 
container datas share 02 : bind mounts
root@ubuntu1604:~# curl 172.27.9.31:7702
container datas share 02 : bind mounts
root@ubuntu1604:~# curl 172.27.9.31:7703
container datas share 02 : bind mounts

更新并再次访问web,发现所有容器的web访问页面已更新。

Volume container方式

 创建一个容器专门用于定义Volumes(可以是上文提到的volumes也可以是bind mounts),这个容器可以不运行,因为停止的容器也会保留对Volume的引用。然后其它的容器在创建或运行时通过--volumes-from从该容器复制Volume定义。 
使用该方式的注意事项:

  • 一般容器名加前缀 vc_,表示 volume container
  • 涉及的各容器对于 Volume 绑定的目录位置及命名规范都必须协同一致

1.新建container vc_data

root@ubuntu1604:~# docker create -v /mywebtest/:/usr/local/apache2/htdocs -v /apps --name vc_data  busybox

使用的busybox无实际意义,该镜像很小很适合测试

2.查看vc_data

root@ubuntu1604:~# docker inspect vc_data

Docker实践(三):数据持久化及共享_第5张图片
/mywebtest/通过bind mounts方式挂载到容器/usr/local/apache2/htdocs目录,卷6a635ec450eeecd487df4f25615c14443aa2a4da07b8c605a031d5ab9b5bbf47通过volume方式挂载至容器/apps目录。

3.使用vc_data

root@ubuntu1604:~# docker run -d -p 7704:80 --volumes-from vc_data --name web04 httpd
25ff55566720281791f2b1956236204dbca6ad529fa460a76d80e970786df22d
root@ubuntu1604:~# docker run -d -p 7705:80 --volumes-from vc_data --name web05 httpd 
dcce95be79b1ecc888d748d684b814743583c5db1ac41f026ca4ed5525f51c67
root@ubuntu1604:~# docker run -d -p 7706:80 --volumes-from vc_data --name web06 httpd 
edd257edaf6101ee8911afeb08b70ec95673b5e1c64c774e2b00f1486b025ba8

新建容器web04/web05/web06并使用vc_data。

4.查看web04

root@ubuntu1604:~# docker inspect web04

Docker实践(三):数据持久化及共享_第6张图片
挂载信息和vc_data一致。

5.共享验证

root@ubuntu1604:~# echo "abc123" > /var/lib/docker/volumes/6a635ec450eeecd487df4f25615c14443aa2a4da07b8c605a031d5ab9b5bbf47/_data/test.txt
root@ubuntu1604:~# curl 172.27.9.31:7704
container datas share : Volume container
root@ubuntu1604:~# curl 172.27.9.31:7705
container datas share : Volume container
root@ubuntu1604:~# curl 172.27.9.31:7706
container datas share : Volume container
root@ubuntu1604:~# docker exec -it web04 bash
root@25ff55566720:/usr/local/apache2# cd /apps/ && ls -l
total 4
-rw-r--r-- 1 root root 7 Mar 27 08:12 test.txt
root@25ff55566720:/apps# more test.txt 
abc123

分别在共享目录更新新建文件,各容器也能正常访问读取共享数据。

Data-packed volume container 方式

 这种模式扩展至Volume container模式。Data-packed volume container不仅定义Volumes,而且将从本容器的映像中的一些数据(如静态文件、配置数据、代码等)复制到这个定义的Volumes中,从而可与其它容器共享。

1.构建镜像

配置查看

root@ubuntu1604:~# pwd
/root
root@ubuntu1604:~# ls
Dockerfile  htdocs
root@ubuntu1604:~# more htdocs/index.html 
container datas share : Data-packed volume
root@ubuntu1604:~# more Dockerfile 
FROM busybox
ADD htdocs /usr/local/apache2/htdocs
VOLUME /usr/local/apache2/htdocs

构建镜像

root@ubuntu1604:~# docker build -t image_datapacked .
Sending build context to Docker daemon  18.43kB
Step 1/3 : FROM busybox
 ---> d8233ab899d4
Step 2/3 : ADD htdocs /usr/local/apache2/htdocs
 ---> d9266ad06c41
Step 3/3 : VOLUME /usr/local/apache2/htdocs
 ---> Running in c7d1cab717d4
Removing intermediate container c7d1cab717d4
 ---> f8f9f9c15171
Successfully built f8f9f9c15171
Successfully tagged image_datapacked:latest

通过Dockerfile方式构建镜像,镜像名为image_datapacked

2.创建data-packed volume container

root@ubuntu1604:~# docker run -it  --name vc_datapacked image_datapacked
/ # cd /usr/local/apache2/htdocs/
/usr/local/apache2/htdocs # ls
index.html
/usr/local/apache2/htdocs # more index.html 
container datas share : Data-packed volume

运行data-packed volume container:vc_datapacked,可以看到之前写进镜像层的index.html:container datas share : Data-packed volume
查看vc_datapacked详情

root@ubuntu1604:~# docker inspect vc_datapacked  

Docker实践(三):数据持久化及共享_第7张图片
因为在 Dockerfile中已经使用了VOLUME指令,这里就不需要指定volume的 mount point了。
volume为/var/lib/docker/volumes/3c37bc016de3018cb59292cdfca581893a77d19ba8b8f5538b04aab71b3603a1,容器挂载点为/usr/local/apache2/htdocs

3.使用vc_datapacked

root@ubuntu1604:~# docker run -d -p 7707:80 --volumes-from vc_datapacked --name web_datapacked httpd

新建容器web_datapacked并使用vc_datapacked。

4.查看web_datapacked

root@ubuntu1604:~# docker inspect web_datapacked

Docker实践(三):数据持久化及共享_第8张图片
volume为/var/lib/docker/volumes/3c37bc016de3018cb59292cdfca581893a77d19ba8b8f5538b04aab71b3603a1,容器挂载点为/usr/local/apache2/htdocs。挂载信息和vc_datapacked一致。

5.共享验证

root@ubuntu1604:~# curl 172.27.9.31:7707
container datas share : Data-packed volume

访问容器web_datapacked的web服务,返回结果与写入镜像image_datapacked的index.html一直,实现了静态文件index.html

6.总结

  • 1.相较于volume container,data-packed volume container方式直接把共享数据写进镜像层,通过上传至私有仓库,其它docker host都能使用该镜像实现容器的数据共享;
  • 2.data-packed volume container只适用于静态数据共享;
  • 3.若要修改data-packed volume container中的共享数据,只能重建image,不能动态修改。

本文参考:
https://docs.docker.com/storage/
http://www.atjiang.com/persistent-storage-and-shared-state-with-volumes-in-docker/
https://blog.51cto.com/cloudman/1948779