TaoTaoFu 2020-06-04
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到容器。
[ ~]# 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的数据或者数据不在相同的路径时,操作会失败。
在使用上不需指定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
相同:两者都是 host 文件系统的某个路径
不同:
共享数据是 volume 的关键特性, volume 如何在容器与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。
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的静态文件等,
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:
[ ~]# docker volume rm 3b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff463b1a7628fe1772d91497ad2a518b1b1637ea51f26a70f9415d477c539739ff46 [ ~]# docker volume rm $(docker volume ls -q