Docker系列05—Docker 存储卷详解

本文收录在容器技术学习系列文章总目录html

1、存储卷介绍

1.1 背景

1docker AFUS 分层文件系统mysql

  docker镜像由多个只读层叠加面成,启动容器时,docker加载只读镜像层并在镜像栈顶部加一个读写层;nginx

  若是运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即写时复制(COW)”机制git

 

2)示意图github

  描述:若是一个文件在最底层是可见的,若是在layer1上标记为删除,最高的层是用户看到的Layer2的层,在layer0上的文件,在layer2上能够删除,可是只是标记删除,用户是不可见的,总之在到达最顶层以前,把它标记来删除,对于最上层的用户是不可见的,当标记一删除,只有用户在最上层建一个同名同样的文件,才是可见的。web

 

1.2 为何要使用存储卷

  •  对于这类的操做,修改删除等,通常效率很是低,若是对一于I/O要求比较高的应用,如redis在实现持化存储时,是在底层存储时的性能要求比较高。
  •  假设底层运行一个存储库mysql,mysql原本对于I/O的要求就比较高,若是mysql又是运行在容器中本身的文件系统之上时,也就是容器在中止时,就意味着删除,其实现数据存取时效率比较低,要避免这个限制要使用存储卷来实现。
  •  存在的问题:
    •  存储于联合文件系统中,不易于宿主机访问;
    •  容器间数据共享不便
    •  删除容器其数据会丢失

 

1.3 存储卷

1)介绍redis

  “是容器上的一个或多个目录,此类目录可绕过联合文件系统与宿主机上的某个目录绑定(关联)sql

  相似于挂载同样,宿主机的/data/web目录与容器中的/container/data/web目录绑定关系,而后容器中的进程向这个目录中写数据时,是直接写在宿主机的目录上的,绕过容器文件系统与宿主机的文件系统创建关联关系,使得能够在宿主机和容器内共享数据库内容,让容器直接访问宿主机中的内容,也能够宿主机向容器供集内容,二者是同步的docker

  mount名称空间原本是隔离的,可让两个原本是隔离的文件系统,在某个子路径上创建必定程度的绑定关系,从而使得在两个容器之间的文件系统的某个子路径上再也不是隔离的,实现必定程度上共享的效果。数据库

  在宿主机上可以被共享的目录(能够是文件)就被称为volume

 

2)存储卷做用

  优势是容器中进程所生成的数据,都保存在存储卷上,从而脱离容器文件系统自身后,当容器被关闭甚至被删除时,都不用担忧数据被丢失,实现数据能够脱离容器生命周期而持久,当再次重建容器时,若是可让它使用到或者关联到同一个存储卷上时,再建立容器,虽然不是以前的容器,可是数据仍是那个数据,特别相似于进程的运行逻辑,进程自己不保存任何的数据,数据都在进程以外的文件系统上,或者是专业的存储服务之上,因此进程每次中止,只是保存程序文件,对于容器也是同样;容器就是一个有生命周期的动态对象来使用,容器关闭就是容器删除的时候,可是它底层的镜像文件仍是存在的,能够基于镜像再从新启动容器。

  可是容器有一个问题,通常与进程的启动不太同样,就是容器启动时选项比较多,若是下次再启动时,很容器会忘记它启动时的选项,因此最好有一个文件来保存容器的启动,这就是容器编排工具的做用。通常状况下,是使用命令来启动操做docker,可是能够经过文件来读,也就读文件来启动,读所须要的存储卷等,可是它也只是操做一个容器,这也是须要专业的容器编排工具的缘由。

  另外一个优点就是容器就能够不置于启动在那台主机之上了,如几台主机后面挂载一个NFS,在各自主机上建立容器,而容器上经过关联到宿主机的某个目录上,而这个目录也是NFS所挂载的目录中,这样容器若是中止或者是删除均可以不限制于只能在原先的宿主机上启动才能够,能够实现全集群范围内调试容器的使用,当再分配存储、计算资源时,就不会再局限于单机之上,能够在集群范围内创建起来,基本各类docker的编排工具都能实现此功能,可是后面严重依赖于共享存储的使用。

 

