容器集群管理mysql
在设计理念方面,k8s 只有 APIServer 与 etcd (存储) 通讯,其余组件在内存中维护状态,经过 APIServer 持久化数据。管理组件动做的触发是 level-based 而非 edge-based,并根据资源「当前状态」和「指望状态」进行相应动做。k8s 采用分层设计,基于各种抽象接口、由不一样的插件知足不一样的需求。git
在抽象方面,不一样的 workload 服务于不一样的应用,如针对无状态应用的 Deployment、针对有状态应用的 StatefulSet 等。在访问管理方面,Service 解耦了集群内部服务访问方和提供者,Ingress 提供了集群外部到集群内部的访问管理。github
k8s 虽然有良好的设计理念和抽象,但陡峭的学习曲线和不完善的开发资料极大增长了应用开发的难度。sql
本次分享将基于笔者的开发实践,以 MySQL on k8s 为例,描述如何基于 k8s 开发高可靠应用,尽量抽象出最佳实践,下降基于 k8s 开发高可靠应用的成本。docker
应用的设计和开发不能脱离业务需求,对 MySQL 应用的需求以下:网络
为了实现上述需求,须要依靠 k8s 和应用两方面协同工做,即开发基于 k8s 高可靠应用,既须要 k8s 相关的知识,也须要应用领域内的知识。架构
下述将根据上述需求来分析相应的解决方案。并发
1.数据高可靠负载均衡
数据的高可靠通常依赖于这几方面:框架
咱们使用 Percona XtraDB Cluster 做为 MySQL 集群方案,它是 multi-master 的 MySQL 架构,实例间基于 Galera Replication 技术实现数据的实时同步。这种集群方案能够避免 master-slave 架构的集群在主从切换时可能出现的数据丢失现象,进一步提高数据的可靠性。
备份方面,咱们使用 xtrabackup 做为备份/恢复方案,实现数据的热备份,在备份期间不影响用户对集群的正常访问。
提供「定时备份」的同时,咱们也提供「手动备份」,以知足业务对备份数据的需求。
2.服务高可用
这里从「数据链路」和「控制链路」两个角度来分析。
「数据链路」是用户访问 MySQL 服务的链路,咱们使用 3 主节点的 MySQL 集群方案,经过 TLB (七牛自研的四层负载均衡服务) 对用户提供访问入口。TLB 既实现了访问层面对 MySQL 实例的负载均衡,又实现了对服务的健康检测,自动摘除异常的节点,同时在节点恢复时自动加入该节点。以下图:
基于上述 MySQL 集群方案和 TLB,一个或两个节点的异常不会影响用户对 MySQL 集群的正常访问,确保 MySQL 服务的高可用。
「控制链路」是 MySQL 集群的管理链路,分为两个层面:
这种拆解将每一个集群的管理工做下放到每一个集群中,下降了集群间控制链路的相互干扰,同时又减轻了全局控制器的压力。 以下图:
这里简单介绍下 Operator 的理念和实现。
Operator 是 CoreOS 公司提出的一个概念,用来建立、配置、管理复杂应用,由两部分构成: Resource
工做流程以下图所示:
即:
咱们根据实践,对开发 Operator 作了以下抽象:
在上述抽象的基础上,七牛提供了一个简单的 Operator 框架,透明化了建立 CR、监听 CR events 等的操做,将开发 Operator 的工做变的更为简单。
咱们开发了 MySQL Operator 和 MySQL Data Operator,分别用来负责「建立/删除集群」和「手动备份/恢复」工做。
因为每一个 MySQL 集群会有多种类型的任务逻辑,如「数据备份」「数据恢复」「健康检测」「故障自动处理」等,这些逻辑的并发执行可能会引起异常,故须要任务调度器来协调任务的执行,Controller 起到的就是这方面的做用:
经过 Controller 和各种 Worker,每一个 MySQL 集群实现了自运维。
在「健康检测」方面,咱们实现了两种机制:
对于健康检测的数据,Controller 和 Operator 均会使用,以下图所示:
Controller 使用健康检测数据是为了及时发现 MySQL 集群的异常,并作相应的故障处理,故须要准确、及时的健康状态信息。它在内存中维护全部 MySQL 实例的状态,根据「主动检测」和「被动检测」的结果更新实例状态并作相应的处理。
Operator 使用健康检测数据是为了向外界反映 MySQL 集群的运行状况,并在 Controller 异常时介入到 MySQL 集群的故障处理中。
在实践中,因为健康检测的频率相对较高,会产生大量的健康状态,若每一个健康状态都被持久化,那么 Operator 和 APIServer 均会承受巨大的访问压力。因为这些健康状态仅最新的数据有意义,故在 Controller 层面将待向 Operator 汇报的健康状态插入到一个有限容量的 Queue 中,当 Queue 满时,旧的健康状态将被丢弃。
当 Controller 检测到 MySQL 集群异常时,将会进行故障自动处理。
先定义故障处理原则:
Green
同时针对每一个 mysqld 节点,定义了以下状态:
Green
Controller 收集到全部 MySQL 节点状态后,会根据这些节点的状态推算 MySQL 集群的状态。当检测到 MySQL 集群状态不是 Green 时,会触发「故障处理」逻辑,该逻辑会根据已知的故障处理方案进行处理。若该故障类型未知,人工介入处理。整个流程以下图:
因为每种应用的故障场景和处理方案不一样,这里再也不叙述具体的处理方法。
3.易使用
咱们基于 Operator 的理念实现了高可靠的 MySQL 服务,为用户定义了两类资源,即 QiniuMySQL 和 QiniuMySQLData。前者描述用户对 MySQL 集群的配置,后者描述手动备份/恢复数据的任务,这里以 QiniuMySQL 为例。
用户可经过以下简单的 yaml 文件触发建立 MySQL 集群的操做:
在集群建立好后,用户可经过该 CR object 的 status 字段获取集群状态:
这里再引入一个概念:Helm。
Helm 是为 k8s 提供的包管理工具,经过将应用打包为 Chart,标准化了 k8s 应用的交付、部署和使用流程。
Chart 本质上是 k8s yaml 文件和参数文件的集合,这样能够经过一个 Chart 文件进行应用的交付。Helm 经过操做 Chart,可一键部署、升级应用。
因为篇幅缘由及 Helm 操做的通用性,这里再也不描述具体的使用过程。
4.易运维
除了上述实现的「健康检测」「故障自动处理」以及经过 Helm 管理应用的交付、部署,在运维过程当中还有以下问题须要考虑:
咱们经过 prometheus + grafana 作监控/告警服务,服务将 metric 数据以 HTTP API 暴露给 prometheus,由 prometheus server 定时拉取。开发人员在 grafana 上将 prometheus 中的监控数据可视化,根据对监控图表和应用的把握,在监控图中设置告警线,由 grafana 实现告警。
这种先可视化监控后告警的方式,极大程度上加强了咱们对应用运行特征的把握,明确须要关注的指标和告警线,下降无效告警的产生量。
在开发中,咱们经过 gRPC 实现服务间的通讯。在 gRPC 生态系统中,有个名为 go-grpc-prometheus 的开源项目,经过在服务中插入几行简单的代码,就能够实现对 gRPC server 全部 rpc 请求的监控打点。
对于容器化服务,日志管理包括「日志收集」和「日志滚动」两方面维度。
咱们将服务日志打到 syslog 中,再经过某种手段将 syslog 日志转入到容器的 stdout/stderr 中,方便外部采用常规的方式进行日志收集。同时,在 syslog 中配置了 logrotate 功能,自动进行日志的滚动操做,避免日志占满容器磁盘空间引起服务异常。
为了提高开发效率,咱们使用 https://github.com/phusion/baseimage-docker 做为基础镜像,其中内置了 syslog 和 lograte 服务,应用只关心把日志打入 syslog 便可,不用关心日志的收集和日志滚动问题。
经过上述描述,完整的 MySQL 应用架构以下:
在开发基于 k8s 的高可靠 MySQL 应用过程当中,随着对 k8s 和 MySQL 理解的深刻,咱们不断进行抽象,逐步将以下通用的逻辑和最佳实践以模块的方式实现:
牛人说专栏致力于技术人思想的发现,其中包括技术实践、技术干货、技术看法、成长心得,还有一切值得被发现的技术内容。咱们但愿集合最优秀的技术人,挖掘独到、犀利、具备时代感的声音。