docker容器持久化卷讲解

时间:2021-09-15 19:56:18

docker容器自身存储数据效率比较低,因此我们为了提高磁盘IO的性能等,需要在容器中挂载一个外部存储设备。关于讲解大致如下:

Docker中的数据可以存储在类似于虚拟机磁盘的介质中,在Docker中称为数据卷(Data Volume)。数据卷可以用来存储Docker应用的数据,也可以用来在Docker容器间进行数据共享。
数据卷呈现给Docker容器的形式就是一个目录,支持多个容器间共享,修改也不会影响镜像。使用Docker的数据卷,类似在系统中使用 mount 挂载一个文件系统。
)一个数据卷是一个特别指定的目录,该目录利用容器的UFS文件系统可以为容器提供一些稳定的特性或者数据共享。数据卷可以在多个容器之间共享。
)创建数据卷,只要在docker run命令后面跟上-v参数即可创建一个数据卷,当然也可以跟多个-v参数来创建多个数据卷,当创建好带有数据卷的容器后,
就可以在其他容器中通过--volumes-froms参数来挂载该数据卷了,而不管该容器是否运行。也可以在Dockerfile中通过VOLUME指令来增加一个或者多个数据卷。
)如果有一些数据想在多个容器间共享,或者想在一些临时性的容器中使用该数据,那么最好的方案就是你创建一个数据卷容器,然后从该临时性的容器中挂载该数据卷容器的数据。
这样,即使删除了刚开始的第一个数据卷容器或者中间层的数据卷容器,只要有其他容器使用数据卷,数据卷都不会被删除的。
)不能使用docker export、save、cp等命令来备份数据卷的内容,因为数据卷是存在于镜像之外的。备份的方法可以是创建一个新容器,挂载数据卷容器,同时挂载一个本地目录,
然后把远程数据卷容器的数据卷通过备份命令备份到映射的本地目录里面。如下:
# docker run -rm --volumes-from DATA -v $(pwd):/backup busybox tar cvf /backup/backup.tar /data
)也可以把一个本地主机的目录当做数据卷挂载在容器上,同样是在docker run后面跟-v参数,不过-v后面跟的不再是单独的目录了,它是[host-dir]:[container-dir]:[rw|ro]这样格式的,
host-dir是一个绝对路径的地址,如果host-dir不存在,则docker会创建一个新的数据卷,如果host-dir存在,但是指向的是一个不存在的目录,则docker也会创建该目录,然后使用该目录做数据源。 Docker Volume数据卷可以实现:
)绕过“拷贝写”系统,以达到本地磁盘IO的性能,(比如运行一个容器,在容器中对数据卷修改内容,会直接改变宿主机上的数据卷中的内容,所以是本地磁盘IO的性能,而不是先在容器中写一份,最后还要将容器中的修改的内容拷贝出来进行同步。)
)绕过“拷贝写”系统,有些文件不需要在docker commit打包进镜像文件。
)数据卷可以在容器间共享和重用数据
)数据卷可以在宿主和容器间共享数据
)数据卷数据改变是直接修改的
)数据卷是持续性的,直到没有容器使用它们。即便是初始的数据卷容器或中间层的数据卷容器删除了,只要还有其他的容器使用数据卷,那么里面的数据都不会丢失。 Docker数据持久化:
容器在运行期间产生的数据是不会写在镜像里面的,重新用此镜像启动新的容器就会初始化镜像,会加一个全新的读写入层来保存数据。
如果想做到数据持久化,Docker提供数据卷(Data volume)或者数据容器卷来解决问题,另外还可以通过commit提交一个新的镜像来保存产生的数据。

接下来我们讲解一下案例:

1、docker有两种卷管理的方式:

docker容器持久化卷讲解

区别就是:第一种是容器内部指定保存的路径并绑定物理卷的路径映射,第二种是容器内部指定保存数据的路径,但是外部存储在磁盘的/var/lib/docker/volumes目录下的路径。相对来说第一种最好。

2、先讲解第二种:

[root@ELK-chaofeng08 ~]# docker run --name bbox1 -v /opt/data -it busybox
/ # cd /opt/data
/opt/data # ls/opt/data # echo "" > .txt
/opt/data # cat .txt

然后打开另外一个终端查看。

[root@ELK-chaofeng08 ~]# docker inspect bbox1 | grep  /var/lib/docker/volumes
"Source": "/var/lib/docker/volumes/2e7dddadd5ec5a261666735787bba3747abc1d767d8b680065781fa052e52946/_data",
[root@ELK-chaofeng08 ~]# ls /var/lib/docker/volumes/2e7dddadd5ec5a261666735787bba3747abc1d767d8b680065781fa052e52946/_data
.txt
[root@ELK-chaofeng08 ~]# cat /var/lib/docker/volumes/2e7dddadd5ec5a261666735787bba3747abc1d767d8b680065781fa052e52946/_data/.txt

此时即便你把容器删除了,数据依然不会丢失,因为保存到了外部存储设备。

docker容器持久化卷讲解

删除容器后查看之前的数据

[root@ELK-chaofeng08 ~]# cat /var/lib/docker/volumes/2e7dddadd5ec5a261666735787bba3747abc1d767d8b680065781fa052e52946/_data/.txt

