docker存储

1.概述

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

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

1.1 storage driver

image

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

(1)新数据会直接存放在最上面的容器层。

(2)修改现有数据会先从镜像层将数据复制到容器层,修改后的数据直接保存在容器层中,镜像层保持不变。

(3)如果多个层中有命名相同的文件,用户只能看到最上面那层中的文件。

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

Docker支持多种storage driver,有AUFS、Device Mapper、Btrfs、OverlayFS、VFS 和ZFS。它们都能实现分层的架构,同时又有各自的特性。对于Docker 用户来说,具体选择使用哪个 storage driver 是一个难题,因为:
(1) 没有哪个 driver 能够适应所有的场景。
(2) driver 本身在快速发展和迭代。
不过 Docker 官方给出了一个简单的答案: 优先使用Linux发行版默认的 storage driver

Docker 安装时会根据当前系统的配置选择默认的driver。默认 driver 具有最好的稳定性,因为默认 driver 在发行版上经过了严格的测试。运行 docker info 査看 Ubuntu 的默认 driver。

image

对于某些容器,直接将数据放在由storage driver 维护的层中是很好的选择,比如那些无状态的应用。无状态意味着容器没有需要持久化的数据,随时可以从镜像直接创建。比如 busybox,它是一个工具箱,启动 busybox 是为了执行诸如 wget、ping 之类的命令,不需要保存数据供以后使用,使用完直接退出,容器删除时存放在容器层中的工作数据也一起被删除,这没问题,下次再启动新容器即可。
但对于另一类应用这种方式就不合适了,它们有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,也就是说,这类容器是有状态的。这就要用到 Docker的另一种存储机制:Data Volume。

1.2 Data Volume

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

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

现在有数据层(镜像层和容器层)和volume都可以用来存放数据,具体使用的时候要怎样选择呢?考虑下面几个场景:
(1) Database 软件 vs Database 数据:
(2) Web 应用 vs 应用产生的日志。
(3) 数据分析软件 vs input/output 数据。
(4) Apache Server vs 静态 HTML 文件
相信大家会做出这样的选择:

  • 前者放在数据层中。因为这部分内容是无状态的,应该作为镜像的一部分。
  • 后者放在 Data Volume中。这是需要持久化的数据,并且应该与镜像分开存放。

如何设置volume的容量?

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

docker 提供了两种类型的volume: bind mount 和 docker managed volume

1.2.1 bind mount

bind mount是将host上已存在的目录或文件 mount 到容器,例如 docker host 上有目录 $HOME/htdocs

image

通过 -v 将其 mount 到 httpd 容器

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

image

进入容器,发现映射成功

-v 的格式为 <host path>:<container path>/usr/local/apache2/htdocs 就是 Apache Server 存放静态文件的地方。

由于/usr/local/apache2/htdocs已经存在,原有数据会被隐藏起来,取而代之的是 host $HOME/htdocs/中的数据,这与 Linux mount 命令的行为是一致的。

bind mount可以让 host 与 容器共享数据,在容器中更改数据,host中也被同步更改。这在管理上是非常方便的。

image

将容器销毁,bind mount 也还在,bind mount是host文件系统中的数据,不能删除。

  • bind mount 还可以指定数据的读写权限,默认是可读可写,可指定为只读
docker run -d -p 4555:80 -v ~/htdocs:/usr/local/apache2/htdocs:ro httpd

image

ro 设置了只读权限,在容器中是无法对bind mount 数据进行修改的。只有host 有权修改数据,提高了安全性。
除了 bind mount 目录,还可以单独指定一个文件。

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

image

使用 bind mount 单个文件的场景是: 只需要向容器添加文件,不希望覆盖整个目录。将html文件加到apache 中,同时也保留了容器原有的数据。

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

mount point 有很多应用场景,比如我们可以将源代码目录mount 到容器中,在 host 中修改代码就能看到应用的实时效果。再比如将MYSQL容器的数据放在bind mount里,这样 host 可以方便地备份和迁移数据。
bind mount 的使用直观高效,易于理解,

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

1.2.2 docker managed volume

docker managed volume 与 bind mount 在使用上的最大区别是不需要指定 mount 源,指明 mount point 就行了。以 httpd 容器为例。

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

我们通过-v告诉 docker 需要一个 data volume,并将其 mount 到 /usr/local/apache2/htdocs。那么这个 data volume 具体在哪儿呢? 执行docker inspect 命令,在配置信息中可以查找到

image

Source 就是 该 volumehost 上的目录。
每当容器申请 mount docker manged volume 时,docker 都会在 /home/dockerdata/data/volumes/(默认是/var/lib/docker/volumes目录,如果在 daemon.json中配置过data-root,则用配置文件中的)生成一个目录.

image

在本例中 /home/dockerdata/data/volumes/2fe06f7c41b8dee606edaf75fd8e6b80d8b168f85e12a9c2ce8c780c8c52ccb5/_data

就是mount 源。

image

可以看到,该目录里边的内容和原容器里边的/usr/local/apache2/htdocs完全一样