3)配合各服务应用状态分析

  考虑到容器应用是须要持久存储数据的,多是有状态的,若是考虑使用NFS作反向代理是不必存储数据的,应用能够分为有状态和无状态,有状态是当前此次链接请求处理必定此前的处理是有关联的,无状态是先后处理是没有关联关系的,大多数有状态应用都是数据持久存储的,如mysql,redis有状态应用,在持久存储,如nginx做为反向代理是无状态应用,tomcat能够是有状态的,可是它有可能不须要持久存储数据,由于它的session都是保存在内存中就能够的,会致使节点宕机而丢失session,若是有必要应该让它持久,这也算是有状态的。

  应用状态象限:是否有状态或无状态,是否须要持久存储,能够定立一个正轴坐标系,第一象限中是那些有状态须要存储的,像mysql,redis等服务,有些有有状态可是无需进行存储的,像tomcat把会话保存在内存中时,无状态也无须要存储的数据,如各类反向代理服务器nginx,lvs请求链接都是看成一个独立的链接来调度,本地也不须要保存数据,第四象限是无状态,可是须要存储数据是比较少见。

  运维起来比较难的是有状态且须要持久的,须要大量的运维经验和大量的操做步骤才能操做起来的,如作一个Mysql主从须要运维知识、经验整合进去才能实现所谓的部署,扩展或缩容,出现问题后修复,必需要了解集群的规模有多大,有多少个主节点,有多少个从节点,主节点上有多少个库,这些都要一清二楚,才能修复故障,这些就强依赖于运维经验,无状态的如nginx一安装就能够了,并不复杂,对于无状态的应用能够迅速的实现复制,在运维上实现自动化是很容易的,对于有状态的现状比较难脱离运维人员来管理,即便是k8s在使用上也暂时没有成熟的工具来实现。

  总之:对于有状态的应用的数据,不使用存储卷,只能放在容器本地,效率比较低,而致使一个很严重问题就是没法迁移使用,并且随着容器生命周期的中止,还不能把它删除,只能等待下次再启动状态才能够,若是删除了数据就可能没了,由于它的可写层是随着容器的生命周期而存在的,因此只要持久存储数据,存储卷就是必需的

  docker存储卷难度:对于docker存储卷运行起来并不太麻烦,若是不本身借助额外的体系来维护,它自己并无这么强大,由于docker存储卷是使用其所在的宿主机上的本地文件系统目录,也就是宿主机有一块磁盘,这块磁盘并无共享给其余的docker主要,而后容器所使用的目录,只是关联到宿主机磁盘上的某个目录而已,也就是容器在这宿主机上中止或删除,是能够从新再建立的,可是不能调度到其余的主机上,这也是docker自己没有解决的问题,因此docker存储卷默认就是docker所在主机的本地,可是本身搭建一个共享的NFS来存储docker存储的数据,也能够实现,可是这个过程强依赖于运维人员的能力

 

1.4 存储卷原理

  •  volume于容器初始化之时会建立,由base image提供的卷中的数据会于此期间完成复制
  •  volume的初意是独立于容器的生命周期实现数据持久化,所以删除容器之时既不会删除卷,也不会对哪怕未被引用的卷作垃圾回收操做
  •  卷为docker提供了独立于容器的数据管理机制
    •  能够把镜像想像成静态文件,例如程序,把卷类比为动态内容,例如数据,因而,镜像能够重用,而卷能够共享
    •  卷实现了程序(镜像)"数据()“分离,以及程序(镜像)“"制做镜像的主机分离,用记制做镜像时无须考虑镜像运行在容器所在的主机的环境 

 

1.5 存储卷分类

Docker有两种类型的卷,每种类型都在容器中存在一个挂载点,但其在宿主机上位置有所不一样;

  • Bind mount volume(绑定挂载卷):在宿主机上的路径要人工的指定一个特定的路径在容器中也须要指定一个特定的路径两个已知的路径创建关联关系
  • Docker-managed volumedocker管理卷): 只须要在容器内指定容器的挂载点是什么,而被绑定宿主机下的那个目录,是由容器引擎daemon自行建立一个空的目录,或者使用一个已经存在的目录,与存储卷创建存储关系,这种方式极大解脱用户在使用卷时的耦合关系,缺陷是用户没法指定那些使用目录,临时存储比较适合;

 

2、使用存储卷

docker run 命令使用-v 选项便可使用volume

  •  docker-managed volume
docker run -it -name rbox1 -v /data busybox   #/data指定docker的目录
docker inspect -f {{.Mounts}} rbox1   查看rbox1容器的卷,卷标识符及挂载的主机目录
  •  bind-mount volume
docker run -it -v HOSTDIR:VOLUMEDIR --name rbox2 busybox  #宿主机目录:容器目录
docker inspect -f {{.Mounts}} rbox2

 

2.1 使用 docker-managed volume

1)建立容器b1

[root@along ~]# docker run --name b1 -it -v /data --rm busybox 
/ # ls /data/
/ #

注意:不要关闭此终端,另起一个终端进行一下操做;由于--rm 选项:一旦容器关闭,当即删除容器

 

2)查询存储卷信息

[root@along ~]# docker inspect b1
... ...
        "Mounts": [
            {
                "Type": "volume",
                "Name": "ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201",
                "Source": "/var/lib/docker/volumes/ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
... ...

 

3)由于inspect查询的结果是列表的形式、因此能够精确查询结果

[root@along ~]# docker inspect -f {{.Mounts}} b1
[{volume ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201 /var/lib/docker/volumes/ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201/_data /data local  true }]

 

4)在宿主机的存储卷目录添加任意东西

