黃東旭:When TiDB Meets Kubernetes

本文是我司 CTO 黃東旭同窗在 DTCC2017 上的《When TiDB Meets Kubernetes》演講實錄,主要分享了關於 TiDB 與 Kubernetes 整合的一些工做。文章較長,且乾貨滿滿。
node

640.jpg-26kB

如下爲演講實錄:

今天給你們帶來的分享是關於 TiDB 與 Kubernetes 整合的一些工做。在講以前,想了解一下,在場的各位有據說過 Kubernetes 的同窗舉個手;據說過 TiDB 的舉個手,很是好。由於我也是 DTCC 的常客了,前幾年來說的時候基本上沒有人據說過 TiDB ,隨着這個項目愈來愈成熟,很欣慰,有一些社區的小夥伴已經用起來了。算法

我先簡單介紹一下我本身。我是 PingCAP 的聯合創始人兼 CTO ,也是 TiDB 項目的碼農 ,以前一直也是在作 Infrastructure 和分佈式系統相關的一些工做。同時也是特別喜歡開源,基本上作的全部東西都是開源,包括像 Codis 、TiDB 、 TiKV 這些項目。比較喜歡的編程語言有 GO 、Rust 、Python。數據庫

對於今天的話題,若是說在 40 分鐘以內要去完整的介紹怎麼作一個數據庫或者說怎麼去作一個集成調度的系統,我以爲是不太可能的。編程

因此今天這個 Talk 我主要會分享 TiDB 做爲一個分佈式關係型數據庫與雲整合的經驗,以及咱們遇到的一些問題,來幫你們在遇到類似的問題時提供一個解決思路。你們其實在存儲系統上能夠把 TiDB 換成任何東西,而後對於在 Kubernetes 上的融合,我以爲都會有一些啓發的意義。緩存

首先想強調的是,Cloud 纔是將來。現場作一個小調研:有在線上生產環境中,正使用 Kubernetes 或者 Mesos 這樣的容器化管理方案的同窗嗎?好,一個兩個三個。安全

我相信,若是在三到五年之後,再去問這個問題,應該是至少一半的同窗會舉手。由於其實在可見的將來,數據量是一直在膨脹,業務會愈來愈複雜。包括如今不少微服務的這種思想,當你的業務比較大的時候,會把整個服務拆成很是細的模塊,而後在衆多的模塊的拆分之下,怎麼去高效的運維你的分佈式集羣,其實靠 SRE 或者運維人員手動去管理各個微服務或者說各個的系統,實際上是不太現實的。網絡

可是你們也都知道,對於這種無狀態的業務,好比像 Application 應用層,其實它並不會真正的存儲數據,狀態通常都持久化到數據庫或者緩存中,因此它基本是無狀態的。因此其實一直以來你們在使用容器化遇到的第一個問題就是,有狀態的服務,特別是數據庫或者分佈式存儲系統,怎麼去運維。好比說我在 Docker 裏面寫的數據,這個容器銷燬它直接就掛了,它實際上是很難去作這種數據層面上的東西。架構

而後另一方面就是數據庫的運維,無論是放在雲上作仍是 DBA 本身在物理機上作,同樣的痛苦,因此怎樣去設計一個面向雲的環境下、或者說在分佈式系統上去作數據庫。其實這也是在作 TiDB 的過程當中我一直在想的。TiDB 這個項目一開始的設計就是:它必定會放在雲上去運轉。
app

640-1.jpg-36.3kB

上圖是 Amazon 在它的雲上選擇建立一個數據庫實例的一個界面,你們不用看具體的字是什麼,只是給你們感覺下:就是說我做爲一個業務的開發,我要去存儲一個數據,啓用一個 PG 或者 MySQL 的數據庫,我還要去關心這麼多個選項,或者說我必定要去關心個人這個物理機究竟是什麼樣的狀況:磁盤有多大,什麼機型,各式各樣的配置等。由於畢竟不是全部人都是專業的 DBA ,也不是全部人都是操做系統的專家,當時看到這個頁面的時候,基本上業務開發多是一臉懵逼的狀態。less

