降本40%!Redis多租戶集羣的容器化實踐

 

本文根據石鵬老師在〖deeplus直播第260期〗線上分享演講內容整理而成。(文末有獲取本期PPT&回放的方式,不要錯過)

 

石鵬

VIPKID基礎架構存儲平臺負責人

 

  • 曾就職於摩托羅拉、愛奇藝,10餘年專注於高併發、高可用、分佈式存儲方向,發表過相關專利20餘篇;

  • 目前負責VIPKID基礎架構存儲平臺,包括Redis平臺、Kafka平臺、RocketMQ平臺、 ElasticSearch搜索平臺、數據庫訪問平臺、對象存儲平臺等。


本文主要分享內容如下:

 

 

這樣,我們就可以根據namespace來區分不同應用在Redis集羣中的數據了。

 

但是,這個方案仍然是多個應用共享Redis集羣資源,仍存在不同租戶(應用)間互相影響的風險。

 

2、如何保證租戶間資源互不影響  

 

保證不同租戶間資源互不影響,我們是通過“監控、告警、問題根因定位、限流or禁用指令”這四步來完成的。

 

1)監控

對於Redis集羣,我們會監控ops,熱key等等多維度指標,重點是內存使用率。

 

2)告警

由於我們Redis多租戶集羣,爲用戶峯值還多預留了一倍的資源,所以當用戶達到內存使用峯值時,只會佔用50%的內存。

 

  • 內存使用率達到60%,通過企業IM做1級告警;

  • 內存使用率達到75%,通過短信做2級告警;

  • 內存使用率達到85%,通過電話做3級告警。

 

3)問題根因定位

當告警1級時,管理員就要分析告警原因了,首先執行”RDB內存分析工具“,分析內存不足是哪個應用流量激增導致的,然後分析是bigkey導致的還是就是請求量大導致的。

 

4)限流or禁用指令

定位到問題後,通過限流或禁用指令來確保Redis集羣不被激增流量打掛:

 

  • bigkey導致:採用禁用指令方式

    禁用指令的實現方案爲,通過管理後臺,定時下發配置給Redis客戶端,指明要對該應用下的哪個key做禁用指令。

  • 流量高導致:採用限流

    限流也是管理後臺下發配置給Redis客戶端,做應用維度的限流。

 

3、RDB分析工具的原理  

 

上面”問題根因定位“時用到了RDB內存分析工具,這裏我們基於開源做了一些定製化開發,原理是僞裝成Redis從節點,Dump RDB數據到本地內存,然後逐個key解析,分析出各應用的內存佔比、bigkey top 10、key的總數。

 

另外,RDB內存分析工具會在每天業務低峯時段執行,這樣,在故障時再執行RDB內存分析工具,把結果跟低峯時段的執行結果做比較,就能定位出是哪個業務應用增長快導致的告警。

 

4、問題與挑戰  

 

1)租戶間資源“假”隔離

 

如果流量增長太快,來不及做限流或禁用指令,那麼不同租戶間理論上仍存在互相影響的微小可能。

 

挑戰:怎樣既能“真”隔離又能共享資源,租戶間資源如果“真”隔離,就不會互相影響了。

 

2)解決激增流量告警時做限流或禁用指令都是對業務有損的

 

雖然限流或禁用指令的目標是異常使用redis的某個應用的key, 但這樣做對業務是有損的。

 

挑戰:能否做到租戶間既不互相影響又對業務無損呢?

 

3)集羣數雖減少了但運維工作量仍不小

 

相比獨享集羣,集羣數雖少了,但集羣的創建、銷燬、擴容、縮容、故障容災等,仍需要人工運維,運維工作量跟集羣數成正比。

 

挑戰:運維成本是否有進一步降低的空間?

 

對於這幾個問題,我們在Redis容器雲的方案中都一一解決了。

 

三、Redis容器雲數據庫平臺優化實戰

 

在這個階段,我們公司開始支持容器環境,這時我們想,Redis作爲有狀態服務,通過上容器是否可以解決我們當前存在的問題。

 

1、爲什麼需要租戶間資源嚴格隔離  

 

按上面的分析,大家知道不同租戶間的資源僅做到邏輯隔離,存在互相影響的風險,因此要消除風險,需要做到租戶間嚴格隔離資源邊界。

 

這裏有人會問,Redis多租戶的目的,本來就是爲了共享資源提升資源利用率以降低成本,如果嚴格隔離了租戶間的資源,那多租戶的意義何在呢?

 

