Docker笔记(五)存储

本笔记是记录一些学习微信公众号CloudMan的Docker的文章。本篇介绍容器的存储。

Docker的2类存储资源

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

  • 由 storage driver 管理的镜像层和容器层。
  • Data Volume

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

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

分层结构使镜像和容器的创建、共享以及分发变得非常高效,而这些都要归功于 Docker storage driver。正是 storage driver 实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图。
Docker 支持多种 storage driver,有 AUFS、Device Mapper、Btrfs、OverlayFS、VFS 和 ZFS。它们都能实现分层的架构,同时又有各自的特性。
那么,到底选择哪一种storage driver呢?
Docker官方给出的答案是:优先使用 Linux 发行版默认的 storage driver.

可以运行docker info查看当前默认的 driver.
Ubuntu 用的 AUFS,底层文件系统是 extfs,各层数据存放在 /var/lib/docker/aufs。
Redhat/CentOS 的默认 driver 是 Device Mapper,SUSE 则是 Btrfs。

对于某些容器,直接将数据放在由 storage driver 维护的层中是很好的选择,比如那些无状态的应用。无状态意味着容器没有需要持久化的数据,随时可以从镜像直接创建。
但对于另一类应用这种方式就不合适了,它们有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,也就是说,这类容器是有状态的。这就要用到 Docker 的另一种存储机制:Data Volume

Data volume 之 bind mount

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

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

因为 volume 实际上是 docker host 文件系统的一部分,所以 volume 的容量取决于文件系统当前未使用的空间,目前还没有方法设置 volume 的容量。

在具体的使用上,docker 提供了两种类型的 volume:bind mount 和 docker managed volume.
bind mount 是将 host 上已存在的目录或文件 mount 到容器。

例如 docker host 上有目录 $HOME/htdocs, 通过 -v 将其 mount 到 httpd 容器:

docker run -d -p 80:80 -v ~/htdocs:/usr/local/apache2/htdocs httpd 

-v 的格式为 :
/usr/local/apache2/htdocs 就是 apache server 存放静态文件的地方。由于 /usr/local/apache2/htdocs 已经存在,原有数据会被隐藏起来,取而代之的是 host $HOME/htdocs/ 中的数据,这与 linux mount 命令的行为是一致的。

另外,bind mount 时还可以指定数据的读写权限,默认是可读可写,可指定为只读:

docker run -d -p 80:80 -v ~/htdocs:/usr/local/apache2/htdocs:ro httpd

ro 设置了只读权限,在容器中是无法对 bind mount 数据进行修改的。只有 host 有权修改数据,提高了安全性。

除了 bind mount 目录,还可以单独指定一个文件:

docker run -d -p 80:80 -v ~/htdocs/index.html:/usr/local/apache2/htdocs/index.html httpd

使用单一文件有一点要注意:host 中的源文件必须要存在,不然会当作一个新目录 bind mount 给容器。

bind mount也有不足的地方:
bind mount 需要指定 host 文件系统的特定路径,这就限制了容器的可移植性,当需要将容器迁移到其他 host,而该 host 没有要 mount 的数据或者数据不在相同的路径时,操作会失败。

docker managed volume

docker managed volume 不需要指定mount源,只需要指定mount point就可以了。
还是以httpd为例:

docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd 

这里通过 -v 告诉docker需要一个data volume, 并将其mount到/usr/local/apache2/htdocs.
但是这个data volume在哪里呢?
可以在容器的配置信息中找到。执行 docker inspect 命令可以看到:

docker inspect 21accc2ca072
......