如今全部人都跟你說,個人系統是一個分佈式系統,在廣告裏寫的很是漂亮。如今哪個數據庫說本身不是分佈式的,那基本上只能是落後於時代。可是,你們有沒有想過,如今全部的這些分佈式系統、分佈式數據庫,運維起來都是很是痛苦,沒有辦法去很好的把它用好。

以 Hadoop 爲例,如今 setup 一個 Hadoop 的集羣居然能成爲一個生意,這個生意還讓兩個創業公司都上市了,一個是 Cloudera,一個是 Hortonworks。其實嚴格來講,他們只是作 Hadoop 運維的創業公司。而後還有無數的公司在作 Spark 的維護、Spark 的管理。各類各樣的數據庫運維公司,靠這個都過的很是好。

這個其實在我看來是很是不正常的,由於你們想,若是你去運維一兩臺機器那沒有問題,我寫一個腳本,輕輕鬆鬆的就能夠搞定;而後三五十臺機器,也還行,招一個 OPs 或者說招一個 DBA ,仍是能夠人工的管理。

可是若是是在 100 臺、1000 臺甚至 10000 臺規模之上,機器的故障會是天天每夜無時無刻都發生的,網絡的抖動,磁盤 IO 的異常,一直都在發生,靠人是沒有辦法作的。總的來講就是,當你去運維一個 single node 的系統時,基本上沒有什麼難度;可是若是要去運維一個特別大的 P2P 的 distributed system,尤爲是節點數特別多的時候,你的狀態和維護的成本就變得很是高。

以前我作過一個項目叫 Codis ,可能有不少同窗據說過,也可能不少同窗已經用在生產環境之中。還有另一個不是我作的項目,就是官方的 Redis Cluster。當時不少社區裏面的 Redis Cluster 的粉絲一直噴我,說 Codis 的配置怎麼這麼複雜,一點都很差用,組件怎麼這麼多。如今這個事情又在重演。

不少系統作成了 P2P 的模型了之後,組件不多部署很方便,可是真正在去運維它的時候,好比說要去作一個滾動升級或者我想清楚的知道整個集羣的數據分佈和各個組件的狀態,又或者說是個人分佈式邏輯出了個bug,可是個人存儲層沒事,須要作個熱更新這個時候,p2p 系統的運維複雜度就凸顯了。Codis 它其實有一個 Proxy,一個存儲層,這兩層在邏輯上實際上是分離的;可是 Redis Cluster ,每個節點既是它的分佈式調度模塊,同時又是它的數據存儲模塊,這時候整個系統架構是混在一塊兒的,對於運維的同窗來講這就是一個惡夢。

若是我想清楚的知道個人數據究竟是在哪幾臺機器上,好比說這塊數據特別熱我想把它挪走,這時候像在 Redis Cluster 這種純 P2P 的系統裏面是很可貴到它的當前狀態。因此這也是影響了我後來一系列的系統設計的想法,因此 operation 是一個很是困難的事情,在一個特別大的分佈式系統裏,是一個很是困難的事情。

由於你的服務和組件特別多,不一樣的組件,不一樣的模塊,而後再加上一個分佈式系統裏邊特別不穩定的網絡狀態,使得各類各樣的異常狀況,人是沒有辦法去掌控的。

還好,Google 是一個很是偉大的公司,像 TiDB 整個模型你們也知道是參考了Google Spanner/F1。Kubernetes 背後的系統的前身就是 Google 的 Borg。

Borg 實際上是 Google 內部一直在用着的大規模的集羣調度器。Borg 這個單詞就是星際迷航裏面的一個角色,至關於它做爲整個集羣的一個大腦來去控制集羣的業務的分佈跟數據的均衡,因此 Google 給咱們帶來了 Kubernetes 這個項目。

