docker存储

docker为容器提供了两种存放数据的资源
1.由storage driver管理的镜像层和容器层
2.Data Volume

storage driver
“”"
在前面镜像章节我们学习到Docker镜像的分层结构,我们来简单回顾一下
容器由最上面的一个可写的容器层,以及若干只读的镜像层组成,容器的数据就存放在这些层中,这样的分层结构最大的特性就是Copy-on-Write:
1.数据会直接存放在最上面的容器层
2.修改现有的数据会先从镜像层将数据复制到容器层,修改后的数据直接操存在容器层中,镜像层保持不变
3.如果有多个层中有命名相同的文件,用户只能看到最上面的那层中的文件
分层结构使镜像和容器的创建,共享以及分发变得非常高效,而这些都要归功于Docker storage diriver,正是storage diriver实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图

[root@docker ~]# docker info|grep Storage
Storage Driver: overlay2

Docker安装时会根据当前系统的配置选择默认的driver 默认的driver具有最好的稳定性

“”"
对于某些容器,直接将数据放在由storage diriver维护的层中是很好的选择,比如那些无状态的应用,无状态意味着容器没有需要持久话的数据,随时可以从镜像直接创建
但是对于另一类应用这种方式就不合适了,它们有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,也就是说,这类容器是有状态的
这就要用到docker的另一种机制:Data Volume

Data Volume
本质上是 Docker Host文件系统中的目录或文件,能够直接被mount到容器的文件系统中,具有以下特点:
1.Data Volume是目录或者文件,而非没有格式化的磁盘(块设备)
2.容器可以读写volume中的数据
3.volume数据可以白永久地保存,即使使用它的容器已经被销毁

1.bind mount
bind mount 是将host上已经存在的目录或文件 mount到容器
[root@foundation0 htdocs]# pwd
/mnt/htdocs
[root@foundation0 htdocs]# cat index.html

Have a nice day!

-v的格式为::

[root@foundation0 htdocs]# docker run -d -p 8080:80 -v /mnt/htdocs:/usr/local/apache2/htdocs httpd
eaaad31d18a5a53464f057c60c43a1438e5b16a988fd5351ed3b0fb48994df02

[root@foundation0 ~]# curl 127.0.0.1:8080

Have a nice day!

[root@foundation0 htdocs]# echo ‘lucky!!’ > index.html
 [root@foundation0 htdocs]# curl -I 127.0.0.1:8080
 HTTP/1.1 200 OK
 Date: Tue, 21 May 2019 08:03:23 GMT
 Server: Apache/2.4.39 (Unix)
 Last-Modified: Tue, 21 May 2019 08:03:03 GMT
 ETag: “8-5896147425d6b”
 Accept-Ranges: bytes
 Content-Length: 8
 Content-Type: text/html[root@foundation0 htdocs]# curl 127.0.0.1:8080
 lucky!![root@foundation0 htdocs]# curl 127.0.0.1:8080
 lucky!!

即使我们停止并且删除容器,本地文件也不会删除

[root@foundation0 htdocs]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eaaad31d18a5 httpd “httpd-foreground” 3 minutes ago Up 3 minutes 0.0.0.0:8080->80/tcp xenodochial_kilby
[root@foundation0 htdocs]# docker stop eaaad31d18a5
eaaad31d18a5
[root@foundation0 htdocs]# docker rm -f eaaad31d18a5
eaaad31d18a5
[root@foundation0 htdocs]# cat index.html
lucky!!

整个目录的映射

[root@foundation0 htdocs]# docker run -d -p 8080:80 -v /mnt/htdocs/:/usr/local/apache2/htdocs httpd970f628c4b1fd25e54747c5acbdfb6670afe845faeeeb45fe049895559fb9955
[root@foundation0 htdocs]# curl 127.0.0.1:8080/test_index.html
test!!!
[root@foundation0 htdocs]# ls
index.html test_index.html

另外,bind mount时还可以指定数据的读写权限
[root@foundation0 htdocs]# docker run -d -p 8080:80 -v /mnt/htdocs:/usr/local/apache2/htdocs:ro httpd
a4753bd187caa049e6782a38d676032285c59f198bde9a3239cf51b0ef81945e
[root@foundation0 htdocs]# docker exec -it a4753bd187 /bin/bash
root@a4753bd187ca:/usr/local/apache2# echo “do some changes” > htdocs/index.html
bash: htdocs/index.html: Read-only file system
#ro 设置了只读权限,在容器中是无法对bind mount数据进行修改的,只有host有权修改数据,提高了安全性

除了bind mount 目录,还可以单独指定一个文件
[root@foundation0 htdocs]# docker run -d -p 8080:80 -v /mnt/htdocs/index.html:/usr/local/apache2/htdocs/new_index.html httpd
292129fc9a5a850dfed1c0cc8416c7699833454821f3362ff7cc1bbfea251c4a
[root@foundation0 htdocs]# curl 127.0.0.1:8080