"Mounts": [
    {
        "Name": "f4a0a1018968f47960efe760829e3c5738c702533d29911b01df9f18babf3340",
        "Source": "/var/lib/docker/volumes/f4a0a1018968f47960efe760829e3c5738c702533d29911b01df9f18babf3340/_data",
        "Destination": "/usr/local/apache2/htdocs",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],
......

这里,Mounts中的Source就是该volume在host上的目录。

原来,每当容器申请 mount docker manged volume 时,docker 都会在/var/lib/docker/volumes 下生成一个目录。这个目录就是 mount 源。
以上这个volume里的内容和容器的/usr/loca/apache2/htdocs里的内容是一样的。因为如果mount point指向的是已有目录,那么原有数据会被复制到volume中。
但要明确一点:此时的 /usr/local/apache2/htdocs 已经不再是由 storage driver 管理的层数据了,它已经是一个 data volume.

回顾一下 docker managed volume 的创建过程:
1. 容器启动时,告诉 docker “我需要一个 volume 存放数据,帮我 mount 到目录 /abc”。
2. docker 在 /var/lib/docker/volumes 中生成一个随机目录作为 mount 源。
3. 如果 /abc 已经存在,则将数据复制到 mount 源,
4. 将 volume mount 到 /abc

以下对2种data volume做个对比:
1. 相同点:两者都是host文件系统中的某个路径
2. 不同点,如下:
bind mount data managed volume
volume位置 可任意指定 /var/lib/docker/volumes/xxxx
权限控制 可设置,默认为读写 无控制,均为读写权限
移植性 移植性弱,与host path绑定 移植性强,无须指定host目录
是否支持单个文件 支持 不支持,只能是目录
对已有的mount point的影响 隐藏并替换为volume 原有数据复制到volume

关于共享数据

容器与host共享数据

两种类型的 data volume 均可实现在容器与 host 之间共享数据,但方式有所区别。

  • 对于 bind mount 是非常明确的:直接将要共享的目录 mount 到容器。
  • 对于 docker managed volume , 由于 volume 位于 host 中的目录是在容器启动时才生成的,所以需要将共享数据拷贝到 volume 中。
    docker cp 可以在容器和 host 之间拷贝数据:

    docker cp ~/index.html 3re5eretet:/usr/local/apache2/htdocs

容器之间共享数据

第一种方法:

将共享数据放在bind mount中,然后将其mount到多个容器。

第二种方法:

使用volume container.

volume container 是专门为其他容器提供 volume 的容器。它提供的卷可以是 bind mount,也可以是 docker managed volume.
下面创建一个 volume container:

docker create --name vc_data \
    -v ~/htdocs:/usr/local/apache2/htdocs \
    -v /other/useful/tools \
    busybox

注意,这里执行的是 docker create 命令,这是因为 volume container 的作用只是提供数据,它本身不需要处于运行状态。

容器 mount 了两个 volume:

  • bind mount,存放 web server 的静态文件。
  • docker managed volume,存放一些实用工具(当然现在是空的,这里只是做个示例)。

通过 docker inspect vc_data 可以看到这2个volume.

其他容器可以通过 –volumes-from 使用 vc_data 这个 volume container:

docker run --name web1 -d -p 80 --volume-from vc_data httpd 
docker run --name web2 -d -p 80 --volume-from vc_data httpd 
docker run --name web3 -d -p 80 --volume-from vc_data httpd 

注:
-p后不使用 : 的格式,只使用一个port,则相当于:

volume container 的特点:

  • 与 bind mount 相比,不必为每一个容器指定 host path,所有 path 都在 volume container 中定义好了,容器只需与 volume container 关联,实现了容器与 host 的解耦。
  • 使用 volume container 的容器其 mount point 是一致的,有利于配置的规范和标准化,但也带来一定的局限。

第三种方法:

使用data-packed volume container

将数据打包到镜像中,然后通过 docker managed volume 共享。

使用下面的Dockerfile构建镜像:

FROM busybox:latest
ADD htdocs /usr/local/apache2/htdocs
VOLUME /usr/local/apache2/htdocs

ADD 将静态文件添加到容器目录 /usr/local/apache2/htdocs.
VOLUME 的作用与 -v 等效,用来创建 docker managed volume,mount point 为 /usr/local/apache2/htdocs,因为这个目录就是 ADD 添加的目录,所以会将已有数据拷贝到 volume 中。

build新镜像datapacked:

docker build -t datapacked . 

用新镜像创建data-packed volume container:

docker create --name vc_data datapacked

启动httpd容器并使用data-packed volume container:

docker run -d -p 80:80 --volumes-from vc_data httpd

容器能够正确读取 volume 中的数据。data-packed volume container 是自包含的,不依赖 host 提供数据,具有很强的移植性。

volume 生命周期管理

备份

因为 volume 实际上是 host 文件系统中的目录和文件,所以 volume 的备份实际上是对文件系统的备份。
若镜像都是存放在本地的registry中,则定期备份本地目录即可。

恢复

volume 的恢复也很简单,如果数据损坏了,直接用之前备份的数据拷贝到 /myregistry 就可以了。

迁移

如果我们想使用更新版本的 Registry,这就涉及到数据迁移,方法是:
1. docker stop 当前 Registry 容器。
2. 启动新版本容器并 mount 原有 volume.

docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:latest

当然,在启用新容器前要确保新版本的默认数据路径是否发生变化。

销毁

可以删除不再需要的 volume,但不可恢复。

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

如果删除容器时没有带 -v ,就会产生孤儿volume.
可以使用 docker volume rm 命令删除孤儿volume:

docker volume ls  # 列出孤儿volume
docker volume rm  

如果想批量删除孤儿 volume,可以执行:

docker volume rm $(docker volume ls -q)

小结

  1. docker 为容器提供了两种存储资源:数据层和 Data Volume。
  2. 数据层包括镜像层和容器层,由 storage driver 管理。
  3. Data Volume 有两种类型:bind mount 和 docker managed volume。
  4. bind mount 可实现容器与 host 之间,容器与容器之间共享数据。
  5. volume container 是一种具有更好移植性的容器间数据共享方案,特别是 data-packed volume container。

你可能感兴趣的:(docker,云计算与虚拟化)