這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
初衷
為什麼要在kubernetes上搭建codis系統,一個原因是很多服務已經運行在kubernetes上,將codis遷移到kubernetes上,可以更方便的使用;另一個重要原因是,結合kubernetes的特性,搭建一套基本不需要人為營運參與的codis系統(如果熟悉codis的話,會知道codis需要人的參與有些重,異常情況基本都需要人為參與處理),後面會詳細說明如果做到基本不需要人為參與,原理、實現及一些折中選擇。
目標
在kubernetes環境,一鍵部署一套codis系統,該codis系統能夠穩定運行,並在一些意外情況,甚至極端下(如物理機當機等),能夠自己進行修複,並恢複正常運行狀態(部署完成後,基本不再需要人為參與)。一鍵對codis進行擴容(包括proxy和後端server)。經過大量測試,並在生產環境運行驗證,當前這些目標基本滿足。
github
https://github.com/left2right/codis (基於codis 3.2)
依賴
kubernetes環境
在kubernetes上部署一套codis系統,需要基本的kubernetes環境和docker環境,具體部署方法見相應文檔。
如果想快速部署一套自己測試或者研究的單機kubernets環境,可以按照下面兩個步驟來(mac):
- 部署docker
https://docs.docker.com/toolbox/toolbox_install_mac/
- 部署minikube
https://github.com/kubernetes/minikube (brew cask install minikube) kubernetes啟動及驗證(minikube)
###啟動$ minikube start###驗證$ minikube statusminikubeVM: Runninglocalkube: Running
image依賴
- zookeeper image:
gcr.io/google_samples/k8szk:v1
- golang image:
golang:1.7.5
這兩個image均需要翻牆才能擷取,可以找個國內的代理擷取。image 擷取成功檢查
### 檢查zookeeper image$ docker images |grep k8szkgcr.io/google_samples/k8szk v1 ... ...### 檢查 golang image$ docker images |grep golanggolang 1.7.5 ... ...
構建步驟
在kubernetes平台安裝啟動成功,而且拉取到上面依賴的兩個image後,就可以構建codis系統了,具體如下:
###擷取codis源碼$ git clone https://github.com/left2right/codis -b release3.2-k8s### 構建codis docker鏡像$ cd codis$ docker build -f Dockerfile -t codis-image .### 構建kubernetes環境的codis叢集$ cd kubernetes$ sh start.sh buildup... ... plz waitPONG #當你看到這個PONG的時候說明這套codis叢集已經構建成功了,#如果你沒有等到PONG,那就需要檢查下具體的報錯內容了,祝順利~
構成組件
檔案說明
在codis/kubernetes/目錄裡面有以下這些檔案:
- README.md 使用說明,包括建立codis叢集,銷毀叢集,擴容等,具體如下:
### Build one codis cluster (codis master server has one slave)$ sh start.sh buildup### Clean up the codis cluster$ sh start.sh cleanup### Scale codis cluster proxy$ sh start.sh scale-proxy $(number)### Scale codis cluster server$ sh start.sh scale-server $(number)
- start.sh 真箇codis叢集操作指令碼,具體功能如上面README.md說明,挺簡單的一個指令碼,如果你想做些什麼改動或者測試,可以在這個基礎上做些嘗試~
- codis-service.yaml codis在kubernetes環境中所有service的yaml檔案,裡麵包括codis-dashboard, codis-proxy, codis-server, codis-fe, codis-ha
- codis-dashboard.yaml codis dashboard在kuberenetes環境的yaml檔案
- codis-proxy.yaml codis proxy在kuberenetes環境的yaml檔案
- codis-server.yaml codis server 在kuberenetes環境的yaml檔案
- codis-ha.yaml codis-ha 在kuberenetes環境的yaml檔案
- codis-fe.yaml codis-fe 在kuberenetes環境的yaml檔案
- zookeeper/zookeeper-service.yaml zookeeper service 在kubernetes環境yaml檔案
- zookeeper/zookeeper-service.yaml zookeeper 在kubernetes環境yaml檔案
組件介紹
通過上面檔案介紹,可以看到主要有zookeeper(可以用etcd等替代),codis-dashboard, codis-proxy, codis-server, codis-ha, codis-fe這幾個組件組成,每個組件有一個相應的service,及一個具體的pod組織實現。
zookeeper
zookeeper是參考kubernetes官網https://kubernetes.io/docs/tutorials/stateful-application/zookeeper/ ,只是為了操作方便將相應的yaml檔案的volume mounts注釋掉了(生產環境使用中應該將zookeeper的持久化資料通過volume mounts 掛載出來),具體使用及相關說明見官網介紹
codis-dashboard
codis-dashboard即codis dashboard,其在codis中起的作用見GitHub介紹,dashboard使用StatefulSet來組織pod,並且replicas設定為1,整個叢集只允許有一個。如果codis-dashboard異常,由kubernetes將其關閉,並重新啟動起來,並加入到叢集中。
dashboard啟動成功後,會通過kubernetes的postStart hook,發送一條命令檢查zookeeper是否串連正常,以方便快速定位問題原因。在dashboard關閉時通過preStop hook,來確保dashboard正常關閉(將zookeeper上的鎖刪掉)。
當一些極端情況(如物理機當機)導致dashboard非正常關閉,zookeeper上的鎖沒有刪除掉(這會導致dashboard下次啟動不起來),為了能讓叢集快速恢複正常狀態,我們為codis-dashboard啟動時添加了一個清除鎖的開關(--remove-lock),如果開啟開關,每次註冊dashboard鎖前,會先做一次清除鎖的操作。
codis-proxy
codis-proxy即codis proxy,由於proxy是無狀態的,使用ReplicationController來組織pod,這樣可以快速的擴容縮容。通過replicas設定數量,預設是2個。
codis proxy關閉前會發送一條命令將自己從叢集中摘除出去。如果是一些異常情況,我們下面會介紹,由codis-ha將proxy從叢集中摘除出去。然後由kubernetes重啟一個proxy加入叢集。
codis-server
codis-server即codis後端redis server,由於redis server要分屬不同的group,為方便管理以及方便在沒有人蔘與情況下恢複正常,使用StatefulSet來組織pod,replicas的數量即是整個叢集中所有codis-server的數量,codis-server.yaml中有個配置的環境變數SERVER_REPLICA,這個設定的是每個group中codis-server的數量,預設是2個(一主一從)。
為了使codis的server,尤其是一個group的server不部署在一個物理節點上,我們使用了kubernetes pod的反親和性(podAntiAffinity),為了在物理節點有限時能夠順利部署codis叢集,我們使用preferredDuringSchedulingIgnoredDuringExecution。如果想讓所有codis server絕對不在一個node上,可以設定為requiredDuringSchedulingIgnoredDuringExecution,這樣會提高系統的高可用性,但如果node節點不足會導致部署codis叢集失敗或者擴容codis server失敗。
在codis-server啟動時,通過postStart hook,嘗試為該server建立group,並將該server加入到group中,並嘗試和group的master建立主從關係。在codis server關閉時嘗試將該server從group中刪除出去。該codis-server從屬於哪個group,是由StatefulSet為該server分配的id,以及配置的環境變數SERVER_REPLICA,計算獲得 gid=$(expr $sid / ${SERVER_REPLICA} + 1) 。這個過程中一些操作會失敗,如建立已經存在的group等。一些操作失敗並沒有什麼影響,直接跳過,一些失敗會導致系統狀態不正常,由後面的codis-ha將狀態恢複為正常的狀態,具體見後面說明。
為提高系統的高可用性,可以將save的rdb檔案掛載到外面,在啟動時載入,掛載的目錄是/codis,具體掛載方式和zookeeper類似,但考慮到效能,預設沒有將rdb檔案掛載出來,如果為了更高的可用性,可以參考zookeeper.yaml檔案掛載出來。
codis-ha
當前codis 官方維護server ha使用的工具是redis-sentinel,並將之前的codis-ha去掉,這樣選擇的原因是codis-ha是一個單點,如果它狀態不正常了,那叢集的高可用就難以保證了,尤其是如果codis-ha需要人為參與的情況下,恢複正常著實會花費不少時間,redis-sentinel是叢集的模式,不會因為某個節點的不正常,導致不能正常工作。但我們最終選擇了codis-ha(在原有codis-ha的基礎上進行修改),是因為sentinel能實現的功能太有限,只是在master狀態異常時,重新選舉一個master,但如果slave狀態異常,以及重建立立主從關係,將group內各個server狀態恢複正常,以及將proxy狀態恢複正常,sentinel顯然還無法做到。我們知道在kubernetes環境如果一個pod狀態不正常了,會快速的關閉並重新啟動起來,在這種情境下,codis-ha前面對比sentinel的不足,一定程度上縮減了不少,實際使用中效果確實可以。
codis-ha的策略就是,通過dashboard,檢查proxy和server的狀態,如果proxy狀態異常就關閉proxy,由kubernetes重啟proxy,並加入叢集;如果server狀態異常,分幾種情況:1. 是master,且有slave,則選擇一個合適的slave(根據和master中斷連線時間),提升為master;2.是master,沒有slave,則直接關閉重啟;3.是slave,則關閉,然後重啟,加入叢集。
由於codis-ha嚴重依賴codis-dashboard,我們使用pod親和性,將codis-ha和codis-dashboard部署在同一個節點。
codis-fe
codis-fe即codis-fe,codis的網頁圖形操作介面。PS在kubernetes環境主要用來觀察下叢集狀態,qps等。
源碼改動
kubernetes環境下的codis對源碼做了如下修改:
- 增加kubernetes目錄,裡面存放相應的kubernets yaml檔案及指令碼
- 修改codis-ha,維護叢集(server和proxy)狀態,使codis proxy和server快速恢複為正常狀態。
- 修改codis-dashboard,增加命令列傳遞product_name, product_auth,以滿足建立不同product的codis,增加remove-lock開關,以使異常情況下,快速恢複dashboard
- 修改codis-proxy,增加命令列傳遞product_name, product_auth,以滿足建立不同product的codis
注意點
注意事項
- 盡量確保codis-server的replicas能夠整除SERVER_REPLICA,通過yaml檔案可以看到SERVER_REPLICA為1,和其他在加入group時是對錯誤處理是不一樣的,思考下你就明白,這是為了避免一個死結。無論SERVER_REPLICA設定為任何正整數均可以,只是請確保叢集中的group不會出現有的group有slave,有的只有master,整除即可~
- 留意你的資料量和機器實際可用記憶體~
- 建議先將該codis作為緩衝使用一段時間,直到你熟悉了相關內容,及對其穩定性(也許和你的實際環境有很大關係)有了更深入的瞭解,再考慮將其作為一些資料的DB
問題定位
codis-ha的log會即時的反應整個叢集的狀態,包括proxy,server,以及ha和dashboard,因為如果ha有問題log就看不了了,dashboard有問題,ha也就掛掉了。。。查看方法
$ kubectl exec -it codis-ha-0 bash$ tail -f log/codis-ha-0.....
通過dashboard log更進一步定位問題的原因,方法類似
$ kubectl exec -it codis-dashboard-0 bash$ tail -f log/codis-dashboard-0.....
如果你按照上面操作,尤其是你成功運行codis後,測試發現的任何問題,歡迎和我交流(yqzhang@easemob.com)~
其他
還有些事情及思考沒有寫完,改天再補充~