Docker数据卷
1.数据卷介绍
数据卷简单的来说就是一个目录,它是由docker daemon挂载到容器中的,因此数据卷并不属于联合文件系统。也就是说数据卷是不会随着容器的丢失而丢失的。就像我们将U盘插到计算机,即使计算机的硬盘坏了,也不会影响U盘里面的数据。因此在使用docker commit提交时并不会把数据卷里面的数据提交到镜像中。
2.数据卷容器介绍
如果让两个容器共享一个数据卷,最快的办法是将数据卷挂载到两个容器上面即可。
3.挂载数据卷
docker run 或者是docker create命令时,可以指定-v参数来添加数据卷,并且这个参数可以用多次,可以挂载多个数据卷。
el:
$docker run -d -v /vol --name volume bonc:5000/busybox:v1 |
我们现在已经为容器volume挂载了一个数据卷,位置是/vol
但是这样我们不易管理,可以使用docker inspect来查看数据卷的位置:
"Mounts": [
{
"Type": "volume",
"Name": "3b1e12ca553a462526c9079e5ea95869af7b93ca98ca7f5497624c7f9ab8f00f",
"Source": "/var/lib/docker/volumes/3b1e12ca553a462526c9079e5ea95869af7b93ca98ca7f5497624c7f9ab8f00f/_data",
"Destination": "/vol",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
如果删除容器 :
$docker rm –f volume
再去查看数据卷目录会发现,数据卷还是存在的。虽然数据还是存在,但是我们的这样子做超级麻烦。
下面我们用简单的方式去解决这个问题,我们通过使用映射目录就可以方便的解决这个问题。
$
docker run -d -v /vol:/vol--name=volume bonc:5000/busybox:v1
目前数据卷存储在宿主机的/vol,不用去担心数据卷的丢失。数据卷的路径必须是绝对路径,另外删除数据卷时可以指定docker rm –v <container ID>
4.挂载数据卷容器
在上面的挂载数据卷虽然很好的解决了数据持久化的问题,但是在迁移上面还是很麻烦的。例如多个容器之间的共享数据需要迁移时,使用挂载宿主机的文件夹的方法迁移起来显得很麻烦。所以为了方便起见,我们可以启一个容器来专门负责存放数据
$ docker run -it -v /var/lib/mysql --name=mysql_volume mysql /bin/true
上面就启动了一个数据卷容器,/bin/true是为了覆盖原有进程并防止容器退出
然后启动其他容器容器并挂载数据卷容器
$ docker run -d --volumes-from mysql_volume --name db1 mysql
$ docker run -d --volumes-from mysql_volume21 --name db2 mysql
甚至可以通过db1来挂载到后续启动的容器中:
$ docker run -d --volume-from db1 --name db4 mysql
这样删除上面4个容器中的3个,数据卷也 不会丢失,即使四个都丢失了,但是还会保存在/var/lib/docker/volume/的某个目录下,这事先通过docker inspect 命令来查看volume ID.
综上所述,docker volume 并不是特别好用。 在挂载的时候一定要注意,避免以后的数据找不到。
还有补充的就是如果删除一个一个含有挂载数据卷的容器,在删除时没有用-v,则这些数据卷会成为dangling状态
$ docker volume ls -f dangling=true #查看所有没有挂载到容器上的数据卷
删除dangling状态的数据。
$docker volume rm <volume NAME>
>>>>>>>>>>>>小结<<<<<<<<<<<<<<<<
为了更直观的了解数据卷的挂载操作,做实验。
1.将本地宿主机不存在的文件挂载到容器中,而容器中存在,发现失败了
$ docker run --name=test -v ~/test.txt:/etc/hosts -d httpd #这里启动容器失败
报错:
/usr/bin/docker-current: Error response from daemon: oci runtime error: container_linux.go:247: starting container process caused "process_linux.go:364: container init caused "rootfs_linux.go:54: mounting \"/root/test.txt\" to rootfs \"/var/lib/docker/overlay2/549a2539ade911b73d6025bcf17bd91de98379ef21099f63edefead57863703e/merged\" at \"/var/lib/docker/overlay2/549a2539ade911b73d6025bcf17bd91de98379ef21099f63edefead57863703e/merged/etc/hosts\" caused \"not a directory\"""
: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type.
2.将本地不存在的文件夹挂载到容器的文件夹中。bonc:5000/busybox:v1镜像中存在一个/srv的文件夹,在文件夹有一个index.php文件。
$ docker run -d --name=test14 -v ~/srv:/srv bonc:5000/busybox:v1
最后发现容器启动成功了。
上面两个实验告诉我们,数据卷的挂载是通过把本地的目录覆盖到容器中的。也就是说宿主机文件不存在时,不能挂载;当文件夹不存在时,挂载到容器会用一个空文件夹覆盖容器原有的目录。
3.继续假设宿主机存在文件,容器内部存在该文件:
$ docker run -d --name=a23 -v ~/a.txt:/srv/a.txt -d docker.io/domeos/openfalcon-domeos:0.5 #假设本地文件a.txt是存在的
4.接下来是宿主机存在文件夹,容器不存在文件夹,宿主机的a文件夹里面存在一个hello文件
$ docker run --name=b99 -v ~/a:/srv/a -d docker.io/domeos/k8s-domeos:1.4.7 #容器启动成功
$ docker exec -it b99 sh #进入容器
$cd /srv/a
$ls
Hello
上面这两个例子说明:
如果容器内不存在文件,宿主机可直接挂载。
5.接下来继续测试,如果容器内存在文件夹b,宿主机内存在文件夹b。这样挂载结果如下:
$ docker run --name=y77 -v ~/b:/svr/b -d docker.io/domeos/k8s-domeos:1.4.7
会报错,容器启动失败。
6.假设宿主机是文件夹,容器也是文件夹,两个文件夹里面的内容不一样,宿主机内部是hello文件,容器文件夹里面是index.php:
$ docker run --name=c9 -v ~/a:/srv/b -d b:latest #能正确的启动
______________________综上所述___________________________
根据上面的6个实验,不难总结出一个规律:
宿主机文件 | 容器内文件 | 启动参数(加粗表示不存在) | 容器启动情况 |
不存在 | 文件 | -v ~/test.txt:/etc/hosts | 启动错误 |
不存在 | 文件夹 | -v ~/srv:/srv | 启动正常 |
文件 | 不存在 | -v ~/test.txt:/srv/test.txt | 启动正常 |
文件夹 | 不存在 | -v ~/test:/srv/test | 启动正常 |
文件夹 | 文件 | -v ~/test:/srv/test.txt | 启动错误 |
文件夹 | 文件夹 | -v ~/test:/srv/test | 启动正常 |
文件 | 文件 | -v ~/test.txt:/srv/test.txt | 启动正常 |
文件 | 文件夹 | -v ~/test.txt:/srv/test | 启动错误 |