通常服務器的最低配是有限制的,例如2C4G,但可能我的應用僅需要0.5C1G,假設我們可以做到資源邊界的嚴格隔離,那麼2C4G的資源就僅分配給4個0.5C1G的應用,這樣這4個應用就都沒有了互相影響的風險。

 

達到這樣的效果,我們很容易想到藉助容器來實現。但做了嚴格的資源邊界隔離,在保證租戶間不會互相影響的同時,帶來的弊端是不能夠充分共享資源。

 

綜上,我們推薦的做法是:

 

  • 對於Redis可用性非常敏感的應用,推薦採用資源的嚴格邊界隔離方式,即一個Pod裏只部署一個應用;

  • 對於Redis可用性不是非常敏感的應用,推薦採用資源的邏輯隔離方式,即一個Pod裏同時部署多個應用,當激增流量時,通過容器水平擴容來解決。

 

這樣在穩定性與最大限度提高資源利用率之間,根據應用對穩定性的不同要求,做出了相應的取捨。

 

2、如何藉助K8S容器化Redis多租戶集羣  

 

1)K8S容器化Redis多租戶集羣的目的

 

  • 可做租戶間資源邊界的嚴格隔離:通過容器Pod最小化資源單位;

  • 解決激增流量對集羣的影響:藉助Redis集羣節點水平擴容機制來解決激增流量時租戶間互相影響,且不再有虛機方案時限流或禁用指令對業務有損的問題;

  • 降低運維成本:容器環境運維自動化,解決Redis集羣規模大,人工運維成本高的問題。

 

2)K8S容器化Redis集羣面臨的挑戰

 

  • K8S如何部署Redis有狀態服務;

  • 容器Crash後如何不影響服務可用性;

  • 容器重啓後如何保證Redis內存中的數據不丟;

  • 節點水平擴容時如何做到slots遷移時不影響業務。

 

3)Redis多租戶容器化的架構設計

 

 

上圖主要分四個部分:

 

  • K8s管理的Redis集羣;

  • K8s控制器面板;

  • Redis管理後臺;

  • 由我們基礎架構提供的Redis客戶端。

 

由K8S管理的Redis集羣設計要點:

 

  • 一個node中部署2個Pod,一主一從,但主從不是一對,避免一個node宕機一主一從同時故障導致丟數據;同時這一主已從也不是同一個集羣,目的是確保一個node宕機時,最多隻影響一個集羣中的一個主節點;

  • 基於開源實現了定製化的Redis Operator;

  • 用anti-affinity來確保同一組master和slave不會部署到同一個node上;

  • VKShark服務爲K8s的api-server的代理和controller:當訪問k8s api-server時,通過VKShark做代理;同時VKShark作爲controller會監聽k8s node的節點的變化,然後通知Redis管理後臺;

  • 通過Redis operator的CRD來配置一組主從節點作爲相同的StatefulSet管理。

     

節點擴容調用流程:

 

  • 由Redis管理後臺選擇集羣,輸入擴容節點數如1後,點擊按鈕發起節點擴容請求;

  • 擴容請求經由VKShark做代理,訪問k8s api-server, api-server會把相應的CRD持久化到ETCD中;

  • Redis operator監控api-server, 發現有擴容請求,會創建statefulset,通過api-server調用k8s API做節點擴容,新建一主一從兩個Pod;

  • Redis operator經過api-server監聽Pod的變化,並把新節點加入到Redis Cluster集羣;

  • VKShark通過api-server監聽Pod的變化,發現有新節點,通知Redis管理後臺;

  • Redis管理後臺先檢測新的Pod是否已加入到了Redis Cluster集羣,如已加入則只需migrate指令做槽位遷移;

  • Redis管理後臺會定時下發Redis的集羣節點信息給Redis客戶端;

  • Redis客戶端用最新的節點信息計算key的slot。

 

3、如何讓激增流量租戶互不影響且對業務無損  

 

1)容器可快速自動水平擴容  

 

有了容器環境,對於Redis可用性敏感的應用,可以一個Pod只部署一個應用,這個應用間不會互相爭搶資源。那麼對於某個應用,如果他的流量激增,容器環境也可以通過自動水平擴容來解決,而不必像虛機時Redis多租戶方案那樣通過限流或禁用指令對業務有損的方式。

 

當然,這裏節點不能無限制擴容,當擴容節點數達到我們預先設置的最大閾值時,說明該流量激增屬於”非正常“,有可能是系統bug等導致,這時會採取限流與禁用指令的方式

 

2)Redis Cluster集羣擴容時遷移槽位是否會影響業務

 

訪問正在遷移的槽位中的某個key:

 

