Cloud + TiDB 技術解讀

做者:鄧栓
來源:細說雲計算算法

做爲一款定位在 Cloud-native 的數據庫,現現在 TiDB 在雲整合上已取得了階段性的進展。日前 Cloud TiDB 產品在 UCloud 平臺正式開啓公測,TiDB 彈性伸縮的特性在 Cloud 提供的基礎設施支持下發揮的淋漓盡致。在感覺雲數據庫魅力的同時,讓咱們來一探究竟,看一下 TiDB 與 Cloud 背後的技術祕密。數據庫

TiDB 的架構

首先仍是要先從 TiDB 的架構提及,TiDB 和傳統的單機關係型數據庫有什麼不一樣?相信長期以來一直關注 TiDB 的同窗都比較瞭解了,但這裏仍是科普一下。TiDB 做爲一個開源的分佈式數據庫產品,具備多副本強一致性的同時可以根據業務需求很是方便的進行彈性伸縮,而且擴縮容期間對上層業務無感知。TiDB 的主體架構包含三個模塊,對應 GitHub 上面 PingCAP 組織下的三個開源項目,TiDB / TiKV / PD:後端

  • TiDB 主要是負責 SQL 的解析器和優化器,它至關於計算執行層,同時也負責客戶端接入和交互;安全

  • TiKV 是一套分佈式的 Key-Value 存儲引擎,它承擔整個數據庫的存儲層,數據的水平擴展和多副本高可用特性都是在這一層實現;網絡

  • PD 至關於分佈式數據庫的大腦,一方面負責收集和維護數據在各個 TiKV 節點的分佈狀況,另外一方面 PD 承擔調度器的角色,根據數據分佈情況以及各個存儲節點的負載來採起合適的調度策略,維持整個系統的平衡與穩定。架構

上面的這三個模塊,每一個角色都是一個多節點組成的集羣,因此最終 TiDB 的架構看起來是這樣的。負載均衡

架構圖.png

因而可知,分佈式系統自己的複雜性致使手工部署和運維的成本是比較高的,而且容易出錯。傳統的自動化部署運維工具如 Puppet / Chef / SaltStack / Ansible 等,因爲缺少狀態管理,在節點出現問題時不能及時自動完成故障轉移,須要運維人員人工干預。有些則須要寫大量的 DSL 甚至與 Shell 腳本一塊兒混合使用,可移植性較差,維護成本比較高。less

TiDB 與 Kubernetes 的整合歷程

在雲時代,容器成爲應用分發部署的基本單位,而谷歌基於內部使用數十年的容器編排系統 Borg 經驗推出的開源容器編排系統 Kubernetes 成爲當前容器編排技術的主流。做爲 Cloud Native Database,TiDB 選擇擁抱容器技術,並與 Kubernetes 進行深度整合,使其能夠很是方便地基於 Kubernetes 完成數據庫的自動化管理。運維

Kubernetes 項目能夠說是爲 Cloud 而生,利用雲平臺的 IaaS 層提供的 API 能夠很方便的和雲進行整合。這樣咱們要作的事情就很明確了,只要讓 TiDB 與 Kubernetes 結合的更好,進而就實現了和各個雲平臺的整合, 使得 TiDB 在雲上的快速部署和高效運維成爲現實。分佈式

Kubernetes 最先是做爲一個純粹的容器編排系統而誕生的,用戶部署好 Kubernetes 集羣以後,直接使用其內置的各類功能部署應用服務。因爲這個 PaaS 平臺使用起來很是便利,吸引了不少用戶,不一樣用戶也提出了各類不一樣的需求,有些特性需求 Kubernetes 直接在其核心代碼裏面實現了,可是有些特性並不適合合併到主幹分支,爲知足這類需求,Kubernetes 開放出一些 API 供用戶本身擴展,實現本身的需求。當前 Kubernetes 已經發展到 v1.8,其內部的 API 變得愈來愈開放,使其更像是一個跑在雲上的操做系統。用戶能夠把它看成一套雲的 SDK 或 Framework 來使用,並且能夠很方便地開發組件來擴展知足本身的業務需求。對有狀態服務的支持就是一個頗有表明性的例子。

Kubernetes 項目最先期只支持無狀態服務 (Stateless Service) 來管理的,無狀態服務經過 ReplicationController 定義多個副本,由 Kubernetes 調度器來決定在不一樣節點上啓動多個 Pod,實現負載均衡和故障轉移。對於無狀態服務,多個副本對應的 Pod 是等價的,因此在節點出現故障時,在新節點上啓動一個 Pod 與失效的 Pod 是等價的,不會涉及狀態遷移問題,於是管理很是簡單。可是對於有狀態服務 (Stateful Service),因爲須要將數據持久化到磁盤,使得不一樣 Pod 之間不能再認爲成等價,也就不能再像無狀態服務那樣隨意進行調度遷移。Kubernetes v1.3 版本提出 PetSet 的概念用來管理有狀態服務並於 v1.5 將其改名爲 StatefulSet。StatefulSet 明肯定義一組 Pod 中每一個的身份,啓動和升級都按特定順序來操做。另外使用持久化卷存儲 (PersistentVolume) 來做爲存儲數據的載體,當節點失效 Pod 須要遷移時,對應的 PV 也會從新掛載,而 PV 的底層依託於分佈式文件系統,因此 Pod 仍然能訪問到以前的數據。同時 Pod 在發生遷移時,其網絡身份例如 IP 地址是會發生變化的,不少分佈式系統不能接受這種狀況。因此 StatefulSet 在遷移 Pod 時能夠經過綁定域名的方式來保證 Pod 在集羣中網絡身份不發生變化。

然而現實中一些分佈式系統更爲複雜,StatefulSet 也顯得捉襟見肘。舉例來講,某些分佈式系統的節點在加入集羣或下線時還須要作些額外的註冊和清理操做,或者滾動升級要考量版本兼容性等。基於這個緣由 CoreOS 公司提出了 Operator 概念,並實現了 etcd-operator 和 prometheus-operator 來管理 Etcd 和 Prometheus 這樣的複雜分佈式系統。用戶能夠開發本身的 Operator,在 Kubernetes 之上實現自定義的 Controller,將有狀態服務的領域特定的運維知識編碼進去,從而實現對特定分佈式系統的管理。同時 Operator 自己也是跑在 Kubernetes 中的一組 Pod(deployment),對 Kubernetes 系統並沒有侵入性。

TiDB 系列組件及其做用

針對 TiDB 這種複雜的分佈式服務,咱們開發了 tidb-operator 等一系列組件,來管理 TiDB 集羣實例在 Kubernetes 平臺上的建立、銷燬、擴縮容、滾動升級和故障轉移等運維操做。同時在上層封裝一個 tidb-cloud-manager 組件,提供 RESTful 接口,實現與雲平臺的控制檯打通。這樣也就實現了一個 DBaaS (數據庫即服務)架構的基本形態。

因爲 TiDB 對磁盤 I/O 有比較高的要求,經過 PV 掛載網絡盤性能上會有明顯的性能損耗。另外 TiKV 自己維護了數據多副本,這點和分佈式文件系統的多副本是有重複的。因此咱們要給 Pod 上掛載本地磁盤,而且在 Kubernetes 上面把 Local PV 管理起來,做爲一種特定的資源來維護。Kubernetes 長期以來官方一直沒有提供 Local PV 支持,本地存儲只支持 hostPath 和 emptyDir 兩種方式。其中 hostPath 的生命週期是脫離 Kubernetes 管理的,使用 hostPath 的 Pod 銷燬後,裏面的數據是不會被自動清理,下次再掛載 Pod 就會形成髒數據。而 emptyDir 更像一個臨時磁盤,在 Pod 重建時會被清理重置,不能成爲持久化 PV 來使用。爲此咱們開發了一個 tidb-volume-manager 組件,用於管理 Kubernetes 集羣中每臺物理主機上的本地磁盤,而且將其暴露成一種特殊的 PV 資源。結合 Operator 在部署 TiDB 節點時會參考 Local PV 資源的狀況來選擇特定的節點來部署,分配一個空的 Local PV 和 Pod 綁定。而當 Pod 銷燬時候會根據具體狀況來決定是否結束 Local PV 的生命週期,釋放掉的 Local PV 再經歷一個 gc 週期後,被 tidb-volume-manager 回收,清理其盤上數據等待再次被分配使用。

2.png

將這些組件整合起來,就造成了上圖描述了 Cloud TiDB 的整體架構,在 Kubenetes 管理的集羣之上經過 tidb-operator 等組件來針對性的調配和使用集羣資源,從而實現 TiDB 集羣實例的生命週期管理。經過這種方式,來實現 TiDB 分佈式數據庫和雲平臺的整合。接下來,咱們再針對 Cloud TiDB 的關鍵特性和實現細節分別進行解讀。

自動化運維

