馴服 Kubernetes!網易數帆雲原生運維體系建設之路

網易數帆發表於2022-05-02

本文系作者GOPS全球運維大會演講內容,由高效運維社群整理。

本次主題主要會包括兩個方面,首先面對雲原生技術的快速發展和落地,傳統運維體系應該怎麼去構建及過程中遇到的衝擊和挑戰,會有一個簡單的分析。

其次,在面對不同的挑戰時我們做了哪些事情,我會根據內部實踐來做一些分享,希望能給大家一些參考。

對於運維來說,其實就是效率、穩定性、成本。其實,不管是穩定性,還是提升運維效率都是為了錢:效率提升了能減少人力成本,穩定性保障/監控報警做得好,可以少故障少賠錢。對於運維來說,當然還有一個非常重要的是安全,不過今天我們不會怎麼講安全。

在正式開始之前,我先簡單介紹一下網易這邊的技術情況。網易內部的各個BU之間的技術棧往往是有很大差異的,比如遊戲、音樂、嚴選等,基本上可以認為是完全不同行業的,都有自己的特色以及行業背景,這導致在網易建設大一統的平臺不太現實,所以我們更關注一些更細微的點。

1. 運維的新挑戰

新技術棧

網易數帆部門大概從 Kubernetes 1.0 釋出時就開始接觸容器了,大規模使用容器是在18年前後。業務使用容器後面臨的首個挑戰是全新的技術棧,這個時候運維體系該如何規劃?相關的技術選型,包括網路/儲存方案的選擇,叢集規模容量規劃,往往因為前面的短視造成後面的很多窘破。

容器化的場景下,對於 Kubernetes 的使用上,我們沒有對業務部門做任何限制,業務部門可以任意呼叫 Kubernetes API。因為使用模式多樣,導致運維保障面臨更多挑戰。很多由於業務的誤用導致的問題,一樣需要運維保障來兜底。

早期基礎設施(包括Docker/核心/Kubernetes)一直Bug不斷,比如,Docker 18年前很多的經典Bug,我們都遇到過。不過這兩年比較新的版本問題已經少了很多了。

我們部門使用的主流作業系統發行版是debian,可能跟在座各位同仁絕大部分使用centos的不太一樣。Debian發行版的好處是核心以及軟體版本都相對較新。所有遇到的核心問題,也是需要我們自己去處理修復。

另外,容器化剛開始的時候,畢竟是新的技術棧,想招到匹配崗位的人才較困難,人力成本比較高。

技術慣性

技術慣性大家比較能理解,很多公司都有自己的傳統的運維平臺。從傳統的運維平臺到轉變使用 Kubernetes 來做釋出管理,從思想上,操作方式上,實現方式上多個方面,我們發現中間有很多鴻溝,彌合鴻溝也是很痛苦的事情。

這樣就導致開發人員的一個認知,本來虛擬機器用得好好的,你們搞什麼容器,現在出問題了吧,反正賴容器就對了。

一句話,傳統的運維開發方式對雲原生沒有做好準備。

知識庫

知識庫的問題,首先雲原生落地過程中,當前狀態很多知識庫還不夠完善,有時候遇到問題去搜尋的話,還是會抓瞎的。

我們團隊的人員因為處理了大量的實踐問題,經驗豐富,我們也輸出了不少文件。

但是我們發現業務方真正遇到問題的時候,壓根不翻文件,而是直接甩給我們。當然這裡面的原因,可能是因為缺乏雲原生相關技術背景不足而看不懂,或者就是一個意識問題。總的來說,知識庫的傳承成本比較高,一些問題的預案和效率是極低的。

我們認為推動雲原生容器化落地過程中運維在這一塊目前面臨比較大的挑戰。

組織與人員架構

在傳統開發場景下最上層是開發、測試,中間會有業務的架構團隊、應用運維、系統運維、平臺開發,下面是整個IDC的基礎設施保障,整個架構層次分明。

