Docker存储

Docker存储

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

(1)由storage driver管理的镜像层和容器层。

(2)Data Volume。

1. storage driver

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

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

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

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

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

Docker支持多种storage driver,有AUFS、Device Mapper、Btrfs、OverlayFS(overlay2)、VFS和ZFS。它们都能实现分层的架构,同时又有各自的特性。对于Docker用户来说,具体选择使用哪个storage driver是一个难题,因为:

(1)没有哪个driver能够适应所有的场景。

(2)driver本身在快速发展和迭代。

不过Docker官方给出了一个简单的答案:优先使用Linux发行版默认的storage driver。

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

运行docker info查看Centos的默认driver。

docker info 

Centos默认driver用的是overlay2,底层文件系统是extfs,各层数据存放在/var/lib/docker。

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

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

这就要用到Docker的另一种存储机制:Data Volume。

2. Data Volume

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

(1)Data Volume是目录或文件,而非没有格式化的磁盘(块设备)。

(2)容器可以读写volume中的数据。

(3)volume数据可以被永久地保存,即使使用它的容器已经销毁。

数据层(镜像层和容器层)和volume都可以用来存放数据,具体使用的时候要怎样选择呢?考虑下面几个场景:

(1)Database软件vs Database数据。

(2)Web应用vs应用产生的日志。

(3)数据分析软件vs input/output数据。

(4)Apache Server vs静态HTML文件。

相信大家会做出这样的选择:

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

如何设置volume的容量?

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

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

bind mount和docker managed volume。

2.1 bind mount

bind mount是将host上已存在的目录或文件mount到容器。

例如docker host上有目录 $HOME/htdocs。

<html> <body><h1>This is a file in host file system !</h1></body></html>

通过 -v将其mount到httpd容器

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

-v的格式为 :

/usr/local/apache2/htdocs就是Apache Server存放静态文件的地方。

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

curl显示当前主页确实是 $HOME/htdocs/index.html中的内容。更新一下,看是否能生效。

host中的修改确实生效了,bind mount可以让host与容器共享数据。这在管理上是非常方便的。

下面我们将容器销毁,看看对bind mount有什么影响

可见,即使容器没有了,bind mount也还在。这也合理,bind mount是host文件系统中的数据,只是借给容器用用,哪能随便就删了啊。

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

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

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

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

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

使用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。

2.2 docker managed volumesh

docker managed volume与bind mount在使用上的最大区别是不需要指定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 5f3101f1fb519

docker inspect的输出很多,我们感兴趣的是Mounts这部分,这里会显示容器当前使用的所有data volume,包括bind mount和docker managed volume。

Source就是该volume在host上的目录。

原来,每当容器申请mount docker manged volume时,docker都会在/var/lib/docker/volumes下生成一个目录(例子中是/var/lib/docker/volumes/9b5d437e83b97565862a293548f6ef877a5fa1b4e40e4a3c9aa1b091605cb468/_data),这个目录就是mount源。

下面继续研究这个volume,看里面有些什么东西

volume的内容跟容器原有 /usr/local/apache2/htdocs完全一样,这是怎么回事呢?

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

但要明确一点:此时的 /usr/local/apache2/htdocs已经不再是由storage driver管理的层数据了,它已经是一个data volume。我们可以像bind mount一样对数据进行操作,例如更新数据

简单回顾一下docker managed volume的创建过程:

(1)容器启动时,简单地告诉docker"我需要一个volume存放数据,帮我mount到目录/abc"。

(2)docker在/var/lib/docker/volumes中生成一个随机目录作为mount源。

(3)如果/abc已经存在,则将数据复制到mount源。

(4)将volume mount到/abc。

除了通过docker inspect查看volume,我们也可以用docker volume命令

[root@Wesuiliye ~]# docker volume ls
DRIVER    VOLUME NAME
local     9b5d437e83b97565862a293548f6ef877a5fa1b4e40e4a3c9aa1b091605cb468
[root@Wesuiliye ~]# docker volume inspect 9b5d437e83b97565862a293548f6ef877a5fa1b4e40e4a3c9aa1b091605cb468

