Docker学习之五:Docker本地存储Volumes

Docker存储

Docker运行容器时,一般是一个容器执行一个程序。对于容器的启动,依赖于不止一种镜像联合挂载并启动。能存储此类n层镜像构建联合挂载的文件系统包括aufs,overlayfs2,dm等等,并且最上层需要构建可写层。

对于读写层,所有容器的操作都是保存在最上层的。对下层内容的操作比如删除最下层镜像本来已经存在的文件,则基于cow的方式实现。

在到达最上层之前,如果将文件标记为删除,则最上层是不可见的。如果删除后,用户又创建一个相同的文件,如此才能可见。底层存在的文件,如果被修改,用户也是可见的。

这种方式来修改,访问和删除,其效率会很低,因此,对于IO要求较高的应用,比如redis,它势必会对底层应用数据存储时,对IO性能要求高。比如存储服务如MySQL MariaDB。如果运行在容器上的文件系统之上,在数据存取时效率必然很低。如果需要绕过限制,可以使用存储卷的形式来实现。

另外,要实现持久性存储时,也需要使用存储卷的形式来完成。

存储卷

在宿主机中,找一个本地文件系统的某一目录,将目录与容器内部的文件系统之上的某一访问路径建立绑定关系。如此前的挂载方式一般,将容器内的/data/web 和宿主机的/container/data/web 建立绑定关系。容器内的进程向目录写数据时,实际上是写到宿主机的目录中。

因此,容器内的进程在实现数据存取时,能绕过容器的文件系统的限制,从而与宿主机内的本地文件系统建立关联关系。如此容器和宿主机共享数据内容,二者是同步的。

也可以让两个本来隔离的文件系统,在某个子路径上建立绑定关系,让两个容器的文件系统子路径实现共享的效果。这个子路径,称为存储卷 volume。

如此一来,当容器关闭或者删除时,也无需担心数据丢失。只要数据目录存在,即可实现数据脱离容器的生命周期而实现持久化。只要重建容器时,只要关联到此前的存储卷上即可。如此可复现一个相同的容器。

当启动容器时,添加的命令和存储卷如果可以保存在一个配置文件中,将大大简化重现容器的难度。这就是容器编排工具的作用。

容器关联的卷不一定是host本地文件系统,也可以是NFS共享存储。只要host挂载NFS即可。如此某个host之上的某个NFS路径,关联到容器的某个目录上。当容器如redis保存数据时,数据实际上是保存在NFS之上,当容器退出时,自动删除。

当容器再次启动时,可以全局调度某台host来启动此容器。如此分配存储计算和内存资源时,不局限于单个host,而是集群范围内。这就是云的范畴。

主流的docker编排工具,都能实现此类功能。不过,这严重依赖于共享存储。

有状态和无状态

如果容器的应用是有状态的,当前的连接处理与此前的连接状态是有关联关系的。而大多数有状态的应用都需要存储数据的,比如MySQL;而无状态的应用比如nginx反向代理;而tomcat的会话可以保存在内存中,这类是有状态,而无需持久存储。

有状态的应用,一般都需要持久存储。并且,是否有状态,是否需要持久存储,可以使用正交关系来联合。

对于有状态的应用,对运维人员的技术能力要求是很高的。比如数据库集群,主从,扩容等等。而无状态的应用,则无需太多要求。比如反向代理nginx。

如果期望应用能跨主机启动时,必须要求该应用在下次启动时能够找回原来使用的持久存储的数据,才能实现复现应用。

Docker默认存储卷是使用容器所在宿主机的本地文件系统目录,但是如此就无法跨主机。这是docker无法解决的问题。

存储卷类型

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

绑定挂载卷

挂载点为宿主机上的路径,需要人工指定特定路径。容器也需要指定特定路径,然后二者建立关联关系。

docker管理卷

在容器内指定挂载点,而宿主机的哪个目录建立关联关系则无需管理,由docker自行创建目录。这是docker daemon所维护的,一般是在特定的路径下。如此极大解脱了用户使用卷时的耦合关系,但是用户无法指定目录。

作为临时存储而言,第二种方式很有效。如果需要复现容器时,则需要使用第一种存储,否则第二种存储可能重新初始化一个新的挂载目录。

在容器中使用Volumes

在docker run 命令使用-v选项即可使用volumes

docker管理卷

创建容器时,指定特定的目录作为存储卷:

root@eto:~# docker run --name b2 -it -v /data busybox
/ # 

此路径是共享的,并且是宿主机自动生成的,这就是docker管理的卷

 "Mounts": [
            {
                "Type": "volume",
                "Name": "a11c30a5ec5a2e4bd7ac76f5d41b177d4504c463720532b90e89721c62e9f9bb",
                "Source": "/var/lib/docker/volumes/a11c30a5ec5a2e4bd7ac76f5d41b177d4504c463720532b90e89721c62e9f9bb/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

绑定挂载卷

root@eto:~# docker run --rm --name b3 -it -v /data/volumes/b3:/data busybox
/ # ls /data
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/data/volumes/b3",
                "Destination": "/data",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],

如果宿主机上的路径不存在,则会自动创建。

如此即使容器删除,保存在宿主机上的文件依然存在。

可是使用go模板,来过滤相关信息:

root@eto:~# docker inspect -f {{.Mounts}} b3
[{bind  /data/volumes/b3 /data   true rprivate}]
root@eto:~# docker inspect -f {{.NetworkSettings.IPAddress}} b3
172.17.0.2

让两个docker容器,同时关联到宿主机的目录,实现容器间的目录共享:

root@eto:~# docker run --rm --name b4 -it -v /data/volumes/b3:/data busybox
/ # ls /data
mydata

期望需要让多个容器,使用一个卷,并且如果不希望人为记住-v指定的路径。Docker支持容器可以去复制另一个容器的存储卷的路径,通过如下方式:

  • 创建容器,指定它应该使用何种存储卷。它无需启动,而是作为其他容器的基础支撑容器(所谓的pause容器)
  • 其他存储卷直接复制第一个容器的存储卷

在联盟式容器中,共享名称空间(IPS,Network,UTS)。比如nginx对外提供服务,tomcat容器仅服务于nginx。这两个容器使用同一个网络名称空间,如此可以隐藏了tomcat容器。而最底下的,由提供存储卷的容器作为基础支撑,如此就可以将nginx容器和tomcat容器联合工作,并打包镜像。

在同一个基础上,建立MariaDB,仅监听lo网卡即可。无需对外服务,以此实现nmt。看起来,如同一个主机,仅跑了三个应用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值