在使用?Kubernetes(簡稱K8s)?時,通常會在同一臺機器上部署多個 Pod。如果某個 Pod 中的服務(wù)出現(xiàn)問題(如出現(xiàn)死循環(huán)),將會導(dǎo)致占用大量的 CPU 時間,從而影響到其他 Pod 的正常運行。
為了解決這個問題,K8s 提供了一個限制 Pod 使用 CPU 資源的配置項,如下所示:
?
resources: ??limits: ????cpu:?"0.5"
?
上述配置限制了 Pod 只能使用 0.5 個 CPU 資源。
K8s 通過使用 Linux 資源控制組(cgroup)中的?CPU子系統(tǒng)?來限制 Pod 對 CPU 資源的使用。
下面我們來分析一下 Linux 內(nèi)核是如何限制進程對 CPU 資源的使用。
CPU限流原理
如果讓我們來設(shè)計一個限制進程對 CPU 資源使用的算法(如限制進程 A 只能使用 10% 的 CPU 運行時間),應(yīng)該如何實現(xiàn)呢?
最簡單的方法是,將 CPU 的運行時間劃分成一個個時間片段(稱為?周期(period),如 100 毫秒)。由于進程 A 被限制為只能使用 10% 的 CPU 運行時間,所以在一個周期內(nèi),進程 A 只能獲得 10 毫秒的運行時間。當(dāng)進程 A 在一個周期內(nèi)運行超過 10 毫秒后,將會被內(nèi)核移除可運行隊列。那么在當(dāng)前周期內(nèi),進程 A 將不會被調(diào)度,從而實現(xiàn)?CPU限流?的目的。
當(dāng)一個新的周期開始時,進程 A 將會重新獲得 10% 的 CPU 運行時間。如下圖所示:

進程在一個周期內(nèi)能運行的時間被稱為?配額(quota)。
CPU限流實現(xiàn)
內(nèi)核以?進程組?作為資源控制的主體,進程組使用?task_group?結(jié)構(gòu)體來描述,其定義如下:
?
struct?task_group?{
????...
????//?可運行隊列,每個CPU一個
????struct?cfs_rq?**cfs_rq;
????...
????//?用于限制進程組對CPU資源的使用
????struct?cfs_bandwidth?cfs_bandwidth;
};
?
可以看出,每個進程組都有一個可運行隊列,可運行隊列中包含了進程組中所有可以被調(diào)度的進程。
在多核環(huán)境下,每個 CPU 都有一個可運行隊列,主要為了解決資源競爭問題。
另外,task_group?結(jié)構(gòu)體中還有個類型為?cfs_bandwidth?的字段,用于控制進程組對 CPU 資源的使用。cfs_bandwidth?結(jié)構(gòu)體的定義如下:
?
struct?cfs_bandwidth?{
????...
????ktime_t?period;
????u64?quota;
????u64?runtime;
????...
????struct?hrtimer?period_timer;
};
?
下面介紹一下?cfs_bandwidth?結(jié)構(gòu)體各個字段的作用:
period:就是上面介紹的周期,用戶可以通過修改 cgroup 的?cpu.cfs_period_us?文件進行設(shè)置。
quota:進程組在周期內(nèi)能夠運行的時間,用戶可以通過修改 cgroup 的?cpu.cfs_quota_us?文件進行設(shè)置。
runtime:進程組在周期內(nèi)剩余的可運行時間。
period_timer:定時器,每隔一個周期執(zhí)行一次,主要用于更新?runtime?字段的值。
runtime?字段用于保存進程組在當(dāng)前周期內(nèi)剩余的可運行時間,如果調(diào)度器選中了進程組中某個進程進行運行時,將會減少進程組的剩余可運行時間。如下圖所示:

在上圖中,進程組 A 中的進程 D 被調(diào)度器選中運行。如果進程組 A 原來的可運行時間為 50 毫秒,而進程 D 運行了 10 毫秒。那么,進程組 A 的可運行時間將從會減少 10 毫秒,從而變?yōu)?40 毫秒。
在一個周期內(nèi),當(dāng)進程組的可運行時間變?yōu)?0 時,那么此進程組將會被限制運行(稱為 CPU Throttling),直到下一個周期開始。
當(dāng)進程組被限制運行時,進程組內(nèi)的所有進程都不能被執(zhí)行。
當(dāng)進程組開啟了 CPU 限流功能時(也就是設(shè)置了?period?和?quota?的值),內(nèi)核將會為其啟動一個定時器。定時器每隔一個周期觸發(fā)一次,用于更新進程組的可運行時間,從而解除進程組被限制運行的情況。
定時器通過調(diào)用?__refill_cfs_bandwidth_runtime()?函數(shù)來更新進程組的可運行時間,其代碼如下:
?
void?__refill_cfs_bandwidth_runtime(struct?cfs_bandwidth?*cfs_b)
{
????...
????cfs_b->runtime?=?cfs_b->quota;
????...
}
?
上面的代碼將?quota?字段的值賦給了?runtime?字段,所以進程組重新獲得了可運行時間,從而解除被限制運行的狀態(tài)。
審核編輯:劉清
電子發(fā)燒友App





































評論