日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

硬核分析|腾讯云原生OS内存回收导致关键业务抖动问题

發(fā)布時間:2024/4/11 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 硬核分析|腾讯云原生OS内存回收导致关键业务抖动问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

實戰(zhàn)系列:?精選各種常見的代表性實際問題,分享一步一步思考和解決方法,梳理整個問題脈絡(luò),可以學(xué)習(xí)到解決問題各種技巧和通用技能,鍛煉解決問題思維能力,讓大家成為解決問題的高手;

往期文章推薦:

一個刁鉆的網(wǎng)絡(luò)問題,技術(shù)磚家的精彩分析

導(dǎo)語

云原生場景,相比于傳統(tǒng)的 IDC 場景,業(yè)務(wù)更加復(fù)雜多樣,而原生 Linux kernel 在面對云原生的各種復(fù)雜場景時,時常顯得有些力不從心。本文基于騰訊云原生場景中的實際案例,展現(xiàn)針對類似問題的一些排查思路,并希望借此透視 Linux kernel 的相關(guān)底層邏輯以及可能的優(yōu)化方向。

背景

騰訊云客戶某關(guān)鍵業(yè)務(wù)容器所在節(jié)點,偶發(fā) CPU sys (內(nèi)核態(tài)CPU占用)沖高的問題,導(dǎo)致業(yè)務(wù)抖動,復(fù)現(xiàn)無規(guī)律。節(jié)點使用內(nèi)核為 upstream 3.x 版本。

現(xiàn)象

在業(yè)務(wù)負載正常的情況下,監(jiān)控可見明顯的CPU占用率毛刺,最高可達 100%,同時節(jié)點 load 飆升,此時業(yè)務(wù)會隨之出現(xiàn)抖動。

捕獲數(shù)據(jù)

思路

故障現(xiàn)象為 CPU sys 沖高,即 CPU 在內(nèi)核態(tài)持續(xù)運行導(dǎo)致,分析思路很簡單,需要確認 sys 沖高時,具體的執(zhí)行上下文信息,可以是堆棧,也可以是熱點。

難點:由于故障出現(xiàn)隨機,持續(xù)時間比較短(秒級),而且由于是內(nèi)核態(tài) CPU 沖高,當故障復(fù)現(xiàn)時,常規(guī)排查工具無法得到調(diào)度運行,登錄終端也會 hung 住(由于無法正常調(diào)度),所以常規(guī)監(jiān)控(通常粒度為分鐘級)和排查工具均無法及時抓到現(xiàn)場數(shù)據(jù)。

具體操作

秒級監(jiān)控

通過部署秒級監(jiān)控(基于atop),在故障復(fù)現(xiàn)時能抓到故障發(fā)生時的系統(tǒng)級別的上下文信息,示例如下:

