请添加图片描述

专栏往期文章

  1. 《Docker是什么?Docker从介绍到Linux安装图文详细教程》
  2. 《30条Docker常用命令图文举例总结》
  3. 《Docker如何构建自己的镜像?从镜像构建到推送远程镜像仓库图文教程》

前言

你是否担心 Docker 容器被删除后,容器内的重要数据就丢失了?
你是否想知道,Docker容器中的重要数据如何备份到外面的宿主机中?
你是否想知道,多个容器之间如何能够数据共享并相互同步?
恭喜你,看完本篇博文,你将能解决上述所有问题。



1. 避坑

更新:记录笔者遇到的大坑。

创建容器卷命令记得加入 --privileged=true

这条命令是用来解决 Docker 挂载主机目录访问时出现 cannot open directory.:Permission denied

【出现的原因】CentOS 7 安全模块比之前系统版本加强,不安全的会先禁止,所以目录挂载的情况被默认为不安全的行为。在 SELinux 里面挂载目录是被禁止的。如果要开启,一般使用命令 --privileged=true 命令,扩大容器的权限解决挂载目录没有权限的问题,也即使用该参数,容器内的 root 拥有真正的 root 权限。否则,容器内的 root 只是外部的一个普通用户权限。


2. 什么是容器数据卷

卷就是目录或文件,存在于 Docker 容器中,由 Docker 挂载到容器,但不属于 UnionFS 联合文件系统,因此能够绕过 UnionFS 提供一些用于持续存储或共享数据的特性。

容器数据卷,作用就是将 Docker 容器内的数据保存并同步到宿主机的硬盘中。如下图所示。卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会在容器删除时删除其挂载的数据卷。

image-20221212141222508

将应用与运行的环境打包成镜像,run 之后形成容器实例运行,但我们希望容器中的数据能持久化到宿主机的硬盘中,不会随着容器的删除而消失。然而,Docker 容器产生的数据,如果不备份,那么当容器实例被删除后,容器内的数据自然也没有了。为了能保存容器中的数据,我们就要使用容器数据卷。

特点:

  1. 数据卷可以在容器之间共享或重用;
  2. 卷中的数据更改可以直接实时自动同步到宿主机上;
  3. 宿主机卷中的数据更改也会实时同步到容器内的卷中;(双向同步)
  4. 卷中的数据更改不会包含在镜像的更新中;
  5. 数据卷的生命周期一直持续到没有容器使用它为止。

3. 数据卷常用操作


3.1 创建带数据卷的容器

$ 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 目录中。

image-20221212134243040

image-20221212134410192

可以看到,Docker 确实自动创建了目录。

下面,我们在容器内 /tmp/docker_data 下创建数据 dockerin.txt ,验证是否能实时同步到宿主机的 /tmp/host_data 下。

$ cd /tmp/docker_data/
$ touch dockerin.txt

image-20221212134657604

此时,查看宿主机的 /tmp/host_data 目录。

$ cd /tmp/host_data/
$ ls

image-20221212134901705

容器内的数据 dockerin.txt 成功同步到宿主机的 /tmp/host_data 目录下。

接下来,我们在宿主机的 /tmp/host_data 目录下也创建数据 hostin.txt 来验证这种同步是否是双向的。

$ touch hostin.txt

image-20221212135543914

切回到容器内,查看容器内 /tmp/docker_data 目录。

$ ls /tmp/docker_data

image-20221212135701761

可以发现,宿主机的 /tmp/host_data 目录下也创建数据 hostin.txt 也出现在了容器的数据卷 /tmp/docker_data 中。

得出结论:容器和宿主机的数据卷之间是实时双向同步的。


3.2 查看数据卷的挂载路径

可以使用下面的命令来查看数据卷挂载路径。

$ docker inspect 容器ID

举例:查看刚刚创建的 Ubuntu 容器数据卷挂载目录。

$ docker inspect 16e840d638e7

返回的是一大堆有关这个容器信息的 JSON 字符串。在 "Mounts" 挂载这段可以看到该容器数据卷的挂载目录。

image-20221212140809472

其中,"Source" 是其对应的宿主机挂载目录,"Destination" 是 Docker 容器数据卷目录。


3.3 数据共享特性总结

  1. Docker 容器修改,主机同步获得数据;
  2. 主机修改,Docker 容器同步获得数据;
  3. Docker 容器 stop ,主机修改,Docker 容器 start 重启后依然可以获得数据;

4. 读写规则映射

在上一节的案例中,使用的是 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

image-20221212142509350

此时如果宿主机写入内容,可以同步给容器实例,容器可以读取到。


5. 卷的继承和共享

数据卷的继承,指的是容器 2 继承容器 1 的卷规则,卷规则包括数据卷的同步目录、读写规则等;此外,容器 1 、容器 2 与宿主机三者之间可以数据共享和同步。用 docker run 的一个参数 --volumes-from 父容器ID 来设置。

$ docker run -it --privileged=true --volumes-from 父容器 --name=容器名称 镜像名或镜像ID

卷继承的特点:

  1. 继承父容器与宿主机的同步目录、数据;
  2. 三者之间的数据能够相互共享,相互实时同步;
  3. 即使在容器停止的状态下,其他容器或宿主机更改的数据依然能在容器重启后同步过来。

举例:

第 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

image-20221212150141433

在容器 u2 中,我们查看 /tmp/docker_data 目录下是否有数据。

image-20221212150620698

发现在 u1 和宿主机之间同步的数据,在 u2 容器中也同步了。

如果我在 u2 中创建数据,能不能同步到 u1 和宿主机上呢?

$ touch u2data.txt

image-20221212151307353

切到 u1 容器,发现 u2data.txt 也同步过来了。

image-20221212151446306

切到宿主机,发现 u2data.txt 也同步过来了。

image-20221212151609877

因此,使用卷的继承能实现多个容器与宿主机的数据共享和互相同步。

接下来做个实验,在停止 u1 容器的情况下,在 u2 容器中更改数据,三者之间还能实现数据共享和同步吗?

停止 u1 容器。

$ exit

image-20221212151923357

切到宿主机,新增数据 host2.txt

$ touch host2.txt

image-20221212152204618

切到 u2 容器,发现宿主机的新增数据成功同步过来。

image-20221212152313849

u2 容器中新增数据 u2datav2.txt

$ touch u2data2.txt

image-20221212152421119

切到宿主机,发现 u2 容器的新增数据成功同步过来。

image-20221212152517272

重启 u1 容器并进入,查看宿主机和 u2 新增的数据是否能同步过来。

$ docker start u1
$ docker exec -it u1 /bin/bash

image-20221212152844225

可以看到,即使 u1 容器已经停止运行的情况下,宿主机和 u2 新增的数据依然成功同步过来。实现三者之间数据共享同步。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