天天看點

自動化運維産品的命門——中繼資料庫

概述

企業機器和資料庫上規模後,運維不再單純依賴個人的能力,而依賴各種運維工具平台。如針對主機的運維平台,能維護主機基礎資訊(機房資訊、機櫃資訊、機器位置資訊)、自動安裝作業系統、更新軟體、通過模闆初始化主機、管理機器使用狀态(待初始化/待使用/上線/下線等等),針對資料庫的運維平台,能自動安裝資料庫執行個體、搭建高可用備庫、自動主備切換、資料遷移、彈性擴容/縮容、執行個體下線、性能分析等等。善假于物是人的能力。

這個​

​物​

​,做得粗糙一點稱為工具,功能再豐富完善一點稱為産品。産品在宣傳上會突出管理多大規模的機器,節省多少人力,一鍵上線/擴容/遷移/切換,智能化運維,自治或自愈等等。博眼球也是人的本性。(本文标題也不能免俗~)。

本文首先是指出自動化運維産品架構設計的關鍵子產品——中繼資料庫的重要性和風險,以及常用的風險規避方法。這些産品在雲計算業務裡随處可見。然後闡述螞蟻的分布式資料庫OceanBase的關鍵子產品——rootservice以及OceanBase運維平台OCP中繼資料庫的特殊作用。最後以示例解釋OceanBase叢集不同安裝方法的差別所在。

看完本文能夠從另外一個角度了解為什麼OceanBase資料庫的可靠性可以說是金融級的(本文并不是重提​

​RPO​

​和​

​RTO​

​概念)。OceanBase的這個設計對其他分布式叢集産品的架構設計也有一定參考意義。

自動化運維産品的可用性分析

自動化運維産品的功能大體有這麼幾類:

  • 主機、網絡、資料庫執行個體或者其他資訊的生命周期的管理。主機和資料庫執行個體好了解,網絡資訊有靜态和動态的VIP或VLAN及其通路規則等資訊。其他資訊有如某個中間件叢集(如LVS、業務服務、分布式資料庫中間件等)。
  • 一些常用運維操作的自動化執行。這類操作可以規範化,多是重複的操作,通過程式自動化後可以提高運維效率和降低人為失誤風險。如初始化、安裝、擴容/縮容、遷移、下線等。
  • 資料庫執行個體或者其他服務的高可用。提供服務的執行個體,出現故障後業務都要求能快速恢複服務。這類服務分為兩類。一類是有狀态的,即有資料的。以資料庫為例。另外一類是無狀态的。這類多是以叢集形式提供服務,個别節點出現故障後就直接被踢出叢集。如F5後面挂載的服務叢集、LVS自身叢集、業務服務叢集(服務化的産物)。由于是無狀态的叢集,服務擴容也是很友善的。詳情可以看看之前的文章《​​常見關系資料庫的架構​​》.
  • 運維資料采集和監控告警,進階一點的還有自動化的性能分析。監控告警好做,自動化的性能分析就要在大量基礎資料(資訊)基礎上活用規則和機器學習技術去做異常現象和基礎資訊(原因)的關聯。

不同産品的能力特點或許有點不一樣,但都有一個共同點,就是有自己的中繼資料庫。作為一個自動化運維産品,必然要管理很多對象(主機、網絡、資料庫或者其他服務等)。這些對象的資訊不能存在産品的記憶體中,一定會持久化到資料庫中,産品的功能在使用過程中多少會依賴中繼資料庫。是以中繼資料庫的可用性對自動化運維産品的可用性就至關重要。

中繼資料庫的正确性也很重要,在企業運維體系裡,越是偏底層基礎的産品的中繼資料庫,越是重要。中繼資料如果錯誤,在自動化運維的作用下,其影響範圍可能會非常大。如正在提供服務的機器被批量下線、新的網絡通路規則被應用到不合适的網段導緻大面積網絡通路中斷等等。是以自動化運維産品的中繼資料庫的變更操作要規範、備份要頻繁保證有效。

中繼資料庫出現故障時,自動化運維産品的某些功能就會異常。是以中繼資料庫本身也要有高可用。如果這個自動化運維産品就是資料庫運維産品,它提供資料庫高可用切換的前提就依賴這個中繼資料庫,是以這個中繼資料庫的高可用就不能靠自己來解決,必須使用另外一套産品去保證。而那個産品顯然在功能上不如目前這個自動化運維産品(否則那個産品就有理由替換這個産品)。這是一個很有趣的現象。