槽位中key的遷移指令的遷移指令是同步阻塞的,所以訪問正在遷移的key,請求會被拒絕,這時利用我們基礎架構封裝的Jedis的客戶端來做重試,在重試周期內,如果key遷移完成,就可以正常訪問了,這在絕大多數情況下都是正常的。但如果遷移的key是bigkey, 可能遷移時間很久,在Redis客戶端重試周期內沒有完成遷移,那麼此時業務對這個key的訪問就是失敗的,後面會講怎樣解決bigkey的問題。

 

一個slot下可能有部分key被遷移完成,部分key正在等待遷移,如果被讀寫的key所屬的slot正在被遷移,則Redis Cluster會自動處理:先去源節點找,沒找到則重定向到目標節點,這個處理過程是Redis Cluster自帶的,詳細過程不再贅述。

 

3)如何解決大Key、熱Key等原因導致的Redis集羣熱點問題

 

對於Redis某些數據結構,如集羣類型,會導致大key; 某些節點訪問頻繁,會產生熱key。大key或熱key都會導致集羣中某節點成爲熱點。

 

如何定位大key熱key:

 

  • 大key:通過RDB內存分析工具,可獲取top n的大key;

  • 熱key:通過Redis客戶端向服務端監控投遞監控數據。

 

解決方案:

 

  • 定位到導致問題的Key後,通過Key計算出對應的slot,然後擴容個新節點,把除大Key或熱Key之外的槽位遷移至新節點中。然後在集羣節點擴容縮容做槽位遷移時,排除掉大key熱key槽位所在的節點;

  • 大key熱key通常是業務使用不當導致,也會線下通知業務進行治理。

 

4)如何做Redis多租戶平臺故障容災

 

①Redis節點故障怎樣做故障容災

 

Redis上容器後,單個節點故障:

 

  • 如故障的是從節點,對服務無影響,因爲主節點承接所有讀寫流量,從節點僅作爲主節點災備,從故障後,Redis operator會監聽到,並拉起新的從節點,然後加入到Redis cluster集羣,再從主節點向從節點同步數據,這時僅在數據同步時對主節點的資源有些消耗,在實際使用中我們發現對主節點資源的消耗對集羣基本無大的影響;

     

  • 如故障的是主節點,主節點是承接業務所有的讀寫流量,確認主節點故障的時間是可配置的,如配置5秒,那麼在確認週期內,對業務是有影響的,待確認主節點故障後,從節點會頂替主節點。由於主節點向從節點做數據同步是異步的,所以在主節點故障時,還沒來得及向從節點同步的數據,是會丟失的。在從節點頂替主節點後,Redis Operator會拉起新的Pod作爲從節點,然後加入Redis cluster集羣,從主節點同步數據。

 

②如何做Redis機房故障容災

 

當前公司有A、B兩個機房,假設A機房故障時,公司監控系統會監控發現,然後可通過Redis管理平臺下發B機房的Redis集羣節點地址給Redis客戶端,Redis客戶端收到後,會自動重連到機房B,從而做到了機房故障容災切換。

 

當然原理A機房中Redis中的數據是全部丟失的,這個方案主要用於Redis作爲緩存,同時業務可降級的場景。重點核心業務不強依賴於Redis。

 

5)如何保證容器中Redis節點重啓後數據不丟

 

Redis目前公司內幾乎所有的業務使用都作爲緩存數據使用,無需持久化,其高可用通過主從節點數據冗餘來保證。

 

對於個別需要持久化的Redis需求,K8S可通過存儲持久化數據到文件存儲,當容器重啓後可從文件存儲中重新獲取持久化數據,這裏我們驗證了持久化數據到PVC共享存儲,採用的阿里雲的NAS文件存儲,測試是OK的,待線上實際場景驗證。

 

6)多租戶共享Key的解決方案

 

多租戶共享key,是指兩個不同的應用要使用相同的key來共享數據。由於只有相同namespace的中的key的前綴才相同,要兩個應用共享key,就需要讓這兩個應用在同一個namespace中,Redis平臺會把這兩個應用視爲同一個應用。

 

由於namespace是資源配額的最小單位,這兩個應用需要共同申請資源配額:即不只key共享,申請的資源配額也要共享。

 

四、Redis多租戶平臺收益總結

 

1、降低服務器使用成本  資源共享

 

在VIPKID實戰中,對比Redis容器雲優化前後,服務器使用成本減少了40%。

 

2、提升運維效率 – 從人工運維到自動化運維

 

在Redis集羣部署在虛機時代,Redis集羣需要人工維護,集羣數越多,運維成本越高。當Redis上K8S容器後,一切運維操作都是自動的,這時我們已經不再關注集羣數的多少,依賴K8S與Redis管理平臺配合,做到了全自動化運維。

 