目前,docker volume只能查看docker managed volume,还看不到bind mount;同时也无法知道volume对应的容器,这些信息还得靠docker inspect。

我们已经学习了两种data volume的原理和基本使用方法,下面做个对比:

(1)相同点:两者都是host文件系统中的某个路径。

(2)不同点:如下图

3. 数据共享

3.1 容器与host共享数据

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

对于bind mount是非常明确的:直接将要共享的目录mount到容器。

docker managed volume就要麻烦点。由于volume位于host中的目录,是在容器启动时才生成,所以需要将共享数据复制到volume中。

 docker cp ./htdocs/index.html 40f2ada179ce:/usr/local/apache2/htdocs

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

3.2 容器之间共享数据

第一种方法是将共享数据放在bind mount中,然后将其mount到多个容器。还是以httpd为例,不过这次的场景复杂些,我们要创建由三个httpd容器组成的Web server集群,它们使用相同的html文件,操作如下:

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

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

 docker run --name web2 -d -p80 -v /root/htdocs:/usr/local/apache2/htdocs httpd 

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

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

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

echo "NO,it is not new "  > /root/htdocs/index.html

另一种在容器之间共享数据的方式是使用volume container。

4. volume container

volume container是专门为其他容器提供volume的容器。

它提供的卷可以是bind mount,也可以是docker managed volume。

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

我们将容器命名为vc_data(vc是volume container的缩写)。注意这里执行的是docker create命令,这是因为volume container的作用只是提供数据,它本身不需要处于运行状态。容器mount了两个volume:

(1)bind mount,存放Web Server的静态文件。

(2)docker managed volume,存放一些实用工具(当然现在是空的,这里只是做个示例)。

通过docker inspect可以查看到这两个volume。

docker inspect vc_data

其他容器可以通过--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  

三个httpd容器都使用了vc_data,看看它们现在都有哪些volume,以web1为例

web1容器使用的就是vc_data的volume,而且连mount point都是一样的。验证一下数据共享的效果。

echo "This content is from a volume container! " > /root/htdocs/index.html

可见,三个容器已经成功共享了volume container中的volume。

下面我们讨论一下volume container的特点:

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

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

5. data-packed volume container

上面的例子中volume container的数据归根到底还是在host里,有没有办法将数据完全放到volume container中,同时又能与其他容器共享呢?

当然可以,通常我们称这种容器为data-packed volume container。其原理是将数据打包到镜像中,然后通过docker managed volume共享。

我们用下面的Dockerfile构建镜像

vim 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

因为在Dockerfile中已经使用了VOLUME指令,这里就不需要指定volume的mount point了。

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

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

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

6. Data Volume生命周期管理

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

6.1 备份

因为volume实际上是host文件系统中的目录和文件,所以volume的备份实际上是对文件系统的备份。

跟之前的一样。搭建本地Registry。

所有的本地镜像都保存在host的/myregistry目录中,我们要做的就是定期备份这个目录。

6.2 恢复

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

6.3 迁移

如果我们想使用更新版本的Registry,这就涉及数据迁移,方法是:

(1)docker stop当前Registry容器。

(2)启动新版本容器并mount原有volume。

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

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

6.4 销毁

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

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

如果删除容器时没有带 -v呢?这样就会产生孤儿volume,好在docker提供了volume子命令可以对docker managed volume进行维护。请看下面的例子。

容器bbox使用的docker managed volume可以通过docker volume ls查看到。

docker volume ls

可以看到,有很多孤儿volume,可以用docker volume rm删除

docker volume rm

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

docker volume rm $(docker volume ls -q)

相关命令

运行docker info查看的默认driver。

docker info 

bind mount将host上已存在的目录或文件mount到容器。

 docker run -d -p 80:80 -v /root/htdocs:/usr/local/apache2/htdocs httpd
 # -v 的格式  <host path>:<container path>

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

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

查看指定容器的配置信息

docker inspect 5f3101f1fb519

列出所有volume

docker volume ls

查看指定容器的volume

docker volume inspect 9b5d437e83b97565862a293548f6ef877a5fa1b4e40e4a3c9aa1b091605cb468
posted @ 2023-04-23 21:05  0x1e61  阅读(49)  评论(0编辑  收藏  举报