但如果某個公司正在做雲原生容器化落地,你會發現中間層成為了一團漿糊,多多少少大家工作都有一些交叉。如果一個平臺開發不知道業務方使用容器的姿勢,可能會出現業務方會用各種奇怪的玩法而導致出問題,最後還是運維運維來兜底。

問題的原因在於每個工程師的定位發生了改變,比如 SA 以前只管理機器,現在會涉及到容器的網路和儲存,需要去了解 Kubernetes 的工作原理,這樣一來很多人都必須去學習 Kubernetes。

容量管理

關於容量,有幾個方面。一個是業務方不合理申請資源,另一個是業務方也無法預知情況,比如突然來了一個促銷之類的活動,容量需求增長。

運維 Kubernetes 還一個比較關鍵的容量問題是,控制元件的資源消耗的容量評估常常被忽略。客戶往往部署了 Kubernetes 叢集並配置了報警,然後後續就不停地加節點。突然某一天發生了事故崩掉了,找運維問怎麼回事,結果可能發現就是管控面容量不足了。

這裡展示一個截圖,因為 Kubernetes APIserver 重啟了一下,記憶體在極短時間增加了百分之二十多,重啟的那一剎那會有大量的請求進來,導致記憶體消耗得比較厲害。這種情況下,一般都會配置有閾值報警。當然如果你不處理這種問題,一旦觸發了,接下來可能會出現雪崩效應,再也拉不起來了,直到你增大資源容量為止。

接下來簡單從剛才我說的運維提效、穩定性保障、成本上我們做的實踐。

2. 運維提效

首先我們叢集使用了中心化的託管,當然並不是所有部門都是我們管的,我們只管跟我們關係比較密切的叢集。整個許可權認證體系,直接走我內部的員工認證系統,做到了統一認證,許可權還是走RBAC,授權到個人。其次是因為我們大量的人力在幫客戶做排障,不同人和不同部門一遍遍找過來,不願意看文件和你做的事情,你兜底就可以了,我們團隊一直是超載的狀態。因此,我們要把一些常見的排障診斷過程做成自動化。最後,針對監控資料這一塊,監控資料的儲存沒有直接使用開源系統,而是使用內部實現的TSDB,來把監控資料統一存下來,這樣可以更好對資料進行消費。

下面說下自動化診斷運維,剛才前面的兩位老師也都分享過類似的內容。類似的,我們也是有知識庫和流水線執行。很多公司做流水線的時候是做了自己內部一個平臺,和其他的內部系統進行對接,這樣一來可能解決了自己的問題,但是通用性並不高。我們在網易內部面臨什麼問題呢?我們還按那種方式去做別人不會用你的東西,因為你是一個平臺。別的部門要和它的做適配用起來比較痛苦。我們更多想通用一些方案,Kubernetes場景下有CRD的支援,把運維診斷、效能排查等各種東西抽象成CRD的方式去做。

我們把一個運維操作抽象成一個原子運維操作Operation,把一個機器設定為不可排程,判斷是不是符合某個已知Bug場景等。多個Operation的編排會構成一個運維流水線OperationSet。針對診斷上下文,我們做了個Diagnosis的抽象。

診斷流水線的觸發方式可以有更多種。首先使用者可以自己手動建立一個Diagnosis執行。

我們內部也使用泡泡(網易內部的IM)聊天機器人,來實現Chatops,通過與機器人聊天來觸發相關的流程。對於聊天機器人,我們不想去做比較複雜的知識理解,我們更多的是很直接的。給聊天機器人發相對結構化的語句,告訴他你幫助我看一下什麼問題就可以了。因為我們公司整個認證體系是一塊的,泡泡機器人也會通過統一的認證體系,可以很輕易找到你的許可權,避免你做一些超越許可權的事情。通過這種ChatOps你可以觸發流水線的執行。

