Docker核心原理之cgroups
cgroups資源限制
上一篇文章中,我們了解了Docker的資源隔離技術namespace,通過系統調用構建了一個相對隔離的shell環境。也可以稱之為一個簡單的容器。接下來將講解另一個強大的內核工具-cgroups。它不僅可以限制被namespace隔離起來的資源,還可以為資源設置權重、計算使用量、操控任務(進程或線程)啟停等。
1.cgroups是什么
cgroups顧名思義就是把任務放到一個組里面統一加以控制。官方的定義如下:
cgroups是Linux內核提供的一種機制,這種機制可以根據需求吧一系列系統任務及其子任務整合(或分隔)到按資源劃分等級的不同組中,從而為系統資源管理提供一個統一的框架。
通俗地說,cgroups可以限制,記錄任務組做使用的無力資源(包括CPU、Memory、IO等),為容器實現虛擬化提供了保證,是構建Docker等一系列虛擬化管理工具的基石。
對開發者來說,cgroups有以下4個特點:
- cgroups的API以一個偽文件系統的方式實現,用戶態的程序可以通過文件的操作實現cgroups的組織管理。
- cgroups的組織管理操作單元可以細粒度到線程級別,另外用戶可以創建和銷毀cgroup,從而實現資源的再分配和管理。
- 所有資源管理的功能都以子系統的方式實現,接口統一。
- 子任務創建之初與父任務處于同一個cgroups的控制組。
本質上來說,cgroups是內核附加在程序上的一系列鉤子(hook),通過程序運行時對資源的調度觸發相應的鉤子以達到資源追蹤和限制的目的。
2.cgroups的作用
實現cgroups的主要目的是為了不同的用戶層面的資源管理,提供一個統一化的接口。從單個任務的資源控制到操作系統層面的虛擬化,cgroups提供了以下四大功能:
- 資源限制:cgroups可以對任務使用的資源總額進行限制。如設定應用運行時使用內存的上限,一旦超過這個配額就發出OOM(Out Of Memory)提示。
- 優先級分配:通過分配的CPU時間片數量及磁盤IO寬帶大小,實際上就相當于控制了任務運行的優先級。
- 資源統計:cgroups可以統計系統的資源使用量,如CPU使用時長、內存用量等,這個功能非常適用于計費。
- 任務控制:cgroups可以對任務執行掛起、恢復等操作。
3.cgroups術語表
- task(任務):在cgroups的術語中,任務表示一個進程或線程。
- cgroup(控制組):cgroups中的資源控制都以cgroup為單位實現。cgroup表示按某種資源控制標準劃分而成的控制組,包含了一個或多個子系統。一個任務可以加入某個cgroup,也可以從某個cgroup遷移到另外一個cgroup。
- subsystem(子系統):cgroups中的子系統就是一個資源調度控制器。比如CPU子系統可以控制CPU時間分配,內存子系統可以限制cgroup內存使用量。
- hiererchy(層級):層級由一系列cgroup以一個樹狀結構排列而成,每個層級通過綁定對應的子系統進行資源控制。層級中的cgroup幾點可以包括零或多個這幾點,子節點繼承父節點掛載的子系統。整個操作系統可以有多個層級。
4.組織結構與基本規則
上一篇文章已經介紹過,傳統的Unix任務管理,實際上是先啟動init任務作為根基點,再由init節點創建子任務作為子節點,而每個子節點又可以創建新的子節點,如此往復,形成一個樹狀結構。而系統中的多個cgroup也構成類似的樹狀結構,子節點從父節點繼承屬性。
它們最大的不同在于,系統中的多個cgroup構成的層級并非單根結構,可以允許存在多個。如果任務模型由init作為根節點構成一棵樹,那么系統中的多個cgroup則是由多個cgroup則是由多個層級構成的森林。這樣做的目的很好理解,如果只有一個層級,那么所有的任務都將被迫綁定其上的所有子系統,這會給某些任務造成不必要的限制。在Docker中,每個子系統獨自構成一個層級,這樣做非常易于管理。
5.子系統簡介
子系統實際上就是cgroups的資源控制系統,每個子系統獨立地控制一種資源,目前Docker使用了如下9種子系統,其中net_cls子系統在內核中已經廣泛實現,但在Docker中尚未使用,Docker在網絡方案的控制方式在以后的文章中會繼續介紹。
- blkio: 可以為塊設備設定輸入/輸出限制,比如物理驅動設備(包括磁盤、固態硬盤、USB等)。
- cpu:使用調度程序控制任務對CPU的使用。
- cpuacct:自動生成cgroup中任務對CPU資源使用的情況的報告。
- cpuset:可以為cgroup中的任務分配獨立的CPU(針對多處理器系統)和內存。
- devices:可以啟動或關閉cgroup中任務對設備的訪問。
- freezer:可以掛起或恢復cgroup中的任務。
- memory:可以設定cgroup中任務對內存的使用量的限定,并且自動生成這些任務對內存資源使用情況的報告。
- perf_event:使用后使cgroup中的任務可以進行統一的性能測試。(Pref:linux CPU性能探測器)
- net_cls:Docker沒有直接使用它,它通過使用等級識別符(classid)標記網絡數據包,從而允許Linux流量控制程序(Traffic Controller,TC)識別從具體cgroup中生成的數據包。
上述子系統如何使用雖然很重要,但是Docker并沒有對cgroup本身做增強,容器用戶一般也不需要直接操作cgroup。這里我只是大致說明下操作流程,讓讀者有一個感性的認識。
Linux中cgroup的實現形式表現為一個文件系統,因此需要mount這個文件系統才能使用,掛載成功后就能看到各類子系統。
6.cgroups實現方式及工作原理
在對cgroups規則和自通有一定了解以后,下面簡單介紹操作系統內核級別上cgroups的工作原理,希望能有助于讀者理解cgroups如何對Docker容器中的進程產生作用。
cgroups的實現本質上是給任務掛上鉤子,當任務運行的過程中涉及某種資源時,就會觸發鉤子上所附帶的子系統進行檢測,根據資源列別不同,使用對應的技術進行資源限制和優先級分配。
- cgroups如何判斷資源超限及超出限額后的措施
對于不同的系統資源,cgroups提供了統一的接口對資源進行控制和統計,但限制的具體方式則不盡相同。比如memorary子系統,會描述內存狀態的“mm_struct”結構體中記錄它所屬的cgroup,當進程需要申請更多內存時,就會觸發cgroup用量檢測,用量超過cgroup規定的限額,則拒絕用戶的內存申請,否則就給予相應內存并在cgroup的統計信息中記錄。實際實現要比上述描述復雜的多,不僅需要考慮內存的分配和回收,還需要考慮不同類型的內存如cache和swap等。
進程所需的內存超過它所屬的cgroup最大限額以后,如果設置了OOM Control(內存超限控制),那么進程就會收到OOM信號并結束;否則進程就會被掛起,進入睡眠狀態,進入睡眠狀態,直到cgroup中其他進程釋放了足夠的內存資源為止。Docker中默認是開啟OOM Control的。其他子系統的實現與此類似,cgroups提供了多種資源限制的策略供用戶選擇。
- cgroup與任務之間的關聯關系
實現上,cgroup與任務之間是多對多關系,所以它們并不直接關聯,而是通過一個中間結構把雙向的關聯信息記錄起來。每個任務結構體tsak_struct中都包含了一個指針,可以查到對應的cgroup的情況,同時也可以查詢到各個子系統的狀態,這些子系統狀態中也包含了找到任務的指針,不同類型的子系統按需定義本身的控制信息結構體,最終在地定義的結構體中吧子系統狀態指針包含進去,然后內核通過container_of(這個宏可以通過一個結構體的成員找到結構體自身)等宏定義來獲取對應的結構體,關聯到任務,從此達到資源限制的目的。同時,為了讓cgroups便于用戶理解和使用,也為了用精簡的內核代買為cgroup提供熟悉的權限和命名空間管理,內核開發者們按照Linux虛擬文件轉化器(Virtual Filesystem Switch,VFS)接口實現了一套名為cgroup的文件系統,非常巧的用來表示cgroups的層級概念,把各個子系統的實現都瘋撞到文件系統的各項操作中。
- Docker在使用cgroup時的注意事項
在實際使用過程中,Docker需要通過掛載cgroup文件系統新建一個層級結構,掛載時指定要綁定的子系統。把cgroup文件系統掛載上以后,就可以像操作文件一樣對cgroup的層級進行瀏覽和操作管理(包括權限管理、子文件管理等)。除了cgroup文件系統以外,內核沒有為cgroups的訪問和操作添加任何系統調用。
- /sys/fs/cgroup/cpu/docker/下文件的作用
前面已經說過,以資源開頭的文件都是用來限制這個cgroup下任務的可用配置文件。
一個cgroup創建完成,不管綁定了何種子系統,其目錄下都會生成以下幾個文件,用來描述cgroup的相應信息。同樣,把相應信息寫入這些配置文件中就可以生效。
本文由淺入深的講解了cgroups,從cgroups是什么,到cgroups要怎么用,最后對大量的cgroup子系統進行了講解。可以看到內核對cgroups的支持已經較多,但是依舊有許多工作要完善。如網絡方面目前通過TC(Traffic Controller)來控制,未來需要統一整合;由縣級調度方面依舊有很大的改進空間。
總結
以上是生活随笔為你收集整理的Docker核心原理之cgroups的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Docker核心原理之namespace
- 下一篇: 解决kubectl get pods时