It works!

[root@foundation0 htdocs]# curl 127.0.0.1:8080/new_index.html lucky!! #使用bind mount单个文件的场景是:只需要向容器添加文件,不希望覆盖整个目录 # 使用单一文件有一点要注意:host中的源文件必须要存在

“”"
mount point 有很多应用场景,比如我们可以将源代码目录mount到容器中,在host中修改代码就能看到应用的实时效果,再比如将Mysql容器的数据放在bind mount里,这样host可以方便地备份和迁移数据

bind mount的使用直观高效,易于理解,但它也有不足的地方:bind mount需要指定host文件系统的特定路径,这就限制了容器的可移植性,当需要将容器迁移到其他host,该host没有要mount的数据或者数据在不同的路径时,操作会失败
“”"
2.

docker managed volume
 docker managed volume与bind mount在使用上最大的区别是不需要指定mount 源,指明mount point就行了
 [root@foundation0 htdocs]# docker run -d -p 8080:80 -v /usr/local/apache2/htdocs httpd
 0a8b3290d4fff435bfc24fa8024d8470092e976b699fc21317d819a924cbd431
 [root@foundation0 htdocs]# docker inspect 0a8b3290d4fff43
 “Mounts”: [
 {
 “Type”: “volume”,
 “Name”: “05e10fc94bc6ad3156d423aefb72b739753832764f99494fe5d658ba82fa9463”,
 “Source”: “/var/lib/docker/volumes/05e10fc94bc6ad3156d423aefb72b739753832764f99494fe5d658ba82fa9463/_data”,
 “Destination”: “/usr/local/apache2/htdocs”,
 “Driver”: “local”,
 “Mode”: “”,
 “RW”: true,
 “Propagation”: “”
 }
 ],
 [root@foundation0 volumes]# cd 05e10fc94bc6ad3156d423aefb72b739753832764f99494fe5d658ba82fa9463
 [root@foundation0 05e10fc94bc6ad3156d423aefb72b739753832764f99494fe5d658ba82fa9463]# ls
 _data
 [root@foundation0 05e10fc94bc6ad3156d423aefb72b739753832764f99494fe5d658ba82fa9463]# cd _data/
 [root@foundation0 _data]# ls
 index.html
 [root@foundation0 _data]# cat index.html

It works!

[root@foundation0 _data]# pwd /var/lib/docker/volumes/05e10fc94bc6ad3156d423aefb72b739753832764f99494fe5d658ba82fa9463/_data

[root@foundation0 _data]# echo ‘update volume from host!!!’ > index.html
[root@foundation0 _data]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0a8b3290d4ff httpd “httpd-foreground” 33 minutes ago Up 33 minutes 0.0.0.0:8080->80/tcp wizardly_edison
[root@foundation0 _data]# curl 127.0.0.1:8080
update volume from host!!!
[root@foundation0 _data]# pwd
/var/lib/docker/volumes/05e10fc94bc6ad3156d423aefb72b739753832764f99494fe5d658ba82fa9463/_data

“”"
回顾一下docker managed volume的创建过程
1.容器启动时,简单的告诉docker我需要一个volume存放数据,帮我mount到目录
2.docker在/var/lib/docker/volumes中生成一个随机目录作为mount源
3.如果/abc已经存在,则将数据复制到mount源
4.如果/abc已经存在,则将数据复制到mount源
4.将volume mount到/abc
“”"
我们已经学习了两种data volume的原理和基本使用方法,下面做个对比:
相同点:两者都是host文件系统的某个路径
不同点:

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

docker 数据共享

Docker 数据共享
数据共享是volume的关键特性,我们将详细讨论通过volume如何在容器与host之间,容器与容器之间共享数据

1.容器与host共享数据
有两种类型的data volume 它们均可实现容器与host之间共享数据,但方式有别
对于bind mount 是非常明确:直接将要共享的目录mount到容器(具体参考之前讲的httpd的例子)
docker managed volume就要麻烦点,由于volume位于host中的目录,是在容器中启动才生成的,所以需要将共享数据复制到volumezhong

[root@server1 htdocs]# docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd
 Unable to find image ‘httpd:latest’ locally
 latest: Pulling from library/httpd
 f5d23c7fed46: Pull complete
 b083c5fd185b: Pull complete
 bf5100a89e78: Pull complete
 98f47fcaa52f: Pull complete
 622a9dd8cfed: Pull complete
 Digest: sha256:dc4c86bc90593c6e4c5b06872a7a363fc7d4eec99c5d6bfac881f7371adcb2c4
 Status: Downloaded newer image for httpd:latest
 70f0c75396a90971d2da44ab2d1f22392ea105f99f5ed01aa78c720572fd2b40
 [root@server1 htdocs]# curl 127.0.0.1:80