還有一個更大的流水線觸發源,就是監控報警的觸發。比如說業務的某個應用,容器使用的CPU/記憶體佔用達到了閾值之後,可以自動觸發一次拿堆疊的資訊,做記憶體的dump,然後把對應的對戰資訊,以及dump的記憶體檔案上傳到物件儲存裡面去。接下來的流程中,是可以把這些dump出來的資料拿出來進行分析的。

當然有一些像中介軟體也會有這樣一些情況,他們往往要做穩定性保障,如果我的中介軟體例項出現了某種情況,應該執行什麼操作?類似於這樣的邏輯我們也可以把它編排起來,這樣我們可以讓其他的operater來去建立這種我們新的Diagnosis的Oparater,通過這種方式把這個東西實現起來。

簡單來說我們整個場景就是Kubernetes下的一套應用,就是用apiserver接受相關的CRD,然後用Operator做執行,大概就是這麼一個東西。

這塊我們希望這個東西后續在內部把它做成一個平臺,我們希望這個東西更泛化來看,就是通過一個事件觸發一個流程,做一些運維操作、運維診斷,傳統遺留下來的指令碼都可以完整繼承下來。詳見:KubeDiag 框架技術解析

因為Kubernetes是標準的API,如果說你是基於Kubernetes的場景,那我們的一些經驗可能是對你們有用的,很多東西景是共通的。比如,大家可能都遇到過核心版本在4.19之前的,memcg的回收處理是有問題的,會導致大量的洩露,包括像Docker的早期版本也會有大量的容器刪除不掉的問題。

我們都有一系列的workaround的手段,這樣的手段我們可以把它做得非常的智慧化,比如說我們報警監測到一個Pod退出超過15分鐘,還沒有被幹死,那我們可能就觸發一次診斷,來看是不是已知的Bug,如果是的話,我們就通過一些手段把它自動恢復掉。這種型別的診斷是通用的。

在傳統的場景下,可能不同的公司,運維人員登陸機器的方式都不一樣,因此傳統的場景下我們沒有辦法做到通用。但是Kubernetes的場景下我們可以做到通用,因為Kubernetes RBAC可以做許可權控制,我們整體的有daemonset的方式去對你的程式做一操作,去幫你收集很多東西是可以做到的。

還有比較頭疼的,像很多做AI、大資料相關的,主要是AI訓練,他們有C/CPP程式碼,會出現coredump,coredump會帶來幾個問題,會導致本地的磁碟當時使用率會很高,會影響同節點上其他的業務。這個時候我們可以通過一些方法,做到全域性統一的本地不落盤的coredump採集。還有像發資料包、打火焰圖等等類似這種,很多時候像效能診斷,還有一些常規的軟體Bug workaround是非常通用,這個都是底層的能力了。

我們再往上考慮業務層面也會有一些很通用的能力,比如說一個Node出現半死不活的狀態可以把Node隔離掉等等。我們希望後面能夠把這個東西做得更完善一點,希望有更多的場景,有更多人蔘與進來。

我們整個專案是一個框架加很多的規則,這塊目前也已經把這個東西放出去了,我們短期內的一個想法是把介面做好一點,然後我們把之前傳承下來的很多經驗輸出成程式碼,放到流程裡面去,達到開箱即用的流程體驗。

關於提效這一塊通過這種方式能夠做到什麼效果呢?不再讓我們團隊Overload了,不用看文件看怎麼回事自動解決掉了,解決自己人力耗費的問題。同時這個東西是相對程式碼化的,不存在某個兄弟比較猛技術比較好,他走了搞不定了,因為程式碼留下來了。

3. 監控與報警

穩定性大家知道 Tracing、logging 和 Metric,但是像 Tracing 和 logging 幾年前提得比較少,現在分散式場景下的很多問題,導致分散式 Tracing 也做起來了。但是 Metric 這方面一直是一個金字標,今天也主要關注的是 Metric 這一塊。

在採集系統指標時,傳統的方式可能是*stat資料,各種從procfs中的資料進行採集,現在我們希望通過 eBPF 拿到更細粒度的資料。採集這些個細粒度的資料的主要原因是分清責任。比如業務方要查一下業務抖動,可能是基礎設施和機房的問題,這個時候很難自證清白。可以通過這樣的手段,像我們採集 TCP 的 RTT 資料可以證明是不是系統的問題。