在機房容災切換裡,所有自動化運維産品的中繼資料庫的切換一定是首先進行的,其次才是各個産品的容災切換,最後才是這些産品的客戶應用的容災切換。這裡面有個層層依賴的關系。理論上如果産品A的高可用還要依賴另外一個産品B的話,那麼産品B的高可用就要先于産品A生效。以此往前推,總有一個産品的高可用是不依賴其他産品的,隻能自己解決或靠運維人工介入才可以。而它能否切換成功則關系着整個容災演練計劃的成敗。是以中繼資料庫是自動化運維産品的命門這說法也不過。

有些運維産品意識到這個問題,會采用将中繼資料緩存起來的方法來降低對中繼資料庫可用性的依賴。在中繼資料變更時它會同時更新緩存和中繼資料庫,并能接受中繼資料庫更新失敗的情景。如果是産品服務當機再啟動了,緩存中資料就丢失了,這時候就需要去中繼資料庫裡再拉去一份最新的資料。隻要中繼資料庫跟産品不是同時出現故障,這個設計還是有效的。很多産品都采用了這個設計。

中繼資料庫的高可用分析

自動化運維産品的中繼資料庫可用性分析

既然上面提到中繼資料庫的重要性,這裡就多提一下中繼資料庫的高可用技術。傳統資料庫(ORACLE、SQLServer和MySQL)使用主備架構時,其高可用需要依賴外部工具産品。但是最近幾年流行的分布式資料庫,使用了Paxos協定做多副本(至少三副本)同步時,可以在原主節點故障時自動選出新的主節點繼續提供服務,這個提升了中繼資料庫的可靠性。詳情檢視之前的文章《​​揭開資料庫RPO為0的秘密(上)​​》和《​​揭開資料庫RPO為0的秘密(下)​​》。這類産品的具體架構技術也不完全相同,是以在RPO和RTO上還是有細微差別的,這個要注意辨識。

螞蟻的分布式資料庫OceanBase也有自動化運維平台OCP,OCP也有一個中繼資料庫。目前這個中繼資料庫是一個獨立的三節點的OceanBase叢集,它的高可用是自身能力,不需要幹預,并且故障切換後還保證資料絕對不丢。

OceanBase運維平台OCP的中繼資料庫的作用

OceanBase自動化運維平台(又叫雲平台​

​OCP​

​)的中繼資料庫主要是服務于​

​OCP​

​的。OCP的功能就是管理大規模的OceanBase叢集。會涉及到基礎資訊、自動化任務、監控告警等對象。這些資訊都儲存在OceanBase的中繼資料庫裡。這點跟其他自動化運維産品是一樣的。

​OCP​

​的中繼資料庫還有個特殊的作用就是也儲存了所有OceanBase叢集内部的中繼資料管理服務的關鍵資訊。這個跟OceanBase的架構有關。

OceanBase叢集可用性分析

OceanBase的當機應對邏輯