數據庫產品上雲的一個先決條件是能實現自動化的運維管理,不然在雲上靠手工運維幾乎是不現實的。咱們首先用 Kubernetes 將雲平臺的主機資源管理起來,組成一個大的資源池。而後再經過 tidb-opeartor 及 tidb-cloud-manager 等組件來自動化完成 TiDB 實例的一鍵部署、擴容縮容、在線滾動升級、自動故障轉移等運維操做。

  • 首先拿集羣建立來講。前面提到過,TiDB 包含三大核心組件:TiDB / TiKV / PD,每一個服務又都是一個多節點的分佈式結構。服務和服務之間的啓動順序也存在依賴關係。此外,PD 節點的建立和加入集羣方式和 etcd 相似,是須要先建立一個單節點的 initial 集羣,後面加入的節點須要用特殊的 join 方式,啓動命令上都有差異。有一些操做完成後還須要調用 API 進行通知。Kubernetes 自身提供的 StatefulSet 是很難應付這種複雜的部署,因此須要 tidb-operator 中實現特定的 Controller 來完成這樣一系列的操做。而且結合 Kubernetese 強大的調度功能,合理的規劃和分配整個集羣資源,儘可能讓新部署的 TiDB 實例節點在集羣中均勻分佈,最終經過 LB 暴露給對應的租戶使用。

  • 在線升級也是相似。因爲 TiKV / PD 的 Pod 掛載的是本地存儲,並不能像雲平臺提供的塊存儲或網絡文件系統那樣能夠隨意掛載。若是 TiKV / PD 遷移到其它節點,至關於數據目錄也被清空,因此必須保證 TiKV / PD 的 Pod 在升級完成後仍然可以調度在原地,這也是要由 tidb-operator 的 Controller 來保證。TiDB 的數據副本之間由 Raft 算法來保證一致性,所以當集羣中某一個節點暫時斷開能夠不影響整個服務的。因此在集羣升級的過程當中,必須嚴格按照服務的依賴關係,再依次對 Pod 進行升級。

  • 當節點出現故障時,一樣是因爲掛載本地數據盤的緣由,也不能像 StatefulSet 那樣直接把 Pod 遷移走。當 TiDB Operator 檢測到節點失效,首先要等必定的時間確認節點不會再恢復了,開始遷移恢復的操做。首先調度選擇一個新節點啓動一個 Pod, 而後通知 TiDB 將失效的節點放棄掉,並將新啓的 Pod 加入集羣。後面會由 TiDB 的 PD 模塊來完成數據副本數的恢復,以及數據往新節點上進行搬移,從而從新維持集羣內數據平衡。

以上只是列舉了 TiDB 幾種典型的運維操做流程,實際生產上運維還有不少 case 須要考慮,這些都以程序的方式實如今 tidb-operator 裏面。藉助 Kubernetes 和 tidb-operator 來代替人工,高效的完成 TiDB 數據庫在雲平臺上的複雜運維管理。

動態擴縮容

彈性水平伸縮是 TiDB 數據庫最主要的特性之一。在大數據時代,人們對數據存儲的需求在快速膨脹。有時候用戶很難預估本身的業務規模的增加速度,若是採用傳統的存儲方案,可能很快發現存儲容量達到了瓶頸,而後不得不停機來作遷移和完成擴容。若是使用 Cloud TiDB 的方案,這個過程就很是簡單,只須要在 Cloud 控制檯上修改一下 TiDB 的節點數量,很快就能完成擴容操做,期間還不會影響業務的正常服務。

那麼在 Cloud 後臺,一樣藉助 Kubernetes 和 tidb-operator 的能力來完成 TiDB 增減節點操做。Kubernetes 自己的運做是基於一種 Reconcile 的機制。簡單來講當用戶提交一個新的請求,好比指望集羣裏面跑 5 個 TiKV 節點,而目前正在跑的只有 3 個,那麼 Reconcile 機制就會發現這個差別,首先由 Kubernetes 的調度器根據集羣總體資源狀況,並結合 TiDB 節點分配的親和性原則和資源隔離原則來分配節點。另外很重要一點就是選擇有空閒 Local PV 的機器來建立 Pod 並進行掛載。最終經過 tidb-operator 將 2 個節點加入 TiDB 集羣。

對於縮容的過程也是相似。假如數據庫存儲的總數據量變少,須要減小節點以節省成本。首先用戶經過雲控制檯向後端提交請求,在一個 Reconciling 週期內發現差別,tidb-operator 的 Controller 開始通知 TiDB 集羣執行節點下線的操做。安全下線多是個比較長的過程,由於期間須要由 PD 模塊將下線節點的數據搬移到其餘節點,期間集羣均可以正常服務。當下線完成,這些 TiKV 變成 tombstone 狀態。而 tidb-operator 也會通知 Kubernetes 銷燬這些 Pod,而且由 tidb-volume-manager 來回收 Local PV。

資源隔離

