1.Storage Driver
数据存储
CentOS7版本的docker
[root@docker ~]# docker info
...
Storage Driver: overlay2
Backing Filesystem: xfs
...
正常情况下,只有很少量的数据被写入到容器最上层的写入层,并且通过volume来写数据,然而也会遇到一些情况需要我们可以直接写入到容器的写入层,这时就需要storage driver来完成此项任务。
Docker使用一系列不同的storage driver来管理镜像层和容器层,这些storage driver不同于volume。
一个镜像是由若干镜像层组成;
Dockerfile中的每条指令都会生成一个镜像层,除了最上面的一层之外,其他的都是只读的;
最上一层主要是镜像运行时的一些命令;
每一层只是与它之前的层有一些不同,层层堆叠在一起;
创建容器时,只是在底层上添加一个新的可写层,这一层通常称为“容器层”;
Docker支持多种storage driver,有AUFS、Device Mapper、Btrfs、OverlayFS、VFS和ZFS。
它们都能实现分层的架构,同时又有各自的特性。
对于Docker用户来说,具体选择使用哪个storage driver是一个难题,主要是因为:
(1)没有哪个driver能够适应所有的场景;
(2)driver本身在快速发展和迭代;
2.优先使用Linux发行版默认的storage driver
Docker安装时会根据当前系统的配置选择默认的driver。默认driver具有最好的稳定性,因为默认driver在发行版上经过了严格的测试。
Ubuntu用的AUFS,底层文件系统是extfs,各层数据存放在/var/lib/docker/aufs下。
Redhat/CentOS的默认driver是Device Mapper,SUSE则是Btrfs。
对于某些容器,直接将数据放在由storage driver维护的层中是很好的选择,比如那些无状态的应用。无状态意味着容器没有需要持久化的数据,随时可以从镜像直接创建。
比如busybox,它是一个工具箱,启动busybox是为了执行诸如wget,ping之类的命令,不需要保存数据供以后使用,使用完直接退出,容器删除时存放在容器层中的工作数据也一起被删除,下次再启动新容器即可。
但对于另一类应用就不合适了,它们有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,这类容器是有状态的。这就要用到Docker的另一种存储机制:Data Volume。
1.简介
本质上是Docker Host文件系统中的目录或文件,能够直接被mount到容器的文件系统中。
有以下特点:
(1)Data Volume是目录或文件,而非没有格式化的磁盘(块设备);
(2)容器可以读写volume中的数据;
(3)volume数据可以被永久的保存,即使使用它的容器已经销毁;
现在有数据层(镜像层和容器层)和volume都可以用来存放数据,具体使用时要怎样选择呢?
考虑下面几个场景:
Database软件 vs Database数据
Web应用 vs 应用产生的日志
数据分析软件 vs input/output数据
Apache Server vs 静态HTML文件
前者放在数据层中,因为这部分内容是无状态的,应该作为镜像的一部分;后者放在Data Volume中,这是需要持久化的数据,并且应该与镜像分开存放。
关于如何设置volume的容量?
因为volume实际上是docker host文件系统的一部分,所以volume的容量取决于文件系统当前未使用的空间,目前还没有方法设置volume的容量。
1.简介
持久化存储:本质上是Docker Host文件系统中的目录或文件,能够直接被Mount到容器的文件系统中。在运行容器时,可以通过-v实现。
2.示例
运行一个nginx服务,做数据持久化。
[root@docker ~]# mkdir html
[root@docker ~]# cd html
[root@docker html]# vim index.html
this is testfile.
[root@docker html]# docker run -itd --name testweb -p 80 --restart always -v /root/html:/usr/share/nginx/html nginx
[root@docker html]# docker inspect testweb | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
[root@docker html]# curl 172.17.0.2
this is testfile.
注意:Docker Host上需要被挂载的源文件或目录,必须是已经存在,否则当做一个目录挂载到容器中。
host中的修改确实生效了,bind mount可以让host与容器共享数据。这在管理上非常方便。
[root@docker html]# docker exec -it testweb bash
root@6e3827be6c51:/# echo 123 > /usr/share/nginx/html/index.html
root@6e3827be6c51:/# cat /usr/share/nginx/html/index.html
123
root@6e3827be6c51:/# exit
exit
[root@docker html]# cat index.html
123
即使容器没有了,bind mount也还在。bind mount是host文件系统中的数据,只是借给容器使用。
默认挂载到容器内的文件,容器是有读写权限。可以在运行容器是-v后加“:ro”限制容器的写入权限。
[root@docker html]# docker run -itd --name testweb -p 80 --restart always -v /root/html:/usr/share/nginx/html:ro nginx
并且还可以挂载单独的文件到容器内部,一般它的使用场景是:如果不想对整个目录进行覆盖,而只希望添加某个文件,就可以使用挂载单个文件。
[root@docker html]# docker run -itd --name testweb -p 80 --restart always -v /root/html/index.html:/usr/share/nginx/html/index.html:ro nginx
mount point有很多应用场景,比如可以将源代码目录mount到容器中,在host中修改代码就能看到应用的实时效果。再比如将mysql容器的数据放在bind mount里,这样host可以方便地备份和迁移数据。
bind mount的使用直观高效,易于理解,但它也有不足的地方:bind mount需要指定host文件系统的特定路径,这就限制了容器的可移植性,当需要将容器迁移到其它host,而该host没有要mount的数据或者数据不在相同的路径时,操作会失败。
1.简介
docker manager volume与bind mount在使用上的最大区别是不需要指定mount源,指明mount point就行。
[root@docker html]# docker run -itd --name t1 -p 80 --restart always -v /usr/share/nginx/html nginx
通过-v告诉docker需要一个data volume,并将其mount到/usr/share/nginx/html。
这个data volume可以在容器的配置信息中找到,执行docker inspect命令即可。
[root@docker html]# docker inspect t1
...
"Mounts": [
{
"Type": "volume",
"Name": "fd8381b019f90f9e86509bf6fb3e43ccaebab09df2d1f3743dd8c63880ae9475",
"Source": "/var/lib/docker/volumes/fd8381b019f90f9e86509bf6fb3e43ccaebab09df2d1f3743dd8c63880ae9475/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
...
每当容器申请mount docker manger volume时,docker都会在/var/lib/docker/volumes下生成一个目录,这个目录就是mount源。删除容器的操作,默认不会对dockerHost上的源文件操作,如果想要在删除容器时把源文件也删除,可以在删除容器时添加-v选项(一般不推荐使用这种方式,因为文件有可能被其他容器使用)。
2.手动创建volume
[root@docker html]# docker volume create testweb
testweb
[root@docker html]# docker volume ls
DRIVER VOLUME NAME
local fd8381b019f90f9e86509bf6fb3e43ccaebab09df2d1f3743dd8c63880ae9475
local testweb
[root@docker html]# docker volume inspect testweb
[
{
"CreatedAt": "2020-09-08T04:18:33-04:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/testweb/_data",
"Name": "testweb",
"Options": {},
"Scope": "local"
}
]
[root@docker html]# docker run -itd --name t2 -p 80 --restart always -v testweb:/usr/share/nginx/html nginx
[root@docker html]# docker inspect t2
...
"Mounts": [
{
"Type": "volume",
"Name": "testweb",
"Source": "/var/lib/docker/volumes/testweb/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
...
注意:运行容器时使用手动创建的volume,在书写格式上和bind mount一样,但查看容器的详细信息,其实它仍然使用的是docker manager volume这种方式。
3.总结
Bind Mount和Docker Manager Volume的特点
bind mount | docker manager volume | |
---|---|---|
volume位置 | 可任意指定 | /var/lib/docker/volumes/… |
有mount point影响 | 隐藏并替换为volume | 原有数据复制到volume |
是否支持单个文件 | 支持 | 不支持,只能是目录 |
权限控制 | 可设置为只读,默认为读写权限 | 无控制,均为读写权限 |
移植性 | 移植性弱,与host path绑定 | 移植性强,无需指定host目录 |
1.简介
volume container:给其他容器提供volume存储卷的容器。并且它可以提供bind mount,也可以提供docker manager volume。
2.创建一个vc_data容器
[root@docker ~]# docker create --name vc_data -v ~/html:/usr/share/nginx/html -v /other/useful/tools busybox
3.使用vc容器
[root@docker ~]# docker run -itd --name t3 -P --volumes-from vc_data nginx
4.容器的跨主机数据共享
docker1 | docker2 | docker3 |
---|---|---|
httpd | httpd | nfs |
要求:docker1和docker2上基于httpd镜像运行2个或多个容器,保证主目录(默认访问界面内容)是一样的。
5.docker3上的操作
[root@docker3 ~]# yum -y install nfs-utils
[root@docker3 ~]# mkdir /datashare
[root@docker3 ~]# vim /etc/exports
/datashare *(rw,sync,no_root_squash)
[root@docker3 ~]# systemctl start rpcbind
[root@docker3 ~]# systemctl enable rpcbind
[root@docker3 ~]# systemctl start nfs-server
[root@docker3 ~]# systemctl enable nfs-server
[root@docker3 ~]# vim /datashare/index.html
snow-webshare
6.docker1上的操作
[root@docker1 ~]# yum -y install nfs-utils
[root@docker1 ~]# showmount -e 192.168.229.50
[root@docker1 ~]# mkdir /htdocs
[root@docker1 ~]# mount -t nfs 192.168.229.50:/datashare /htdocs
[root@docker1 ~]# cat /htdocs/index.html
snow-webshare
7.docker2的操作与docker1上一样
这里先不考虑将代码写入镜像,先以这种方式,分别在docker1和docker2部署httpd服务。
docker1
[root@docker1 ~]# docker run -itd --name snow-web1 -P -v /htdocs:/usr/local/apache2/htdocs httpd:latest
docker2
[root@docker2 ~]# docker run -itd --name snow-web2 -P -v /htdocs:/usr/local/apache2/htdocs httpd:latest
用浏览器访问,两个WEB服务的主界面是一样。如果NFS服务器上的源文件丢失,则两个web服务都会异常。
想办法将源数据写入镜像内,再基于镜像做一个vc_data容器。在docker1和docker2上先手动创建镜像。
docker1
[root@docker1 htdocs]# vim Dockerfile
FROM busybox
COPY index.html /usr/local/apache2/htdocs/index.html
VOLUME /usr/local/apache2/htdocs
[root@docker1 htdocs]# docker build -t back_data .
[root@docker1 htdocs]# docker create --name back_container1 back_data
8.总结
(1)解决容器跨主机数据共享的方案:NFS;
(2)容器与容器的数据共享:基于某个容器而来(--volumes-from选项),意味着这些容器和vc容器的数据存储是一样的;