Kubernetes 主要的工做就是一個面向 Container 的集羣管理的服務。它同時會去作服務編排,Auto deployment、 Auto scaling、Auto healing ,你的整個集羣的這些服務的生命週期的管理,而後故障的轉移、擴容...你能夠認爲它是一個集羣的操做系統。你們可能認爲操做系統就是單機上的一個概念,可是若是放到一個大規模的分佈式系統裏面,你有無數的 CPU 資源,無數的內存,無數的磁盤資源,怎麼高效的去把你的服務在這些海量的資源上進行合理的分配,這個就是 Kubernetes 乾的事情。

TiDB 你們也都很是熟悉了,我簡單介紹一下吧。

咱們作 TiDB 的目標就是,但願構建一個徹底彈性的,用戶不須要去知道數據的分佈信息,也不須要去作手工的數據分片,能夠把它當作一個單機的數據庫、 MySQL 的數據庫在用,可是它背後是一個高度彈性和智能的分佈式的數據庫。對業務層你不須要再去想分庫分表,也不須要再去想熱點的 balance 這種事情。它支持百分之百的 OLTP 的功能,能夠支持跨行事務,像 MySQL 同樣:我開始一個 transaction,而後寫寫寫,最後 commit,全成功或者全失敗,沒有第三種可能 。支持事務的前提之下還支持 80% 的 OLAP 。

因此 TiDB 是很是適合去作這種一邊有實時寫入,一邊有複雜 Join 和實時分析的場景,它的 SQL 優化器其實也有從 Spark SQL 裏面學到了不少東西。

固然對外的接口是完整的 MySQL 的接口,你能夠直接用 MySQL 的客戶端就連上了。另外,背後它支持高可用。由於底層的複製協議並非經過像這種主從模型去作數據冗餘的,而是用 Raft,Raft 跟 multi-paxos 是比較接近的,都是基於選舉的算法。

在遇到 MySQL 的擴展性問題的時候,你們過去只能一臉懵逼,而後反回來去拆庫拆表,或者去用 MyCat 或者依託 MySQL 的中間件去作 sharding 。其實這對於業務層來講,侵入性很是大,因此 TiDB 的初衷就是解決這個問題,可是它沒有用任何一行 MySQL 的代碼。

TiDB 大概是這樣一個架構:

640-2.jpg-15.8kB

TiDB 其實也是由不少的組件組成,它並非一個純粹的 P2P 系統,若是看這個架構其實很是像 Codis 。

今天的主題是說咱們怎麼在 Kubernetes 上去作 cluster 的 setup、rolling update,怎麼去解決 Kubernetes 對於本地磁盤存儲的綁定問題。

因此面臨着一個與你們以前在 Kubernetes 上去部署帶狀態的服務很是接近的問題。由於 TiDB 自己是一個帶狀態的數據庫,數據庫沒有狀態那不可能。Kubernetes 的調度實際上是對這種 stateless applications 很是友好的,可是若是你是一個帶狀態的,好比像 MySQL、PG、TiDB,或者 Etcd、Zookeeper 等等,怎麼去作?真正的困難並非 Kubernetes 作不了,而是每個不一樣的系統都有本身的數據分佈模型,同時每個不一樣的系統它的運維方式也不太同樣。因此做爲 Kubernetes 的平臺來講,沒有辦法去針對每個不一樣的存儲系統去設計一套本身的調度策略。這是 Kubernetes 沒有辦法去作的一個事情。

舉個例子,好比說你想去運維好一個 Redis Cluster ,那你必須得了解 Redis Cluster 一些原理,還有必須得去知道它怎麼運維;若是你想要去運維 Codis ,你必須得知道 Codis 的一些原理方法才能把它運維好。但這些領域知識怎麼去告訴 Kubernetes 說你幫我按照這個方法來去運維這個系統?

