Docker 存储卷

TaoTaoFu 2020-06-04

Docker的两类存储资源

storage driver

Docker镜像的分层结构

Docker 存储卷

容器由最上面一个可写的容器层,以及若干只读的镜像层组成,容器的数据就存放在这些层中。
分层结构使镜像和容器的创建、共享以及分发变得非常高效,而这些都要归功于Docker storage driver。正是storage driver实现了多层数据的堆叠并为用户提供个单一 的合并之后的统一 视图。
Docker支持多种storage driver,有AUFS ,Device Mapper, Btrfs, OverlayFS, VFS 和ZFS。它们都能实现分层的架构,同时又有各自的特性。Docker 官方给优先使用Linux发行版默认的storage driver。
Docker安装时会根据当前系统的配置选择默认的driver.默认driver具有最好的稳定性,因为默认driver在发行版上经过了严格的测试。

运行docker info查看centos的默认driver:

docker info

Ubuntu用的AFUS,底层文件系统是extfs,各层数据存放在 /var/lib/docker/aufs。

Redhat/CentOS的默认driver是Device Mapper, SUSE 则是Btrfs。
对于某些容器,直接将数据放在由storage driver。

Data Volume之bind mount

storage driver和data volume是容器存放数据的两种方式。

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

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

这是需要持久化的数据,并且应该与镜像分开存放。

在具体的使用上,docker提供了两种类型的volume:bind mount 和 docker managed volume。

bind mount

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

[ ~]# mkdir /htdocs
[ ~]# mv index.html  /htdocs/

通过 -v将其mount到httpd容器:

[ ~]# docker run -d -p 8000:80 -v /htdocs/:/usr/local/apache2/htdocs httpd

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

[ ~]# curl 127.0.0.1:8000
<html>
<body>
<h1>this is test</h1>
</body>
</html>

curl显示的确实是/htdocs/html.index的内容,更新一下:

[ ~]# echo "hug"  > /htdocs/index.html 
[ ~]# curl 127.0.0.1:8000
hug

确实生效了,bind mount 可以让 host 与容器共享数据。

把容器销毁,对bind mount 也不会有什么影响。

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

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

在容器中是无法修改的,只有host有权限修改

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

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

使用单一文件有一点要注意:host 中的源文件必须要存在,不然会当作一个新目录bind mount给容器。
mount point有很多应用场景,比如我们可以将源代码目录mount到容器中,在host中修改代码就能看到应用的实时效果。再比如将mysql容器的数据放在bind mount里,这样host可以方便地备份和迁移数据。
bind mount的使用直观高效,易于理解,但它也有不足的地方: bind mount需要指定host文件系统的特定路径,这就限制了容器的可移植性,当需要将容器迁移到其他host,而该host没有要mount的数据或者数据不在相同的路径时,操作会失败。

docker managed volume

在使用上不需指定mount 源,指名 mount point 就行了。

[ ~]# docker run -d -p 8000:80 -v /usr/local/apache2/htdocs httpd

我们通过-v告诉 docker 需要一个 data volume,并将其 mount 到 /usr/local/apache2/htdocs。
data volume 可以在容器的配置信息中找到:

[ ~]# docker inspect f480d2
Mounts": [
        {
            "Type": "volume",
            "Name": "3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46",
            "Source": "/var/lib/docker/volumes/3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46/_data",
            "Destination": "/usr/local/apache2/htdocs",

Source 就是该 volume 在host上的目录。
每当容器申请mount docker managed volume时,docker都会在/var/lib/docker/volumes 下生成一个目录,这个目录就mount源。

[ ~]# ls /var/lib/docker/volumes/3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46/_data/

还可以用 docker volume查看目录:

[ ~]# docker volume  inspect 3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46
[
    {
        "CreatedAt": "2020-06-02T17:50:00+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46/_data",
        "Name": "3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46",
        "Options": null,
        "Scope": "local"
    }
]

目前,docker volume只能查看docker managed volume,还看不到 bind mount,也无法知道volume对应的容器。

查看index:

[ ~]# curl 127.0.0.1:8000
<html><body><h1>It works!</h1></body></html>

可以直接修改index,或添加一个index:

[ ~]# vim /var/lib/docker/volumes/3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46/_data/index.html 
[ ~]# vim /var/lib/docker/volumes/3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46/_data/test.index
bind mount 与 docker managed volume的区别

相同:两者都是 host 文件系统的某个路径

不同:
Docker 存储卷

共享数据

共享数据是 volume 的关键特性, volume 如何在容器与host之间,容器与容器之间共享数据。

容器与host共享数据

有两种类型的data volume,它们均可实现在容器与host之间共享数据,但方式有所区别。
对于bind mount是非常明确的:直接将要共享的目录mount到容器。具体请参考前面httpd的例子,不再赘述。
docker managed volume就要麻烦点。由于volume位于host中的目录,是在容器启动时才生成,所以需要将共享数据拷拷贝到 volume中。

[ ~]# docker run -d -p 8000:80 -v /usr/local/apache2/htdocs httpd
[ ~]# curl 127.0.0.1:8000
<html><body><h1>It works!</h1></body></html>
[ ~]# docker cp /htdocs/index.html  97373dd7f9b15f241:/usr/local/apache2/htdocs
[ ~]# curl 127.0.0.1:8000
hug

Docker cp可以在容器与host之间拷贝数据,也可以直接通过Linux的cp命令复制到/var/lib/docker/volumes/。

容器之间共享数据

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

将 /htdocs mount 到三个httpd容器:

[ ~]# docker run --name web1 -d -p 80 -v /htdocs:/usr/local/apache2/htdocs httpd
b004d4df2fe4667d01e15b1a936afd726ac7ef896dba9336d064636018cd0ab3
[ ~]# docker run --name web2 -d -p 80 -v /htdocs:/usr/local/apache2/htdocs httpd
fe50f6b90793bfd385ea86ad205cab2a20b3985f911435c2f1c4463d1416f6f9
[ ~]# docker run --name web3 -d -p 80 -v /htdocs:/usr/local/apache2/htdocs httpd
2da85061d406c8cf5cbe061fb42a5e58257816dcd9eaa7db82b7ce951305ae39

查看当前主页:

[ ~]# docker ps 
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                   NAMES
2da85061d406        httpd               "httpd-foreground"   6 seconds ago       Up 5 seconds        0.0.0.0:32770->80/tcp   web3
fe50f6b90793        httpd               "httpd-foreground"   11 seconds ago      Up 10 seconds       0.0.0.0:32769->80/tcp   web2
b004d4df2fe4        httpd               "httpd-foreground"   20 seconds ago      Up 19 seconds       0.0.0.0:32768->80/tcp   web1

[ ~]# curl 127.0.0.1:32768
hug
[ ~]# curl 127.0.0.1:32769
hug
[ ~]# curl 127.0.0.1:32770
hug

修改 volume 中的主页文件,再次查看:

[ ~]# echo "hhhhhhhhhh" > /htdocs/index.html 
[ ~]# curl 127.0.0.1:32769
hhhhhhhhhh
[ ~]# curl 127.0.0.1:32768
hhhhhhhhhh
[ ~]# curl 127.0.0.1:32770
hhhhhhhhhh

用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 httpd
f5192fe00a8932df333ee2eafb0ff8797b053329e5e236bc29975f848d9a0b56

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

1.bind mount,存放web server的静态文件
2.docker managed volume,存放一些实用工具

通过docker inspect 查看 volume

[ ~]# docker inspect  vc_data  | grep Destina
                "Destination": "/usr/local/apache2/htdocs",
                "Destination": "/other/userful/tools",

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

[ ~]# docker run --name web1 -d -p 80  --volumes-from  vc_data  httpd
aab5751a7bb371f5deffef3ad98bcee95d87dcc69c08525eaec6b3417852b381
[ ~]# docker run --name web2 -d -p 80  --volumes-from  vc_data  httpd
a9bf189425bd4e258750fcc7b81be1e86a7184971cb49303ddf5dad579b6c676
[ ~]# docker run --name web3 -d -p 80  --volumes-from  vc_data  httpd
bdd982b6d819fab3cfd8791f5058d8f02f875507cc98030309a9707d36f35046

三个httpd容器都使用了 vc_data,查看都有哪些volume:

[ ~]# docker inspect web1 | grep Destina
                "Destination": "/usr/local/apache2/htdocs",
                "Destination": "/other/userful/tools",

验证共享效果:

[ ~]# docker ps
CONTAINER ID        IMAGE               COMMAND              CREATED              STATUS              PORTS                   NAMES
bdd982b6d819        httpd               "httpd-foreground"   About a minute ago   Up About a minute   0.0.0.0:32773->80/tcp   web3
a9bf189425bd        httpd               "httpd-foreground"   About a minute ago   Up About a minute   0.0.0.0:32772->80/tcp   web2
aab5751a7bb3        httpd               "httpd-foreground"   About a minute ago   Up About a minute   0.0.0.0:32771->80/tcp   web1
[ ~]# echo "this this hsith" > /htdocs/index.html 
[ ~]# curl 127.0.0.1:32771
this this hsith
[ ~]# curl 127.0.0.1:32772
this this hsith
[ ~]# curl 127.0.0.1:32773
this this hsith

volume container的特点:

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

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

另-种在容器之间共享数据的方式是data-packed volume container。

data-packed volume container

volume container 的数据在host里,有没有办法将数据完全放到volume container中,同时又能与其他容器共享呢?
当然可以,通常我们称这种容器为data-packed volume container.其原理是将数据打包到镜像中,然后通过docker managed volume共享。

我们用Dockfile构建镜像:

[ /]# mkdir datapacked
[ /]# cd datapacked/
[ datapacked]# ls
[ datapacked]# cat Dockerfile
FROM httpd
ADD htdocs  /usr/local/apache2/htdocs
VOLUME /usr/local/apache2/htdocs

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

build 新镜像

[ datapacked]# mv /htdocs/ .
[ datapacked]# docker build -t data .

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

[ datapacked]# docker create --name vc_data2 data
345255edfd3dfb6368d3c3ad932dd6aadcffd46a00cacc6f2bf849d69a8f885c

因为在Dockerfile中使用了VOlUME指令,这里就不需要指定volume的mount point,启动容器并使用data-packed volume container。

[ datapacked]# docker run -d -p 8000:80 --volumes-from vc_data2 httpd
dca8789bd56ea79574866ac115a352d9466708eb0abd6ade0129df0b5ff550bd
[ datapacked]# curl 127.0.0.1:8000
this this hsith

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

volume生命周期管理

Data Volume中存放的是要的应用数据,如何管理volume对应用至关重要。前面我们主要关注的是volume的创建、共享和使用。

备份

因为volume实际上是host文件系统中的目录和文件,所以volume的备份实际上是对文件系统的备份。所有的本地镜像都存在host的/myregistry目录中,我们要做的就是定期备份这个目录。

恢复

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

迁移

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

  1. docker stop当前Registry容器。
  2. 启动新版本容器并mount原有volume.

docker run -d -P 0005000 -V /myregisty:/ar/lib/registry registry:latest

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

销毁

可以删除不再需要的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 Is查看到。

删除孤儿volume:

[ ~]# docker volume rm 3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff463b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46
[ ~]# docker volume rm  $(docker volume ls -q

相关推荐