It works!

[root@server1 htdocs]# vim /mnt/index.html [root@server1 htdocs]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 70f0c75396a9 httpd "httpd-foreground" 3 minutes ago Up 3 minutes 0.0.0.0:80->80/tcp quirky_northcutt [root@server1 htdocs]# docker cp /mnt/index.html 70f0c75396a9:/usr/local/apache2/htdocs/ [root@server1 htdocs]# curl 127.0.0.1:80 redhat7 index page!

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

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

[root@server1 htdocs]# docker run --name web1 -d -p 80 -v /root/htdocs:/usr/local/apache2/htdocs httpd
1c2db45e04786e4cb36e5ae66846c02e105ad76a5e4b0f4edf40c654d5266d5e
[root@server1 htdocs]# docker run --name web2 -d -p 80 -v /root/htdocs:/usr/local/apache2/htdocs httpd
8d33516133fbcb7e23bfa20f0a1204686cfdb2fa6fa245654df2dbf18e681e62
[root@server1 htdocs]# docker run --name web3 -d -p 80 -v /root/htdocs:/usr/local/apache2/htdocs httpd
71b725630727522dbaf6eadd400bc15c7394045292a85c762a51bc44b6755f20
[root@server1 htdocs]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
71b725630727 httpd “httpd-foreground” 5 seconds ago Up 5 seconds 0.0.0.0:32770->80/tcp web3
8d33516133fb httpd “httpd-foreground” 13 seconds ago Up 12 seconds 0.0.0.0:32769->80/tcp web2
1c2db45e0478 httpd “httpd-foreground” 32 seconds ago Up 31 seconds 0.0.0.0:32768->80/tcp web1
[root@server1 htdocs]# curl 127.0.0.1:32770
hahahaha~
[root@server1 htdocs]# curl 127.0.0.1:32769
hahahaha~
[root@server1 htdocs]# curl 127.0.0.1:32768
hahahaha~
[root@server1 htdocs]# echo “This is a new hahaha” >/root/htdocs/index.html
[root@server1 htdocs]# curl 127.0.0.1:32770This is a new hahaha
[root@server1 htdocs]# curl 127.0.0.1:32769This is a new hahaha
[root@server1 htdocs]#

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

volume container是专门为其他容器提供volume的容器 它提供的卷可以是bind mount 也可以是docker managed volume

创建一个volume container

我们将容器命名为vc_data 注意这里执行的是create命令 因为volume container 的作用只是提供数据 它本身不需要处于运行状态

[root@server1 htdocs]# docker create --name vc_data -v /root/htdocs:/usr/local/apache2/htdocs busybox
4f9e9b99f9908307a8a8db28a705f07061384081ed7ff113574644f9719f586f

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

[root@server1 htdocs]# docker run --name web1 -d -p 80 --volumes-from vc_data httpd
5b7cd15d6a54e379f4be94bb3ae965626f4ace0edaf1b260aca8f56b7a844638
[root@server1 htdocs]# docker run --name web2 -d -p 80 --volumes-from vc_data httpd
e4cfd1091b8b82697c61195436a3ce5373484f584cf6f3561f250b450dfd922a
[root@server1 htdocs]# docker run --name web3 -d -p 80 --volumes-from vc_data httpd
2a193e9d32008457a7d2af65b482550ef45d751aa34d245b78338a855c764da
1

三个容器已经成功共享了 volume container 中volume
[root@server1 htdocs]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a193e9d3200 httpd “httpd-foreground” 7 seconds ago Up 6 seconds 0.0.0.0:32776->80/tcp web3
e4cfd1091b8b httpd “httpd-foreground” 12 seconds ago Up 12 seconds 0.0.0.0:32775->80/tcp web2
5b7cd15d6a54 httpd “httpd-foreground” 16 seconds ago Up 16 seconds 0.0.0.0:32774->80/tcp web1
[root@server1 htdocs]# echo “This content is from a volume container” >/root/htdocs/index.html
[root@server1 htdocs]# curl 127.0.0.1:32776
This content is from a volume container
[root@server1 htdocs]# curl 127.0.0.1:32775
This content is from a volume container
[root@server1 htdocs]# curl 127.0.0.1:32774
This content is from a volume container

我们讨论以下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共享

“”"
小结
1.docker 为容器提供了两种存储资源:数据层和data volume
2.数据层包括镜像层和容器层,由storage driver管理
3.data volume有两种类型:bind mount 和docker managed volume
4.bind mount 可实现容器与host之间,容器与容器之间共享数据
5.volume container 是一种具有更好移植性的容器间数据共享方案,特别是volume container
“”"