OceanBase是一個分布式資料庫,是一個叢集。叢集的中繼資料資訊在自己内部儲存了一份。比如說叢集有哪些節點,各個節點的運作狀态等。OceanBase叢集在機器上的存在形式就是一個OBServer程序。每個節點上的OBServer程序地位基本相同,除了内部租戶sys所在的三個節點稍有不同。這個租戶下會運作一個rootservice服務,管理叢集内部節點狀态、參數、内部任務(當機合并轉儲、負載均衡)排程等。(rootservice并不負責實際選舉,選舉是由一個election子產品負責。不過rootservice可以通過規則指導選舉)。各個節點組成為一個分布式叢集,通過啟動時都指定了相同的rootservice位址來找到叢集并注冊。 (詳情參見此前的文章《​​OceanBase實踐入門——OceanBase叢集手動搭建​​》。這是一個​

​shared-nothing​

​架構的叢集。

OceanBase裡所有節點跟這個rootservice保持聯系(有心跳檢測機制)。如果節點故障,rootservice很快就會發現節點(OBServer)心跳丢失,rootservice做節點成員變更(狀态)管理。同時election子產品會對節點上的分區判斷是否要選舉(切換),選主政策會受rootservice裡規則影響。 rootservice依托于于sys租戶内部的一個系統表 __all_core_table,服務存在于三個節點的OBServer程序内部,隻有其中一個提供服務(該系統表的leader角色所在節點)。如果節點故障導緻rootservice也出現故障,election子產品會對__all_core_table選舉出新的leader,然後啟動新的rootservice。是以rootservice的可用性對OceanBase叢集至關重要。

如果rootservice所在sys租戶的節點出現故障并沒有及時恢複(預設等2小時),rootservice會将該節點永久下線(注意隻是下線,節點資訊還存在叢集中繼資料裡),并從叢集中其他節點裡選一個節點(叢集規模至少是​

​2-2-2​

​布局的)來補齊sys租戶資料的三副本,相應的資料都會遷移到那個節點。普通節點故障同理。

OceanBase的當機恢複邏輯

上面說了OceanBase即使有節點當機了,叢集對外服務的可用性是自動恢複。接下來就說當機的節點起來後如何恢複?

通常分布式叢集産品的節點跟叢集成員關系是通過配置或者啟動參數指定的,或者叢集端記錄了節點的資訊。即使節點當機後叢集會自動将節點踢出,在節點恢複服務後,節點自身可能有記錄叢集的連接配接資訊或者叢集端有該節點的資訊。好一點的産品做到了叢集發現之前故障節點恢複後,又重新把該節點加入到叢集當中提供服務。比如說​

​F5​

​或者​

​LVS​

​都是這樣管理後端節點的。稍微差一點的産品可能要在節點故障時手動将節點從叢集中剔除,在節點恢複時手動将節點加入叢集服務中。

是以在分析之前,再回顧一下OceanBase各個節點是怎麼跟叢集聯系的,關鍵點就在于節點上OBServer首次啟動時的參數​

​rootservice_list​

​裡。下面是啟動示例:

​cd /home/admin/node1/oceanbase && /home/admin/node1/oceanbase/bin/observer -i bond0 -P 2882 -p 2881 -z zone1 -d /home/admin/node1/oceanbase/store/obdemo ​

​​

​-r '11.***.84.78:2882:2881;11.***.84.79:2882:2881;11.***.84.83:2882:2881'​

​​

​ -c 20190423 -n obdemo -o "memory_limit=51200M,datafile_size=100G,config_additional_dir=/data/data/1/obdemo/etc3;/data/log/log1/obdemo/etc2"​

其中 ​

​-r​

​參數就是指定了​

​rootservice_list​

​位址,節點就跟叢集有了聯系(當然第一次初始化時,節點還要指令加入到叢集中)。這個參數會記錄到OBServer程序的參數檔案裡。假設這個OBServer程序當機或關閉,再啟動的時候就不需要指定這些參數了。如下。OBServer程序會自動讀取軟體目錄下面​

​etc​

​目錄下的參數檔案。

​cd /home/admin/node1/oceanbase && bin/observer​

是以OB節點恢複的時候通常是可以自己跟叢集恢複聯系。但是,運維經驗豐富的人很快就會意識到這個啟動參數的​

​rootservice​

​位址是寫死的。如果​

​rootservice​

​所在節點發生變化了呢?

首先,如果sys租戶所在節點出現當機之後,​

​rootservice​

​選舉後的​

​leader​

​資訊會發生變化,它會自動通知所有活着的節點修改參數​

​rootservice_list​

​資訊,并且立即持久化到各個活着的節點本地的參數檔案中。​

​rootservice​

​成員節點變化或者角色變化都會反映在活着的節點記憶體以及配置檔案裡。

如果這個當機的節點很久才恢複,它還使用老的​

​rootservice_list​

​參數啟動,并且如果這個參數裡的多數派成員不可通路或者沒有運作​

​rootservice​

​服務,則這個節點是無法跟叢集取得聯系,節點也就無法啟動。

這個時候修複辦法就是在啟動OBServer的時候再次指定最新的​

​rootservice​

​參數。但這樣就有點不完美,會給運維人員帶來一點複雜性。實際上OceanBase生産環境選擇了一個更靈活的辦法。生産環境的節點上OBServer是通過OCP自動安裝的,OBServer啟動時不指定​

​rootservice_list​

​參數,而是指定一個​

​obconfig_url​

​參數。這個參數值是​

​OCP​

​暴露出來的一個​

​API​

​,可以傳回最新的​

​rootservice_list​

​資訊。它是存在​

​OCP​

​的中繼資料庫裡。

​​[[email protected] /home/admin/oceanbase]
$strings ​​​​etc/observer.config.bin​​​​|grep obconfig
obconfig_url=​​http://10.***.167.20:8082/services?Action​​=ObRootServiceInfo&User_ID=alibaba&UID=admin&ObRegion=oms_2x

[[email protected] /home/admin/oceanbase]
$curl -L 'http://10.***.167.20:8082/services?Action=ObRootServiceInfo&User_ID=alibaba&UID=admin&ObRegion=oms_2x'
{"Code":200,"Cost":5,"Data":{"ObRegionId":2100006,"RsList":[{"sql_port":2881,"address":"11.***.84.83:2882","role":"LEADER"},{"sql_port":2881,"address":"11.***.84.78:2882","role":"FOLLOWER"},{"sql_port":2881,"address":"11.***.84.84:2882","role":"FOLLOWER"}],"ReadonlyRsList":[],"ObRegion":"oms_2x"},"Message":"successful","Success":true,"Trace":"9.9.9.9:10.***.167.20:1556447216807"}
[[email protected] /home/admin/oceanbase]
$curl -L 'http://10.***.167.20:8082/services?Action=ObRootServiceInfo&User_ID=alibaba&UID=admin&ObRegion=oms_2x' | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   382  100   382    0     0  23226      0 --:--:-- --:--:-- --:--:-- 23875
{
"Code": 200,
"Cost": 3,
"Data": {
"ObRegion": "oms_2x",
"ObRegionId": 2100006,
"ReadonlyRsList": [],
"RsList": [
{
"address": "11.***.84.83:2882",
"role": "LEADER",
"sql_port": 2881
},
{
"address": "11.***.84.78:2882",
"role": "FOLLOWER",
"sql_port": 2881
},
{
"address": "11.***.84.84:2882",
"role": "FOLLOWER",
"sql_port": 2881
}
]
},
"Message": "successful",
"Success": true,
"Trace": "9.9.9.9:10.***.167.20:1556447216868"
}      

當OceanBase叢集的​

​rootservice​

​成員資訊發生變動時,會通過這個​

​API​

​更新​

​OCP​

​的中繼資料庫裡該叢集的​

​rootservcie_list​

​資訊。那麼其他新增節點啟動OBServer時就可以通過這個​

​API​

​擷取到最新的​

​rootservice​

​資訊。那麼此前說的那個當機很久的節點恢複的時候,就能恢複跟OceanBase叢集的聯系了(如果被下線了,狀态也會恢複為線上)。不過OceanBase對這個API的依賴是弱依賴,沒有這個API并不影響OceanBase叢集的可用性,隻是極端情況下部分節點不能自動恢複。

OceanBase叢集參數檔案的重要性

每個節點的OBServer程序的參數檔案内容大體相同,不同的就是相應本地特殊的地方。OBServer的參數檔案是二進制檔案,不建議直接編輯。修改參數有兩種方式。一是啟動OBServer的時候指定參數  ​

​-o "xxx=yyyy"​

​; 二是在叢集sys租戶裡通過 ​

​alter system set xxx=yyy [scope='xxx.xxx.xxx.xxx:2882'] ;​

​ 修改。這個跟ORACLE修改參數方法大體相同。

​alter system set obconfig_url='http://10.***.167.20:8082/services?Action=ObRootServiceInfo&User_ID=alibaba&UID=admin&ObRegion=oms_2x' server = '11.***.84.78:2882';​

OceanBase參數檔案對OBServer的重要性前面已經說過,參數不對OBServer有可能就跟叢集失去聯系。這個重要性就跟ORACLE的控制檔案重要性一樣,OBServer會建議把參數檔案儲存在多個獨立的目錄(檔案系統)下。參數​

​config_additional_dir​

​ 就是用來指定額外的儲存路徑。同時每次​

​observer.config.bin​

​内容的變更都會先儲存一下上一次的值在備份檔案​

​observer.config.bin.history​

​裡,友善參數修改不當後再恢複老的設定。

​$strings etc/observer.config.bin|grep config_additional_dir​

​​

​config_additional_dir=/data/log1/oms_2x/etc2;/data/1/oms_2x/etc3​

​​

​​

​$find /data/ |grep "observer.conf"​

​​

​/data/1/oms_2x/etc3/observer.conf.bin.history​

​​

​/data/1/oms_2x/etc3/observer.conf.bin​

​​

​/data/log1/oms_2x/etc2/observer.conf.bin.history​

​​

​/data/log1/oms_2x/etc2/observer.conf.bin​

​​

​​

​$find /home/admin |grep "observer.config"​

​​

​/home/admin/oceanbase/etc/observer.config.bin​

​​

​/home/admin/oceanbase/etc/observer.config.bin.history​

OceanBase叢集手動部署方案補遺

在前文《​​OceanBase實踐入門——OceanBase叢集手動搭建​​》裡,為了降低網友安裝OB學習環境的機器資源不足問題,方法裡沒有推薦用​

​OCP​

​安裝,也沒有采用​

​obconfig_url​

​參數(因為沒有​

​OCP​

​的​

​API​

​)。不過我們可以利用​

​python​

​搭建一個簡單的​

​HTTPServer​

​ 來模拟這個​

​API​

​。 方法如下:

​注:下面的***是出于安全考慮估計屏蔽顯示的。​

​​

​​

​1. 寫一個 rs.json檔案模拟api的結果​

​​

​$cat /home/admin/test/rs.json​

​​

​{"Code":200,"Cost":3,"Data":{"ObRegionId":2100006,"RsList":[{"sql_port":2881,"address":"11.***.84.83:2882","role":"LEADER"},{"sql_port":2881,"address":"11.***.84.78:2882","role":"FOLLOWER"},{"sql_port":2881,"address":"11.***.84.84:2882","role":"FOLLOWER"}],"ReadonlyRsList":[],"ObRegion":"oms_2x"},"Message":"successful","Success":true,"Trace":"9.9.9.9:10.***.167.20:1556446480671"}​

​​

​​

​2. 本地用python起一個httpserver​

​​

​[[email protected] /home/admin/test]​

​​

​$python -m SimpleHTTPServer 8080​

​​

​Serving HTTP on 0.0.0.0 port 8080 ...​

​​

​​

​3. 驗證api​

​​

​[[email protected] /home/admin/oceanbase]​

​​

​$curl -L 'http://11.***.84.78:8080/rs.json'​

​​

​{"Code":200,"Cost":3,"Data":{"ObRegionId":2100006,"RsList":[{"sql_port":2881,"address":"11.***.84.83:2882","role":"LEADER"},{"sql_port":2881,"address":"11.***.84.78:2882","role":"FOLLOWER"},{"sql_port":2881,"address":"11.***.84.84:2882","role":"FOLLOWER"}],"ReadonlyRsList":[],"ObRegion":"oms_2x"},"Message":"successful","Success":true,"Trace":"9.9.9.9:10.***.167.20:1556446480671"}​

​​

​​

​​

​4. 以新的參數啟動observer​

​​

​[[email protected] /home/admin/oceanbase]​

​​

​$bin/observer -o "obconfig_url='http://11.***.84.78:8080/rs.json'"​

​​

​bin/observer -o obconfig_url='http://11.***.84.78:8080/rs.json'​

​​

​optstr: obconfig_url='http://11.***.84.78:8080/rs.json'​

​​

​[2019-05-09 17:27:18.094254] ERROR [LIB] pidfile_test (utility.cpp:1152) [71476][0][Y0-0000000000000000] [lt=0] fid file doesn't exist(pidfile="run/observer.pid") BACKTRACE:0x7bfc359 0x7b400ab 0x6f4419 0x7c00a96 0x413266 0x7f7a35604445 0x43c025​

​​

不過模拟的​

​API​

​終究是假的,不支援​

​POST​

​方法,是以​

​rootservice​

​服務在成員變更後是沒辦法通過​

​POST​

​方法去調用​

​API​

​更新的。這個時候可以直接修改檔案 rs.json裡​

​rootservice_list​

​資訊。那麼故障節點OBServer啟動時就不需要做任何修改了。

再次重申,這個方法隻适合機器很少時搭建OB學習驗證環境使用(這裡特地舉例是為了加深了解)。在生産環境,還是要用​

​OCP​

​來搭建和管理OceanBase叢集。

總結

中繼資料庫是自動化運維産品的重要子產品,需要有有效機制保證中繼資料庫的高可用和可恢複。

OceanBase雲平台(OCP)是用來自動化運維多個OceanBase叢集,OCP的中繼資料庫是一個獨立的OceanBase叢集。每個OceanBase叢集在自己内部租戶sys裡儲存有中繼資料,并通過rootservice服務提供叢集級别管理,rootservice的位址即儲存在sys租戶系統表裡,也通過OCP API儲存到OCP的中繼資料庫裡。OceanBase叢集部分節點故障後再啟動的時候通過OCP的API擷取最新的rootservice_list資訊。rootservice的機制比這裡說的還要複雜,此外還有election子產品的機制也很複雜,以後有機會再詳細說明。

​​​​

其他

  • 個人了解,難免有誤。我會在月底集中對當月文章裡的錯誤進行修正。敬請關注。
  • 更多分享請查閱公衆号