成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Kube-apiserver 又 OOM 了?

云計算 云原生
我們經常看到各種源碼分析,原理解析的文章,容易輕信其內容,但隨著版本迭代,以及一些細節的處理,可能會導致我們理解不到位,或者并不能真正的掌握。

由來

前一篇已經介紹了 Informer 的實現,Informer 對 kube-apiserver 發起了 list 和 watch 請求。我們知道大規模集群下,kube-apiserver 會成為瓶頸,尤其在內存方面,相信很多人也遇到過 kube-apiserver OOM 等問題(碰巧的是最近線上連續出現兩次 kube-apiserver OOM 的問題)。本篇主要講 kube-apiserver 中 Informer 需要用到的兩個接口 list 和 watch 的實現。

網上搜索的話,可以找到大量相關的源碼解析的文章,這里我并不會去過多涉及代碼,主要還是以講原理、流程為主,最后簡單介紹下當前存在的問題,理論實踐相結合。本篇主要講當前實現,只有了解了當前實現,明白了為什么會有問題,才知道如何去解決問題,接下來的一篇會詳細分析如何解決這些問題。

原理

Cacher 加載

圖片圖片

核心組件:Cacher,watchCache,cacheWatcher,reflector。其中 watchCache 作為 reflector 的 store,Etcd 作為 listerWatcher 的 storage,store 和 listerWatcher 作為參數用來構造 reflector。數據流大致如下:

  1. kube-apiserver 啟動,針對每種資源類型,調用其對應 cacher 的 startCaching,進而調用 reflector.ListAndWatch,觸發 listerWatcher 的 list 和 watch,對應 Etcd list 之后再 watch,watch 時會創建 watchChan,從 Etcd 讀到的結果會先進入到 watchChan 的 incomingEventChan 中,經過 transform 處理后發送到 watchChan 的 resultChan 中,供 reflector 消費;
  2. reflector 會消費上述 resultChan 的數據,即 watch.Event 對象,并根據事件類型調用 store 的增刪改方法,此處 store 即 watchCache,經過 watchCache.processEvent 處理,組裝 watchCacheEvent 對象,更新 watchCache 的 cache(大小自適應的喚醒緩沖區,保留歷史 event)和 store(全量數據),并最終通過 eventHandler 將其發送到 cacher 的 incoming chan 中;
  3. cacher.dispatchEvents 消費 incoming chan 的數據,經過處理后發送給每個 cacheWatcher 的 input chan;
  4. 外部調用 kube-apiserver watch 請求后會創建一個對應的 cacheWachter 對象,最終到 cacheWatcher 的 Watch 處理機中,消費 input chan,調用 watchCacheEvent 進行事件分發;

圖片圖片

Cacher 數據流

用來緩存數據的核心結構是 watchCache,其內部又兩個關鍵結構:cache(cyclic buffer),store(thread safe store),分別用來存儲歷史的 watchCacheEvent 和真實的資源對象,其中 store 里面存儲的是全量對象,而 cache 雖然是自適應大小的,但還是有最大容量限制的,所以他存儲的 watchCacheEvent 所代表的對象集合并不一定能覆蓋 store 的全部數據。

歷史問題

kube-apiserver 在優化自身內存使用方面做了很多優化了,不過至今仍然存在一些尚未完全解決的問題。

kube-apiserver OOM

內存消耗來源

kube-apiserver 的內存消耗,主要兩個來源:

  1. 一部分來自于他緩存了集群所有數據(Event 除外,此 Event 為 k8s 的資源類型),并且為每種資源緩存了歷史 watchCacheEvent,以及一些內部的數據結構和 chan 等,這部分是不可避免的,雖然可以適當優化,但作用并不大;
  2. 另一部分來自于客戶端請求,尤其是 list 請求,kube-apiserver 需要在內存中進行數據深拷貝,序列化等操作,所需內存量和數據量、請求量正相關,隨著數據量的增加,請求量的增加,所需要的內存也越大,而且這部分的內存通過 golang GC 是沒有辦法完全回收的,而 list 請求的主要來源就是 Informer;

list 請求占用內存多的原因如下:

  1. 默認情況下(沒有指定 resourceversion 的情況下),直接從etcd獲取數據可能需要大量內存,超過數據存儲的完整響應大小數倍;
  2. 請求明確指定 ResourceVersion 參數來從緩存中獲取數據(例如,ResourceVersinotallow="0"),這實際上是大多數基于 client-go 的控制器因性能原因而使用的方法。內存使用量將比第一種情況低得多。但這并不是完美的,因為我們仍然需要空間來存儲序列化對象并保存完整響應,直到發送。

常見場景

有兩個常見的容易引起 kube-apiserver OOM 的場景:

  1. 一些 DaemonSet 之類的程序里面用到了 Informer,在進行變更,或者故障重啟的時候,隨著集群規模的增加,請求量也隨之增加,對 kube-apiserver 內存的壓力也增加,在沒有任何防護措施(限流)的情況下,很容易造成 kube-apiserver 的 OOM,而且在 OOM 之后,異常連接轉移到其他 master 節點,引起雪崩。理論上也屬于一種容量問題,應對措施擴容(加 master 節點)、限流(服務端限流:APF、MaxInflightRequest等,及客戶端限流)。
  2. 某種類型資源的數據量很大,kube-apiserver 配置的 timeout 參數太小,不足以支持完成一次 list 請求的情況下,Informer 會一直不斷地嘗試進行 list 操作,這種情況多發生在控制面組件,因為他們往往需要獲取全量數據。

too old resource version

原理

嚴格說,這并不能算是一個問題,機制如此,理論上單機資源無限的情況下是可以避免這個現象的。為了方便描述,用 RV 代指 resourceversion。

其本質是客戶端在調用 watch api 時攜帶非 0 的 RV,服務端在走到 cacher 的 watch 實現邏輯時需要根據傳入的 RV 去 cyclic buffer 中二分查找大于 RV 的所有 watchCacheEvent 作為初始 event 返回給客戶端。當 cyclic buffer 的最小 RV 還要比傳入的 RV 大時,也就是說服務端緩存的事件的最小 RV 都要比客戶端傳過來的大,意味著緩存的歷史事件不全,可能是因為事件較多,緩存大小有限,較老的 watchCacheEvent 已經被覆蓋了。

常見場景

  1. 這種情況多發生在 kubelet 連接 apiserver 的場景下,或者說 watch 帶了 labelselector 或 fieldselector 的情況下。因為每個 kubelet 只關心自己節點的 Pod,如果自身節點 Pod 一直沒有變化,而其他節點上的 Pod 變化頻繁,則可能 kubelet 本地 Informer 記錄的 last RV 就會比 cyclic buffer 中的最小的 RV 還要小,這時如果發生重連(網絡閃斷,或者 Informer 自身 timeout 重連),則可以在 kube-apiserver 的日志中看到 "too old resoure version" 的字樣。
  2. kube-apiserver 重啟的場景,如果集群中部分類型資源變更頻繁,部分變更不頻繁,則對于去 watch 變更不頻繁的資源類型的 Informer 來說起本地的 last RV 是要比最新的 RV 小甚至小很多的,在 kube-apiserver 發生重啟時,他以本地這個很小的 RV 去 watch,還是有可能會觸發這個問題;

客戶端 Informer 遇到這個報錯的話會退出 ListAndWatch,重新開始執行 LIstAndWatch,進而造成 kube-apiserver 內存增加甚至 OOM。問題本質原因:RV 是全局的。場景的景本質區別在于場景 1 是在一種資源中做了篩選導致的,場景 2 是多種資源類型之間的 RV 差異較大導致的。

優化

經過上述分析,造成這個問題的原因有兩個:

  1. cyclic buffer 長度有限;
  2. 客戶端 Informer 持有的 last RV 過于陳舊;

社區也已經在多個版本之前進行了優化來降低這個問題出現的概率。

針對問題一,采用了自適應窗口大小,雖然還是會有問題,但相比之前寫死一個值出現問題的概率要小,同時在不必要的時候縮小長度,避免內存資源的浪費。

針對問題二,有兩個優化,引入了 BOOKMARK 機制來優化同一種資源不同篩選條件導致的問題,BOOKMARK 是一種 event 類型,定期將最新的 RV 返回客戶端;引入 ProgressNotify 解決多種資源類型 RV 差異較大,在 kube-apiserver 重啟后,Informer resume 時導致的問題,本質是利用了 Etcd 的 clientv3 ProgressNotify 的機制,kube-apiserver 在 Watch Etcd 的時候攜帶了特定的 Options 開啟此功能。ProgressNotify 參考 Etcd 官方文檔[1]:

WithProgressNotify makes watch server send periodic progress updates every 10 minutes when there is no incoming events. Progress updates have zero events in WatchResponse.

詳情可以參考如下 KEP 956-watch-bookmark[2] 和 1904-efficient-watch-resumption[3]

stale read

這更是一個歷史悠久的問題了,自從有了 watchCache 之后就有了這個問題,本質是將之前直接訪問 Etcd 時的線性一致性讀(Etcd 提供的能力),降級成了讀 kube-apiserver cache 的順序一致性。

場景

  1. T1: StatefulSet controller creates pod-0 (uid 1) which is scheduled to node-1
  2. T2: pod-0 is deleted as part of a rolling upgrade
  3. node-1 sees that pod-0 is deleted and cleans it up, then deletes the pod in the api
  4. The StatefulSet controller creates a second pod pod-0 (uid 2) which is assigned to node-2
  5. node-2 sees that pod-0 has been scheduled to it and starts pod-0
  6. The kubelet on node-1 crashes and restarts, then performs an initial list of pods scheduled to it against an API server in an HA setup (more than one API server) that is partitioned from the master (watch cache is arbitrarily delayed). The watch cache returns a list of pods from before T2
  7. node-1 fills its local cache with a list of pods from before T2
  8. node-1 starts pod-0 (uid 1) and node-2 is already running pod-0 (uid 2).

詳情可以參考 issue 59848[4]。

思考

我們經常看到各種源碼分析,原理解析的文章,容易輕信其內容,但隨著版本迭代,以及一些細節的處理,可能會導致我們理解不到位,或者并不能真正的掌握。例如是否在 list 請求時傳 RV=0 就一定會走 kube-apiserver 的緩存?網上搜的話,應該都是說會走,但看代碼你會發現并不是這樣,例如當 kube-apiserver 重啟后數據還沒有完全加載好的時候,遇到 list 帶了 RV=0 的請求會直接去訪問 Etcd 獲取數據。看似不起眼的細節,可能會影響我們處理問題的思路,比如 Etcd 負載較高要查原因,如果你知道這個細節的話,就會有意識的去看所有的 list 請求,而不只是那些 RV != 0 的請求。

最后留一個思考,kube-apiserver 的內存壓力主要來自 list 請求,那么我們是否可以不使用 list 請求而是使用一種流式處理來實現 list 的功能呢?這樣是不是就可以把內存消耗限制在一個常數的空間復雜度范圍內了?下一篇將會專門分析使用流式 api 解決 list 導致的內存暴漲的問題,敬請期待~

參考資料

[1]Etcd: https://pkg.go.dev/github.com/coreos/etcd/clientv3#WithProgressNotify

[2]bookmark: https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/956-watch-bookmark/README.md

[3]watch-resumption: https://github.com/kubernetes/enhancements/blob/c63ac8e05de716370d1e03e298d226dd12ffc3c2/keps/sig-api-machinery/1904-efficient-watch-resumption/README.md

[4]stale read: https://github.com/kubernetes/kubernetes/issues/59848

責任編輯:武曉燕 來源: 云原生散修
相關推薦

2023-12-02 20:41:32

內存kube

2023-03-10 08:24:27

OOMdump線程

2022-03-16 07:58:02

OOMdubbo內存

2023-07-18 19:11:21

配置信令系統

2021-09-09 18:12:22

內存分段式網絡

2022-10-10 08:05:34

線程池OOM問題

2021-11-05 11:10:13

MyBatisSQL查詢

2017-12-28 10:44:08

JavaScript瀏覽器網頁

2022-12-07 07:35:20

B站裁員隱情

2014-07-23 10:19:02

小米4

2021-07-22 07:50:47

刪庫系統數據

2019-05-27 08:09:43

WiFi無線信道上網

2022-10-17 10:13:58

谷歌云游戲

2020-03-31 16:02:23

戴爾

2020-07-08 09:50:37

Java內存快速定位

2022-11-18 07:34:12

Docker項目目錄

2020-07-30 07:47:32

互聯網

2020-10-27 10:50:04

軟件教父網絡

2023-11-26 09:04:10

Vue性能

2022-10-14 08:18:07

Guavaweb應用
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 中文字幕国产高清 | 日本色综合 | 国产精品久久久久无码av | 欧美一级高潮片免费的 | 91高清视频在线观看 | 亚洲成人免费av | 国产探花在线观看视频 | 欧美日韩一区二区三区四区五区 | 欧美电影在线观看网站 | 成人精品在线 | 91香蕉嫩草 | 天天操天天射天天 | 国产一区二区三区久久久久久久久 | 欧美一区二区在线观看 | 日韩欧美中文 | 午夜视频在线播放 | 懂色中文一区二区三区在线视频 | 久久99深爱久久99精品 | av在线一区二区三区 | 亚洲精品在线观看网站 | 99久久精品免费看国产四区 | 久久一热 | 久久国产精品免费 | 一区二区三区精品视频 | 欧美精品91爱爱 | 国产日韩视频 | 伊人网综合 | 欧美色综合一区二区三区 | 国产a级毛片 | 99精品免费 | 国产高清一区二区 | 欧美成人精品一区二区男人看 | 国产98色在线 | 日韩 | 亚洲欧美中文日韩在线v日本 | 天堂成人av | 久久99深爱久久99精品 | 成人在线| 91久久久久久久久久久 | 亚洲视频一区在线观看 | 精品视频在线一区 | 精品久久久久久久久久久久久久 |