[root@along ~]# cd /var/lib/docker/volumes/ca18526588ba7cbe3934086807a95415644aec17119c811338efb1db2c5f5201/_data
[root@along _data]# echo "hello" > test.html

 

5)在容器b1中查看,并在容器中任意修改存储卷

/ # cat /data/test.html 
hello
/ # echo "world" >> /data/test.html 

 

6)在宿主机上查看认证

[root@along _data]# cat test.html 
hello
world

 

2.2 使用 docker mount volume

1)建立容器b2

[root@along ~]# docker run --name b2 -it -v /data/volumes/b2:/data --rm busybox 
/ # ls /data/
/ # 

注:若是设置存储卷的目录不存在,会自动建立

 

2)查询存储卷信息

[root@along ~]# docker inspect -f {{.Mounts}} b2
[{bind  /data/volumes/b2 /data/web/html   true rprivate}]

 

3)在宿主机的存储卷上进行简单操做

[root@along ~]# cd /data/volumes/b2/
[root@along b2]# echo "<h1>Bustbox httpd server</h1>" > index.html

 

4)在容器中验证

/ # cat /data/index.html 
<h1>Bustbox httpd server</h1>

 

5)即便容器被删除,再新建立容器b3,修改存储卷路径,存储卷也不会改变,证实持久功能

[root@along ~]# docker run --name b3 -it -v /data/volumes/b2:/data/web/html --rm busybox 
/ # cat /data/web/html/index.html 
<h1>Bustbox httpd server</h1>

 

6)多个docker容器同时关联到同一个宿主机的目录中

实现共享使用同一个存储卷,容器之间的数据共享

[root@along ~]# docker run --name b4 -it -v /data/volumes/b2:/data/ --rm busybox
/ # cat /data/index.html 
<h1>Bustbox httpd server</h1>

 

2.3 volumes-from 基于已有容器的存储器,建立容器

1)先建立一个 infracon container

[root@along ~]# docker run --name infracon -it -v /data/infracon/volume/:/data/web/html busybox:latest 
/ # echo "<h1>Nginx server</h1>" > /data/web/html/index

宿主机的存储卷能够查询

[root@along ~]# cat /data/infracon/volume/index.html   
<h1>Nginx server</h1>

 

2)基于infracon container 的存储器,启动一个 nginx container

[root@along ~]# docker run --name nginx --network container:infracon --volumes-from infracon -it --rm busybox:latest 
/ # cat /data/web/html/index.html 
<h1>Nginx server</h1>

其实,对nginx 这个容器来讲,volume 的本质没变,它只是将infracon 容器的/data/web/html 目录映射的主机上的目录映射到自身的/data/web/html 目录。

[root@along ~]# docker inspect -f {{.Mounts}} nginx
[{bind  /data/infracon/volume /data/web/html   true rprivate}]

可是,其好处是,能够无论其目录的临时性而不断地重复使用它。

 

3Volume 删除和孤单 volume 清理

3.1 在删除容器时删除 volume

可使用 docker rm -v 命令在删除容器时删除该容器的卷。

[root@along ~]#  docker run --name web2 -v /data/ -d nginx:1.14-alpine
59a3db695835a9f1a8be97c0ca0f70bc792f5303302264dba913c7c1b6d81ebd
[root@along ~]# docker volume ls
DRIVER              VOLUME NAME
local               17ac2071805d1609cf5501f81bec81d3d19467ea5a0c3428d2e77b414607775b
local               1e28bac2454d8c92ba39e8e22b9d88004284310a776e50dc379282de63c0e149
[root@along ~]# docker kill web2
web2
[root@along ~]# docker rm -v web2
web2
[root@along ~]# docker volume ls
DRIVER              VOLUME NAME
local               1e28bac2454d8c92ba39e8e22b9d88004284310a776e50dc379282de63c0e149

 

3.2 批量删除孤单 volumes

从上面的介绍能够看出,使用 docker run -v 启动的容器被删除之后,在主机上会遗留下来孤单的卷。可使用下面的简单方法来作清理:

[root@along ~]# docker volume ls -qf dangling=true
1e28bac2454d8c92ba39e8e22b9d88004284310a776e50dc379282de63c0e149
[root@along ~]# docker volume rm $(docker volume ls -qf dangling=true)
1e28bac2454d8c92ba39e8e22b9d88004284310a776e50dc379282de63c0e149
[root@along ~]# docker volume ls
DRIVER              VOLUME NAME

 

3.3 github 上有不少脚本能够自动化地清理孤单卷

好比:

相关文章
相关标签/搜索