Linux 4.1內核熱補丁成功實踐

很久不見的乾貨重現江湖!今日的內容是基於UCloud運維同窗反饋的個別宿主機上存在進程CPU峯值使用率異常現象問題進行的相關闡述。本文詳細介紹了該問題的完整分析思路和用熱補丁的方式成功解決此問題的實踐分享,若是以爲還不錯,歡迎點贊分享!正文開始~服務器

最開始公司運維同窗反饋,個別宿主機上存在進程CPU峯值使用率異常的現象。而數萬臺機器中只出現了幾例,也就是說萬分之幾的機率。監控產生的些小偏差,不會形成宕機等嚴重後果,很容易就此被忽略了。但咱們考慮到這個異常轉瞬即逝、並不易被察覺,可能還存在更多這樣的機器,又或者如今正常未來又不正常,內核研發本能的好奇心讓咱們感到:此事必有蹊蹺!因而追查下去。運維

問題現象工具

現象一:CPU監控非0即100%測試

該問題現象表如今Redis進程CPU監控的峯值時而100% 時而爲0,有的甚至是幾十分鐘都爲0,突發1秒100%後又變爲0,以下圖。優化

而從大量機器的統計規律看,這個現象在2.6.32 內核不存在,在4.1內核存在幾例。2.6.32是咱們較早期採用的版本,爲平臺的穩定發展作了有力支撐,4.1 能夠知足不少新技術需求,如新款CPU、新板卡、RDMA、NVMe和binlog2.0等。後臺無縫維護着兩個版本,併爲了能力提高和優化而逐步向4.1及更高版本過渡。線程

現象二:top顯示非0即300%設計

登陸到機器上執行top -b -d 1 –p <pid> | grep <pid> , 能夠看到進程的CPU利用率每隔幾分鐘到幾十分鐘出現一次300%,這意味着該進程3個線程佔用的3個CPU都跑滿了,跟監控程序呈現一樣的異常。3d

問題分析blog

上述異常程序使用的是一樣的數據源:/proc/pid/stat中進程運行佔用的用戶態時間utime和內核態時間stime。咱們抓取utime和stime更新狀況後,發現utime或者stime每隔幾分鐘或者幾十分鐘才更新,更新的步進值達到幾百到1000+,而正常進程看到的是每幾秒更新,步進值是幾十。進程

定位到異常點後,還要找出緣由。排除了監控邏輯、IO負載、調用瓶頸等可能後,確認是4.1內核的CPU時間統計有 bug

cputime統計邏輯

檢查/proc/pid/stat中utime和stime被更新的代碼執行路徑,在cputime_adjust()發現了一處可疑的地方:

當utime+stime>=rtime的時候就直接跳出了,也就是不更新utime和stime了!這裏的rtime是runtime,表明進程運行佔用的全部CPU時長,正常應該等於或近似進程用戶態時間+內核態時間。 但內核配置了CONFIG_VIRT_CPU_ACCOUNTING_GEN選項,這會讓utime和stime分別單調增加。而runtime是調度器裏統計到的進程真正運行總時長。

內核每次更新/proc/pid/stat的utime和stime的時候,都會跟rtime對比。若是utime+stime很長一段時間都大於rtime,那代碼直接goto out了, /proc/pid/stat就不更新了。只有當rtime持續更新追上utime+stime後,才更新utime和stime。

冷補丁和熱補丁

第一回合:冷補丁

