干货 | 数万实例数百TB数据量,携程Redis治理演进之路
作者簡介
向晨,攜程資深數據庫工程師;布萊德,攜程技術專家;皓月,攜程技術培訓生;
一、背景
攜程自2013年開始使用Redis,舊時期為Memcached和Redis混用狀態。由于Redis在處理性能,可儲存key的多樣化上有著顯著的優勢,2017年開始,Memcached全部下線,全公司開始大規模使用Redis。Redis實例數量也由剛開始的幾十個增長到幾萬個,數據量達到百TB規模。作為Redis的運維方,為保證Redis的高可用性,DBA的壓力也隨Redis使用規模的增大而增大,集群的擴容,上下線,實例擴容都面臨著不小的挑戰。
攜程Redis并非采用原生的cluster或者第三方開源的proxy,而是使用自主研發的CRedis Client組件,部署在所有的應用端。該方案的好處是,Redis的使用者只需要知道自己的Redis名稱,就可以訪問自己的Redis,而不需要關心Redis的實際部署情況。
這也意味著全部的運維操作都需要在CRedis中注冊并推送到所有的客戶端,為保障Redis的持續可用性,DBA的所有運維操作都是以數據的安全性為第一準則,盡可能做到擴容遷移等操作透明化,保證用戶至上。
圖1 CRedis架構二、單機多實例時期
Redis使用早期(2016年以前),Redis服務器上線后,由DBA在物理服務器上部署多個實例,然后這些實例在CRedis中注冊,提供給應用訪問。資源優化和性能的平衡由DBA管理,主要是基于服務器的內存、CPU、網絡帶寬等指標來手動調節,和MySQL的單機多實例類似。DBA在集群上下線和部署時需要自行在pool中尋找合適的機器,當然我們也總結出了一套比較合理的優化算法和方式來管理Redis的部署。
在Redis的使用爆發增長時期(2016-2018),我們上線了一套Redis自動化治理系統RAT(Redis Administration tools)。Redis的上下線和擴容從手工時代來到了自動化部署和自動擴容時期,Redis運維管理難度,隨著實例大規模增加而增加。
由于前期對Redis的申請和擴容沒有做太多限制,物理服務器的使用率一直不太理想,維持在40%+左右。加上頻繁擴容導致的超大實例(20GB+),稍不注意在全量同步時就容易引起實例OOM,影響業務。為了提高內存效率和降低手動操作風險,DBA迫切需要一種更先進的部署治理系統來管理Redis。
三、容器化時期
2018年開始,隨著容器化的流行以及Kubernetes成為容器編排的事實標準,我們也開始逐步探索有狀態應用容器化。
Redis的部署由Kubernetes根據設定的規則自動化調度部署,DBA再也不需要操心資源的問題,操作效率提高了幾十倍。Redis的上線和無狀態的應用一樣接入到了PaaS系統中,Redis的分配也劃分了多個可用域(Region),每個Region劃分多個可用Pool滿足相關性強的Redis集中部署。
Redis作為一種典型的有狀態應用,很多的落地經驗可以參考前面的文章《攜程Redis容器化實踐》。但在當時容器化的規模只有幾千個,而目前已經增加了十倍多,還在不斷增長,對于如此大規模的實例數,治理策略的調整勢在必行。
3.1 二次調度
很多情況下,業務方在申請Redis時,并不特別清楚該Redis會用到多大,隨著業務量的增長或調整,Redis的使用量可能會遠超或遠小于原始的分配額度。對于遠超額度的,為避免Key剔除,以及由此帶來的主從切換,影響業務的持續可用性等問題,我們會在Redis的使用率(UsedMemory/MaxMemory)達到90%的時候進行自動擴容處理(修改Redis的MaxMemory),也就是內存超分,但支持內存超分帶來的負面效果也很明顯:
1)Kubernetes的Request會失去它原先占位作用,因為真實的用量無法感知。
2)因為Request無法感知,所以為了防止實際的宿主機內存被打爆,我們必須限制宿主機實例的個數。
3)限制實例個數是根據每個實例分配的平均期望內存來估算,實際分配中會導致資源的利用率并不平衡。
而對于遠小于原始分配額度的,缺點也很明顯:
1)實例占用了大量的Request配額,而實際用的很小。
2)大量的配額無法釋放,導致新的實例無法部署到某些很空的宿主機上。
3)某些很空的宿主機無法部署新實例,利用率低下。
由于Kubernetes對于這種有狀態應用支持的不夠完善,而我們的Redis集群又有幾千臺的規模,在處理這些問題時必須分而治之,各個擊破。
因為遷移實例需要遷移一組,一組一般是2個或更多實例,對于運維來說是個非常重的操作,但遷移實例可以修正Request配額,讓其適配UsedMemory。而漂移只需要遷移一個實例,是個相對輕量級的操作。對于Request和UsedMemory嚴重不匹配的(一般是2倍以上或1/3以下的關系),我們通過遷移來修正。而對于Request和UsedMemory相差不是很大的,我們必須在外圍進行二次調度。
二次調度可以認為是宿主機資源Rebalance的過程,而在二次調度之前,我們必須要厘清二次調度的目標:
1)對于一個標準的宿主機,為了支持自動擴容,我們認為Request 100%,內存使用率為60%-65%為最優狀態。
2)每個宿主機上內存使用率盡可能地平均,也就說方差盡可能地小。
3)Node可用內存小于35%,禁止調度,大于45%,開放調度。
對于第三點,在外圍有專門的Job來檢測Node的可用內存,來cordon/uncordon符合條件的Node。
對于上面第一第二點的兩個目標,我們設計了支持2種模式的局部最優平衡算法:
1)預留制
由于Redis實例在使用過程中內存使用量不斷增長,且增長趨勢無序無規律,使得某些宿主機上的內存可用率很低(如圖2所示宿主機可用率約為24%-26%)。因此,可以通過將內存不足的宿主機上的實例漂移到閑置或內存充足的宿主機上來緩解源宿主機的內存壓力。
圖 2 預留制算法下宿主機的二次調度情況首先,通過選擇需要被二次調度的源宿主機以及指定目標內存可用率(如圖2為50%),預留制算法可將源宿主機上需要被調度的實例漂移到閑置宿主機上,或是調用已經實現的bestnode接口,自動為源宿主機上需要被調度的實例選擇符合調度條件的目標宿主機,且二次調度后目標宿主機以及閑置宿主機的內存可用率不低于指定的目標內存可用率。
bestnode是已經實現的一種二次調度的策略,可為實例選擇相同label、相同zone且可用內存、可分配內存及宿主機可容納實例個數充足的最優目標宿主機,從而保證實例可以成功地漂移到目標宿主機上。
2)完全平衡制
由于集群中宿主機內存使用率的差距非常大(如圖3所示),為了使每個宿主機上內存使用率盡可能平均,即方差盡可能小,可以通過將內存緊張的宿主機上的實例漂移到內存充足的宿主機上,從而縮小宿主機內存使用率的差距。
圖 3 完全平衡制下宿主機的二次調度情況如圖3所示,通過手動選擇需要平衡的宿主機實例,完全平衡制算法將計算宿主機群內存使用率的最小方差(圖3中宿主機群內存使用率達到最小方差約為61%),并在宿主機群間做實例的調度。
在實例漂移前Redis集群中還存在如圖4所示的情況。由于某些宿主機上實例的UsedMemory很小,導致宿主機的內存可用率很高卻由于實例個數已滿無法再將實例漂移到宿主機上,使得宿主機的內存利用率不高。相反,也存在實例的UsedMemory很大導致的宿主機內存可用率低但還可將實例漂移到宿主機上的情況。
對于這種情況,我們首先將內存可用率高的宿主機上UsedMemory最小的幾個實例漂移到內存使用率低的宿主機上,從而為宿主機騰出實例個數配額,接著將內存可用率低的宿主機上UsedMemory較大的幾個實例漂移過來,從而平均宿主機的內存使用率。
圖 4 內存可用率高卻無可分配pod的宿主機與內存可用率低有可分配pod的宿主機用戶可配置二次調度的參數,如指定可漂移實例的Max/Min UsedMemory,Rebalance次數,最后生成一個config文件。
預留制算法與完全平衡制算法在選擇源宿主機上需要被調度的實例時都使用的是First Fit Decreasing(FFD)算法,即首先計算達到目標內存可用率需從源宿主機上釋放內存(ReleaseMemory)的大小,接著遍歷由大到小排序后源宿主機上實例的RequestMemory以及UsedMemory,將符合條件(RequestMemory<ReleaseMemory且UsedMemory<ReleaseMemory)的實例列入候選調度名單。通過對歷史遷移記錄使用機器學習等方法,根據以往的遷移開銷可以生成實例調度的集群畫像(黑/白名單)。
在先對宿主機進行預處理后(如上圖4),根據二次調度config文件,綜合考慮黑白名單、實例優先級,并且排除不能被調度的實例類型(如XPipe以及多slave實例)后,即可確定最終需要被二次調度的實例名單。最后即可生成如圖5所示的二次調度方案。
Redis主要瓶頸在內存,因此我們暫時也只考慮內存。但這種二次調度的模式同樣可以應用于其他有狀態應用的場景,如Mysql/Mongodb/Es等,只是考慮的維度更全面(Cpu/內存/磁盤)。
圖5 實例二次調度方案3.2 自動化漂移
有了上面的二次調度,我們可以手工或者自動生成二次調度的計劃或任務,在指定時間觸發,此外我們上線了容器自動化漂移系統,漂移操作支持下面幾種類型:
1)指定實例漂移到指定宿主機。
2)宿主機完全down掉,無法恢復,一鍵漂移。
3)宿主機有故障需要維修,一鍵漂移。
4)二次調度計劃自動或手動漂移。
根據之前的描述,我們所有的運維操作需要在CRedis中注冊,我們也針對Redis實例在CRedis中的不同角色,在邏輯和物理層面進行了無縫銜接,在漂移過程中自動修改CRedis的訪問策略,數據同步,Xpipe DR系統自動化注冊(Xpipe是攜程Redis跨IDC容災的方案),自動添加刪除哨兵等。
?
圖6?容器漂移拉出流程3.3 Cilium
由于我們漂移過程中,整個IP是不變的,IP不變的邏輯由OVS來保證,這樣好處是對客戶端和中間件透明。但隨著攜程單個IDC內容器部署密度的越來越大,大二層網絡的交換機表項無法承受這么多IP在整個IDC內可漂移。
Cilium是下一代云原生的網絡解決方案,之前有其他同事的文章有所描述,這里不再展開。我們將Redis容器跑在了Cilium上,漂移過程中Redis換宿主機后IP會變,這樣會涉及多個系統的數據變更,如哨兵記錄了老IP,當前實例卻變成新IP,這時候正好分配一個老的IP給了新的實例,導致復制關系錯亂,稍有不慎便會導致生產事故。也對漂移流程的可靠性提出了更高的要求,我們細化每一步漂移流程,設計合適的狀態機,保證每一步的可重試和冪等性。
3.4 傲騰落地
大規模的Redis用量以及增長速度迫切需要我們調研性價比更高的解決方案,這時候Intel傲騰技術進入了我們的視線。目前為止,傲騰宿主機大約為整個Redis宿主機的10%左右。
傲騰SSD
率先被我們引入的是傲騰SSD,通過2塊SSD組合,可以提供操作系統約700多G的內存。
圖7 傲騰SSD和純內存宿主機對比從實際效果上看(圖7)傲騰SSD的平均延遲相比內存還是相對明顯,從0.3ms上升到了0.9ms,與測試的結果吻合,但某些業務能接受這種延遲的上升,并且能節約60%成本,因此我們首先在攜程內部小范圍的部署了傲騰SSD。
傲騰AEP
盡管傲騰SSD可以符合我們的部分要求,但缺點還是比較多。
1)驅動支持不完善,基本所有的廠商需要對應的RAID卡驅動,并且每家還不太一樣,現實狀況是需要編譯多個內核版本來適配不同廠商的驅動。
2)裝機繁瑣,需要注冊碼,還要開關機多次,并且還有失敗的概率,是否能最終進入系統有點憑運氣。
3)700G+作為Redis宿主機來說相對太大了,因為我們需要考慮Redis宿主機萬一掛了恢復的時間。
圖8 傲騰AEP和純內存宿主機對比從線上結果來看,傲騰AEP和純內存的耗時比較接近,業務的監控狀態如上圖。傲騰與普通物理內存實際運行區別已經非常小,可以滿足絕大部分業務場景。并且相對于傲騰SSD,傲騰AEP還有以下的優點:
1)裝機方便,不需要注冊碼,也無需額外的驅動程序
2)可以選擇的規格比較多,可以方便組合CPU/內存/傲騰AEP的配比
我們經過數量和大小的權衡,選擇的規格是32C CPU+4個128G的傲騰AEP,傲騰AEP與內存配比是1:4。單GB成本約為純內存的50%。
四、混合云時期
2019年開始,隨著集團業務的全球化,業務海外訪問量的增加,Redis出海的需求也不斷上升,由于Redis的快速響應要求,我們也需要在公有云上部署Redis群集,以提供給當地的應用訪問,減少延遲。
然而公有云上無法提供裸金屬服務器,實際提供的宿主機都是虛擬機,出于容災方面的考慮,我們將Redis的宿主機嚴格按zone打散,讓master/slave部署在不同的zone,這樣即使一個zone全部down掉也完全不影響Redis的業務。此外對申請部署透明,對于用戶來說,只需要選擇想要部署的Region即可將Redis部署到海外,與私有云完全一致的體驗,至此攜程的Redis走到了混合云時期。
Redis On SSD
公有云上的Redis價格非常昂貴,約為私有云的10倍左右,并且私有云還有類似傲騰這種方案來進一步降低成本,但Redis需要出海的規模巨大,而攜程大部分Redis需要通過內部的Xpipe系統同步到海外,因此迫切需要我們尋找一種廉價的方案來代替Redis跑在公有云上。
我們調研一段時間,最終選擇開源的kvrocks作為二次開發的方案,自己開發實現支持Redis的SYNC和PSYNC協議,無縫對接公司的Xpipe,來作為出海Redis的替代方案,實現方案和詳細的數據后續會有專門文章介紹。
這里簡單說下結論:下圖為線上生產環境同一Group的2個Redis/2個kvrocks的訪問平均延遲,表現較好的那兩條線是kvrocks實例,從線上結果來看,與Redis表現沒有什么差距。而這種替代方案可以節約60%-80%的成本。
圖9 kvrocks/Redis平均響應時間對比五、總結
Redis作為有狀態應用,容器化和治理都需要很大的精力投入,而攜程Redis治理既不盲從社區,為了支持內存超分,也難以照搬公有云廠商的相應的Redis PaaS服務,從實際業務需求出發,走出了一條既可行又現實的路。
總結
以上是生活随笔為你收集整理的干货 | 数万实例数百TB数据量,携程Redis治理演进之路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蚂蚁金服面试经历!临场发挥!
- 下一篇: 图解Spring循环依赖,看过之后再也不