這時候有一個公司,叫作 CoreOS ,相信你們可能也都熟悉,就是 Etcd 背後的那個公司,也是咱們 PingCAP 的好夥伴。CoreOS 也是社區裏面最大的 Kubernetes 的運營的公司,他們引入了一個新的 Kubernetes 的組件,叫作 Operator 。Operator的意義在於它實際上是至關於使用了 Kubernetes 的 TPR(third party resources)的 API,去把你的系統運維的一些領域知識,封裝到 Operator 裏面,而後把 Operator 這個模塊注入到 Kubernetes 上面,整個這些集羣是經過 Operator 這個模塊來去作調度。

CoreOS 官方還提供了一個 Etcd 的 Operator 的實現。其實這思路也很簡單,就是說把這個集羣的建立滾動更新,而後各類運維的一些領域知識放到這個 Operator 裏面,而後在 Operator 裏面去調用 Kubernetes 原生的 API,來作集羣的管理,至關因而 Kubernetes 的一個 Hook 。

通常來講一個 Operator 它其實有這樣一些對外暴露的接口或者是能力。它作的事情的就是:好比我想要讓 Kubernetes 去創建一個 TiDB 的集羣,好比說 deployment;好比我加入新的物理節點之後,我要想對現有的這個集羣作擴容,而後 rebalance;好比說個人集羣的 TiDB 自己的這些 binary須要升級,我要去作業務透明的滾動更新,好比說我有 100 個節點,要在上面去作升級,不可能手動去作,這個其實都是封裝在咱們的 Operator 裏面,而後包括自動化的 backup 跟 restore 這些功能。

本質上來講你能夠認爲 Operator 是一個 Kubernetes 的批處理方案。我剛纔也簡單提到了一下,Kubernetes 能夠做爲一個集羣的操做系統,可是這個操做系統總應該能讓運維去寫腳本的,這個腳本就是 Operator 機制。

其實它的原理很簡單,就是它注入到 Kubernetes 裏面,會實時不停的去觀察集羣的狀態,去 Hook Kubernetes 的一些集羣的狀態,一些 API,而後獲得整個集羣的狀態。把一些分析的東西放在 Operator 裏面,它可能會有一些地方被觸發,好比說我該擴容了或者我該去作 Failover ,而後去反饋。

而後 TiDB 的 Operator 目前來講有這麼幾個功能:建立集羣、滾動更新、Scale out, Failover、Backup/Restore。

由於其實今天的這個話題是說怎麼去跟雲作結合。咱們如今在跟一些公有云的提供方在作合做。可是不可能說每個公有云都本身去接入它的資源管理的 API,由於每一個公有云可能都用的是不同的 API database,因此咱們至關於作的一個方案就是說,無論你是公有云也好仍是私有云也好,你給我一堆物理的機器,而後在這一堆物理的機器上面去部署 Kubernetes ,在這個 Kubernetes 上面,我至關於把個人 TiDB Operator 給放進去,當某個公有云客戶要它去建立一個集羣的時候,會通知 Operator 去建立,好比說劃出一些機器,去作物理隔離。

這在私有云裏邊也是一個比較常見的場景了。用戶他其實想要去作這種業務之間的租戶隔離,TiDB Operator 是作一個比較簡單的物理隔離。

可是作這個 Operator 最難的一個部分其實剛纔也簡單講了一下,就是存儲的問題。若是你們關注 Kubernetes 社區的話,通常都會注意到 persistend local storage 的這個方案一直在社區裏邊扯皮和吵架。如今你們認爲 Kubernetes 本地的磁盤是無法用的,或者說沒有辦法直接當作一個資源來使用的。

640-3.jpg-29.5kB

上圖是放在 Kubernetes 的 issues 裏面的一個問題,就是 persistent local storage ,這個看上去很是難以想象,這麼簡單的功能爲何一直到如今沒有支持。

我我的感受 Google 之因此遲遲不去作這個功能,它背後有一個緣由多是在 Google 內部,它的網絡存儲是很是強的,它有本身很是好的網絡設備。你在同一個數據中內心,去換一塊網絡盤,它的這個 latency 基本上不少業務能夠接受的,因此這樣的話,持久化存儲的問題基本上是靠網絡的磁盤來解決。

