構建一個企業級分佈式存儲系統對於任何一個團隊來講,都是一件極具挑戰性的工做。不只須要大量的理論基礎,還須要有良好的工程能力。SmartX 在分佈式存儲領域已經投入 5 年的時間,積累了不少寶貴的實踐經驗。咱們將經過一系列文章,向你們詳細介紹 SmartX 如何構建分佈式塊存儲產品。本文是第一部分,整理自 SmartX 聯合創始人兼 CTO 張凱在 QCon 2018 大會上的演講,着重介紹相關背景和元數據服務。node
你們下午好,我今天跟你們分享的題目是『ZBS:SmartX 自研分佈式塊存儲系統』。SmartX 是咱們公司的名字,我本人是這個公司的聯合創始人兼 CTO。ZBS 是 SmartX 研發的分佈式塊存儲產品的名字。數據庫
我畢業於清華計算機系,畢業之後加入百度基礎架構部工做了兩年,主要從事分佈式系統和大數據相關的工做。我自己也是開源社區的代碼貢獻者,參與的項目包括 Sheepdog 和 InfluxDB。其中 Sheepdog 是一個開源的分佈式塊存儲項目,InfluxDB 是一個時序數據庫(Time Series Database,TSDB)項目。2013 年我從百度離職,和清華的兩個師兄一塊兒創辦了 SmartX 公司。緩存
SmartX 於 2013 年成立,是一個以技術爲主導的公司,目前主要專一於分佈式存儲及虛擬化這個兩個領域。咱們的產品均爲本身研發,目前已經運行在數千臺物理服務器上,存儲了數十 PB 的數據。SmartX 跟國內主流的硬件服務商、雲服務商都有合做,咱們的產品已經服務了包括公有云、私有云以及金融業、製造業等核心領域的關鍵業務,其中也包括核心應用和核心數據庫等應用場景。今天我將主要圍繞分佈式塊存儲進行介紹。安全
通常來講,咱們根據存儲的訪問接口以及應用場景,把分佈式存儲分爲三種類型,包括分佈式塊存儲,分佈式文件存儲,和分佈式對象存儲。服務器
其中,分佈式塊存儲的主要應用場景包括:架構
虛擬化:好比像 KVM,VMware,XenServer 等 Hypervisor,以及像 Openstack,AWS 等雲平臺。塊存儲在其中的角色是支撐虛擬機中的虛擬盤的存儲。併發
數據庫:好比 MySQL,Oracle 等。不少 DBA 都將數據庫的數據盤運行在一個共享的塊存儲服務上,例如分佈式塊存儲。此外也有不少客戶直接把數據庫運行在虛擬機中。負載均衡
容器:容器最近幾年在企業中使用愈來愈普遍。通常來講,容器中運行的應用都是無狀態的,但在不少應用場景下,應用也會有數據持久化的需求。應用能夠選擇將數據持久化到數據庫中,也能夠選擇將數據持久化到一個共享虛擬磁盤上。這個需求對應到 Kubernetes 中,就是 Persistent Volume 這個功能。框架
今天我將主要圍繞 SmartX 如何打造分佈式塊存儲進行介紹。SmartX 從 2013 年成立開始,到目前已經積累了 5 年左右的分佈式塊存儲的研發經驗,因此今天咱們除了分享 SmartX 如何實現咱們本身研發的分佈式塊存儲 ZBS 之外,還會詳細介紹咱們在分佈式塊存儲的研發過程當中的一些思考和選擇。此外也將介紹一下咱們產品將來的規劃。運維
從普遍意義上講,分佈式存儲中一般須要解決三個問題,分別是元數據服務,數據存儲引擎,以及一致性協議。
其中,元數據服務提供的功能通常包括:集羣成員管理,數據尋址,副本分配,負載均衡,心跳,垃圾回收等等。數據存儲引擎負責解決數據在單機上存儲,以及本地磁盤的管理,磁盤故障處理等等。每個數據存儲引擎之間是隔離的,在這些隔離的存儲引擎之間,須要運行一個一致性協議,來保證對於數據的訪問能夠知足咱們指望的一致性狀態,例如強一致,弱一致,順序一致,線性一致等等。咱們根據不一樣的應用場景,選擇一個適合的一致性協議,這個協議將負責數據在不一樣的節點之間的同步工做。
有了這三部分,咱們基本上就掌握了一個分佈式存儲的核心。不一樣的分佈式存儲系統之間的區別,基本也都來自於這三個方面的選擇不一樣。
接下來我會分別從這三個方面介紹一下咱們在作 ZBS 系統設計的時候是怎樣思考的,以及最終決定採用哪一種類型的技術和實現方法。
首先咱們來介紹一下元數據服務。咱們先來談談咱們對元數據服務的需求。
所謂元數據就是『數據的數據』,好比說數據放在什麼位置,集羣中有哪些服務器,等等。若是元數據丟失了,或者元數據服務沒法正常工做,那麼整個集羣的數據都沒法被訪問了。
因爲元數據的重要性,因此對元數據的第一個需求就是可靠性。元數據必須是保存多份的,同時元數據服務還須要提供 Failover 的能力。
第二個需求就是高性能。儘管咱們能夠對 IO 路徑進行優化,使得大部分 IO 請求都不須要訪問元數據服務,但永遠都有一些 IO 請求仍是須要修改元數據,好比數據分配等等。爲避免元數據操做成爲系統性能的瓶頸,元數據操做的響應時間必須足夠短。同時因爲分佈式系統的集羣規模在不斷的擴大,對於元數據服務的併發能力也有必定的要求。
最後一個需求是輕量級。因爲咱們產品大部分使用場景是私有部署,也就是咱們的產品是部署在客戶的數據中心的,且由客戶本身運維,而非咱們的運維人員運維。這個場景和不少互聯網公司本身來運維本身的產品是徹底不一樣的場景。因此對於 ZBS 來講,咱們更強調整個系統,尤爲是元數據服務的輕量級,以及易運維的能力。咱們指望元數據服務能夠輕量級到能夠把元數據服務和數據服務混合部署在一塊兒。同時咱們但願大部分的運維操做均可以由程序自動完成,或用戶只須要在界面上進行簡單的操做就能夠完成。若是你們瞭解 HDFS 的話,HDFS 中的元數據服務的模塊叫作 Namenode,這是一個很是重量級的模塊。Namenode 須要被獨立部署在一臺物理服務器上,且對硬件的要求很是高,且很是不易於運維,不管是升級仍是主備切換,都是很是重的操做,很是容易因操做問題而引起故障。
以上就是咱們對元數據服務的需求。接下來咱們來看一下具體有哪些方法能夠構造一個元數據服務。
談到存儲數據,尤爲是存儲結構化的數據,咱們第一個想到的就是關係型數據庫,例如 MySQL,以及一些成熟的 KV 存儲引擎,例如 LevelDB,RocksDB 等。但這種類型的存儲最大的問題就是沒法提供可靠的數據保護和 Failover 能力。LevelDB 和 RocksDB 雖然很是輕量級,但都只能把數據保存在單機上。而儘管 MySQL 也提供一些主備方案,但咱們認爲 MySQL 的主備方案是一個太過笨重的方案,且缺少簡易的自動化運維方案,因此並非一個十分好的選擇。
其次,咱們來看一下一些分佈式數據庫,例如 MongoDB 和 Cassandra。這兩種分佈式數據庫均可以解決數據保護和提供 Failover 機制。可是他們都不提供 ACID 機制,因此在上層實現時會比較麻煩,須要額外的工做量。其次就是這些分佈式數據庫在運維上也相對複雜,不是很易於自動化運維。
也有一種選擇是基於 Paxos 或者 Raft 協議本身實現一個框架。但這樣實現的代價很是大,對於一個創業公司不是一個很划算的選擇。而且咱們創業的時間是 2013 年,當時 Raft 也只是剛剛提出。
第四種是選擇 Zookeeper。Zookeeper 基於 ZAB 協議,能夠提供一個穩定可靠地分佈式存儲服務。但 Zookeeper 的最大的問題是可以存儲的數據容量很是有限。爲了提升訪問速度,Zookeeper 把存儲的全部數據都緩存在內存中,因此這種方案致使元數據服務所能支撐的數據規模嚴重受限於服務器的內存容量,使得元數據服務沒法作到輕量級,也沒法和數據服務混合部署在一塊兒。
最後還有一種方式是基於 Distributed Hash Table(DHT)的方法。這種方法的好處元數據中不須要保存數據副本的位置,而是根據一致性哈希的方式計算出來,這樣就極大地下降了元數據服務的存儲壓力和訪問壓力。但使用 DHT 存在的問題,就喪失了對數據副本位置的控制權,在實際生產環境中,很是容易形成集羣中的產生數據不均衡的現象。同時在運維過程當中,若是遇到須要添加節點,移除節點,添加磁盤,移除磁盤的狀況,因爲哈希環會發生變化,一部分數據須要從新分佈,會在集羣中產生沒必要要的數據遷移,並且數據量每每很是大。而這種於運維操做在一個比較大規模的環境中幾乎天天都會發生。大規模的數據遷移很容易影響到線上的業務的性能,因此 DHT 使得運維操做變得很是麻煩。
以上介紹的方法都存在各類各樣的問題,並不能直接使用。最終 ZBS 選擇了使用 LevelDB(也能夠替換成 RocksDB) 和 Zookeeper 結合的方式,解決元數據服務的問題。首先,這兩個服務相對來講都很是輕量級;其次 LevelDB 和 Zookeeper 使用在生產中也很是穩定。
咱們採用了一種叫作 Log Replication 的機制,能夠同時發揮 LevelDB 和 Zookeeper 的優勢,同時避開他們自身的問題。
這裏咱們簡單的介紹一下 Log Replication。簡單來講,咱們能夠把數據或者狀態看做是一組對數據操做的歷史集合,而每個操做均可以經過被序列化成 Log 記錄下來。若是咱們能夠拿到全部 的 Log,並按照 Log 裏面記錄的操做重複一遍,那麼咱們就能夠完整的恢復數據的狀態。任何一個擁有 Log 的程序均可以經過重放 Log 的方式恢復數據。若是咱們對 Log 進行復制,實際上也就至關於對數據進行了複製。這就是 Log Replication 最基本的想法。
咱們具體來看一下 ZBS 是如何利用 Zookeeper + LevelDB 完成 Log Replication 操做的。首先,集羣中有不少個 Meta Server,每一個 Server 本地運行了一個 LevelDB 數據庫。Meta Server 經過 Zookeeper 進行選主,選出一個 Leader 節點對外響應元數據請求,其餘的 Meta Server 則進入Standby 狀態。
當 Leader 節點接收到元數據的更新操做後,會將這個操做序列化成一組操做日誌,並將這組日誌寫入 Zookeeper。因爲 Zookeeper 是多副本的,因此一旦 Log 數據寫入 Zookeeper,也就意味着 Log 數據是安全的了。同時這個過程也完成了對 Log 的複製。
當日志提交成功後,Meta Server 就能夠將對元數據的修改同時提交到本地的 LevelDB 中。這裏 LevelDB 中存儲的是一份全量的數據,而不須要以 Log 的形式存儲。
對於非 Leader 的 Meta Server 節點,會異步的從 Zookeeper 中拉取 Log,並將經過反序列化,將 Log 轉換成對元數據的操做,再將這些修改操做提交到本地的 LevelDB 中。這樣就能保證每個 Meta Server 均可以保存一個完整的元數據。
前面提到,因爲 Zookeeper 存儲數據的容量受限於內存容量。爲了不 Zookeeper 消耗過多內存,咱們對 Zookeeper 中的 Log 按期執行清理。只要 Log 已經被全部的 Meta Server 同步完, Zookeeper 中保存的 Log 就能夠被刪除了,以節省空間。一般咱們在 Zookeeper 上只保存 1GB 的 Log,已經足夠支撐元數據服務。
Failover 的邏輯也很是簡單。若是 Leader 節點發生故障,其餘還存活的的 Meta Server 經過 Zookeeper 再從新進行一次選主,選出一個新的 Meta Leader。這個新的 Leader 將首先從 Zookeeper 上同步全部還未消耗的日誌,並在提交到本地的 LevelDB 中,而後就能夠對外提供元數據服務了。
如今咱們總結一下 ZBS 中元數據服務實現的特色。
首先,這個原理很是容易理解,並且實現起來很是簡單。由 Zookeeper 負責選主和 Log Replication,由 LevelDB 負責本地元數據的存儲。背後的邏輯就是儘量的將邏輯進行拆分,並儘量的複用已有項目的實現。
其次,速度足夠快。Zookeeper 和 LevelDB 自己的性能都不錯,並且在生產中,咱們將 Zookeeper 和 LevelDB 運行在 SSD 上。在實際測試中,對於單次元數據的修改都是在毫秒級完成。在併發的場景下,咱們能夠對元數據修改的日誌作 Batch,以提升併發能力。
此外,這種方式支持 Failover,並且 Failover 的速度也很是快。Failover 的時間就是選主再加上 Log 同步的時間,能夠作到秒級恢復元數據服務。
最後說一下部署。在線上部署的時候,咱們一般部署 3 個或 5 個 Zookeeper 服務的實例以及至少 3 個 Meta Server 服務的實例,以知足元數據可靠性的要求。元數據服務對資源消耗都很是小,能夠作到和其餘服務混合部署。
以上是一些基本的原理,咱們再來看一下 ZBS 內部的對於元數據服務的具體實現。
咱們將上述的 Log Replication 邏輯封裝在了一個 Log Replication Engine 中,其中包含了選主、向 Zookeeper 提交 Log、向 LevelDB 同步數據等操做,進一步簡化開發複雜度。
在 Log Replication Engine 的基礎之上,咱們實現了整個 Meta Sever 的邏輯,其中包含了 Chunk Manager,NFS Manger,iSCSI Manager,Extent Manager 等等不少管理模塊,他們均可以經過 Log Replication Engine,管理特定部分的元數據。RPC 模塊是 Meta Server 對外暴露的接口,負責接收外部的命令,並轉發給對應的 Manager。例如建立/刪除文件,建立/刪除虛擬卷等等。此外,Meta Server 中還包含了一個很是複雜的調度器模塊,裏面包含了各類複雜的分配策略,恢復策略,負載均衡策略,以及心跳,垃圾回收等功能。
以上就是關於 SmartX 超融合系統中 SMTX 分佈式塊存儲元數據服務部分的介紹。
瞭解更多信息可訪問 SmartX 官網:https://www.smartx.com