来自 技术 2019-04-15 00:00 的文章

细述Kubernetes和Docker容器的存储方式

八年Java开发的感悟:什么才是程序员的立身之本>>>   

目前,容器存储是容器离不开的一个话题,对于无状态的Docker容器,容器重启时容器数据会自动清除,一些静态的数据我们可以通过配置文件或者在容器build时直接写死。但是对于数据库、日志文件等可以实时变化的数据,我们不能够通过这种方法存取,容器的存储大多支持Docker或Kubernetes的Volume(数据卷),因此我们下文先介绍这两种Volume的原理。

Docker的容器卷插件

Docker V1.8正式发布了容器卷插件 (Volume Plugin) 的规范,允许第三方厂商的数据卷在Docker引擎中提供数据服务,使得外置存储可以超过容器的生命周期而独立存在。这意味着各种存储设备只要满足接口API的标准,就可以接入Docker容器的运行平台中。Volume Plugin的接口规范定义了5中操作,如下表所示:

这个规范定义非常简洁,现有的各种存储可以通过简单的驱动程序封装,从而实现和Docker容器的对接。可以说,驱动程序实现了和容器引擎的北向接口,底层则调用后端存储的功能完成数据存取等任务。还有不少存储方案实现了额外的高端功能,如容器数据卷迁移等,这部分功能不在Docker的卷插件规范当中,可通过存储自身的管理工具来使用。目前已经实现的Docker Volume Plugin中,后端存储包括常见的NFS, CIFS, GlusterFS和块设备等。

Kubernetes的容器卷

Kubernetes是开源的容器集群管理平台,可以自动化部署、扩展和运维容器应用。Kubernetes的调度单位称作“Pod”(豆荚),每个Pod代表一个应用,包含一个或多个容器。Pod可部署在集群的任意节点中,存储设备可以通过数据卷(Volume)提供给Pod的容器使用。Kubernetes底层支持Docker的容器运行引擎,为了不绑定在特定的容器技术上,Kubernetes没有使用Docker的Volume机制,而是重新制定了自己的通用数据卷插件规范,以配合不同的容器运行时来使用(如Docker和rkt)。

数据卷一般可以贯穿Pod的整个生命周期,当Pod被平台删除的时候,在不同的数据卷实现中,数据可能会被保留或移除。如果数据被保留的话,其他Pod可以重新把该卷的数据加载使用。数据卷分为共享和非共享两种类型,其中非共享型只能被某个节点挂载使用(如iSCSI,AWS EBS等网络块设备),共享型则可以让不同节点上的多个Pod同时使用(如NFS,GlusterFS,CephFS等网络文件系统,以及可支持多方读写的块设备)。对有状态的应用来说,共享型的卷存储能够很方便地支持容器在集群各节点之间的迁移。

Kubernetes的数据卷可把外部预创建的数据卷接入Pod里面,在这个过程中,Pod无法对数据卷配置参数(如卷大小,IOPS等),因为这些参数是由提供数据卷的存储预先设定的,这有点象传统存储先划分数据卷,再供给应用挂载使用。为了给容器提供更细粒度的卷管理,Kubernetes增加了持久化卷PV(Persistent Volume)的功能,把外置存储作为资源池,由平台管理并提供给整个集群使用。每个PV具有一些可被平台感知的存储能力,如卷容量(storage size),读写访问模式(access mode)等。当Pod需要存储时,可以向平台请求所需要存储资源,该请求称作PVC (Persistent Volume Claim)。PVC内容包括访问模式、容量大小等信息。平台根据请求的资源属性(如卷大小等)匹配合适的资源并分配给Pod,并把数据卷挂载到Pod所在的主机中供Pod使用(如下图所示)。

Kubernetes的Persistent Volume功能还在不断发展中,目前PV仅支持存储容量(storage size)的能力(capacity),今后还可能支持IOPS,吞吐量等存储能力,以便配置更丰富的存储策略。Kubernetes的卷管理架构使得存储可用标准的接入方式,并且通过接口暴露存储设备所支持的能力,从而在容器任务调度等方面实现了自动化管理。

Flocker

为了给容器应用提供文件卷存储,比较简单的方式是在重用传统存储的基础上,加上适配容器规范的相应接口。使用这种方式的容器存储很多,如适配Docker的GlusterFS, NFS, CIFS的卷插件。下文介绍的Flocker也是这种模式的开源容器卷管理器,它提供了在集群中管理和编排容器数据卷的方案,并依靠后端的共享块存储提供数据卷跨主机的能力。

如上图所示,Flocker由控制服务作为总控制器,对外提供REST API接口,负责维持和更新系统的状态。Flocker Agent安装在集群的每个节点上,负责确保每个节点上的本地状态符合系统期待的状态,如果发现本地状态和期待状态不符,Flocker Agent将采取必要的纠正措施,使得节点上的数据卷与集群系统的配置实现最终一致性(eventual consistency)。Flocker Plugin也部署在每个节点上,主要以插件形式与Docker、Kubernetes等容器平台的集成,不仅让容器可以使用Flocker提供的数据卷,还能够支持容器的迁移。

例如,在Kubernetes中,当Pod所在的主机失效之后,Kubernetes会把Pod重新调度(迁移)到另一台主机上,相应地,Flocker把Pod在原主机上的数据卷释放出来,并且在新主机中重新挂载给该Pod。这样,有状态容器在迁移主机的时候,其数据卷也能够跟随着容器一起移动。

Flocker后端可采用各种常见的网络块设备,包括AWS EBS,OpenStack Cinder,EMC、DELL、NetApp、VMware VSAN/vVOL等,这些块设备配上驱动程序,即可由Flocker生成数据卷(文件目录形式),挂接到任意的主机上,再通过卷插件的接口,把数据卷提供给容器应用。

Portworx

Portworx开发了容器感知的软件定义存储系统,称为CDS (Container-Defined Storage)。在Portworx的CDS存储中,采用的是计算和存储融合的架构,把集群中所有节点的本地存储聚合成大的资源池,使得每个节点既提供计算能力,也提供本地磁盘作为存储,这样运行在节点中的容器可从本地直接访问数据。

任何存储都要保证数据的完整性和可靠性,由于Portworx采用分布式存储架构,与Ceph、VMware Virtual SAN等类似,需要在多节点之间进行数据复制。如上图所示,当数据在本地写入的时候,根据存储设定的参数,可以把数据复制到其他若干个节点中,从而在集群中存有多个数据副本,确保了数据的可用性和可靠性。如果某个节点出现故障或进行下线维护,该节点上的容器可以被上层的调度器重新调度到其他节点上。因为数据已经复制到了多个节点,容器在新节点上可直接使用本地数据,提高了数据访问的效率(如下图所示)。

Portworx还设计了面向容器卷的存储策略,在创建数据卷的时候可以动态设置,这些策略如下所示:

通过设置上述存储属性的配置,容器卷的QoS等需求可以动态满足,与传统的SAN等块存储有很重要的区别:这些策略是以容器卷的粒度进行配置的,能够很好地符合容器应用的需求,所以称为容器定义的存储(Container Defined Storage),是为容器应用量身定制的软件定义存储。目前,Portworx在架构上实现了软件定义存储的控制平面和数据平面。尽管许多功能还在不断完善之中,但是我们还是可以看出下一代面向容器的软件定义存储的雏形。