記憶體更多是記憶體回收相關的,像 Memory Cgroup 相關的回收、PageCache reclaim 這類報警的指標採集了不少。CPU 排程這塊,基本上就是關注排程延時,程式從它變成 Running 到開始執行到底延遲了多長時間。

還有像檔案IO,有時候磁碟會抖一下,抖一下有的業務方非同步寫日誌不受影響,如果是全同步會導致受到影響。這個時候我們需要定位到到底是什麼原因導致的。我們還需要監視,像VFS的延遲等等,這些指標都需要從底層去獲取到。

像 eBPF 現在支援 uprobe 技術,因此還可以做另外一塊。比如傳統監控應用訪問資料庫,會在客戶端,像 Java 程式通過 agent 注入一些位元組碼增強來發現訪問 MySQL 快了還是慢了。如果有了 uprobe 機制,在 MySQL 或 Kafka,程式碼固定情況下可以用 uprobe 勾服務端的函式,不再從客戶端的方式抓問題了。

這時候也是自證清白的,比如說使用者自己程式碼有Bug,他認為是你後端 Redis 出問題了,你可以說資料沒有到我這裡來,我函式執行勾出來了,效果是好的。這一塊當前我們還是在嘗試和落地中。

然後在 Tracing 領域的話,像一些手段是通過日誌分析,通過SDK程式碼裡面打日誌的。事實上如果在一些傳統的應用,一些非常老的遺留的應用裡面,你不太好讓他做這樣的整合的時候可以用 uprobe 的手段拿到裡面動態的資訊。也可以在在網路層面,做一些 Redis 協議的解析,可以通過網路輸出流量層面拿到錯誤資訊、延遲資訊,錯誤。這個相關的實現相對複雜一點,這個階段我們是嘗試落地階段。

當然我們現在做這個事情非常細粒度指標對於核心版本要求比較高,我們內部有一個部門的核心版本已經到 5.13 了,比較激進。

這樣以來造成我們採集到指標比較多,指標是用來做報警的,報警其實不好做,前面兩個老師也一直在提這個問題,報警解決什麼問題?第一不該報警不能報出來,第二該報的報警都要報出來,這兩個都不好解決。

報警的治理

首先傳統的閾值報警有什麼問題呢?它會變的,你今天的請求和明天的請求由於什麼原因就變了。要麼就是沒有快速配、改閾值出現了問題,或者是出現了誤報警你自己嚇一跳,會導致這種情況。無閾值報警相關的想法,大概我們從2017年、2018年開始探索的東西,只不過我們現在往雲原生去做移植。

還有一塊是關聯報警抑制的問題,比如說你磁碟慢了,磁碟慢了會導致什麼問題呢?會導致使用者打日誌會卡,日誌一卡像Java程式會導致處理執行緒增多,執行緒池會爆掉,導致響應延遲變高等一系列的影響。事實上你想一下這些報警之間還是有關聯的,我們報警的時候可能只需要報出來磁碟慢就可以了,其他沒有必要報,其實需要一個報警關聯作為抑制的策略。

這塊怎麼做的呢?大家今天看到了AIOps的專場裡面人非常多,大家都會對這樣一些話題感興趣。目前來看不管是實踐還是個人感覺來看,完全依賴AI不是那麼現實的,至少現在這個階段好像沒有看到很好的實踐。

我們現在更多是做了數理統計,有少量的機器學習演算法幫助我們建模,這個建模更多是分析單一指標或者是相關聯的指標,相關聯的指標還是靠經驗輸入,不是真正自動分析。因為到現在為止,還是不相信AI真的能達到那麼好的智慧。

我們目前依賴數理統計演算法,正態分佈等等,使用非常基本的機器學習演算法通過離線計算,來生成模型,然後實時計算模組做異常檢測,達到無閾值報警的狀態。還有關聯報警抑制,關聯報警抑制是輸入進去的,不是機器學習學習出來的。

還有報警的反饋問題,報警之後檢測異常發給你一個報警,這個報警是不合適或者是有問題的你可以做一個調參。比如說我們算某一個指標的周期函式做擬合,擬合某個引數,它的週期就是有問題,我們假定就是不對的可以手動調一下、改一下都可以。或者是有些報警瞎報有問題的,我們抑制掉就好了。

我們希望能做好一整套,從監控到問題觸發,到整體離線計算、實時計算,人工反饋回去這樣做的閉環,使得這個東西做下去。

這是我們關於監控和報警這塊的內容。

4. 成本的節約

成本節約手段

下面就是成本方面。成本管理在網易這邊,包括之前面臨最大的問題,一個事業部現在突然間來了一批任務,我們網易有做內容安全的,在某些時候,會突然有大量的業務過來。他們機器不夠用,就會到處藉機器、協調機器,浪費了大量的人力精力。

現在我們做這樣的手段,希望把內部資源池做得更好一點。成本節約主體就是資源配置的推薦問題,剛剛嘉賓也說到這樣的問題,包括升配、降配這樣的問題,就是VPA。VPA其實還是蠻有意義的東西。

另外混合部署,在業界也是比較熱的。這個東西落地下來可能真的很困難,可能真不的適合很多中小企業。

還有剛剛說到融合內部的資源池,希望構建大的池子,供每個業務部門來使用。

還有業務推動的問題,從老闆到幹活的,你做新的東西不可避免會有不穩定因素。我們的實際經驗,你在做容器化的時候遇到問題從業務方看來都是容器的問題,你在做混合部署的時候遇到所有問題,業務方都認為是混合部署導致的問題,業務方認為他自己一點問題都沒有,但是最後查下來都是業務方的問題,你要告訴他有問題。

下面簡單說一下我們的兩塊內容,一塊是混合部署,一塊是資源融合的。

混部系統

網易在前年開始嘗試落地混合部署,現在已經擁有一定的規模。

首先是兩塊,一塊是排程,一塊是隔離,排程我們做得比較簡單,雖然業界有機器學習、資源畫像、資源排程等論文和實踐,但我們目前只是基於實時資料採集來做依據。

從實際來看,基於實時監控資料來去看資源的使用量,目前來看還算好。也就是說不太會有某個線上業務真的不停在波動,至少可以讓離線業務相對平穩執行一段時間。

隔離手段

隔離手段主要有幾塊,計算主要就是CPU的排程和記憶體管理。CPU的排程傳統的CFS的Share隔離有一點問題,因為它的一個要求,公平排程不管你的業務優先順序多低都是公平的。還有最小排程延時的問題,跑一定的時間才會切換CPU。像離線的業務跑起來,比如大資料的業務,CPU一定是滿的,會造成線上業務一定的延遲是正常的。業界有比較好的落地案例,騰訊開了自己的排程類,阿里雲有自己的技術,叫Group Identity。類似的東西業界有些實踐,我們內部會參考別人的思考結合到自己的版本里面去。

還有超執行緒相關的,不知道大家清楚不清楚,超執行緒如果在物理核上實際是單核。不同場景下會有不一樣,但是一般我們認為大概120%-130%,並不是兩個核的算力,這樣一來如果說你的離線業務和線上業務在一個物理核的兩個超執行緒上,線上業務會受到非常大的干擾,在這塊在排程器上可以做一些規避。

還有像L3隔離,我們基本上限制離線任務的量。

Page Cache回收對線上業務基本可控,但是對於中介軟體服務來說,基本是不可控的。如果你的Page Cache回收沒有處理好的話,非預期被回收掉會造成效能極大下降,或者是一直不回收導致業務OOM掉。因為我們核心版本比較新,裡面也加了一些自動回收和主動回收的手段。

