你是否担心 Docker 容器被删除后,容器内的重要数据就丢失了?
你是否想知道,Docker容器中的重要数据如何备份到外面的宿主机中?
你是否想知道,多个容器之间如何能够数据共享并相互同步?
恭喜你,看完本篇博文,你将能解决上述所有问题。
更新:记录笔者遇到的大坑。
创建容器卷命令记得加入 --privileged=true
。
这条命令是用来解决 Docker 挂载主机目录访问时出现 cannot open directory.:Permission denied
。
【出现的原因】CentOS 7 安全模块比之前系统版本加强,不安全的会先禁止,所以目录挂载的情况被默认为不安全的行为。在 SELinux 里面挂载目录是被禁止的。如果要开启,一般使用命令 --privileged=true
命令,扩大容器的权限解决挂载目录没有权限的问题,也即使用该参数,容器内的 root 拥有真正的 root 权限。否则,容器内的 root 只是外部的一个普通用户权限。
卷就是目录或文件,存在于 Docker 容器中,由 Docker 挂载到容器,但不属于 UnionFS 联合文件系统,因此能够绕过 UnionFS 提供一些用于持续存储或共享数据的特性。
容器数据卷,作用就是将 Docker 容器内的数据保存并同步到宿主机的硬盘中。如下图所示。卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会在容器删除时删除其挂载的数据卷。
将应用与运行的环境打包成镜像,run
之后形成容器实例运行,但我们希望容器中的数据能持久化到宿主机的硬盘中,不会随着容器的删除而消失。然而,Docker 容器产生的数据,如果不备份,那么当容器实例被删除后,容器内的数据自然也没有了。为了能保存容器中的数据,我们就要使用容器数据卷。
特点:
$ docker run -it --privileged=true -v /宿主机绝对路径目录/:/容器内目录 镜像名或镜像ID
其中,-v
就是启动容器数据卷,把 /容器内目录
备份到 /宿主机绝对路径目录/
中。同时,如果 /宿主机绝对路径目录
卷中有数据更改,也会实时同步到 /容器内目录
中。换句话说,容器和主机的数据卷之间是双向同步的。
此外,--privileged=true
就是文章开头提到的解决 Docker 挂载主机目录访问时出现 cannot open directory.:Permission denied
报错的问题。
如果目录不存在,Docker 会自动创建 。此外,-v
后可以绑定一对或多对数据卷。
(Ps. 永远要记住容器实例也是一个简易版的 Linux 系统)
举例:接下来创建一个带有容器卷存储功能的 Ubuntu 容器实例。
$ docker run -it --privileged=true -v /tmp/host_data/:/tmp/docker_data --name=u1 ubuntu:latest /bin/bash
其中,把容器中 /tmp/docker_data
目录下的数据备份到宿主机的 /tmp/host_data
目录中。
可以看到,Docker 确实自动创建了目录。
下面,我们在容器内 /tmp/docker_data
下创建数据 dockerin.txt
,验证是否能实时同步到宿主机的 /tmp/host_data
下。
$ cd /tmp/docker_data/
$ touch dockerin.txt
此时,查看宿主机的 /tmp/host_data
目录。
$ cd /tmp/host_data/
$ ls
容器内的数据 dockerin.txt
成功同步到宿主机的 /tmp/host_data
目录下。
接下来,我们在宿主机的 /tmp/host_data
目录下也创建数据 hostin.txt
来验证这种同步是否是双向的。
$ touch hostin.txt
切回到容器内,查看容器内 /tmp/docker_data
目录。
$ ls /tmp/docker_data
可以发现,宿主机的 /tmp/host_data
目录下也创建数据 hostin.txt
也出现在了容器的数据卷 /tmp/docker_data
中。
得出结论:容器和宿主机的数据卷之间是实时双向同步的。
可以使用下面的命令来查看数据卷挂载路径。
$ docker inspect 容器ID
举例:查看刚刚创建的 Ubuntu 容器数据卷挂载目录。
$ docker inspect 16e840d638e7
返回的是一大堆有关这个容器信息的 JSON 字符串。在 "Mounts"
挂载这段可以看到该容器数据卷的挂载目录。
其中,"Source"
是其对应的宿主机挂载目录,"Destination"
是 Docker 容器数据卷目录。
在上一节的案例中,使用的是 Docker 默认的规则:rw
可读可写。换句话说,下面两行命令是等价的。
$ docker run -it --privileged=true -v /宿主机绝对路径目录/:/容器内目录 镜像名或镜像ID
$ docker run -it --privileged=true -v /宿主机绝对路径目录/:/容器内目录:rw 镜像名或镜像ID
但在实际开发中,有时容器实例内部的读写权限被限制,只能读不能写,数据只能由宿主机同步给容器实例。这是我们把容器数据卷的读写权限改为 ro
(Read Only) 只读即可。
$ docker run -it --privileged=true -v /宿主机绝对路径目录/:/容器内目录:ro 镜像名或镜像ID
此时如果宿主机写入内容,可以同步给容器实例,容器可以读取到。
数据卷的继承,指的是容器 2 继承容器 1 的卷规则,卷规则包括数据卷的同步目录、读写规则等;此外,容器 1 、容器 2 与宿主机三者之间可以数据共享和同步。用 docker run
的一个参数 --volumes-from 父容器ID
来设置。
$ docker run -it --privileged=true --volumes-from 父容器 --name=容器名称 镜像名或镜像ID
卷继承的特点:
举例:
第 3 节中我们创建了 Ubuntu 容器 u1
,把容器中 /tmp/docker_data
目录下的数据备份到宿主机的 /tmp/host_data
目录中。
现在我们使用卷的继承,创建一个新的 Ubuntu 容器 u2
,使其继承容器 u1
的卷规则。
$ docker run -it --privileged=true --volumes-from u1 --name=u2 ubuntu:latest /bin/bash
在容器 u2
中,我们查看 /tmp/docker_data
目录下是否有数据。
发现在 u1
和宿主机之间同步的数据,在 u2
容器中也同步了。
如果我在 u2
中创建数据,能不能同步到 u1
和宿主机上呢?
$ touch u2data.txt
切到 u1
容器,发现 u2data.txt
也同步过来了。
切到宿主机,发现 u2data.txt
也同步过来了。
因此,使用卷的继承能实现多个容器与宿主机的数据共享和互相同步。
接下来做个实验,在停止 u1
容器的情况下,在 u2
容器中更改数据,三者之间还能实现数据共享和同步吗?
停止 u1
容器。
$ exit
切到宿主机,新增数据 host2.txt
。
$ touch host2.txt
切到 u2
容器,发现宿主机的新增数据成功同步过来。
在 u2
容器中新增数据 u2datav2.txt
。
$ touch u2data2.txt
切到宿主机,发现 u2
容器的新增数据成功同步过来。
重启 u1
容器并进入,查看宿主机和 u2
新增的数据是否能同步过来。
$ docker start u1
$ docker exec -it u1 /bin/bash
可以看到,即使 u1
容器已经停止运行的情况下,宿主机和 u2
新增的数据依然成功同步过来。实现三者之间数据共享同步。