出現問題的代碼位置已經找到,那就先去內核社區看看有沒有成熟補丁可用,看一下kernel/sched/cputime.c的 changelog,看到一個patch:確保stime+utime=rtime。再看描述:像top這樣的工具,會出現超過100%的利用率,以後又一段時間爲0,這不就是咱們遇到的問題嗎?真是踏破鐵鞋無覓處,得來全不費工夫!(patch連接:https://lore.kernel.org/patchwork/patch/609410/)

該補丁在4.3內核及之後版本才提交, 卻並未提交到4.1穩定版分支,因而移植到4.1內核。打上該補丁後進行壓測,再沒出現cputime時而100%時而0%的現象,而是0-100%之間平滑波動的值。

至此,你可能以爲問題已經解決了。可是,問題才解決了一半。而每每「可是」後邊纔是重點。

第二回合:熱補丁

給內核代碼打上該冷補丁只能解決新增服務器的問題,但公司還有數萬存量服務器是沒法升級內核後重啓的。

若是沒有其它好選擇,那存量更新將被迫採用以下的妥協方案:監控程序修改統計方式進行規避,再也不使用utime和stime,而是經過runtime來統計進程的執行時間。

雖然該方案快速可行,但也有很大的缺點:

不少業務部門都要修改統計程序,研發成本較高; /proc/pid/stat的utime和stime是標準統計方式,一些第三方組件並不容易修改; 並無根本解決utime和stime不許的問題,用戶、研發、運維使用ps、top命令時還會產生困惑,產生額外的溝通協調成本。 幸虧,咱們還能夠依靠UCloud已屢次成功應用的技術:熱補丁技術。

所謂熱補丁技術,是指在有缺陷的服務器內核或進程正在運行時,對已經加載到內存的程序二進制打上補丁,使得程序實時在線狀態下執行新的正確邏輯。能夠簡單理解爲像關二爺那樣不打麻藥在清醒狀態下刮骨療傷。固然,對內核刮骨療傷內核是不會痛的,但刮很差內核就會直接死給你看,沒有絲毫猶豫,很是乾脆利索又耿直。

熱補丁修復

而本次熱補丁修復存在兩個難點:

難點一: 熱補丁製做

此次熱補丁在結構體新增了spinlock成員變量,那就涉及新成員的內存分配和釋放,在結構體實例被複制和釋放時,都要額外的對新成員作處理,稍有遺漏可能會形成內存泄漏進而致使宕機,這就加大了風險。

再一個就是,結構體實例是在進程啓動時初始化的,對於已經存在的實例如何塞進新的spinlock成員?所謂兵來將擋水來土掩,咱們想到能夠在原生補丁使用spinlock成員的代碼路徑上攔截,若是發現實例不含該成員,則進行分配、初始化、加鎖、釋放鎖。

要解決問題,既要攀登困難的山峯,又得控制潛在的風險。團隊編寫了腳本進行幾百萬次的加載、卸載熱補丁測試,並沒有內存泄漏,單機穩定運行,再下一城。

難點二:難以復現

另外一個難題是該問題難以復現,只有在現網生產環境纔有幾個case可驗證熱補丁,而又不能夠拿用戶的環境去冒險。針對這種狀況咱們已經有標準化處理流程去應對,那就是設計完善的灰度策略,這也是UCloud內部一直在強調的核心理念和能力。通過分析,這個問題能夠拆解爲驗證熱補丁穩定性和驗證熱補丁正確性。因而咱們採起了以下灰度策略:

穩定性驗證:先拿幾臺機器測試正常,再拿公司內部500臺次級重要的機器打熱補丁,灰度運行幾天正常,從而驗證了穩定性,風險盡在掌控之中。 正確性驗證:找到一臺出現問題的機器,同時打印utime+stime以及rtime,根據代碼的邏輯,當rtime小於utime+stime時會執行老邏輯,當rtime大於utime+stime時會執行新的熱補丁邏輯。以下圖所示,進入熱補丁的新邏輯後,utime+stime打印正常且與rtime保持了同步更新,從而驗證了熱補丁的正確性。

全網變動: 最後再分批在現網環境機器上打熱補丁,執行全網變動,問題獲得根本解決,此處要感謝運維同窗的全力協助。

總結

綜上,咱們詳細介紹了進程cputime統計異常問題的完整分析和解決思路。該問題並不是嚴重的宕機問題,但卻可能會讓用戶對監控數據產生困惑,誤認爲可能機器負載過高須要加資源,問題的解決會避免產生沒必要要的開支。此外,該問題也會讓研發、運維和技術支持的同窗們使用top和ps命令時產生困惑。最終咱們對問題的本質仔細分析並求證,用熱補丁的方式妥善的解決了問題。

相關文章
相關標籤/搜索