在 web 容器被删除后,/var/lib/docker/volumes/f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b/_data 目录及其中的内容都还会保留下来,但是,新启动的容器无法再使用这个目录,也就是说,已有的数据不能自动地被重复使用了。因此我们一般都通过指定外部存储设备的路径来做。

3、看第一种方法,绑定外部路径与docker容器内部路径

可以直接挂载宿主机文件或目录到容器里,可以理解为目录映射,这样就可以让所有的容器共享宿主机数据,从而只需要改变宿主机的数据源就能够影响到所有的容器数据。

注意:
-v后面的映射关系是"宿主机文件/目录:容器里对应的文件/目录",其中,宿主机上的文件/目录是要提前存在的,容器里对应的文件/目录会自动创建。 数据卷权限:
挂载的数据默认为可读写权限。
但也可以根据自己的需求,将容器里挂载共享的数据设置为只读,这样数据修改就只能在宿主机上操作。

案例演示:

[root@ELK-chaofeng08 ~]# docker run --name bbox1 -v /tmp/test:/opt/data -it busybox
/ # cd /opt/data
/opt/data # ls
/opt/data # touch .txt
/opt/data # echo "abcdefg" > .txt
/opt/data # cat .txt
abcdefg
/opt/data #

然后再打开一个终端,不要进入容器,直接到/tmp/test目录查看是否有1.txt这个文件和文件的内容

[root@ELK-chaofeng08 ~]# cd /tmp/test
[root@ELK-chaofeng08 test]# cat .txt
abcdefg

主机上的目录可以是一个本地目录,也可以在一个 NFS share 内,或者在一个已经格式化好了的块设备上。

其实这种形式和第一种没有本质的区别,容器内对 /opt/data 的操作都会反映到主机上的 /tmp/test 目录内。只是,重新启动容器时,可以再次使用同样的方式来将 /tmp/test 目录挂载到新的容器内,这样就可以实现数据持久化的目标。

在容器的详细信息中是这样的:

"Mounts": [
{
"Type": "bind",
"Source": "/tmp/test",
"Destination": "/opt/data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],

上面的这个是容器内创建文件并写入内容,然后容器外查看。现在我们可以反过来操作,比如

[root@ELK-chaofeng08 test]# touch .txt && echo "abcdefghijklmn" > .txt
[root@ELK-chaofeng08 test]# cat .txt
abcdefghijklmn

然后此时回到容器中查看

/opt/data # ls
.txt .txt
/opt/data # cat .txt
abcdefghijklmn

4、可以挂在多个目录

docker run --name bbox1 -v /opt/data1:/var/www/data1 -v /opt/data2:/var/www/data2 -it busybox

5、挂载为只读,就是在外部存储卷挂载容器内时,在容器中是只读的,只能查看内容,但是不能修改外部存储卷上的文件

[root@localhost ~]# docker run -it --name bbox1 -v /etc/web.list:/etc/web.list:ro busybox

docker新版本引入 docker volumn命令使用方法

1、提前创建好volumn,然后在启动容器中映射。

[root@ELK-chaofeng08 test]# docker volume create vol1
vol1
[root@ELK-chaofeng08 test]# docker volume inspect vol1
[
{
"CreatedAt": "2019-03-21T10:52:59+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/vol1/_data",
"Name": "vol1",
"Options": {},
"Scope": "local"
}
]

docker volumn默认创建的目录是在/var/lib/docker/volumns目录下。

2、使用这个volumn

[root@ELK-chaofeng08 test]# docker run -it --name bbox2 -v vol1:/volumn busybox
/ # ls
bin dev etc home proc root sys tmp usr var volumn

查看这个容器的详细信息

"Mounts": [
{
"Type": "volume",
"Name": "vol1",
"Source": "/var/lib/docker/volumes/vol1/_data",
"Destination": "/volumn",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],

共享存储卷

docker容器持久化卷讲解

案例演示

1、首先是创建第一个容器

[root@ELK-chaofeng08 ~]# docker run  -it --name bbox1 -v /tmp/test2:/opt/data busybox
/ # cd /opt/data
/opt/data # ls

然后我们再打开一个终端,在物理卷下的/tmp/test2目录创建一个文件

[root@ELK-chaofeng08 test2]# touch .txt
[root@ELK-chaofeng08 test2]# ls
.txt

然后回到容器bbox1中查看是否有这个文件

/opt/data # ls
.txt

发现是有的,符合常理。我们看下容器bbox1的挂载信息,如下所示:

"Mounts": [
{
"Type": "bind",
"Source": "/tmp/test2",
"Destination": "/opt/data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],

然后我们再启动一个容器bbox2,这次是共享容器bbox1的存储卷

[root@ELK-chaofeng08 test]# docker run -it --name bbox2 --volumes-from bbox1 busybox
/ # ls /opt/data
.txt

然后查看一下容器bbox2的挂载信息

"Mounts": [
{
"Type": "bind",
"Source": "/tmp/test2",
"Destination": "/opt/data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],

你会发现容器bbox1和容器bbox2的挂载信息是相同的,他们都是映射到物理卷/tmp/test2目录下,这就是容器间的共享数据。