环境说明:
主机名 | 操作系统版本 | 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主机的位置不同。
- 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的位置:
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的位置:
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的位置:
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
/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
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
因为在 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
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