背景介绍
Uber的MySQL集群规模很大,超过1000个集群,共有4000多个数据库服务器。
问题
起初是使用Puppet管理,写了很多脚本,再加上一些人工操作,在集群数量比较小时,这个管理方法比较实用,但现在这个规范已经完全不适用了,所以需要其他的管理方案。
对于MySQL集群的管理,有几点基本的需求:
- 可以在每个主机上运行多个数据库
- 全自动化
- 有一个统一入口来管理所有数据中心中的所有集群
解决方案
最后决定基于Docker设计一套解决方案,命名为 Schemadock。
在Docker容器中运行MySQL,形成一个一个的节点,这些节点如何构成集群拓扑结构,由配置文件来定义。
例如有一个集群 Cluster A,配置文件中便会对其进行说明,指定包含了几个数据库,是哪几个,哪个是 master。
节点有了,拓扑结构说明书也有了,谁来按照说明把集群构建起来呢?由另一个重要角色 agents 代理 来完成。
Schemadock 中还有一个中心化的服务,进行整体的维护和监控,检查各个实例的状态和偏差。
为什么选用Docker
通过容器化的方式,使得在一台主机上运行多个数据库非常容易,可以是不同版本不同配置的,甚至一台主机上可以运行一个小型集群,这样就节省了很多主机资源。
因为MySQL是运行在容器中,容器运行在主机中,对于主机来讲,他们的功能统一了,就是运行容器,所以主机的角色一致了,可以移除Puppet中的依赖关系了。
Docker非常便于测试,在测试环境中就可以执行起集群搭建步骤,实验所有的操作流程。
Docker虽好,但Uber也给出了一点建议:
在规模不够大时,不要轻易使用Docker,因为使用Docker之后,你需要处理更多的事情,例如 镜像的构建管理、容器的监控、Docker升级、日志处理、网络规划 ……,所以,规模较小时,使用 Puppet、Ansible 这类的工具就够用了,例如Uber的情况,MySQL集群数量在16个以下时,用 Puppet 管理就比较轻松。
无状态的MySQL镜像
MySQL镜像被设计为完全无状态的,构建镜像时,不会包含任何逻辑,比如复制逻辑、状态检查等,创建出来的容器就是无状态的,容器的具体角色是通过环境变量来指定,使容器与逻辑分离。
数据也不在容器中,是挂载在主机目录下的。
这样,容器是非常独立的,这会带来很多好处,例如:
- 某个容器出现问题后,不再复用,直接删掉,用同样的参数创建一个新的容器。
- 升级MySQL非常简单,使用新版本的镜像创建容器,替代老版本容器。
- 配置的变化很容易控制。
容器编排和拓扑配置
MySQL镜像需要被启动为容器,指定容器的角色,并放置到复制拓扑结构中的正确位置。
这些工作由 agents 来完成,每台主机中都会运行一个 agent,接收目标说明信息,根据说明来创建和配置容器。
说明信息示例:
“schemadock01-mezzanine-mezzanine-us1-cluster8-db4”: {
“app_id”: “mezzanine-mezzanine-us1-cluster8-db4”,
“state”: “started”,
“data”: {
“semi_sync_repl_enabled”: false,
“name”: “mezzanine-us1-cluster8-db4”,
“master_host”: “schemadock30”,
“master_port”: 7335,
“disabled”: false,
“role”: “minion”,
“port”: 7335,
“size”: “all”
}
}
含义是主机schemadock01上会运行一个Mezzanine数据库,角色是从服务器,端口为7335,可以占用主机所有资源,其master是schemadock30:7335。
agents 是一个无限循环运行的进程,每30秒执行一次,每次循环中会根据目标说明来检查实际状况是否正确。
例如:
- 检查容器是否正常运行,如果没有,就根据说明创建一个,并配置好。
- 检查容器是否在正确的复制拓扑位置,如果不是,就对其进行调整,例如:之前的一个从服务器要变更为master,就需要确保其是安全的,会检查旧master是否为只读,并且GTID是否被全部接收并执行,如果是,就移除和旧master的连接并变为可写。
- 根据角色检查一系列参数,例如,master应该是可写的。
- 启动或关闭一些支持型的容器,如 心跳和死锁的监控。
在创建MySQL复制关系时是有序的,但 agents 是不关心其管理的容器间的顺序的。
例如正常的构建复制过程这样的:
1、 创建master,并等待其就绪
2、 创建第一个slave,连接到master
3、 重复创建slave
agents 工作时,不会先找角色为master的来配置,先遇到谁就配置谁。
如果先遇到了slave,配置时发现master还没有,就停止配置,继续处理下一个,然后等下一个工作循环中再次检查其master是否就绪,如果就绪了,就继续配置,完成了对这个容器的工作。
例如下图:
执行DB3时,DB1还不可用,就等着,直到某个工作循环中发现DB1可用时才配置完成。
小结
通过以上内容,我们可以对Uber的MySQL Docker方案有个大概了解,主要由以下4部分构成:
1、 无状态的MySQL容器
2、 整体集群拓扑结构的配置文件
3、 agents,每台主机上的工作者,负责本机上容器的创建、配置、状态检查与修正
4、 服务中心,负责整体的维护和监控
实现了最初的目标:单机运行多数据库、自动化、统一管理入口。
2016年初开始迁移到Docker,到现在已经运行着1500台Docker服务器,2300个MySQL数据库。
内容整理自Uber官方博客,原文地址:https://eng.uber.com/dockerizing-mysql