資源隔離也是雲上用戶關心的一個問題。尤爲是數據庫這類應用,不一樣租戶的數據庫實例,甚至一個租戶的多套數據庫實例,都跑在一套大的 Kubernetes 管理的集羣上,相互間會不會有資源的爭搶問題,某個實例執行高負載的計算任務時,CPU、內存、I/O 等會不會對同臺機器上部署的其餘實例產生影響。其實容器自己就是資源隔離的一個解決方案,容器的底層是 Linux 內核提供的 cgroups 技術,用於限制容器內的 CPU、內存以及 IO 等資源的使用,並經過 namespace 技術實現隔離。而 Kubernetes 做爲容器編排系統,可以根據集羣中各個節點的資源情況,選擇最優的策略來調度容器。同時 tidb-operator 會根據 TiDB 自身的特性和約束,來綜合決策 TiDB 節點的調度分配。舉例來講,當一個 Kubernetes 集羣橫跨多個可用區,用戶申請建立一個 TiDB 集羣,那麼首先根據高可用性原則,將存儲節點儘可能分配到不一樣的可用區,並給 TiKV 打上 label。那麼同一個可用區內也儘可能不把多個 TiKV 部署到相同的物理節點上,以保證集羣資源最大化利用。此外,每一個 Local PV 也是一塊獨立的磁盤,每一個 TiKV 的 Pod 分別掛載不一樣的盤,因此 I/O 上也是徹底隔離的。Kubernetes 還能夠配置 Pod 之間的親和性(affinity)和反親和性(anti-affinity),例如 TiKV 和 TiDB 之間咱們能夠經過親和性使其調度到網絡延時較小的節點之上,提升網絡傳輸效率,TiKV 之間藉助反親和性,使其分散部署到不一樣的主機、機架和可用區上,下降因硬件或機房故障形成的丟數據的風險。

上面解釋了容器層面的隔離,能夠看做是物理層面的隔離。那麼數據層面的隔離,TiDB 的調度體系也是有所考慮的。好比一個大的 TiDB 集羣,節點分佈在不少臺主機,跨越多個機架、可用區。那麼用戶能夠定義 Namespace,這是一個邏輯概念,不一樣業務的數據庫和表放置在不一樣的 Namespace。再經過配置 Namespace 和 TiKV 節點以及區域的對應關係,由 PD 模塊來進行調度,從而實現不一樣業務的數據在物理上的隔離。

高可用性

TiDB 做爲一個分佈式數據庫自己就具備高可用性,每一個核心組件均可以獨立的擴縮容,任意一個模塊在部署多份副本時若是有一個掛掉,總體仍然能夠正常對外提供服務,這是由 Raft 協議保證的。可是若是對數據庫節點的調度不加任何限制,包含一份數據的多個副本的節點可能會被調度到同一臺主機。這時若是主機發生故障,就會同時失去多個副本,一個 Raft 分組內在失去多數派節點就會使整個集羣處於不可用的狀態。所以 tidb-operator 在調度 TiKV 節點時須要避免出現這種狀況。

另外 TiDB 支持基於 label 的數據調度的,給不一樣的 TiKV 實例加上描述物理信息的 label,例如地域(Region)、可用區(AZ)、機架(Rack)、主機(Host),這樣 PD 在對數據進行調度時就會參考這些信息更加智能的制定調度策略,盡最大可能保證數據的可用性。例如 PD 會基於 label 信息儘可能把相同數據的副本分散調度到不一樣的主機、機架、可用區、地域上,這樣在物理節點掛掉或機架掉電或機房出故障時,其它地方仍然有該數據足夠的副本數。藉助 tidb-operator 中 controller-manager 組件咱們能夠自動給 TiKV 實例加上物理拓撲位置標籤,充分發揮 PD 對數據的智能調度能力,實現數據層面的高可用性。

同時咱們還能夠實現實例級別的高可用性,經過 Kubernetes 強大的調度規則和咱們擴展的調度器,咱們按優先級會盡可能選擇讓 TiKV 部署到不一樣的主機、機架和可用區上,把因主機、機架、機房出問題形成的影響降到最低,使數據具備最大的高可用性。
另外運行在 Kubernetes 之上咱們能實時監測到 TiDB 各組件的運行狀況,當出現問題時,咱們也能第一時間讓 tidb-operator 對集羣進行自動修復 (self-healing)。具體表現爲 TiDB / TiKV / PD 實例出現故障時,執行安全的下線操做。同時增長新的實例,來保證集羣的規模和以前一致。

總結

TiDB 做爲一款 Cloud Native Database,經過 tidb-operator 的方式充分發揮 Kubernetes 平臺的強大能力,實現雲上自動化管理,極大下降人力運維成本。用戶能夠根據業務須要進行動態擴容縮容,多租戶隔離特性讓不一樣租戶的實例能夠共享計算和存儲資源,互不干擾,同時最大程度充分使用雲上資源。Raft 算法和 tidb-operator 自動修復能力以及兩層調度機制保證了 Cloud TiDB 的高可用性。UCloud 和 PingCAP 公司深度合做,推出 Cloud TiDB 產品現已開啓公測,歡迎你們來體驗雲時代的新一代數據庫。

相關文章
相關標籤/搜索