本系列会分析OpenStack 的高可用性(HA)概念和解决方案:html
(1)OpenStack 高可用方案概述node
(2)Neutron L3 Agent HA - VRRP (虚拟路由冗余协议)mysql
(3)Neutron L3 Agent HA - DVR (分布式虚机路由器)git
(4)Pacemaker 和 OpenStack Resource Agent (RA)github
(5)RabbitMQ HAweb
(6)MySQL HAredis
Mysql HA 方案有不少种,包括:sql
这些高可用方案,大可能是基于如下几种基础来部署的:数据库
从 SLA 的角度看:centos
这里 有个各类方案的比较:
在这些可选项中,最多见的就是基于主从复制的方案,其次是基于Galera的方案。这篇文章 分享MYSQL中的各类高可用技术 全面具体地分析了 Mysql 的各类容灾方案。也可见 Mysql 容灾的水很深。
MySQL Cluster 是 MySQL 官方也就是 Oracle 主推的一种提供去中心化集群(shared-nothing clustering)和 自动共享(auto-sharding)的MySQL 数据库管理系统。它被设计来提供高可用(99.999%)、高吐吞吐量、低延迟和几乎线性扩展的解决方案。它是基于 MySQL 的 NDB 或者 NDBCLUSTER 存储引擎实现的。(引用自 https://en.wikipedia.org/wiki/MySQL_Cluster)
官网:https://www.mysql.com/products/cluster/
版本:MySQL Cluster 有独立于 MySQL 的版本(7.4版本使用 MySQL 5.6;7.3版本使用 MySQL 5.5)
价格:https://www.mysql.com/products/
这是 Choosing the right MySQL High Availability Solution – webinar replay MySQL Cluster 和其它几个主要HA方案的 SLA 比较:
(1)特征
(2)架构
支持最多 48 个 data nodes;集群最多 255 个节点
MySQL 被 Oracle 收购后,基于需求以及对 Oracle 的担忧,出现了两个主要的分支。它们都是免费开源的软件。
MariaDB由MySQL的创始人麦克尔·维德纽斯主导开发,他早前曾以10亿美圆的价格,将本身建立的公司MySQL AB卖给了SUN,此后,随着SUN被甲骨文收购,MySQL的全部权也落入Oracle的手中。MariaDB名称来自麦克尔·维德纽斯的女儿玛丽亚(英语:Maria)的名字。
MariaDB的目的是彻底兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,10.0.9版起使用XtraDB(名称代号为Aria)来代替MySQL的InnoDB。
版本方面,MariaDB直到5.5版本,均依照MySQL的版本。所以,使用MariaDB5.5的人会从MySQL 5.5中了解到MariaDB的全部功能。从2012年11月12日起发布的10.0.0版开始,再也不依照MySQL的版号。10.0.x版以5.5版为基础,加上移植自MySQL 5.6版的功能和自行开发的新功能。
相对于最新的MySQL5.6,MariaDB在性能、功能、管理、NoSQL扩展方面包含了更丰富的特性。好比微秒的支持、线程池、子查询优化、组提交、进度报告等。
官网地址:https://mariadb.org/
Percona Server就是这样一款产品,由领先的MySQL咨询公司Percona发布。Percona Server是一款独立的数据库产品,为用户提供了换出其MySQL安装并换入Percona Server产品的能力。经过这样作,就能够利用XtraDB存储引擎。Percona Server声称能够彻底与MySQL兼容,所以从理论上讲,您无需更改软件中的任何代码。这确实是一个很大的优点,适合在您寻找快速性能改进时控制质量。所以,采用Percona Server的一个很好的理由是,利用XtraDB引擎来尽量地减小代码更改。
更多的比较,能够参考网上的大量文章,好比
Galera Cluster 是一套在innodb存储引擎上面实现multi-master及数据实时同步的系统架构,业务层面无需作读写分离工做,数据库读写压力都能按照既定的规则分发到各个节点上去。在数据方面彻底兼容 MariaDB 和 MySQL。
官网:http://galeracluster.com/products/
使用案例:HP, OpenStack,KPN
特征:
Galera Cluster 能够同时支持 MySQL 和 MariaDB:
为了支持MySQL,Galera Cluster 中使用了由 Coreship 提供的补丁 (https://launchpad.net/codership-mysql)。MySQL Galera 集群:
In MySQL-5.5.x/wsrep-23.x, Galera Replication has some limitations, these are documented in readme-wsrep.
Galera replication originally only worked with InnoDB storage engine, but it now also supports MyISAM storage engine. Any writes to other table types, including system (mysql.*) tables are not replicated. However, DDL statements are replicated in statement level, and changes to mysql.* tables will get replicated that way. So, you can safely issue: CREATE USER..., but issuing: INSERT INTO mysql.user..., will not be replicated.
MyISAM replication is recent and should be considered experimental. Non-deterministic functions like NOW() are not supported. The Configurator for Galera enables wsrep_replicate_myisam by default.
DELETE operation is unsupported on tables without primary key. Also rows in tables without primary key may appear in different order on different nodes. As a result SELECT...LIMIT... may return slightly different sets.
Unsupported queries:
* LOCK/UNLOCK TABLES cannot be supported in multi-master setups. 不支持跨节点的表锁
* lock functions (GET_LOCK(), RELEASE_LOCK()... )
Query log cannot be directed to table. If you enable query logging, you must forward the log to a file:
log_output = FILE
Use general_log and general_log_file to choose query logging and the log file name.
Maximum allowed transaction size is defined by wsrep_max_ws_rows and wsrep_max_ws_size. Anything bigger (e.g. huge LOAD DATA) will be rejected.
Due to cluster level optimistic concurrency control, transaction issuing COMMIT may still be aborted at that stage. There can be two transactions writing to same rows and committing in separate cluster nodes, and only one of the them can successfully commit. The failing one will be aborted. For cluster level aborts, MySQL/galera cluster gives back deadlock error.
code (Error: 1213 SQLSTATE: 40001 (ER_LOCK_DEADLOCK)).
XA transactions can not be supported due to possible rollback on commit.
MySQL 5.6 的一样描述在 percona/debian-percona-xtradb-cluster-5.6
缘由和几率:
常规的推荐作法:
减小死锁的一些推荐作法:
与 RabbitMQ HA 方案相似,OpenStack 官方推荐的 Mysql Active/Passive HA 方案也是 Pacemaker + DRBD + CoroSync。具体方案为:
OpenStack 官方推荐的 Mysql HA A/P 方案 配置完成后的效果:
这个文档 详细阐述了具体的配置步骤。这个方案的问题是,drbd 容易出现脑裂;并且,两个 mysql 节点只有一个能提供服务,存在资源浪费。
架构以下:
Galera 主要功能:
优点:
局限:见 4.3.1 章节的详细描述
另外,MySQL Galera Cluster 并非适合全部须要复制的情形,你必须根据本身的需求来决定,好比,
详细配置过程能够参考 OpenStack HA Guide, 这个文章 和 MySQL Multi-master Replication With Galera。
3.1 中的A/A 方案须要三个节点,所以成本比较高。本方案提供使用两个节点状况下的 A/A 方案。相信信息能够参考 这篇文章。
该方案使用 Arbitrator 做为第三个节点来使用,它实际上是一个守护进程。它有两个做用:
这篇文章中,做者对200个OpenStack用户/运维人员作过一个关于数据库使用的调查,结果是
OpenStack 官方推荐的A/A HA 方案是使用 Galera 来作三节点HA(http://docs.openstack.org/ha-guide/controller-ha-galera.html)。这种模式下,Galera 提供多个 Mysql 节点之间的同步复制,使得多个 Mysql 节点同时对外提供服务,这时候每每须要使用负载均衡软件好比 HAProxy 来提供一个 VIP 给各应用使用。可是,OpenStack 文档回避了这种MySql集群的问题。
对于 2.4.1 部分描述的 MySQL Galera 的一些局限,若是在 OpenStack 环境中使用 HAProxy 作整个MySQL Galera 的 LB 的话, 由于该集群不支持跨节点对表加锁,也就是说若是OpenStack 某组件有两个会话分布在两个节点上同时写入某一条数据,那么其中一个会话将会遇到死锁的状况。网上这种状况的报告很是多,好比:
文章 Avoiding Deadlocks in Galera - Set up HAProxy for single-node writes and multi-node reads 对这个问题和解决方法有很是详细的描述。基本原理示意图:
有一个长长的 OpenStack 邮件列表,IMPORTANT: MySQL Galera does *not* support SELECT ... FOR UPDATE (写于 2014年五月)中,做者列出了Nova 和 Neutron 中使用的 SELECT ... FOR UPDATE 代码,其中
可见,在写并发很是高的状况下,死锁的状况的出现几率是不低的,特别是在 Neutron 中。
做者还提出了几个选项:
(1)上面的邮件回复中提到的一个 workaround,就是使得更新请求只发往一个节点。在使用 HAProxy 的状况下,具体作法是,只设定一个节点为 master,其他的为 backup。HAProxy 会在 master 失效时自动切换到某一个 backup 上。
server 192.168.0.101 192.168.0.101:3306 check server 192.168.0.102 192.168.0.102:3306 check backup server 192.168.0.103 192.168.0.103:3306 check backup
若是还须要进一步优化的话,能够只将写操做放到一个节点,而将读操做在全部节点之间作负载均衡从而提升性能。Percona XtraDB Cluster reference architecture with HaProxy 描述了一个改进的方案,就是提供两个 MYSQL 服务端点,一个(端口 3306)只是使用(包括读写)一个节点,另外一个(端口3306)使用三个节点。所以,对 OpenStack 来讲,Neutron 使用 3306 端口(若是Nova解决了问题的话),其它组件使用 3307 端口。
global log 127.0.0.1 local0 log 127.0.0.1 local1 notice maxconn 4096 chroot /usr/share/haproxy user haproxy group haproxy daemon defaults log global mode http option tcplog option dontlognull retries 3 option redispatch maxconn 2000 contimeout 5000 clitimeout 50000 srvtimeout 50000
frontend pxc-front bind *:3307 mode tcp default_backend pxc-back
frontend stats-front bind *:80 mode http default_backend stats-back
frontend pxc-onenode-front bind *:3306 mode tcp default_backend pxc-onenode-back
backend pxc-back #master-master,适用于没有使用 SELECT...UPDATE 语句的应用 mode tcp balance leastconn option httpchk server c1 10.116.39.76:3306 check port 9200 inter 12000 rise 3 fall 3 server c2 10.195.206.117:3306 check port 9200 inter 12000 rise 3 fall 3 server c3 10.202.23.92:3306 check port 9200 inter 12000 rise 3 fall 3
backend stats-back mode http balance roundrobin stats uri /haproxy/stats stats auth pxcstats:secret
backend pxc-onenode-back #一个master,其它是backup,用来避免deadlock mode tcp balance leastconn option httpchk server c1 10.116.39.76:3306 check port 9200 inter 12000 rise 3 fall 3 server c2 10.195.206.117:3306 check port 9200 inter 12000 rise 3 fall 3 backup server c3 10.202.23.92:3306 check port 9200 inter 12000 rise 3 fall 3 backup
(2)另一个方案是,将 OpenStack 全部的MySQL 操做按照读和写作分离(read write splitting),写只在那个主节点上,读在全部节点上作负载均衡。可是,目前 OpenStack 应该尚未原生的支持。一个可选的方案是使用开源软件 maxscale:https://www.percona.com/blog/2015/06/08/maxscale-a-new-tool-to-solve-your-mysql-scalability-problems/
(3)关于 openstack 里面的读写分离,其 db 库 oslo 却是有了接口支持:
def get_session(self, use_slave=False, **kwargs):
"""Get a Session instance.
:param use_slave: if possible, use 'slave' database connection for this session. If the connection string for the slave database wasn't provided, a session bound to the 'master' engine will be returned. (defaults to False)
:type use_slave: bool
可是从代码(Kilo版本)来看,只有 Nova 支持这种 slave_connection 参数(\nova\nova\db\sqlalchemy\api.py):
def model_query(context, model, args=None, session=None, use_slave=False, read_deleted=None, project_only=False): if session is None: if CONF.database.slave_connection == '': use_slave = False session = get_session(use_slave=use_slave)
而 Neutron 模块至少在 Kilo 版本中尚未实现。下面的代码中,调用 oslo 建立 db session 的时候,根本就没有传入 CONF.database.slave_connection 的值:
def get_session(autocommit=True, expire_on_commit=False): """Helper method to grab session.""" facade = _create_facade_lazily() return facade.get_session(autocommit=autocommit, expire_on_commit=expire_on_commit)
在 这个 openstack 邮件列表 中也能确认这个状态:
Nova is the only project that uses slave_connection option and it was kind of broken: nova bare metal driver uses a separate database and there was no way to use a slave db connection for it.
可是,在 Juno 版本中,惟一没有解决 dead lock 问题的就是 Neutorn,所以,该配置项对解决 Neutron MySQL Galera 死锁没有实质性意义。它只对Nova提升DB性能有帮助。关于该配置项,能够参考官方文档 https://wiki.openstack.org/wiki/Slave_usage。
OpenStack 的 Nova 和 Neutorn 模块都在很多地方使用了 SELECT... FOR UPDAT 语句,能够参考 A lock-free quota implementation 文章中的描述。所以,若是不想使用上面的 Workaround(它下降了对扩展性的支持)而要作终极处理的话,就须要修改Nova 和 Neutron 的代码将这些语句替换掉了。
关于代码修改,有以下文档:
[documented]:
[Nova]:
http://specs.openstack.org/openstack/nova-specs/specs/kilo/approved/lock-free-quota-management.html
https://bugs.launchpad.net/oslo.db/+bug/1394298 Galera deadlock on SELECT FOR UPDATE is not handled。这个针对 Nova 的 fix 已经进了 Kilo 版本。
[Neutron]:
https://bugs.launchpad.net/neutron/+bug/1364358https://bugs.launchpad.net/neutron/+bug/1331564
https://bugs.launchpad.net/neutron/+bug/1364358 Remove SELECT FOR UPDATE usage。该 ticket 目前仍是 incomplete 状态,而最新的注释 “the current design disallows to remove all SELECT FOR UPDATE so the right bug would to ensure all SELECT FOR UPDATE are Galera multi-writers compliant” 更是说明Neutron 这部分的修改尚未完成。所以,对于 Neutron 来讲,还得继续使用 workaround。
根据 Mirantis 和 Percona 的 这个报告,Juno 版本中,“All components except neutron are good with using multiple writers”。
这个 OpenStack 邮件列表 描述了该问题:
A: start transaction; A: insert into foo values(1) A: commit; B: select * from foo; <-- May not contain the value we inserted above[3]
这说明,虽然 Galera 是生成同步的,可是做为分布式数据库,本质上仍是须要一些时间,即便很是短,完成写入的数据同步到整个集群的。所以,在某些状况下,特别是MySQL 负载很大致使同步压力很大的状况下,这种读写不一致性的问题可能会更加突出。
注意最下面右侧的Node1 和 node2 上的箭头和红线直接的差距(蓝色圆圈内),这也是为何是 “virtually”同步,而不是直接同步。若是正好在gap时间段内读的话,是没法读到写入的数据的。
好在 MySQL Galera 提供了一个配置项 wsrep_sync_wait,它的含义是 “Defines whether the node enforces strict cluster-wide causality checks.” ,能够有以下值:
Bitmask | Checks |
---|---|
0 | Disabled. |
1 | Checks on READ statements, including SELECT, SHOW, and BEGIN / STARTTRANSACTION. |
2 | Checks made on UPDATE and DELETE statements. |
3 | Checks made on READ, UPDATE and DELETE statements. |
4 | Checks made on INSERT and REPLACE statements. |
它的默认值是0,若是须要保证读写一致性能够设置为1。可是须要注意的是,该设置会带来相应的延迟性,所以,它是一把双刃剑,到底对性能有多大的影响,须要通过测试才能使用。关于该配置项和其它 wresp 配置项的具体说明,能够参考 http://galeracluster.com/documentation-webpages/mysqlwsrepoptions.html。注意到 Mirantis 和 Percona 的 这个报告 所使用的测试环境中该值被设为1了。
看起来,目前至少Neutron 尚未完成代码修改,Nova 中的代码修改看似已经完成,可是须要经过测试来验证。所以,目前状况下,咱们仍是须要使用 4.3.1 中描述的 workaround。
参考连接: