黃東旭DTCC2017演講實錄:When TiDB Meets Kubernetes
本文是我司 CTO 黃東旭同學在 DTCC2017 上的《When TiDB Meets Kubernetes》演講實錄,主要分享了關於 TiDB 與 Kubernetes 整合的一些工作。文章較長,且乾貨滿滿。
以下為演講實錄:
今天給大家帶來的分享是關於n TiDB 與 Kubernetes 整合的一些工作。在講之前,想了解一下,在場的各位有聽說過 Kubernetes 的同學舉個手;聽說過 nTiDB 的舉個手,非常好。因為我也是 DTCC 的常客了,前幾年來講的時候基本上沒有人聽說過 TiDB n,隨著這個項目越來越成熟,很欣慰,有一些社區的小夥伴已經用起來了。
我先簡單介紹一下我自己。我是n PingCAP 的聯合創始人兼 CTO ,也是 TiDB 項目的碼農 ,之前一直也是在做 Infrastructure n和分散式系統相關的一些工作。同時也是特別喜歡開源,基本上做的所有東西都是開源,包括像 Codis 、TiDB 、 TiKV n這些項目。比較喜歡的編程語言有 GO 、Rust 、Python。
對於今天的話題,如果說在 40 分鐘之內要去完整的介紹怎麼做一個資料庫或者說怎麼去做一個集成調度的系統,我覺得是不太可能的。
所以今天這個n Talk 我主要會分享 TiDB n作為一個分散式關係型資料庫與雲整合的經驗,以及我們遇到的一些問題,來幫大家在遇到相似的問題時提供一個解決思路。大家其實在存儲系統上可以把 nTiDB 換成任何東西,然後對於在 Kubernetes 上的融合,我覺得都會有一些啟發的意義。
首先想強調的是,Cloud 才是未來。現場做一個小調研:有在線上生產環境中,正使用 Kubernetes 或者 Mesos 這樣的容器化管理方案的同學嗎?好,一個兩個三個。
我相信,如果在三到五年以後,再去問這個問題,應該是至少一半的同學會舉手。因為其實在可見的未來,數據量是一直在膨脹,業務會越來越複雜。包括現在很多微服務的這種思想,當你的業務比較大的時候,會把整個服務拆成非常細的模塊,然後在眾多的模塊的拆分之下,怎麼去高效的運維你的分散式集群,其實靠n SRE 或者運維人員手動去管理各個微服務或者說各個的系統,其實是不太現實的。
但是大家也都知道,對於這種無狀態的業務,比如像n Application n應用層,其實它並不會真正的存儲數據,狀態一般都持久化到資料庫或者緩存中,所以它基本是無狀態的。所以其實一直以來大家在使用容器化遇到的第一個問題就是,有狀態的服務,特別是資料庫或者分散式存儲系統,怎麼去運維。比如說我在n Docker 裡面寫的數據,這個容器銷毀它直接就掛了,它其實是很難去做這種數據層面上的東西。
然後另外一方面就是資料庫的運維,不管是放在雲上做還是n DBA 自己在物理機上做,一樣的痛苦,所以怎樣去設計一個面向雲的環境下、或者說在分散式系統上去做資料庫。其實這也是在做 TiDB n的過程中我一直在想的。TiDB 這個項目一開始的設計就是:它一定會放在雲上去運轉。
上圖是n Amazon n在它的雲上選擇創建一個資料庫實例的一個界面,大家不用看具體的字是什麼,只是給大家感受下:就是說我作為一個業務的開發,我要去存儲一個數據,啟用一個n PG 或者 MySQL n的資料庫,我還要去關心這麼多個選項,或者說我一定要去關心我的這個物理機到底是什麼樣的情況:磁碟有多大,什麼機型,各式各樣的配置等。因為畢竟不是所有人都是專業的n DBA ,也不是所有人都是操作系統的專家,當時看到這個頁面的時候,基本上業務開發可能是一臉懵逼的狀態。
現在所有人都跟你說,我的系統是一個分散式系統,在廣告里寫的非常漂亮。現在哪一個資料庫說自己不是分散式的,那基本上只能是落後於時代。但是,大家有沒有想過,現在所有的這些分散式系統、分散式資料庫,運維起來都是非常痛苦,沒有辦法去很好的把它用好。
以n Hadoop 為例,現在 setup 一個 Hadoop 的集群竟然能成為一個生意,這個生意還讓兩個創業公司都上市了,一個是 nCloudera,一個是 Hortonworks。其實嚴格來說,他們只是做 Hadoop 運維的創業公司。然後還有無數的公司在做 Spark n的維護、Spark 的管理。各種各樣的資料庫運維公司,靠這個都過的非常好。
這個其實在我看來是非常不正常的,因為大家想,如果你去運維一兩台機器那沒有問題,我寫一個腳本,輕輕鬆鬆的就可以搞定;然後三五十台機器,也還行,招一個 OPs 或者說招一個 DBA ,還是可以人工的管理。
但是如果是在n 100 台、1000 台甚至 10000 台規模之上,機器的故障會是每天每夜無時無刻都發生的,網路的抖動,磁碟 IO n的異常,一直都在發生,靠人是沒有辦法做的。總的來說就是,當你去運維一個 single node n的系統時,基本上沒有什麼難度;但是如果要去運維一個特別大的 P2P 的 distributed nsystem,尤其是節點數特別多的時候,你的狀態和維護的成本就變得非常高。
之前我做過一個項目叫n Codis ,可能有很多同學聽說過,也可能很多同學已經用在生產環境之中。還有另外一個不是我做的項目,就是官方的 Redis nCluster。當時很多社區裡面的 Redis Cluster 的粉絲一直噴我,說 Codis n的配置怎麼這麼複雜,一點都不好用,組件怎麼這麼多。現在這個事情又在重演。
很多系統做成了n P2P n的模型了以後,組件很少部署很方便,但是真正在去運維它的時候,比如說要去做一個滾動升級或者我想清楚的知道整個集群的數據分布和各個組件的狀態,又或者說是我的分散式邏輯出了個bug,但是我的存儲層沒事,需要做個熱更新這個時候,p2pn 系統的運維複雜度就凸顯了。Codis 它其實有一個 Proxy,一個存儲層,這兩層在邏輯上其實是分離的;但是 Redis Cluster n,每一個節點既是它的分散式調度模塊,同時又是它的數據存儲模塊,這時候整個系統架構是混在一起的,對於運維的同學來說這就是一個惡夢。
如果我想清楚的知道我的數據到底是在哪幾台機器上,比如說這塊數據特別熱我想把它挪走,這時候像在n Redis Cluster 這種純 P2P 的系統裡面是很難得到它的當前狀態。所以這也是影響了我後來一系列的系統設計的想法,所以 noperation 是一個非常困難的事情,在一個特別大的分散式系統里,是一個非常困難的事情。
因為你的服務和組件特別多,不同的組件,不同的模塊,然後再加上一個分散式系統裡邊特別不穩定的網路狀態,使得各種各樣的異常情況,人是沒有辦法去掌控的。
還好,Google 是一個非常偉大的公司,像 TiDB 整個模型大家也知道是參考了Google Spanner/F1。Kubernetes 背後的系統的前身就是 Google 的 Borg。
Borg 其實是 Google 內部一直在用著的大規模的集群調度器。Borg 這個單詞就是星際迷航裡面的一個角色,相當於它作為整個集群的一個大腦來去控制集群的業務的分布跟數據的均衡,所以 Google 給我們帶來了 Kubernetes 這個項目。
Kubernetesn 主要的工作就是一個面向 Container 的集群管理的服務。它同時會去做服務編排,Auto deployment、 Auto nscaling、Auto healing n,你的整個集群的這些服務的生命周期的管理,然後故障的轉移、擴容...你可以認為它是一個集群的操作系統。大家可能認為操作系統就是單機上的一個概念,但是如果放到一個大規模的分散式系統裡面,你有無數的n CPU 資源,無數的內存,無數的磁碟資源,怎麼高效的去把你的服務在這些海量的資源上進行合理的分配,這個就是 Kubernetes 乾的事情。
TiDB 大家也都非常熟悉了,我簡單介紹一下吧。
我們做n TiDB 的目標就是,希望構建一個完全彈性的,用戶不需要去知道數據的分布信息,也不需要去做手工的數據分片,可以把它當做一個單機的資料庫、 nMySQL 的資料庫在用,但是它背後是一個高度彈性和智能的分散式的資料庫。對業務層你不需要再去想分庫分表,也不需要再去想熱點的 balance n這種事情。它支持百分之百的 OLTP 的功能,可以支持跨行事務,像 MySQL 一樣:我開始一個 transaction,然後寫寫寫,最後 ncommit,全成功或者全失敗,沒有第三種可能 。支持事務的前提之下還支持 80% 的 OLAP 。
所以 TiDB 是非常適合去做這種一邊有實時寫入,一邊有複雜 Join 和實時分析的場景,它的 SQL 優化器其實也有從 Spark SQL 裡面學到了很多東西。
當然對外的介面是完整的n MySQL 的介面,你可以直接用 MySQL n的客戶端就連上了。另外,背後它支持高可用。因為底層的複製協議並不是通過像這種主從模型去做數據冗餘的,而是用 Raft,Raft 跟 nmulti-paxos 是比較接近的,都是基於選舉的演算法。
在遇到n MySQL 的擴展性問題的時候,大家過去只能一臉懵逼,然後反回來去拆庫拆表,或者去用 MyCat 或者依託 MySQL 的中間件去做 nsharding 。其實這對於業務層來說,侵入性非常大,所以 TiDB 的初衷就是解決這個問題,但是它沒有用任何一行 MySQL 的代碼。
TiDB 大概是這樣一個架構:
TiDB 其實也是由很多的組件組成,它並不是一個純粹的 P2P 系統,如果看這個架構其實非常像 Codis 。
今天的主題是說我們怎麼在 Kubernetes 上去做 cluster 的 setup、rolling update,怎麼去解決 Kubernetes 對於本地磁碟存儲的綁定問題。
所以面臨著一個與大家之前在n Kubernetes 上去部署帶狀態的服務非常接近的問題。因為 TiDB n本身是一個帶狀態的資料庫,資料庫沒有狀態那不可能。Kubernetes 的調度其實是對這種 stateless applications n非常友好的,但是如果你是一個帶狀態的,比如像 MySQL、PG、TiDB,或者 Etcd、Zookeeper 等等,怎麼去做?真正的困難並不是 nKubernetes 做不了,而是每一個不同的系統都有自己的數據分布模型,同時每一個不同的系統它的運維方式也不太一樣。所以作為 nKubernetes 的平台來說,沒有辦法去針對每一個不同的存儲系統去設計一套自己的調度策略。這是 Kubernetes n沒有辦法去做的一個事情。
舉個例子,比如說你想去運維好一個n Redis Cluster ,那你必須得了解 Redis Cluster 一些原理,還有必須得去知道它怎麼運維;如果你想要去運維 Codis n,你必須得知道 Codis 的一些原理方法才能把它運維好。但這些領域知識怎麼去告訴 Kubernetes 說你幫我按照這個方法來去運維這個系統?
這時候有一個公司,叫做n CoreOS ,相信大家可能也都熟悉,就是 Etcd 背後的那個公司,也是我們 PingCAP 的好夥伴。CoreOS 也是社區裡面最大的 nKubernetes 的運營的公司,他們引入了一個新的 Kubernetes 的組件,叫做 Operator n。Operator的意義在於它其實是相當於使用了 Kubernetes 的 TPR(third party resources)的 nAPI,去把你的系統運維的一些領域知識,封裝到 Operator 裡面,然後把 Operator 這個模塊注入到 Kubernetes n上面,整個這些集群是通過 Operator 這個模塊來去做調度。
CoreOSn 官方還提供了一個 Etcd 的 Operator n的實現。其實這思路也很簡單,就是說把這個集群的創建滾動更新,然後各種運維的一些領域知識放到這個 Operator 裡面,然後在 Operatorn 裡面去調用 Kubernetes 原生的 API,來做集群的管理,相當於是 Kubernetes 的一個 Hook 。
一般來說一個n Operator 它其實有這樣一些對外暴露的介面或者是能力。它做的事情的就是:比如我想要讓 Kubernetes 去建立一個 TiDB n的集群,比如說 deployment;比如我加入新的物理節點以後,我要想對現有的這個集群做擴容,然後 rebalance;比如說我的集群的 nTiDB 本身的這些 binary需要升級,我要去做業務透明的滾動更新,比如說我有 100 n個節點,要在上面去做升級,不可能手動去做,這個其實都是封裝在我們的 Operator 裡面,然後包括自動化的 backup 跟 restore n這些功能。
本質上來說你可以認為 Operator 是一個 Kubernetes 的批處理方案。我剛才也簡單提到了一下,Kubernetes 可以作為一個集群的操作系統,但是這個操作系統總應該能讓運維去寫腳本的,這個腳本就是 Operator 機制。
其實它的原理很簡單,就是它注入到n Kubernetes 裡面,會實時不停的去觀察集群的狀態,去 Hook Kubernetes 的一些集群的狀態,一些 nAPI,然後得到整個集群的狀態。把一些分析的東西放在 Operator 裡面,它可能會有一些地方被觸發,比如說我該擴容了或者我該去做 nFailover ,然後去反饋。
然後 TiDB 的 Operator 目前來說有這麼幾個功能:創建集群、滾動更新、Scale out, Failover、Backup/Restore。
因為其實今天的這個話題是說怎麼去跟雲做結合。我們現在在跟一些公有雲的提供方在做合作。但是不可能說每一個公有雲都自己去接入它的資源管理的n API,因為每個公有雲可能都用的是不一樣的 API ndatabase,所以我們相當於做的一個方案就是說,不管你是公有雲也好還是私有雲也好,你給我一堆物理的機器,然後在這一堆物理的機器上面去部署 nKubernetes ,在這個 Kubernetes 上面,我相當於把我的 TiDB Operator n給放進去,當某個公有雲客戶要它去創建一個集群的時候,會通知 Operator 去創建,比如說划出一些機器,去做物理隔離。
這在私有雲裡邊也是一個比較常見的場景了。用戶他其實想要去做這種業務之間的租戶隔離,TiDB Operator 是做一個比較簡單的物理隔離。
但是做這個n Operator 最難的一個部分其實剛才也簡單講了一下,就是存儲的問題。如果大家關注 Kubernetes 社區的話,一般都會注意到 npersistend local storage 的這個方案一直在社區裡邊扯皮和吵架。現在大家認為 Kubernetes n本地的磁碟是沒法用的,或者說沒有辦法直接當做一個資源來使用的。
上圖是放在 Kubernetes 的 issues 裡面的一個問題,就是 persistent local storage ,這個看上去非常不可思議,這麼簡單的功能為什麼一直到現在沒有支持。
我個人感覺n Google 之所以遲遲不去做這個功能,它背後有一個原因可能是在 Google n內部,它的網路存儲是非常強的,它有自己非常好的網路設備。你在同一個數據中心裡,去換一塊網路盤,它的這個 latency n基本上很多業務可以接受的,所以這樣的話,持久化存儲的問題基本上是靠網路的磁碟來解決。
想像你跑一個n MySQL 的業務,MySQL 的業務本身它寫入的磁碟並不是你的物理機的本地盤,而是一塊網路盤,這個網路盤我能給你保證它的 IOPS 跟 nlatency 都是非常好的狀態,這個時候你的業務掛掉了,我再重新啟一個容器把這個網路盤再掛到那個 pod n的後邊,這時候你的業務是幾乎無感知的。這也是 Google 比較推崇的使用存儲的一個模式,所以這就是 Kubernetes 背後的那個 npersistent volumes 的這個方案。
它現在是有這個方案的,但是對於像我們這樣的分散式資料庫或者說對這種本地磁碟有特彆強要求的(n TiDB 底層的存儲引擎對單機的 SSD 做了非常多的優化),並沒有辦法去容忍我底下寫入的一個磁碟是網路盤。因為本身比如說 TiDB n這一層,已經做了三個副本,甚至五個副本的複製,但是在底下網路盤又要去做這個複製其實是沒有太多必要的,所以 Google 一直遲遲沒有推 nLocal Storage Resource。如果在 Google Cloud n上它能更好的去賣它的雲盤,或者說對於這些公有雲廠商來說,這是更友好的。
當然它也不是沒做,它是要在n 1.9 裡面才會去做這個支持,但以 Kubernetes社區的迭代速度,我估計 1.9 n可能還要等個兩三年,所以這是完全不能忍的一個狀態。那既然我們又需要這個本地磁碟的支持,但是官方又沒有,那該怎麼辦呢?這時我們就發揮主觀能動性了。
我們給n Kubernetes 做了一個 patch。這個 patch 也是通過 Kubernetes resource n的方案去做的一個本地磁碟資源的管理模塊,這個怎麼做的呢?也比較簡單。這部分內容就比較幹了,需要大家對 Kubernetes n整個架構有一點點了解。
第一步,先會去創建一個n Kubernetes 的 Configuration map,我們稱之為 TiDB 的 storage ,就是針對 storage n的物理資源寫在配置的文件裡邊。比如說機器的 IP,它的不同盤對應的文件夾在哪兒,相當於是一個配置階段的東西。
第二步,創建一個利用n Kubernetes 的 Third Party Resources(TPR) 的 API,去創建一個叫 tidb-volume n的第三方資源,然後這個資源去剛才 Configuration map 裡面去讀它去註冊的那些物理磁碟分布的狀態資源,相當於 TPR n會把那個配置裡面的磁碟資源 load 出來,變成在 Kubernetes 里的一個第三方 resource,這個對象大概是這樣一個狀態。
第三步,我們在這邊會去寫一個n controller,我們寫這個 controller 是幹嘛呢?叫 volume-controller n。比如說我們的一個磁碟的資源分配給了一個 pod,然後這個 pod 現在在佔用著這個資源,我需要有一個 controller n的模塊來標記這個資源的使用情況,不能說我新的業務在起來的時候,我把資源分配給了兩個正在用的業務,這是不行的。這裡的對應關係其實是由 volumen controller 去維護的。然後另外一方面它還實時的監盯著剛才 Configuration map n裡面的物理資源,我可以動態的添加物理的磁碟資源的一些狀態。
第四步,剛才我們說到 opreator 其實是一個運維的工具,去做創建集群還有滾動升級,相當於總的入口。在這裡面在去創建集群,在啟動進程的時候,把剛才我們創建的本地磁碟資源啟動實例綁定在一起,讓它能夠成功的創建這個資源。
第五步,就是創建一個n DaemonSet。DaemonSet 在 Kubernetes n里就是在每一個物理節點上每一個長度的進程,這個進程是用來去維護磁碟上資源的使用狀況。比如說一個 TiKV n的節點下線了,這個物理的磁碟資源就要被回收。回收了以後你要去做刪除數據、清空數據的操作,或者這個物理機就宕機了,你需要有一個東西來去通知 ncontrollor 把這個資源給下線掉,這個是 DaemonSet 在乾的事情。
這一整套加起來就是相當於只通過 Third Party Resources 來嵌入 Kubernetes,整體來說在旁路卻實現了一種本地的 local 磁碟的管理方案。
因為這個代碼其實還蠻多的,現在還沒有辦法直接n push 回 Kubernetes 社區。雖然現在這還是一個 private n的項目,但是在後續把代碼整理以後我們還是會開源出來。所以這其實也是為大家以後在 Kubernetes n去調度這種單雙向服務的方案,提供了一套可行的路,至少我們用這個還挺爽的。
然後,想稍微展望一下未來的資料庫應該是什麼樣的。
在我看來,未來的資料庫不會再需要n DBA ,或者說未來你在寫業務的時候,並不需要去關心底下數據該去怎麼分片,怎麼去 nsharding,一切都應該是在後面的雲服務本身或者基礎設施去提供的。所有的東西都應該是 nself-driving,相當於自動駕駛。就像在未來大家覺得自動駕駛應該是一個方向,在基礎軟體里我覺得也是越來越多的自動化導致大家對運維的依賴變得越來越輕。但是在很多的極端的情況下,circuit-breaker(斷路器)還是要有的。比如說我的業務產品現在突然出現了一個特別熱的熱點,我需要業務這邊去緊急的做手動的數據切分、移動,把負載手動的均衡出來,所以手動模式還是仍然要有的。類似於你自動駕駛的汽車,突然來一個人加塞,大家還是非常希望能保證自己的安全,那就必須得有一個手動的模式。
第二點是n database as a service ,前面也說到了,serverless 可能會在 database n裡面,有一種新的形態的數據的資料庫。大家如果關注資料庫領域的話,最近出了一個新的資料庫叫 FaunaDB 。FaunaDB n非常有意思,它其實是跟公有雲綁定在一起對外提供服務的,你看不見它實際的進程和部署,也看不見它物理的進程在什麼地方,整個資料庫對外的展現形式就是你去買我的服務,我給你多少的n QPS。比如說你買一萬個 QPS,這個服務就能保證一萬個 nQPS,你買十萬就是十萬,按這個價格來去付費,至於你的容量全都是在背後隱藏著,所有業務的開發者實際上是看不見。
第三點就是n Local storage isn』t necessary。為什麼 Google 它一直沒有在 Kubernetes 裡面去做 Local nstorage,其實仔細想一下也是有道理的,就是說隨著未來硬體的發展,當網路社會的速度和分布跟你的磁碟的存儲差不多的時候,那它到底是不是網路的,已經對業務層沒有什麼意義了,所以這可能是一個未來的趨勢。
這條是一個比較極端的路,就是把整個磁碟放到網路上,用網路盤。還有另外一條反向的特別極端的路,也是我現在正在嘗試的一個東西,就是更加去對硬體做定製。比如說我現在嘗試把一些n TiDB 的資料庫的一些邏輯放到 FPGA 上面,或者是放在 SSD n的控制晶元裡面,這其實是更深的定製,在未來我覺得兩者可能會融合。就是說我雖然可能是掛了一個網路盤,但是對於資料庫來說我有了這個計算邏輯可能直接的去操作硬體,而不需要去例如通過標準的n POSIX API 來轉換內核走 本地 IO 的介面。
總結一下,分散式系統的運維特別的痛苦,然後n Kubernetes 是一個未來集群調度必然的趨勢,但是在存儲層它現在還沒有太多的好辦法。目前來說我們在做的一個事情就是 nOperator,把整個存儲層的運維的領域知識放在 Operator 裡邊,然後讓 Kubernetes 能去調度我們的東西。這個有點像 nDCOS 的 batch script。
TiDB-Operator,其實是把運維乾的事情,全都通過 Operator 的形式來封裝在程序裡邊,然後自動的去運維。
Local Storage 的問題我們是解決了。雖然 Kubernetes 沒有辦法去提供這個能力,但是我們暫時解決了。
推薦閱讀:
※三篇文章了解 TiDB 技術內幕 —— 談調度
※TiDB DevCon2018.tick(1.20)
※【開源訪談】黃東旭:「無人區」的探索者,TiDB 的前行之路
※TiDB 助力牛板金互聯網金融營銷平台
※2017 我們與世界的對話
TAG:TiDB | Kubernetes |