如果mount point 指向的是已有目录,原有数据会被复制到volume中。

此时的/usr/local/apache2/htdocs已经不再是由storage driver管理的层数据它已经是一个 data volume。我们可以像bind mout 一样对数据进行操作。

  • docker managed volume 的创建过程:
    (1) 容器启动时,告诉 docker 我需要一个 volume 存放数据,帮我 mount 到目录 /abc .
    (2) docker 在/var/lib/docker/volumes (如果配置过data-root,用data-root中的值)中生成一个随机目录作为 mount 源。
    (3) 如果/abc 已经存在,则将数据复制到 mount 源。
    (4) 将 volume mount 到 /abc

image

通过docker volume可以查看数据卷,但是只能查看 docker managed volume 看不到bind mount;也不知道数据卷对应的容器是那个,要想查看详细信息,需要通过 docker inspect命令

docker managed volume 和 bind mount的区别

不同点 bind mount docker managed volume
volume 位置 可任意指定 /var/lib/docker/volumes/...
对已有 mount point 影响 隐藏并替换为 volume 原有数据复制到 volume
是否支持单个文件 支持 不支持,只能是目录
权限控制 可设置为只读,默认为读写权限 无控制,均为读写权限
移植性 移植性弱,与hostpath绑定 移植性强,无须指定 host 目录

2. 数据共享

2.1 容器与 host 共享数据

有两种类型的 data volume,它们均可实现在容器与 host 之间共享数据,但方式有所区别。
对于bind mount是非常明确的: 直接将要共享的目录mount到容器。docker managed volume 就要麻烦点。由于volume位于 host 中的目录,是在容器启动时才生成,所以需要将共享数据复制到 volume 中。

docker cp可以在容器和 host 之间复制数据,当然我们也可以直接通过Linux的cp命令复制到 /var/lib/docker/volumes/xxx

docker cp ~/index.html <container id>:/usr/local/apache2/htdocs/

2.2 容器之间共享数据

(1) 将$HOME/htdocs mount 到三个 httpd 容器。

docker run --name  web1 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
docker run --name  web2 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
docker run --name  web3 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd

(2) 查看当前主页内容。

image

(3) 修改 volume 中的主页文件,再次查看并确认所有容器都使用了新的主页。

image

2.3 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/userful/tools busybox

image

volume container 的作用只是提供数据,不需要处于运行状态

容器 mount 了两个 volume :
(1) bind mount,存放 Web Server 的静态文件。
(2) docker managed volume,存放一些实用工具。

docker inspect vc_data

image

  • 其他容器可以通过--volumes-from 使用vc_data这个volume container,
docker run --name  web1 -d -p 80 --volumes-from vc_data httpd
docker run --name  web2 -d -p 80 --volumes-from vc_data httpd
docker run --name  web3 -d -p 80 --volumes-from vc_data httpd

启动情况

image

查看数据卷

docker inpect web1

image

容器 web1、web2、web3用的就是vc_data中的数据卷

(1) 与bind mount相比,不必为每一个容器指定 host path,所有 path 都在volume container 中定义好了,容器只需与 volume container 关联,实现了容器与 host 的解耦。

(2) 使用 volume container 的容器,其 mount point 是一致的,有利于配置的规范和标准化,但也带来一定的局限,使用时需要综合考虑。

2.3. data-packed volume container

volume container 的数据归根到底还是在 host 里,有没有办法将数据完全放到 volume container 中,同时又能与其他容器共享呢? 当然可以,通常我们称这种容器为 data-packed volume container。

其原理是将数据打包到镜像中,然后通过 docker managed volume 共享

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

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

docker build -t datapacked .

image

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

docker create --name vc_data_packed datapacked

因为在 Dockerfile 中已经使用了VOLUME 指令,这里就不需要指定volumemount point 了。启动 httpd 容器并使用 data-packed volume container。

docker run --name  datapacked1 -d -p 80 --volumes-from vc_data_packed httpd

image

可以查看到正确访问到容器内的文件。

image

容器能够正确读取 volume 中的数据。data-packed volume container 是自包含的,不依赖 host 提供数据,具有很强的移植性,非常适合只使用静态数据的场景,比如应用的配置信息、Web server 的静态文件等。

3. Data Volume 生命周期

3.1 备份

因为volume实际上是host文件系统中的目录和文件,所以volume的备份实际上是对文件系统的备份。找到docker下载镜像的host保存目录,一般是/var/lib/docker/,如果配置了data-root,就是用这个配置的路径。

只需要定期备份镜像host保存目录即可

3.2 迁移

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

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

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

3.3 销毁

可以删除不再需要的 volume,但一定要确保知道自己正在做什么,volume 删除后数据是找不回来的。

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

如果删除容器时没有带 -v 呢? 这样就会产生孤儿volumedocker提供了volume子命令可以对 docker managed volume 进行维护。

docker volume rm xxxx # 删除单个volume
docker volume rm $(docker volume ls -q) # 批量删除volume

posted on 2024-03-12 17:00  ccblblog  阅读(5)  评论(0编辑  收藏  举报

导航