最後,混部最大的落地挑戰就是離線業務往往需要做到存算分離,這個跟IDC的基建相關。在自建IDC會發現IDC可能是N年前建的,老舊的裝置能不能支撐全鏈路QoS,都是打問號的。之前遇到過交換機的坑,當把流量打滿,離線業務訪問遠端儲存的網路是滿的,這個時候觸碰交換機的Bug,會導致一些隨機性的延遲,排查起來是很痛苦的。

在IO隔離這一塊,雖然 CgroupV2 裡面有很多 Buffer IO 隔離策略,但可能最簡單粗暴的還是用不同的塊裝置,這是最簡單的。

目前網易內部的隔離手段還是選擇簡單和普適性,並沒有追求極致的混合部署效果。

資源池融合

網易內部的各個部門相對獨立,意味著每個部門都有多套 Kubernetes 叢集。如果一個業務的叢集資源大量剩餘,另一個業務的資源完全不夠用怎麼辦?我們做了一個超大的資源池叫Kubeminer,我們把業務的 Kubernetes 叢集分為 Consumer 和 Provider,一個資源的消費方,一個資源的提供方,都使用 KubeMiner 的方式模擬虛擬節點。

對於資源消費方來說看到了虛擬節點,對於資源提供方來說完全無感知,因為幫別人把業務跑到他的叢集裡面去,中間進行了轉化。

通過這種方式把之前單叢集的資源擴充套件到所有其他 Kubernetes 的叢集裡去。這樣如果某個業務方容量沒有規劃好,或者是某個時間段內資源剩餘比較多,他可以把資源賣給別人。如果某個業務資源方臨時有些需求,可以看看別人誰有資源。

為什麼做成這種架構?首先這個比較普適,對於 Consumer 來說無非改一下業務排程條件,把業務排程到虛擬節點就可以了,Provider來說完全無感知,他們無非看到跑了一堆我不認識的 Pod 上來。

還有一個好處對業務零感知,業務不用做特別多的適配,對於 Consumer 還是在自己的叢集,對於 Provider 沒有感知,因為不關心這個東西。我們中間要做結算體系,我們要給他把錢算清楚,這是我們的工作。

難點

難點列了三個,組織形態導致我們出現了非常分裂的場景,每一方的 Kubernetes 叢集網路方案不一樣,有各種各樣的網路方案,我們做統一資源池的時候要做到互通,這是我們做的過程當中認為挑戰比較大的。

還有對跨叢集物件同步,雖然你把Pod排程過來了,但是像 PV,像 Service 訪問、ConfigMap 之類的token等等都要在這個叢集中同步,因為我們要做到讓他體驗到在自己叢集一樣。

還有我們怎麼做最佳撮合?什麼意思呢?業務方需要10個Pod,Provider沒有一個叢集能夠承擔10個Pod的容量,怎麼辦?我們要往好多個叢集去分。那我們怎麼把它做得更好一點?因為底下Provider叢集還在動態變,人家本來也在用,這個時候我們怎麼做到達到相對好的效果?這一塊我們現在手段比較簡單,基本上根據經驗簡單配了一些引數,因為現在的引數去撮合。

大概就是這樣子,目前沒有做特別複雜的演算法,後續可能會在排程機制上做一些手段。

落地效果

今天的整體分享大概就這些,最後有一個效果,整體成本節約產生的一些價值,我們內部並沒有做到很極致,平均CPU利用率到55%而已;某事業部的視訊轉碼業務實現了沒有消耗任何的真實資源,都以低優先順序的任務在混部著跑;通過彈性的資源池方式聚合了各種各樣 Kubernetes 叢集的算力資源,給其他業務方向提供了彈性資源能力。

今天就到這裡,謝謝大家。

作者簡介

王新勇,網易數帆容器編排團隊負責人,曾參與網易集團負載均衡,SDN等專案建設,目前主要在推動網易網際網路業務雲原生技術落地,致力於雲原生環境下的穩定性保障和成本優化相關工作。

瞭解更多

KubeDiag 框架技術解析

相關文章