從圖中我們可以看到如下現(xiàn)象:

  • sys 很高,usr 比較低

  • 觸發(fā)了頁面回收(PAG行),且非常頻繁

  • 比如ps之類的進程普遍內(nèi)核態(tài) CPU 使用率較高,而用戶態(tài) CPU 使用率較低,且處于退出狀態(tài)

  • 至此,抓到了系統(tǒng)級別的上下文信息,可以看到故障當時,系統(tǒng)中正在運行的、CPU 占用較高的進程和狀態(tài),也有一些系統(tǒng)級別的統(tǒng)計信息,但仍無從知曉故障當時,sys 具體消耗在了什么地方,需要通過其他方法/工具繼續(xù)抓現(xiàn)場。

    故障現(xiàn)場

    如前面所說,這里說的現(xiàn)場,可以是故障當時的瞬時堆棧信息,也可以是熱點信息。對于堆棧的采集,直接能想到的簡單方式:

  • pstack

  • cat /proc//stack

  • 當然這兩種方式都依賴:

  • 故障當時 CPU 占用高的進程的 pid

  • 故障時采集進程能及時執(zhí)行,并得到及時調(diào)度、處理

  • 顯然這些對于當前的問題來說,都是難以操作的。

    對于熱點的采集,最直接的方式就是 perf 工具,簡單、直接、易用。但也存在問題:

  • 開銷較大,難以常態(tài)化部署;如果常態(tài)化部署,采集數(shù)據(jù)量巨大,解析困難

  • 故障時不能保證能及時觸發(fā)執(zhí)行

  • perf 本質(zhì)上是通過 pmu 硬件進行周期性采樣,實現(xiàn)時采用 NMI(x86) 進行采樣,所以,一旦觸發(fā)采集,就不會受到調(diào)度、中斷、軟中斷等因素的干擾。但由于執(zhí)行 perf 命令的動作本身必須是在進程上下文中觸發(fā)(通過命令行、程序等),所以在故障發(fā)生時,由于內(nèi)核態(tài) CPU 使用率較高,并不能保證 perf 命令執(zhí)行的進程能得到正常調(diào)度,從而及時采樣。

    因此針對此問題的熱點采集,必須提前部署(常態(tài)化部署)。通過兩種方式可解決(緩解)前面提到的開銷大和數(shù)據(jù)解析困難的問題:

  • 降低 perf 采樣頻率,通常降低到 99次/s,實測對真實業(yè)務(wù)影響可控

  • Perf 數(shù)據(jù)切片。通過對 perf 采集的數(shù)據(jù)按時間段進行切片,結(jié)合云監(jiān)控中的故障時間點(段),可以準確定位到相應(yīng)的數(shù)據(jù)片,然后做針對性的統(tǒng)計分析。

  • 具體方法:?

    采集:

    ./perf?record?-F99?-g?-a

    分析:

    #查看header里面的captured?on時間,應(yīng)該表示結(jié)束時間,time?of?last?sample最后采集時間戳,單位是秒,可往前追溯現(xiàn)場時間 ./perf?report?--header-only #根據(jù)時間戳索引 ./perf?report?--time?start_tsc,end_tsc

    按此思路,通過提前部署perf工具采集到了一個現(xiàn)場,熱點分析如下:

    可以看到,主要的熱點在于 shrink_dentry_list 中的一把 spinlock。

    分析

    現(xiàn)場分析

    根據(jù) perf 的結(jié)果,我們找到內(nèi)核中的熱點函數(shù) dentry_lru_del,簡單看下代碼:

    // dentry_lru_del()函數(shù): static?void?dentry_lru_del(struct?dentry?*dentry)?{if?(!list_empty(&dentry->d_lru))?{spin_lock(&dcache_lru_lock);__dentry_lru_del(dentry);spin_unlock(&dcache_lru_lock);} }

    函數(shù)中使用到的 spinlock 為 dentry_lru_lock,在3.x內(nèi)核代碼中,這是一把超大鎖(全局鎖)。單個文件系統(tǒng)的所有的 dentry 都放入同一個lru鏈表(位于superblock)中,對該鏈表的幾乎所有操作(dentry_lru_(add|del|prune|move_tail))都需要拿這把鎖,而且所有的文件系統(tǒng)共用了同一把全局鎖(3.x內(nèi)核代碼),參考 add 流程:

    static?void?dentry_lru_add(struct?dentry?*dentry)?{if?(list_empty(&dentry->d_lru))?{//?拿全局鎖spin_lock(&dcache_lru_lock);//?把dentry放入sb的lru鏈表中l(wèi)ist_add(&dentry->d_lru,?&dentry->d_sb->s_dentry_lru);dentry->d_sb->s_nr_dentry_unused++;dentry_stat.nr_unused++;spin_unlock(&dcache_lru_lock);} }

    由于 dentry_lru_lock 是全局大鎖,可以想到的一些典型場景中都會持這把鎖:

  • 文件系統(tǒng) umount 流程

  • rmdir 流程

  • 內(nèi)存回收 shrink_slab 流程

  • 進程退出清理/proc目錄流程(proc_flush_task)-前面抓到的現(xiàn)場

  • 其中,文件系統(tǒng) umount 時,會清理掉對應(yīng) superblock 中的所有 dentry,則會遍歷整個 dentry 的lru鏈表,如果 dentry 數(shù)量過多,將直接導(dǎo)致 sys 沖高,而且其他依賴于 dentry_lru_lock 的流程也會產(chǎn)生嚴重的鎖競爭,由于是 spinlock,也會導(dǎo)致其他上下文 sys 沖高。接下來,再回過頭看之前的秒級監(jiān)控日志,就會發(fā)現(xiàn)故障是系統(tǒng)的 slab 占用近60G,非常大:

    而 dentry cache (位于slab中)很可能是罪魁禍首,確認 slab 中的對象的具體分布的最簡便的方法:Slabtop,在相同業(yè)務(wù)集群其他節(jié)點找到類似環(huán)境,可見確實dentry占用率絕大部分:

    我們接下來可以使用 crash 工具在線解析對應(yīng)文件系統(tǒng)的 superblock 的 dentry lru 鏈表,可見 unused entry 數(shù)量高達2億+

    另一方面,根據(jù)業(yè)務(wù)的上下文日志,可以確認其中一類故障時,業(yè)務(wù)有刪除 pod 的操作,而刪除pod過程中,會 umount overlayfs,然后會觸發(fā)文件系統(tǒng) umount 操作,然后就出現(xiàn)這樣的現(xiàn)象,場景完全吻合!進一步,在有 2億+dentry 環(huán)境中,手工 drop slab 并通過 time 計時,接近40s,阻塞時間也能吻合。time echo 2?>? /proc/sys/vm/drop_caches

    至此,基本能解釋:sys 沖高的直接原因為 dentry 數(shù)量太多。

    億級 Dentry 從何而來

    接下來的疑問:為何會有這么多 dentry?直接的解答方法,找到這些 dentry 的絕對路徑,然后根據(jù)路徑反推業(yè)務(wù)即可。那么 2億+dentry 如何解析?

    兩種辦法:

    方法1:在線解析

    通過 crash 工具在線解析(手工操練), 基本思路:

  • 找到sb中的 dentry lru list 位置

  • List 所有的 node 地址,結(jié)果存檔

  • 由于entry 數(shù)量過多,可以進行切片,分批保存至單獨文檔,后續(xù)可以批量解析。

  • Vim 列編輯存檔文件,批量插入命令(file),保存為批量執(zhí)行命令的文件

  • crash -i批量執(zhí)行命令文件,結(jié)果存檔

  • 對批量執(zhí)行結(jié)果進行文本處理,統(tǒng)計文件路徑和數(shù)量

  • 結(jié)果示例:

    其中:

  • db 為后面提及的類似 xxxxx_dOeSnotExist_.db 文件,占大部分。

  • session 為 systemd 為每個 session 創(chuàng)建的臨時文件

  • db文件分析如下:文件名稱有幾個明顯特征:

  • 有統(tǒng)一的計數(shù),可能是某一個容器產(chǎn)生

  • 名稱中包含字符串 “dOeSnotExist“

  • 都擁有.db 的后綴

  • 對應(yīng)的絕對路徑示例如下(用于確認所在容器)

    如此可以通過繼續(xù)通過 overlayfs id 繼續(xù)查找對應(yīng)的容器(docker inspect),確認業(yè)務(wù)。

    方法2:動態(tài)跟蹤

    通過編寫 systemtap 腳本,追蹤 dentry 分配請求,可抓到對應(yīng)進程(在可復(fù)現(xiàn)的前提下),腳本示例如下:

    probe?kernel.function("d_alloc")?{printf("[%d]?%s(pid:%d?ppid:%d)?%s?%s\n",?gettimeofday_ms(),?execname(),?pid(),?ppid(),?ppfunc(),?kernel_string_n($name->name,?$name->len)); }

    按進程維度統(tǒng)計:

    Xxx_dOeSnotExist_.db 文件分析

    通過前面抓取到的路徑可以判斷該文件與 nss 庫(證書/密鑰相關(guān))相關(guān),https ?服務(wù)時,需要使用到底層 nss 密碼庫,訪問 web 服務(wù)的工具如 curl 都使用到了這個庫,而 nss 庫存在 bug:?

    【 https://bugzilla.mozilla.org/show_bug.cgi?id=956082? 】

    【 https://bugzilla.redhat.com/show_bug.cgi?id=1779325 】

    大量訪問不存在的路徑這個行為,是為了檢測是否在網(wǎng)絡(luò)文件系統(tǒng)上訪問 nss db, 如果訪問臨時目錄比訪問數(shù)據(jù)庫目錄快很多,會開啟 cache。這個探測過程會嘗試 33ms 內(nèi)循環(huán) stat 不存在的文件(最大1萬次), 這個行為導(dǎo)致了大量的 negative dentry。使用 curl 工具可模擬這個 bug,在測試機中執(zhí)行如下命令:

    strace -f?-e?trace=access?curl?'https://baidu.com'

    規(guī)避方法:設(shè)置環(huán)境變量 NSS_SDB_USE_CACHE=yes

    解決方法:升級 pod 內(nèi)的 nss 服務(wù)?

    至此,問題分析近乎完成??雌饋砭褪且粋€由平平無奇的用戶態(tài)組件的 bug 引發(fā)的血案,分析方法和手段也平平無奇,但后面的分析才是我們關(guān)注的重點。

    另一種現(xiàn)象

    回想前面講到的 dentry_lru_lock 大鎖競爭的場景,仔細分析其他幾例出現(xiàn) sys 沖高的秒級監(jiān)控現(xiàn)場,發(fā)現(xiàn)這種場景中并無刪除pod動作(也就是沒有 umount 動作),也就意味著沒有遍歷 dentry lru 的動作,按理不應(yīng)該有反復(fù)持有 dentry_lru_lock 的情況,而且同時會出現(xiàn)sys沖高的現(xiàn)象。

    可以看到,故障前后的 cache 回收了2G+,但實際的 free 內(nèi)存并沒有增加,反而減少了,說明此時,業(yè)務(wù)應(yīng)該正在大量分配新內(nèi)存,導(dǎo)致內(nèi)存不足,從而導(dǎo)致內(nèi)存一直處于回收狀態(tài)(scan 數(shù)量增加很多)。

    而在內(nèi)存緊張進入直接回收后時,會(可能) shrink_slab,以至于需要持 dentry_lru_lock,(這里的具體邏輯和算法不分析了)。當回收內(nèi)存壓力持續(xù)時,可能會反復(fù)/并發(fā)進入直接回收流程,導(dǎo)致 dentry_lru_lock 鎖競爭,同時,在出現(xiàn)問題的業(yè)務(wù)場景中,單 pod 進程擁有 2400+ 線程,批量退出時調(diào)用 proc_flush_task 釋放/proc目錄下的進程目錄項,從而也會批量/并發(fā)獲取 dcache_lru_lock 鎖,加劇鎖競爭,從而導(dǎo)致sys沖高。

    兩種現(xiàn)象都能基本解釋了。其中,第二種現(xiàn)象相比于第一種,更復(fù)雜,原因在于其中涉及到了內(nèi)存緊張時的并發(fā)處理邏輯。

    解決 & 思考

    直接解決/規(guī)避

    基于前面的分析,可以看出,最直接的解決方式為:升級 pod nss 服務(wù),或者設(shè)置設(shè)置環(huán)境變量規(guī)避 但如果再思考下:如果nss沒有 bug,但其他組件也做了類似可能產(chǎn)生大量 dentry 的動作,比如執(zhí)行類似這樣的腳本:

    #!/bin/bash i=0 while?((?i?<?1000000?))?;?doif?test?-e?./$i;?thenecho?$i?>?./$ifi((i++)) done

    本質(zhì)上也會不停的產(chǎn)生 dentry(slab),面對這種場景該怎么辦?可能的簡便的解決/規(guī)避方法是:周期性 drop cache/slab,雖然可能引發(fā)偶爾的性能小波動,但基本能解決問題。

    鎖優(yōu)化

    前面分析指出,導(dǎo)致 sys 沖高的直接原因是 dcache_lru_lock 鎖的競爭,那這把鎖是否有優(yōu)化空間呢??

    答案是:有?

    看看3.x 內(nèi)核代碼中的鎖使用:

    static?void?dentry_lru_add(struct?dentry?*dentry)?{if?(list_empty(&dentry->d_lru))?{//全局鎖spin_lock(&dcache_lru_lock);list_add(&dentry->d_lru,?&dentry->d_sb->s_dentry_lru);dentry->d_sb->s_nr_dentry_unused++;dentry_stat.nr_unused++;spin_unlock(&dcache_lru_lock);} }

    可以明顯看出這是個全局變量,即所有文件系統(tǒng)公用的全局鎖。而實際的 dentry_lru 是放在 superblock 中的,顯然這把鎖的范圍跟lru是不一致的。于是,新內(nèi)核版本中,果真把這把鎖放入了 superblock 中:

    static?void?d_lru_del(struct?dentry?*dentry)?{D_FLAG_VERIFY(dentry,?DCACHE_LRU_LIST);dentry->d_flags?&=?~DCACHE_LRU_LIST;this_cpu_dec(nr_dentry_unused);if?(d_is_negative(dentry))?this_cpu_dec(nr_dentry_negative);//不再加單獨的鎖,使用list_lru_del原語中自帶的per?list的lockWARN_ON_ONCE(!list_lru_del(&dentry->d_sb->s_dentry_lru,?&dentry->d_lru));} bool?list_lru_add(struct?list_lru?*lru,?struct?list_head?*item)?{int?nid?=?page_to_nid(virt_to_page(item));struct?list_lru_node?*nlru?=?&lru->node[nid];struct?mem_cgroup?*memcg;struct?list_lru_one?*l;//使用per?lru?list的lockspin_lock(&nlru->lock);if?(list_empty(item))?{//?…}spin_unlock(&nlru->lock);return?false; } `

    新內(nèi)核中,棄用了全局鎖,而改用了 list_lru 原語中自帶的 lock,而由于 list_lru 自身位于 superblock 中,所以,鎖變成了per list(superblock)的鎖,雖然還是有點大,但相比之前減小了許多。

    所以,新內(nèi)核中,對鎖做了優(yōu)化,但未必能完全解決問題。

    繼續(xù)思考1

    為什么訪問不存在的文件/目錄(nss cache 和上述腳本)也會產(chǎn)生 dentry cache 呢?一個不存在的文件/目錄的 dentry cache 有何用處呢?為何需要保留?表面看,看似沒有必要為一個不存在的文件/目錄保留 dentry cache。其實,這樣的 dentry cache (后文簡稱dcache)在內(nèi)核中有標準的定義:

    Negative dentry?A special form of dcache entry gets created if a process attempts to access a non-existent file. Such an entry is known as a negative dentry.

    Negative dentry 具體有何用途?由于 dcache 的主要作用是:用于加快文件系統(tǒng)中的文件查找速度,設(shè)想如下場景:如果一個應(yīng)用總是從一些預(yù)先配置好的路徑列表中去查找指定文件(類似于 PATH 環(huán)境變量),而且該文件僅存在與這些路徑中的一個,這種情況下,如果存在 negative dcache,則能加速失敗路徑的查找,整體提升文件查找的性能。

    繼續(xù)思考2

    是否能單獨限制 negative dcache 的數(shù)量呢??

    答案是:可以。

    Rhel7.8 版本內(nèi)核中(3.10.0-1127.el7),合入了一個 feature:negative-dentry-limit,專門用來限制 negative dcache 的數(shù)量,關(guān)于這個 feature 的說明請參考:

    【 https://access.redhat.com/solutions/4982351 】

    關(guān)于 feature 的具體實現(xiàn),請參考:

    【 https://lwn.net/Articles/813353/ 】?(具體原理就不解釋了)

    殘酷的現(xiàn)實是:rhel8 和 upstream kernel都沒有合入這個 feature,為啥呢?請參考:

    Redhat 的官方解釋(其實并沒有解釋清楚)

    【 https://access.redhat.com/solutions/5777081 】

    再看看社區(qū)的激烈討論:

    【 https://lore.kernel.org/patchwork/cover/960253/ 】

    Linus 也親自站出來反對。整體基調(diào)是:現(xiàn)有的 cache reclaim 機制已經(jīng)夠用(夠復(fù)雜了),再結(jié)合 memcg 的 low 水線等保護措施(cgroup v2才有哦),能處理好 cache reclaim 的活,如果限制的話,可能會涉及到同步回收等,引入新的阻塞、問題和不必要的復(fù)雜,negative dache 相比于普通的 pagecache 沒有特別之處,不應(yīng)該被區(qū)別對待(被優(yōu)待),而且 negative dcache 本身回收很快,balabala。

    結(jié)果是,還是不能進社區(qū),盡管這個功能看起來是如此“實用”。

    繼續(xù)思考3

    還有其他方式能限制 dcache 嗎??

    答案是:還有?

    文件系統(tǒng)層,提供了 unused_dentry_hard_limit ?參數(shù),可以控制 dcache 的整體數(shù)量,整體控制邏輯類似。具體代碼原理也不贅述了,歡迎大家查閱代碼。?

    遺憾的是,該參數(shù)依賴于各文件系統(tǒng)自身實現(xiàn),3.x內(nèi)核中只看到 overlayfs 有實現(xiàn),其他文件系統(tǒng)沒有。所以,通用性有所限制,具體效果未知(未實際驗證)。?

    至此,看似真的已經(jīng)分析清楚了?

    Think More

    能否再思考一下:為什么 dentry 數(shù)量這么多,而沒有被及時回收呢?

    當前案例表面上看似一個有應(yīng)用(nss)bug引發(fā)的內(nèi)核抖動問題,但如果仔細思考,你會發(fā)現(xiàn)這其實還是內(nèi)核自身面對類似場景的能力不足,其本質(zhì)問題還在于:

  • 回收不及時

  • cache 無限制

  • 回收不及時

    由于內(nèi)核中會將訪問過的所有文件(目錄)對應(yīng)的 dentry 都緩存起來存于slab中(除非有特性標記),用于下次訪問時提示效率,可以看到出問題的環(huán)境中,slab占用都高達60G,其中絕大部分都是 dentry 占用。

    而內(nèi)核中,僅(絕大部分場景)當內(nèi)存緊張時(到達內(nèi)存水線)才會觸發(fā)主動回收cache(主要包括slab和pagecache),而問題環(huán)境中,內(nèi)存通常很充足,實際使用較少,絕大部分為緩存(slab和pagecache)。?

    當系統(tǒng)free內(nèi)存低于low水線時,觸發(fā)異步回收(kswapd);當 free 內(nèi)存低于 min 水線是觸發(fā)同步回收。也就是說僅當free內(nèi)存低到一定程度(水線)時才能開始回收 dentry,而由于水線通常較低,導(dǎo)致回收時機較晚,而當業(yè)務(wù)有突發(fā)內(nèi)存申請時,可能導(dǎo)致短期內(nèi)處于內(nèi)存反復(fù)回收狀態(tài)。

    注:水線(全局)由內(nèi)核默認根據(jù)內(nèi)存大小計算的,upstream內(nèi)核中默認的水線比較低。在部分容器場景確實不太合理,新版本內(nèi)核中有部分優(yōu)化(可以設(shè)置min和low之間的距離),但也不完美。

    Memcg async reclaim

    在云原生(容器)場景中,針對cache的有效、及時回收,內(nèi)核提供了標準異步回收方式:到達low水線后的 kswapd 回收,但 kswapd 是per-node粒度(全局),即使在調(diào)大 min 和 low 水線之間的 distance 之后(高版本內(nèi)核支持),仍存在如下不足:

  • distance 參數(shù)難以通用,難以控制

  • 全局掃描開銷較大,比較笨重

  • 單線程(per-node)回收,仍可能較慢,不及時

  • 在實際應(yīng)用中,也常見因為內(nèi)存回收不及時導(dǎo)致水線被擊穿,從而出現(xiàn)業(yè)務(wù)抖動的問題。針對類似場景的問題,社區(qū)在多年前有人提交了 memcg async relaim 的想法和補丁(相對原始),基本原理為:為每個 pod (memcg)創(chuàng)建一個類似 kswapd 這樣的內(nèi)存異步回收線程,當pod級別的 async low 水線達到后,觸發(fā) per-cgroup 基本的異步內(nèi)存回收。理論上也能比較好的解決/優(yōu)化類似場景的問題。但最終經(jīng)過長時間討論后,社區(qū)最終沒有接受,主要原因還是出于容器資源開銷和 Isolation 的考慮:

  • 如果為每個 cgroup 創(chuàng)建一個內(nèi)核線程,當容器數(shù)量較多時,內(nèi)存線程數(shù)量增多,開銷難以控制。

  • 后續(xù)優(yōu)化版本去除了 per-cgroup 的內(nèi)核回收線程,而借用于內(nèi)核自帶的 workqueue 來做,由于 workqueue 的池化能力,可以合并請求,減少線程線程創(chuàng)建數(shù)量,控制開銷。但隨之而來的是隔離性(Isolation)的問題,問題在于新提交的 workqueue 請求無法 account 到具體的 pod(cgroup),破壞了容器的隔離性。

  • 從 Maintainer 的角度看,拒絕的理由很充分。但從(云原生)用戶的角度看,只能是再次的失落,畢竟實際的問題并未得到真正充分解決。雖然 memcg async reclaim 功能最終未能被社區(qū)接受,但仍有少數(shù)廠商堅持在自己的版本分支中合入了相應(yīng)功能,其中的典型代表如 Google,另外還包括我們的 TencentOS Server (原TLinux),我們不僅合入/增強了原有的 memcg async reclaim 功能,還將其整體融入了我們的云原生資源 QoS 框架,整體為保證業(yè)務(wù)的內(nèi)存服務(wù)質(zhì)量提供底層支撐。

    cache 無限制

    Linux 傾向于盡可能將空閑內(nèi)存利用起來,用作 cache(主要是page cache和slab),用于提升性能(主要是文件訪問)。意味著系統(tǒng)中 cache 可以幾乎不限制(只要有free內(nèi)存)的增長。在現(xiàn)實場景中帶來不少的問題,本案例中的問題就是其中一種典型。如果有 cache limit 能力,理論上能很大程度解決類似問題。

    Cache limit

    而關(guān)于 page cache limit 話題,多年前曾在 Kernel upstream 社區(qū)中持續(xù)爭論了很長一段時間,但最終還是未能進入 upstream,主要原因還在于違背了盡量利用內(nèi)存的初衷。盡管在一些場景中確實存在一些問題,社區(qū)仍建議通過其他方式解決(業(yè)務(wù)或者其他內(nèi)核手段)。雖然社區(qū)未接受,但少部分廠商還是堅持在自己的版本分支中合入了 page cache limit 功能,其中典型代表如SUSE,另外還包括我們的 TencentOS Server (原TLinux),我們不僅合入/增強了 page cache limit 功能,支持同步/異步回收,同時還增強了 slab limit 的限制,可以同時限制 page cache 和 slab 的用量。該功能在很多場景中起到了關(guān)鍵作用。

    結(jié)論

  • 在如下多個條件同時發(fā)生時,可能出現(xiàn) dentry list 相關(guān)的鎖競爭,導(dǎo)致 sys 高:

    • 系統(tǒng)中存在大量 dentry 緩存(容器訪問過的大量文件/目錄,不停累積)

    • 業(yè)務(wù)突發(fā)內(nèi)存申請,導(dǎo)致free內(nèi)存突破水線,觸發(fā)內(nèi)存回收(反復(fù))

    • 業(yè)務(wù)進程退出,退出時需要清理/proc 文件,期間依賴于 dentry list 的大鎖,出現(xiàn) spinlock race。

    • 用戶態(tài)應(yīng)用 nss bug 導(dǎo)致 dcache 過多,是事故的直接原因。

    • 深層次思考,可以發(fā)現(xiàn),upstream kernel ?為考慮通用性、架構(gòu)優(yōu)雅等因素,放棄了很多實用功能和設(shè)計,在云原生場景中,難以滿足極致需求,要成為云原生-OS的核心底座,還需要深度hack。

    • TencentOS Server 為云原生海量場景做了大量深度定制和優(yōu)化,能自如應(yīng)對復(fù)雜、極端云原生業(yè)務(wù)帶來各種挑戰(zhàn)(包括本案例中涉及的問題)。此外,TencentOS Server 還設(shè)計實現(xiàn)了云原生資源 QoS 保障特性(RUE),為不同優(yōu)先級的容器提供了各種關(guān)鍵資源的 QoS 保障能力。敬請期待相關(guān)分享。

    • 結(jié)語

      在云原生場景中,upstream kernel 難以滿足極端場景的極致需求,要成為云原生-OS的底座,還需要深度hack。而 TencentOS Server 正為之不懈努力!

      【注:案例素材取自騰訊云虛擬化團隊和云技術(shù)運營團隊】

      最后,希望本文可以對你有所幫助,謝謝觀賞,有任何問題可以在評論區(qū)留言,如果覺得文章不錯,麻煩一鍵三連支持

      ? 往期精選推薦??

      一個刁鉆的網(wǎng)絡(luò)問題,技術(shù)磚家的精彩分析

    總結(jié)

    以上是生活随笔為你收集整理的硬核分析|腾讯云原生OS内存回收导致关键业务抖动问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。