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。
storage driver和data volume是容器存放数据的两种方式。
Data Volume本质上是Docker Host文件系统中的目录或文件,能够直接被mount到容器的文件系统中。Data Volume有以下特点:
这是需要持久化的数据,并且应该与镜像分开存放。
在具体的使用上,docker提供了两种类型的volume:bind mount 和 docker managed volume。
bind mount是将host上已存在的目录或文件mount到容器。
[root@localhost ~]# mkdir /htdocs
[root@localhost ~]# mv index.html /htdocs/
通过 -v将其mount到httpd容器:
[root@localhost ~]# 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命令的行为是致的。
[root@localhost ~]# curl 127.0.0.1:8000
<html>
<body>
<h1>this is test</h1>
</body>
</html>
curl显示的确实是/htdocs/html.index的内容,更新一下:
[root@localhost ~]# echo "hug" > /htdocs/index.html
[root@localhost ~]# curl 127.0.0.1:8000
hug
确实生效了,bind mount 可以让 host 与容器共享数据。
把容器销毁,对bind mount 也不会有什么影响。
另外还可以指定数据的读写权限,默认是可读写,
指定为只读:
[root@localhost ~]# 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的数据或者数据不在相同的路径时,操作会失败。
在使用上不需指定mount 源,指名 mount point 就行了。
[root@localhost ~]# docker run -d -p 8000:80 -v /usr/local/apache2/htdocs httpd
我们通过-v告诉 docker 需要一个 data volume,并将其 mount 到 /usr/local/apache2/htdocs。
data volume 可以在容器的配置信息中找到:
[root@localhost ~]# 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源。
[root@localhost ~]# ls /var/lib/docker/volumes/3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46/_data/
还可以用 docker volume查看目录:
[root@localhost ~]# 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:
[root@localhost ~]# curl 127.0.0.1:8000
<html><body><h1>It works!</h1></body></html>
可以直接修改index,或添加一个index:
[root@localhost ~]# vim /var/lib/docker/volumes/3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46/_data/index.html
[root@localhost ~]# vim /var/lib/docker/volumes/3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46/_data/test.index
相同:两者都是 host 文件系统的某个路径
不同:
共享数据是 volume 的关键特性, volume 如何在容器与host之间,容器与容器之间共享数据。
有两种类型的data volume,它们均可实现在容器与host之间共享数据,但方式有所区别。
对于bind mount是非常明确的:直接将要共享的目录mount到容器。具体请参考前面httpd的例子,不再赘述。
docker managed volume就要麻烦点。由于volume位于host中的目录,是在容器启动时才生成,所以需要将共享数据拷拷贝到 volume中。
[root@localhost ~]# docker run -d -p 8000:80 -v /usr/local/apache2/htdocs httpd
[root@localhost ~]# curl 127.0.0.1:8000
<html><body><h1>It works!</h1></body></html>
[root@localhost ~]# docker cp /htdocs/index.html 97373dd7f9b15f241:/usr/local/apache2/htdocs
[root@localhost ~]# curl 127.0.0.1:8000
hug
Docker cp可以在容器与host之间拷贝数据,也可以直接通过Linux的cp命令复制到/var/lib/docker/volumes/。
第一种可以将共享数据放在 bind mount 中,然后将其 mount 到多个容器。
将 /htdocs mount 到三个httpd容器:
[root@localhost ~]# docker run --name web1 -d -p 80 -v /htdocs:/usr/local/apache2/htdocs httpd
b004d4df2fe4667d01e15b1a936afd726ac7ef896dba9336d064636018cd0ab3
[root@localhost ~]# docker run --name web2 -d -p 80 -v /htdocs:/usr/local/apache2/htdocs httpd
fe50f6b90793bfd385ea86ad205cab2a20b3985f911435c2f1c4463d1416f6f9
[root@localhost ~]# docker run --name web3 -d -p 80 -v /htdocs:/usr/local/apache2/htdocs httpd
2da85061d406c8cf5cbe061fb42a5e58257816dcd9eaa7db82b7ce951305ae39
查看当前主页:
[root@localhost ~]# 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
[root@localhost ~]# curl 127.0.0.1:32768
hug
[root@localhost ~]# curl 127.0.0.1:32769
hug
[root@localhost ~]# curl 127.0.0.1:32770
hug
修改 volume 中的主页文件,再次查看:
[root@localhost ~]# echo "hhhhhhhhhh" > /htdocs/index.html
[root@localhost ~]# curl 127.0.0.1:32769
hhhhhhhhhh
[root@localhost ~]# curl 127.0.0.1:32768
hhhhhhhhhh
[root@localhost ~]# curl 127.0.0.1:32770
hhhhhhhhhh
用volume container共享数据
volume container是专门为其他容器提供volume 的容器。它提供的卷可以是 bind mount ,也可以是 docker managed volume。
创建一个 volume container:
[root@localhost ~]# 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
[root@localhost ~]# docker inspect vc_data | grep Destina
"Destination": "/usr/local/apache2/htdocs",
"Destination": "/other/userful/tools",
其他容器可以使用 - -volumes-from 使用 vc_data这个 volume container:
[root@localhost ~]# docker run --name web1 -d -p 80 --volumes-from vc_data httpd
aab5751a7bb371f5deffef3ad98bcee95d87dcc69c08525eaec6b3417852b381
[root@localhost ~]# docker run --name web2 -d -p 80 --volumes-from vc_data httpd
a9bf189425bd4e258750fcc7b81be1e86a7184971cb49303ddf5dad579b6c676
[root@localhost ~]# docker run --name web3 -d -p 80 --volumes-from vc_data httpd
bdd982b6d819fab3cfd8791f5058d8f02f875507cc98030309a9707d36f35046
三个httpd容器都使用了 vc_data,查看都有哪些volume:
[root@localhost ~]# docker inspect web1 | grep Destina
"Destination": "/usr/local/apache2/htdocs",
"Destination": "/other/userful/tools",
验证共享效果:
[root@localhost ~]# 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
[root@localhost ~]# echo "this this hsith" > /htdocs/index.html
[root@localhost ~]# curl 127.0.0.1:32771
this this hsith
[root@localhost ~]# curl 127.0.0.1:32772
this this hsith
[root@localhost ~]# 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。
volume container 的数据在host里,有没有办法将数据完全放到volume container中,同时又能与其他容器共享呢?
当然可以,通常我们称这种容器为data-packed volume container.其原理是将数据打包到镜像中,然后通过docker managed volume共享。
我们用Dockfile构建镜像:
[root@localhost /]# mkdir datapacked
[root@localhost /]# cd datapacked/
[root@localhost datapacked]# ls
[root@localhost 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 新镜像
[root@localhost datapacked]# mv /htdocs/ .
[root@localhost datapacked]# docker build -t data .
用新镜像创建 data-packed volume container:
[root@localhost datapacked]# docker create --name vc_data2 data
345255edfd3dfb6368d3c3ad932dd6aadcffd46a00cacc6f2bf849d69a8f885c
因为在Dockerfile中使用了VOlUME指令,这里就不需要指定volume的mount point,启动容器并使用data-packed volume container。
[root@localhost datapacked]# docker run -d -p 8000:80 --volumes-from vc_data2 httpd
dca8789bd56ea79574866ac115a352d9466708eb0abd6ade0129df0b5ff550bd
[root@localhost datapacked]# curl 127.0.0.1:8000
this this hsith
容器能够正确读取volume中的数据。data-packed volume container是自包含的,不依赖host提供数据,具有很强的移植性,非常适合只使用静态数据的场景,比如应用的配置信息、web server的静态文件等,
Data Volume中存放的是要的应用数据,如何管理volume对应用至关重要。前面我们主要关注的是volume的创建、共享和使用。
因为volume实际上是host文件系统中的目录和文件,所以volume的备份实际上是对文件系统的备份。所有的本地镜像都存在host的/myregistry目录中,我们要做的就是定期备份这个目录。
volume的恢复也很简单,如果数据损坏了,直接用之前备份的数据拷贝到/myregistry就可以了。
如果我们想使用更新版本的Registry,这就涉及到数据迁移,方法是:
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:
[root@localhost ~]# docker volume rm 3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff463b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46
[root@localhost ~]# docker volume rm $(docker volume ls -q
原文:https://blog.51cto.com/14833298/2500919