本系列會分析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。
參考連接: