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

Kube-apiserver 又 OOM 了?

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

由來

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

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

原理

Cacher 加載

圖片圖片

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

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

圖片圖片

Cacher 數(shù)據(jù)流

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

歷史問題

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

kube-apiserver OOM

內存消耗來源

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

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

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

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

常見場景

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

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

too old resource version

原理

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

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

常見場景

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

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

優(yōu)化

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

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

社區(qū)也已經在多個版本之前進行了優(yōu)化來降低這個問題出現(xiàn)的概率。

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

針對問題二,有兩個優(yōu)化,引入了 BOOKMARK 機制來優(yōu)化同一種資源不同篩選條件導致的問題,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]。

思考

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

最后留一個思考,kube-apiserver 的內存壓力主要來自 list 請求,那么我們是否可以不使用 list 請求而是使用一種流式處理來實現(xiàn) list 的功能呢?這樣是不是就可以把內存消耗限制在一個常數(shù)的空間復雜度范圍內了?下一篇將會專門分析使用流式 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

配置信令系統(tǒng)

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瀏覽器網頁

2014-07-23 10:19:02

小米4

2022-12-07 07:35:20

B站裁員隱情

2022-10-17 10:13:58

谷歌云游戲

2020-03-31 16:02:23

戴爾

2021-07-22 07:50:47

刪庫系統(tǒng)數(shù)據(jù)

2019-05-27 08:09:43

WiFi無線信道上網

2020-07-08 09:50:37

Java內存快速定位

2022-11-18 07:34:12

Docker項目目錄

2020-07-30 07:47:32

互聯(lián)網

2020-10-27 10:50:04

軟件教父網絡

2022-10-14 08:18:07

Guavaweb應用

2023-11-26 09:04:10

Vue性能
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品2区 | 欧美一区二区三区免费电影 | 日韩视频在线播放 | 一区二区国产精品 | 在线观看视频h | 欧美黑人又粗大 | 精品国产一区二区国模嫣然 | 日韩欧美国产一区二区 | 亚洲美女天堂网 | 婷婷综合五月天 | 在线观看www视频 | 玖玖综合在线 | 一区二区三区欧美 | 成人午夜精品 | 91免费在线看 | 亚洲a视频 | 久久久综合久久 | 国产精品18久久久久久久 | 日日摸夜夜爽人人添av | 亚洲电影免费 | 亚洲二区在线观看 | 丝袜一区二区三区 | 福利视频网站 | 成人免费视屏 | 欧美精品a∨在线观看不卡 国产精品久久国产精品 | 91玖玖 | 日韩欧美在线观看 | 四虎永久在线精品免费一区二 | 韩日免费视频 | 日韩精品色网 | 成人在线中文字幕 | 亚洲网一区 | 男女啪啪网址 | 99精品久久 | 精品av天堂毛片久久久借种 | 一区二区三区四区在线视频 | 欧美精品一区二区三区在线播放 | 久久www免费人成看片高清 | 午夜国产一级片 | 中文字幕精品一区二区三区精品 | 欧美日韩中文字幕在线播放 |