导读:容器得到了越来越广泛的使用,以下问题困扰架构师:
本文探讨了容器和持久化存储相关知识,由张朝潞在高可用架构群分享,转载请注明来自高可用架构公众号 ArchNotes。
张朝潞 有容云平台存储架构师
一.容器对存储插件的定义 以 Docker 为例, Docker 对存储卷定义了一组简单的接口,外部存储只要实现简单的接口便可以和外部存储对接。 Docker daemon 和 plugin daemon 基于 UNIX 域套接字,使用 RESTful API 进行通信,下面是详细的 API:
从上面这组接口可以看出, Docker 容器是通过 mount 的方式将外部存储挂载到本地目录,尽量使内部应用程序对存储是无感知的,应用程序就像使用本地目录一样使用外部存储卷,而将外部存储的管理交给存储 Plugin 负责(如 Flocker、Rancher Convoy,REX-Ray 等)。 容器正逐渐成为云计算平台应用程序的标准部署单元,容器能轻易的将各式各样的应用程序及其 runtime 打包成统一的对象,于是编排调度系统能把各种应用程序当成统一的容器进行处理,大大简化编排调度系统的复杂度。结合 Docker 对存储插件的定义,不难看出 Docker 希望容器的运行环境独立而纯粹,不希望引入有状态和复杂的存储系统。 二、存储插件 Convoy 作为一个 Docker volume plugin,支持不同的后端存储,为 Docker 提供 MountPoint,也就是一个的目录,可能是挂载了后端存储、本地块设备或者就是本地目录。 Convoy 的代码从结构、风格和使用的库,都与 Docker 十分相似,并且比 Docker 简单很多。 (小编:下面展开介绍一下 convoy 源代码的部分模块,不需要了解细节的读者可以直接跳到第三部分:容器、应用程序、持久化存储。) Convoy 在源码级别上值得留意的点,我认为有两点:①插件式结构与 inte**ce 的运用。② 作者对事物的抽象能力与方法。 1、convoy daemon (convoy / daemon) 图 1 中黑色部分 daemon 是主要的功能模块,可以接收来自 convoy client 和 Docker 的命令,对 backend 存储进行了抽象,以便统一管理。下面先从 daemon 的启动开始。 1.1、daemon 进程启动 1)执行命令:
2) convoy 程序解析参数,获得 daemon 子命令,调用到 daemon.Start 函数(convoy/daemon/daemon.go), Start 函数中主要围绕 daemon struct 建立所需要环境和配置。 3) Driver 初始化,优先从配置文件读取信息忽略命令行输入的参数,如果配置文件不存在则根据命令行参数初始化。 图 2. convoy 配置文件内容 遍历 DriverList,找到配置文件或命令行指定的 Driver,执行初始化函数 Init,并添加到 daemon.ConvoyDrivers 中。 4)根据 convoy 的工作目录的内容,更新管理元数据,图 1 中也有相应的模块。
图 3.convoy 工作目录和 volume 配置文件 5) Router 注册: Router 提供两部分的路由,并将 daemon 的 Router 指向该 Router。 (1) 处理 convoy client 的命令 Client Request Router,处理客户端发送的 HTTP request。 (2) 处理来自 Docker 的请求 Docker Volume Plugin Router, convoy 本身就是 Docker 的 volume plugin,提供了如下的接口。 6) HTTP server 启动,根据 sockFile = /var/run/convoy/convoy.sock 和 上一步骤的 Router,启动 HTTP server。 2.2、daemon 的请求处理逻辑 Daemon 启动后便可以处理请求( convoy client 或 Docker),主要处理逻辑 Router 收到 HTTP 请求,将请求分发给各个模块: Docker、 volume、 snapshot、 backup。这个 4 个逻辑模块根据 driver name( 指定的或者默认的 ) 从 daemon.ConvoyDrivers 中获取对应的 Driver。 ConvoyDrivers 中的 Driver 是实现了 ConvoyDriver inte**ce 的结构。 图 4.convoy daemon 请求处理逻辑 从图 4 中可以看出 ConvoyDriver 接口规定了 3 组接口对应 volume, snapshot, backup 的操作,用于操作 backend storage。逻辑处理最终调用这些接口访问 Backend Storage。 2.3、ConvoyDriver implement 截止到 0.4.3 版本, convoy 支持 4 种后端存储(实现了 ConvoyDriver 接口),如下表。 下面来说说,convoy 是如何对后端存储进行抽象和管理,它使用了 4 种结构 Driver, Volume, Snapshot, Device。
图 5.ConvoyDriver 的实现 图 5 Device 结构内容,记录了该 Driver 的后端存储的信息。 2.4、objectstore 提供实现备份的框架 Objectstore 模块是实现 BackupOperations 接口所需要的基本功能,目前实现了两种备份后端: S3 和 VFS。 它提供了两种备份方案: DeltaBlockBackup(增量块)和 BackupFile(备份文件)。
Ebs 在实现 BackupOperations 接口时,使用 ebs 自身的 client 来实现 Backup。 ebs 本身就是一个分布式存储系统,不再需要额外的 objectstore 对其进行备份。 图 6. objectstore 框架 通过 vfs 备份的目录结构: volume.cfg 的内容,保存图 6 中的 Volume 结构 backup_[ID].cfg,保存图 6 中的 Backup 结构 blocks 目录保存了 snapshot 存储的真实数据,以 block 的形式存储在不同目录。 三、容器、应用程序、持久化存储 容器与持久化存储,在我看来本不该拿来一起讨论,二者关联性是比较弱的。
基于容器化应用程序带来的好处,运维工程师都是期望能将更多的应用程序容器化,以减轻运维负担。 对于无状态应用程序,容器化几乎带来的只有好处。 对于一些有状态的应用程序,如数据库,需要进行容器化时,便面临持久化存储的问题。下面是一个外部持久化存储解决 MySQL 容器化问题的例子。 三台运行 MySQL 数据库的主机将持久化存储系统的虚拟磁盘映射上, MySQL 将数据写入这些虚拟磁盘中。 当其中一个 MySQL 数据库发生故障时,在新的主机上将故障主机的虚拟磁盘映射上,供 MySQL 使用,可以快速恢复数据库故障。 此时,将 MySQL 数据库容器化将变得十分简单,编排调度系统,能够快速发现 MySQL 集群异常,并快速调度其他主机上,减少故障时间。 由上述讨论,其实无论容器在或不在,存储还是存储。当然为了适应容器的快速迁移(相对于虚拟机),多种多样的应用程序对存储也提出了细粒度控制、应用感知、快速创删等新的需求,但存储作为以稳定性为重的基础设施,依然万变不离其宗。 四、持久化存储系统的选择 持久化存储系统可分为开源存储系统和商业存储系统。通常商业存储系统会由厂商解决所有问题,这里就不谈商业化存储了。 开源分布式存储方案如下:
块存储、文件存储、对象存储三种存储访问方式不同的存储系统,最合适容器的,我想应该是对象存储系统,对象存储系统是通过 URL 访问的,应用程序只需要知道对象存储系统的 URL 就可以直接访问存储系统,这种方式更贴近容器的无状态、临时性和快速调度。 为什么选择分布式存储系统? 1、云计算时代,传统存储不能满足虚拟化、容器对存储的需求
2、构建存储的 TCO( 总拥有成本 ) 十分高昂
3、高昂的存储系统运营成本 (OPEX)
当然开源分布式存储系统,只解决了第 1, 2 点,第 3 点并没有得到有效的解决,反而有点加深的趋势。 Q |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|