3、提升系統穩定性  故障容災自動化

 

藉助於Redis多租戶平臺對節點故障容災與機房故障容災的能力,做到了故障容災的自動化,大大縮短了故障容災的處理時間。

 

>>>>

Q&A

   

Q1:Redis集羣,一個服務器和另一個服務器是怎樣均衡負載的?監控工具麼?

A1:A1:我們Redis集羣採用Redis Cluster集羣模式,slot的範圍是0~16383,通過算法把槽位平均負載到Redis集羣的每個節點中。

 

Q2:Redis在K8S環境自動擴容後,slot也是自動遷移嗎?

A2:K8S自動擴容後,slot是自動遷移的,但在擴容時,不是由redis operator觸發的,而是由Redis管理後臺觸發,因爲擴容時做槽位到節點的負載均衡,是不包含bigkey所在槽位專用的節點的,管理後臺才知道哪個節點是bigkey專用的。

 

Q3:如何避免多個用戶同時讀取Redis中相同的數據?

A3:這位同學是想問,不同租戶間數據怎樣不會互相讀取到吧?這是通過每個應用申請不同的namespace來區分的,例如,應用1和應用2在同一個Redis多租戶集羣,他們都執行了set aaa bbb指令,但應用1的namespace是 cluster1@app1, 應用2的namespace是 cluster1@app2,那麼他們的set aaa bbb指令經過Redis客戶端後,變成了"set cluster1@app1:aaa bbb"和“set cluster1@app2:aaa bbb”,通過key的不同前綴來區分。

 

Q4:Pod的IP是固定的嗎?

A4:Pod的IP重啓後是變化的。具體細節詳見問題6答案。

 

Q5: Redis內存大小配置多少?配置rdb嗎?

A5:Redis多租戶集羣的內存分配,跟裏面應用的峯值內存有關,例如有兩個應用共享Redis集羣,一個應用內存峯值是2G,另一個應用內存峯值是3G,那麼虛機環境下,集羣的內存會預留2倍,即分配10G(10G=(2G+3G)*2);容器環境下,預留1.5倍,即7.5G(7.5G = (2G+3G)* 1.5)。

 

我們公司的Redis場景全部是內存場景,無持久化場景,Redis數據的高可用是通過一主一從冗餘實現,其中”主“承接業務的全部讀寫流量,”從“不承接流量,僅用於”主“故障時做災備。做RDB僅在故障定位時,dump RDB模型做問題分析定位。

 

Q6:Redis是通過容器IP訪問,還是通過K8S訪問?

A6:通過容器中Pod的IP訪問。當Pod重啓後,IP會變化。Redis客戶端會緩存集羣中所有IP列表,Redis管理後臺會定時同步最新的集羣IP列表給Redis客戶端,假設Pod重啓IP變化了,但Redis客戶端還沒來得及從Redis管理後臺同步到最新IP,那麼Redis客戶端會訪問舊的IP,這時它找不到該IP,會訪問集羣中的其他IP來拉取集羣中最新的IP列表,從而訪問到變化的IP。

 

Q7:Redis管理平臺有演示環境嗎?

A7Redis管理平臺,主要提供監控、數據展示、自動化運維等能力,有機會可以單獨交流。

 

Q8:Redis是共享存儲還是本地存儲,如果用共享存儲,是否遇到過aof刷緩存導致的線程阻塞?

A8:當前我們公司的場景Redis數據全部存在內存中,通過主從數據冗餘來保證數據高可用,暫沒有持久化場景。

 

Q9:Redis集羣如何解決key分佈不均的問題呢?

A9:某個key具體落到哪個槽位,是通過負載均衡算法保證每個槽位中的key數量保持均衡,然後確保槽位平均分配到Redis集羣中的每個節點。

 

當然每個槽位中key數量基本相同的情況下,每個key的大小不同,對於bigkey的治理方案,請參考我發的PPT及文檔;對於槽位落點不均衡的情況,我們在業務低峯時段有巡檢,然後通過自定義算法,平均負責槽位到每個Redis節點(大key、熱key的槽位除外)。

 

Q10:Redis集羣壓測工具有推薦麼?

A10:都可以,無特別推薦,我們QA選的redis-benchmark。

 

Q11:Key生命週期更新策略的優化?

A11 : 對於Redis無過期時間的key的治理,我們的策略是通過RDB內存分析工具按key的大小從大到小排名,找出top n的key, 再用debug命令逐個查找每個key有多久沒有被訪問了,然後拿着這個結果找對應的業務聊,逐步進行治理。

 

↓點這裏回看本期直播

閱讀原文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章