想像你跑一個 MySQL 的業務,MySQL 的業務自己它寫入的磁盤並非你的物理機的本地盤,而是一塊網絡盤,這個網絡盤我能給你保證它的 IOPS 跟 latency 都是很是好的狀態,這個時候你的業務掛掉了,我再從新啓一個容器把這個網絡盤再掛到那個 pod 的後邊,這時候你的業務是幾乎無感知的。這也是 Google 比較推崇的使用存儲的一個模式,因此這就是 Kubernetes 背後的那個 persistent volumes 的這個方案。

它如今是有這個方案的,可是對於像咱們這樣的分佈式數據庫或者說對這種本地磁盤有特別強要求的( TiDB 底層的存儲引擎對單機的 SSD 作了很是多的優化),並無辦法去容忍我底下寫入的一個磁盤是網絡盤。由於自己好比說 TiDB 這一層,已經作了三個副本,甚至五個副本的複製,可是在底下網絡盤又要去作這個複製實際上是沒有太多必要的,因此 Google 一直遲遲沒有推 Local Storage Resource。若是在 Google Cloud 上它能更好的去賣它的雲盤,或者說對於這些公有云廠商來講,這是更友好的。

固然它也不是沒作,它是要在 1.9 裏面纔會去作這個支持,但以 Kubernetes社區的迭代速度,我估計 1.9 可能還要等個兩三年,因此這是徹底不能忍的一個狀態。那既然咱們又須要這個本地磁盤的支持,可是官方又沒有,那該怎麼辦呢?這時咱們就發揮主觀能動性了。

咱們給 Kubernetes 作了一個 patch。這個 patch 也是經過 Kubernetes resource 的方案去作的一個本地磁盤資源的管理模塊,這個怎麼作的呢?也比較簡單。這部份內容就比較幹了,須要你們對 Kubernetes 整個架構有一點點了解。

第一步,先會去建立一個 Kubernetes 的 Configuration map,咱們稱之爲 TiDB 的 storage ,就是針對 storage 的物理資源寫在配置的文件裏邊。好比說機器的 IP,它的不一樣盤對應的文件夾在哪兒,至關因而一個配置階段的東西。

第二步,建立一個利用 Kubernetes 的 Third Party Resources(TPR) 的 API,去建立一個叫 tidb-volume 的第三方資源,而後這個資源去剛纔 Configuration map 裏面去讀它去註冊的那些物理磁盤分佈的狀態資源,至關於 TPR 會把那個配置裏面的磁盤資源 load 出來,變成在 Kubernetes 裏的一個第三方 resource,這個對象大概是這樣一個狀態。

第三步,咱們在這邊會去寫一個 controller,咱們寫這個 controller 是幹嗎呢?叫 volume-controller 。好比說咱們的一個磁盤的資源分配給了一個 pod,而後這個 pod 如今在佔用着這個資源,我須要有一個 controller 的模塊來標記這個資源的使用狀況,不能說我新的業務在起來的時候,我把資源分配給了兩個正在用的業務,這是不行的。這裏的對應關係實際上是由 volume controller 去維護的。而後另一方面它還實時的監盯着剛纔 Configuration map 裏面的物理資源,我能夠動態的添加物理的磁盤資源的一些狀態。

第四步,剛纔咱們說到 opreator 實際上是一個運維的工具,去作建立集羣還有滾動升級,至關於總的入口。在這裏面在去建立集羣,在啓動進程的時候,把剛纔咱們建立的本地磁盤資源啓動實例綁定在一塊兒,讓它可以成功的建立這個資源。

第五步,就是建立一個 DaemonSet。DaemonSet 在 Kubernetes 裏就是在每個物理節點上每個長度的進程,這個進程是用來去維護磁盤上資源的使用情況。好比說一個 TiKV 的節點下線了,這個物理的磁盤資源就要被回收。回收了之後你要去作刪除數據、清空數據的操做,或者這個物理機就宕機了,你須要有一個東西來去通知 controllor 把這個資源給下線掉,這個是 DaemonSet 在乾的事情。

這一整套加起來就是至關於只經過 Third Party Resources 來嵌入 Kubernetes,總體來講在旁路卻實現了一種本地的 local 磁盤的管理方案。

由於這個代碼其實還蠻多的,如今尚未辦法直接 push 回 Kubernetes 社區。雖然如今這仍是一個 private 的項目,可是在後續把代碼整理之後咱們仍是會開源出來。因此這其實也是爲你們之後在 Kubernetes 去調度這種單雙向服務的方案,提供了一套可行的路,至少咱們用這個還挺爽的。

而後,想稍微展望一下將來的數據庫應該是什麼樣的。

在我看來,將來的數據庫不會再須要 DBA ,或者說將來你在寫業務的時候,並不須要去關心底下數據該去怎麼分片,怎麼去 sharding,一切都應該是在後面的雲服務自己或者基礎設施去提供的。全部的東西都應該是 self-driving,至關於自動駕駛。就像在將來你們以爲自動駕駛應該是一個方向,在基礎軟件裏我以爲也是愈來愈多的自動化致使你們對運維的依賴變得愈來愈輕。可是在不少的極端的狀況下,circuit-breaker(斷路器)仍是要有的。好比說個人業務產品如今忽然出現了一個特別熱的熱點,我須要業務這邊去緊急的作手動的數據切分、移動,把負載手動的均衡出來,因此手動模式仍是仍然要有的。相似於你自動駕駛的汽車,忽然來一我的加塞,你們仍是很是但願能保證本身的安全,那就必須得有一個手動的模式。

第二點是 database as a service ,前面也說到了,serverless 可能會在 database 裏面,有一種新的形態的數據的數據庫。你們若是關注數據庫領域的話,最近出了一個新的數據庫叫 FaunaDB 。FaunaDB 很是有意思,它實際上是跟公有云綁定在一塊兒對外提供服務的,你看不見它實際的進程和部署,也看不見它物理的進程在什麼地方,整個數據庫對外的展示形式就是你去買個人服務,我給你多少的 QPS。好比說你買一萬個 QPS,這個服務就能保證一萬個 QPS,你買十萬就是十萬,按這個價格來去付費,至於你的容量全都是在背後隱藏着,全部業務的開發者其實是看不見。

第三點就是 Local storage isn’t necessary。爲何 Google 它一直沒有在 Kubernetes 裏面去作 Local storage,其實仔細想一下也是有道理的,就是說隨着將來硬件的發展,當網絡社會的速度和分佈跟你的磁盤的存儲差很少的時候,那它究竟是不是網絡的,已經對業務層沒有什麼意義了,因此這多是一個將來的趨勢。

這條是一個比較極端的路,就是把整個磁盤放到網絡上,用網絡盤。還有另一條反向的特別極端的路,也是我如今正在嘗試的一個東西,就是更加去對硬件作定製。好比說我如今嘗試把一些 TiDB 的數據庫的一些邏輯放到 FPGA 上面,或者是放在 SSD 的控制芯片裏面,這實際上是更深的定製,在將來我以爲二者可能會融合。就是說我雖然多是掛了一個網絡盤,可是對於數據庫來講我有了這個計算邏輯可能直接的去操做硬件,而不須要去例如經過標準的 POSIX API 來轉換內核走 本地 IO 的接口。

總結一下,分佈式系統的運維特別的痛苦,而後 Kubernetes 是一個將來集羣調度必然的趨勢,可是在存儲層它如今尚未太多的好辦法。目前來講咱們在作的一個事情就是 Operator,把整個存儲層的運維的領域知識放在 Operator 裏邊,而後讓 Kubernetes 能去調度咱們的東西。這個有點像 DCOS 的 batch script。

TiDB-Operator,實際上是把運維乾的事情,全都經過 Operator 的形式來封裝在程序裏邊,而後自動的去運維。

Local Storage 的問題咱們是解決了。雖然 Kubernetes 沒有辦法去提供這個能力,可是咱們暫時解決了。

相關文章
相關標籤/搜索