Docker 存储

Docker 为容器提供了两种存放数据的资源:

  • 由 Storage Driver 管理的镜像层和容器层
  • Data Volume

Storage Driver

镜像分层结构

容器由最上面一个可写的容器层,以及若干只读的镜像层组成,容器的数据就存放在这些层中。这样的分层结构最大的特性是 Copy-on-Write:

  • 新数据会直接存放在最上面的容器层
  • 修改现有数据会先从镜像层将数据复制到容器层,修改后的数据直接保存在容器层中,镜像层保持不变
  • 如果多个层中有命名相同的文件,用户只能看到最上面那层中的文件

分层结构使镜像和容器的创建、共享以及分发变得非常高效,而这些都要归功于 Docker storage driver。正是 storage driver 实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图。

Docker 支持多种 storage driver,有 AUFS、Device Mapper、Btrfs、OverlayFS、VFS 和 ZFS。它们都能实现分层的架构,同时又有各自的特性。对于 Docker 用户来说,具体选择使用哪个 storage driver 是一个难题,不过 Docker 官方给出了一个简单的答案:优先使用 Linux 发行版默认的 storage driver。Docker 安装时会根据当前系统的配置选择默认的 driver。默认 driver 具有最好的稳定性,因为默认 driver 在发行版上经过了严格的测试。

  • Docker CE on Ubuntu aufs, devicemapper, overlay2 (Ubuntu 14.04.4 or later, 16.04 or later), overlay, zfs, vfs
  • Docker CE on Debian aufs, devicemapper, overlay2 (Debian Stretch), overlay, vfs
  • Docker CE on CentOS devicemapper, vfs
  • Docker CE on Fedora devicemapper, overlay2 (Fedora 26 or later, experimental), overlay (experimental), vfs

在CentOS 7 中默认选择使用 devicemapper。

对于某些容器,直接将数据放在由 storage driver 维护的层中是很好的选择,比如那些无状态的应用。无状态意味着容器没有需要持久化的数据,随时可以从镜像直接创建。

比如 busybox,它是一个工具箱,我们启动 busybox 是为了执行诸如 wget,ping 之类的命令,不需要保存数据供以后使用,使用完直接退出,容器删除时存放在容器层中的工作数据也一起被删除,这没问题,下次再启动新容器即可。

但对于另一类应用这种方式就不合适了,它们有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,也就是说,这类容器是有状态的。这就要用到 Docker 的另一种存储机制:Data Volume。

Data Volume

Data Volume 本质上是 Docker Host 文件系统中的目录或文件,能够直接被 mount 到容器的文件系统中。Data Volume 有以下特点:

  • Data Volume 是目录或文件,而非没有格式化的磁盘(块设备)
  • 容器可以读写 volume 中的数据
  • volume 数据可以被永久的保存,即使使用它的容器已经销毁

在具体的使用上,docker 提供了两种类型的 volume:

  • bind mount
  • docker managed volume

容器volume

bind mount

bind mount 是将 host 上已存在的目录或文件 mount 到容器,容器不会将挂载目录下已存在的文件拷贝到宿主机目录

  1. docker run -tid -name apache -p 80:80 -v /data1/code:/usr/local/apache2/htdocs httpd

通过 -v 将其 mount 到 httpd 容器,-v 的格式为 \:\。/usr/local/apache2/htdocs 就是 apache server 存放静态文件的地方。由于 /usr/local/apache2/htdocs 已经存在,原有数据会被隐藏起来,取而代之的是 host /data1/code/ 中的数据,这与 linux mount 命令的行为是一致的。另外,bind mount 时还可以指定数据的读写权限,默认是可读可写,可以通过 –v /data1/code:/usr/local/apache2/htdocs:ro,设置了只读权限,在容器中是无法对 bind mount 数据进行修改的。只有 host 有权修改数据,提高了安全性。

docker managed volume

Docker managed volume 不需要指定 mount 源,指明 mount point 就行了。容器会将挂载目录下已存在的文件拷贝到宿主机目录

  1. docker run -tid --name apache2 -p 8080:80 -v /usr/local/apache2/htdocs httpd

docker managed volume 的创建过程:

  • 容器启动时,简单的告诉 docker “我需要一个 volume 存放数据,帮我 mount 到目录 /usr/local/apache2/htdocs”
  • docker 在 /var/lib/docker/volumes 中生成一个随机目录作为 mount 源
  • 如果 /usr/local/apache2/htdocs 已经存在,则将数据复制到 mount 源
  • 将 volume mount 到 /usr/local/apache2/htdocs

我们可以通过 docker inspect 查看容器挂载的 volume,也可以用 docker volume inspect命令查看。docker volume ls 只能列出Docker managed volume。

数据共享

数据共享是 volume 的关键特性,通过 volume 可以在容器与 host 之间,容器与容器之间共享数据。

容器与 host 共享数据

通过将宿主机目录挂载到容器,实现在容器与 host 之间共享数据。也可以通过 docker cp 在容器和 host 之间拷贝数据。

容器之间共享数据

多个容器挂载相同的宿主机目录

容器volume共享

用 volume container 共享数据

volume container 是专门为其他容器提供 volume 的容器。它提供的卷可以是 bind mount,也可以是 docker managed volume。首先通过docker create 创建 volume container,然后其他容器可以通过 —volumes-from 使用这个 volume container。

容器volume共享

volume 生命周期管理

Data Volume 中存放的是重要的应用数据,如何管理 volume 对应用至关重要。

docker 不会销毁 bind mount。对于 docker managed volume,在执行 docker rm 删除容器时可以带上 -v 参数,docker 会将容器使用到的 volume 一并删除,但前提是没有其他容器 mount 该 volume,目的是保护数据,非常合理。

如果删除容器时没有带 -v 呢?这样就会产生孤儿 volume,好在 docker 提供了 volume 子命令可以对 docker managed volume 进行维护。。对于这样的孤儿 volume,可以用 docker volume rm 删除。如果想批量删除孤儿 volume,可以执行:

  1. docker volume rm $(docker volume ls -q)