docker 容器基礎技術:linux cgroup 簡介
Linux cgroups 的全稱是 Linux Control Groups,它是 Linux 內核的特性,主要作用是 限制、記錄和隔離進程組(process groups)使用的物理資源(cpu、memory、IO 等) 。
2006 的時候,Google 的一些工程師(主要是 Paul Menage 和 Rohit Seth)啟動了這個項目,最初的名字叫 process containers 。因為 container 在內核中名字有歧義,2007 的時候改名為 control groups ,并合并到 2008 年發布的 2.6.24 內核版本。
最初 cgroups 的版本被稱為 v1,這個版本的 cgroups 設計并不友好,理解起來非常困難。后續的開發工作由 Tejun Heo 接管,他重新設計并重寫了 cgroups,新版本被稱為 v2,并首次出現在 kernel 4.5 版本。
cgroups 從設計之初使命就很明確,為進程提供資源控制,它主要的功能包括:
- 資源限制 :限制進程使用的資源上限,比如最大內存、文件系統緩存使用限制
- 優先級控制 :不同的組可以有不同的優先級,比如 CPU 使用和磁盤 IO 吞吐
- 審計 :計算 group 的資源使用情況,可以用來計費
- 控制 :掛起一組進程,或者重啟一組進程
目前 cgroups 已經成為很多技術的基礎,比如 LXC、docker、systemd等。
NOTE:資源限制是這篇文章的重點,也是 docker 等容器技術的基礎。其他特性可以參考內核 cgroups 文檔。
cgroups 核心概念
前面說過,cgroups 是用來對進程進行資源管理的,因此 cgroup 需要考慮如何抽象這兩種概念:進程和資源,同時如何組織自己的結構。cgroups 中有幾個非常重要的概念:
- task :任務,對應于系統中運行的一個實體,一般是指進程
- subsystem :子系統,具體的資源控制器(resource class 或者 resource controller),控制某個特定的資源使用。比如 CPU 子系統可以控制 CPU 時間,memory 子系統可以控制內存使用量
- cgroup :控制組,一組任務和子系統的關聯關系,表示對這些任務進行怎樣的資源管理策略
- hierarchy :層級樹,一系列 cgroup 組成的樹形結構。每個節點都是一個 cgroup,cgroup 可以有多個子節點,子節點默認會繼承父節點的屬性。系統中可以有多個 hierarchy
雖然 cgroups 支持 hierarchy,允許不同的子資源掛到不同的目錄,但是多個樹之間有各種限制,增加了理解和維護的復雜性。在實際使用中,所有的子資源都會統一放到某個路徑下(比如 ubuntu16.04 的 /sys/fs/cgroup/ ),因此本文并不詳細介紹多個樹的情況,感興趣的可以參考 RedHat 的這篇文檔。
子資源系統(Resource Classes or SubSystem)
目前有下面這些資源子系統:
- Block IO( blkio ):限制塊設備(磁盤、SSD、USB 等)的 IO 速率
- CPU Set( cpuset ):限制任務能運行在哪些 CPU 核上
- CPU Accounting( cpuacct ):生成 cgroup 中任務使用 CPU 的報告
- CPU ( CPU ):限制調度器分配的 CPU 時間
- Devices ( devices ):允許或者拒絕 cgroup 中任務對設備的訪問
- Freezer ( freezer ):掛起或者重啟 cgroup 中的任務
- Memory ( memory ):限制 cgroup 中任務使用內存的量,并生成任務當前內存的使用情況報告
- Network Classifier( net_cls ):為 cgroup 中的報文設置上特定的 classid 標志,這樣 tc 等工具就能根據標記對網絡進行配置
- Network Priority ( net_prio ):對每個網絡接口設置報文的優先級
- perf_event :識別任務的 cgroup 成員,可以用來做性能分析
Hierarchy
Linux 進程之間組成一棵樹的結構,每個進程(除了 init 根進程之外)都有一個父進程,子進程創建之后會繼承父進程的一些屬性(比如環境變量,打開的文件描述符等)。
和進程模型類似,只不過 cgroups 是一個森林結構。
使用 cgroups
cgroup 內核功能比較有趣的地方是它沒有提供任何的系統調用接口,而是通過文件系統來操作,cgroup 實現了一個
使用 cgroups 的方式有幾種:
- 使用 cgroups 提供的虛擬文件系統,直接通過創建、讀寫和刪除目錄、文件來控制 cgroups
- 使用命令行工具,比如 libcgroup 包提供的 cgcreate、cgexec、cgclassify 命令
- 使用 rules engine daemon 提供的配置文件
- 當然,systemd、lxc、docker 這些封裝了 cgroups 的軟件也能讓你通過它們定義的接口控制 cgroups 的內容
直接操作 cgroup 文件系統
查看 cgroups 掛載信息
在 ubuntu 16.04 的機器上,cgroups 已經掛載到文件系統上了,可以通過 mount 命令查看:
? ~ mount -t cgroup cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event) cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
如果沒有的話,也可以通過以下命令來把想要的 subsystem mount 到系統中:
$ mount -t cgroup -o cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem
上述命令表示把 cpu、cpuset、memory 三個子資源 mount 到 /cgroup/cpu_and_mem 目錄下。
每個 cgroup 目錄下面都會有描述該 cgroup 的文件,除了每個 cgroup 獨特的資源控制文件,還有一些通用的文件:
- tasks :當前 cgroup 包含的任務(task)pid 列表,把某個進程的 pid 添加到這個文件中就等于把進程移到該 cgroup
- cgroup.procs :當前 cgroup 中包含的 thread group 列表,使用邏輯和 tasks 相同
- notify_on_release :0 或者 1,是否在 cgroup 銷毀的時候執行 notify。如果為 1,那么當這個 cgroup 最后一個任務離開時(退出或者遷移到其他 cgroup),并且最后一個子 cgroup 被刪除時,系統會執行 release_agent 中指定的命令
- release_agent :需要執行的命令
創建 cgroup
創建 cgroup,可以直接用 mkdir 在對應的子資源中創建一個目錄:
? ~ mkdir /sys/fs/cgroup/cpu/mycgroup ? ~ ls /sys/fs/cgroup/cpu/mycgroup cgroup.clone_children cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.stat tasks cgroup.procs cpuacct.usage cpu.cfs_period_us cpu.shares notify_on_release
上面命令在 cpu 子資源中創建了 mycgroup ,創建 cgroup 之后,目錄中會自動創建需要的文件。我們后面會詳細講解這些文件的含義,目前只需要知道它們能夠控制對應子資源就行。
刪除 cgroup
刪除子資源,就是刪除對應的目錄:
$ rmdir /sys/fs/cgroup/cpu/mycgroup/
刪除之后,如果 tasks 文件中有進程,它們會自動遷移到父 cgroup 中。
設置 cgroup 參數
設置 group 的參數就是直接往特定的文件中寫入特定格式的內容,比如要限制 cgroup 能夠使用的 CPU 核數:
$ echo 0-1 > /sys/fs/cgroup/cpuset/mycgroup/cpuset.cpus
把進程加入到 cgroup
要把某個已經運行的進程加入到 cgroup,可以直接往需要的 cgroup tasks 文件中寫入進程的 PID:
$ echo 2358 > /sys/fs/cgroup/memory/mycgroup/tasks
在 cgroup 中運行進程
如果想直接把進程運行在某個 cgroup,但是運行前還不知道進程的 Pid 應該怎么辦呢?
我們可以利用 cgroup 的繼承方式來實現,因為子進程會繼承父進程的 cgroup,因此我們可以把當前 shell 加入到要想的 cgroup:
echo $$ > /sys/fs/cgroup/cpu/mycgroup/tasks
上面的方案有個缺陷,運行完之后原來的 shell 還在 cgroup 中。如果希望進程運行完不影響當前使用的 shell,可以另起一個臨時的 shell:
sh -c "echo \$$ > /sys/fs/cgroup/memory/mycgroup/tasks & & stress -m 1"
把進程移動到 cgroup
如果想要把進程移動到另外一個 cgroup,只要使用 echo 把進程 PID 寫入到 cgroup tasks 文件中即可,原來 cgroup tasks 文件會自動刪除該進程。
cgroup-tools
cgroup-tools 軟件包提供了一系列命令可以操作和管理 cgroup,ubuntu 系統中可以通過下面的命令安裝:
sudo apt-get install -y cgroup-tools
列出 cgroup mount 信息
最簡單的, lssubsys 可以查看系統中存在的 subsystems:
? ~ lssubsys -am cpuset /sys/fs/cgroup/cpuset cpu,cpuacct /sys/fs/cgroup/cpu,cpuacct blkio /sys/fs/cgroup/blkio memory /sys/fs/cgroup/memory devices /sys/fs/cgroup/devices freezer /sys/fs/cgroup/freezer net_cls,net_prio /sys/fs/cgroup/net_cls,net_prio perf_event /sys/fs/cgroup/perf_event hugetlb /sys/fs/cgroup/hugetlb pids /sys/fs/cgroup/pids
創建 cgroup
cgcreate 可以用來為用戶創建指定的 cgroups:
? sudo cgcreate -a cizixs -t cizixs -g cpu,memory:test1 ? ls cpu/test1 cgroup.clone_children cpuacct.stat cpuacct.usage_all cpuacct.usage_percpu_sys cpuacct.usage_sys cpu.cfs_period_us cpu.shares notify_on_release cgroup.procs cpuacct.usage cpuacct.usage_percpu cpuacct.usage_percpu_user cpuacct.usage_user cpu.cfs_quota_us cpu.stat tasks
上面的命令表示在 /sys/fs/cgroup/cpu 和 /sys/fs/cgroup/memory 目錄下面分別創建 test1 目錄,也就是為 cpu 和 memory 子資源創建對應的 cgroup。
- 選項 -t 指定 tasks 文件的用戶和組,也就是指定哪些人可以把任務添加到 cgroup 中,默認是從父 cgroup 繼承
- -a 指定除了 tasks 之外所有文件(資源控制文件)的用戶和組,也就是哪些人可以管理資源參數
- -g 指定要添加的 cgroup,冒號前是逗號分割的子資源類型,冒號后面是 cgroup 的路徑(這個路徑會添加到對應資源 mount 到的目錄后面)。也就是說在特定目錄下面添加指定的子資源
刪除 cgroup
知道怎么創建,也要知道怎么刪除。不然系統中保留著太多用不到的 cgroup 浪費系統資源,也會讓管理很麻煩。
cgdelete 可以刪除對應的 cgroups,它和 cgcreate 命令類似,可以用 -g 指定要刪除的 cgroup:
? cgroup sudo cgdelete -g cpu,memory:test1
cgdelete 也提供了 -r 參數可以遞歸地刪除某個 cgroup 以及它所有的子 cgroup。
如果被刪除的 cgroup 中有任務,這些任務會自動移到父 cgroup 中。
設置 cgroup 的參數
cgset 命令可以設置某個子資源的參數,比如如果要限制某個 cgroup 中任務能使用的 CPU 核數:
$ cgset -r cpuset.cpus=0-1 /mycgroup
-r 后面跟著參數的鍵值對,每個子資源能夠配置的鍵值對都有自己的規定,我們會在后面詳細解釋。
cgset 還能夠把一個 cgroup 的參數拷貝到另外一個 cgroup 中:
$ cgset --copy-from group1/ group2/
NOTE: cgset 如果設置沒有成功也不會報錯,請一定要注意。
在某個 cgroup 中運行進程
cgexec 執行某個程序,并把程序添加到對應的 cgroups 中:
? cgroup cgexec -g memory,cpu:cizixs bash
cgroups 是可以有層級結構的,因此可以直接創建具有層級關系的 cgroup,然后運行在該 cgroup 中:
$ cgcreate -g memory,cpu:groupname/foo $ cgexec -g memory,cpu:groupname/foo bash
把已經運行的進程移動到某個 cgroup
要把某個已經存在的程序(能夠知道它的 pid)移到某個 cgroup,可以使用 cgclassify 命令:
比如把當前 bash shell 移入到特定的 cgroup 中
$ cgclassify -g memory,cpu:/mycgroup $$
$$ 表示當前進程的 pid 號,上面命令可以方便地測試一些耗費內存或者 CPU 的進程,如果 /mycgroup 對 CPU 和 memory 做了限制。
這個命令也可以同時移動多個進程,它們 pid 之間用空格隔開:
$ cgclassify -g cpu,memory:group1 1701 1138
cgroup 子資源參數詳解
每個 subssytem 負責系統的一部分資源管理,又分別提供多個參數可以控制,每個參數對應一個文件,往文件中寫入特定格式的內容就能控制該資源。
blkio:限制設備 IO 訪問
限制磁盤 IO 有兩種方式:權重(weight)和上限(limit)。權重是給不同的應用(或者 cgroup)一個權重值,各個應用按照百分比來使用 IO 資源;上限是直接寫死應用讀寫速率的最大值。
設置 cgroup 訪問設備的權重:
設置的權重并不能保證什么,當只有某個應用在讀寫磁盤時,不管它權重多少,都能使用磁盤。只有當多個應用同時讀寫磁盤時,才會根據權重為應用分配讀寫的速率。
- blkio.weight :設置 cgroup 讀寫設備的權重,取值范圍在 100-1000
- blkio.weight_device :設置 cgroup 使用某個設備的權重。當訪問該設備時,它會使用當前值,覆蓋 blkio.weight 的值。內容的格式為 major:minor weight ,前面是設備的 major 和 minor 編號,用來唯一表示一個設備,后面是 100-1000 之間的整數值。設備號的分配可以參考:https://www.kernel.org/doc/html/v4.11/admin-guide/devices.html
設置 cgroup 訪問設備的限制:
除了設置權重之外,還能設置 cgroup 磁盤的使用上限,保證 cgroup 中的進程讀寫磁盤的速率不會超過某個值。
- blkio.throttle.read_bps_device :最多每秒鐘從設備讀取多少字節
- blkio.throttle.read_iops_device :最多每秒鐘從設備中執行多少次讀操作
- blkio.throttle.write_bps_device :最多每秒鐘可以往設備寫入多少字節
- blkio.throttle.write_iops_device :最多每秒鐘可以往設備執行多少次寫操作
讀寫字節數的限制格式一樣 major:minor bytes_per_second ,前面兩個數字代表某個設備,后面跟著一個整數,代表每秒讀寫的字節數,單位為比特,如果需要其他單位(KB、MB等)需要自行轉換。比如要限制 /dev/sda 讀速率上線為 10 Mbps,可以運行:
$ echo "8:0 10485760" > /sys/fs/cgroup/blkio/mygroup/blkio.throttle.read_bps_device
iops 代表 IO per second,是每秒鐘執行讀寫的次數,格式為 major:minor operations_per_second 。比如,要限制每秒只能寫 10 次,可以運行:
$ echo "8:0 10" > /sys/fs/cgroup/blkio/mygroup/blkio.throttle.write_iops_device
除了限制磁盤使用之外,blkio 還提供了 throttle 規則下磁盤使用的統計數據。
- blkio.throttle.io_serviced :cgroup 中進程讀寫磁盤的次數,文件中內容格式為 major:minor operation number ,表示對磁盤進行某種操作(read、write、sync、async、total)的次數
- blkio.throttle.io_service_bytes :和上面類似,不過這里保存的是操作傳輸的字節數
- blkio.reset_stats :重置統計數據,往該文件中寫入一個整數值即可
- blkio.time :統計 cgroup 對各個設備的訪問時間,格式為 major:minor milliseconds
- blkio.io_serviced :CFQ 調度器下,cgroup 對設備的各種操作次數,和 blkio.throttle.io_serviced 剛好相反,所有不是 throttle 下的請求
- blkio.io_services_bytes :CFQ 調度器下,cgroup 對各種設備的操作字節數
- blkio.sectors :cgroup 中傳輸的扇區次數,格式為 major:minor sector_count
- blkio.queued :cgroup IO 請求進隊列的次數,格式為 number operation
- blkio.dequeue :cgroup 的 IO 請求被設備出隊列的次數,格式為 major:minor number
- blkio.avg_queue_size :
- blkio.merged :cgroup 把 BIOS 請求合并到 IO 操作請求的次數,格式為 number operation
- blkio.io_wait_time :cgroup 等待隊列服務的時間
- blkio.io_service_time :CFQ 調度器下,cgroup 處理請求的時間(從請求開始調度,到 IO 操作完成)
cpu:限制進程組 CPU 使用
CPU 子資源可以管理 cgroup 中任務使用 CPU 的行為,任務使用 CPU 資源有兩種調度方式:完全公平調度(CFS,Completely Fair Scheduler)和 實時調度(RT,Real-Time Scheduler)。前者可以根據權重為任務分配響應的 CPU 時間片,后者能夠限制使用 CPU 的核數。
CFS 調優參數:
CFS 調度下,每個 cgroup 都會分配一個權重,但是這個權重并不能保證任務使用 CPU 的具體數據。如果只有一個進程在運行(理論上,現實中機器上不太可能只有一個進程),不管它所在 cgroup 對應的 CPU 權重是多少,都能使用所有的 CPU 資源;在 CPU 資源緊張的情況,內核會根據 cgroup 的權重按照比例分配個給任務各自使用 CPU 的時間片。
CFS 調度模式下,也可以給 cgroup 分配一個使用上限,限制任務能使用 CPU 的核數。
設置 CPU 數字的單位都是微秒(microsecond),用 us 表示。
- cpu.cfs_quota_us :每個周期 cgroup 中所有任務能使用的 CPU 時間,默認為 -1 ,表示不限制 CPU 使用。需要配合 cpu.cfs_period_us 一起使用,一般設置為 100000 (docker 中設置的值)
- cpu.cfs_period_us :每個周期中 cgroup 任務可以使用的時間周期,如果想要限制 cgroup 任務每秒鐘使用 0.5 秒 CPU,可以在 cpu.cfs_quota_us 為 100000 的情況下把它設置為 50000 。如果它的值比 cfs_quota_us 大,表明進程可以使用多個核 CPU,比如 200000 表示進程能夠使用 2.0 核
- cpu.stat :CPU 使用的統計數據, nr_periods 表示已經過去的時間周期; nr_throttled 表示 cgroup 中任務被限制使用 CPU 的次數(因為超過了規定的上限); throttled_time 表示被限制的總時間
- cpu.shares :cgroup 使用 CPU 時間的權重值。如果兩個 cgroup 的權重都設置為 100,那么它們里面的任務同時運行時,使用 CPU 的時間應該是一樣的;如果把其中一個權重改為 200,那么它能使用的 CPU 時間將是對方的兩倍。
RT 調度模式下的參數:
RT 調度模式下和 CFS 中上限設置類似,區別是它只是限制實時任務的 CPU。
- cpu.rt_period_us :設置一個周期時間,表示多久 cgroup 能夠重新分配 CPU 資源
- cpu.rt_runtime_us :設置運行時間,表示在周期時間內 cgroup 中任務能訪問 CPU 的時間。這個限制是針對單個 CPU 核數的,如果是多核,需要乘以對應的核數
cpuacct: 任務使用 CPU 情況統計
cpuacct 不做任何資源限制,它的功能是資源統計,自動地統計 cgroup 中任務對 CPU 資源的使用情況,統計數據也包括子 cgroup 中的任務。
- cpuacct.usage :該 cgroup 中所有任務(包括子 cgroup 中的任務,下同)總共使用 CPU 的時間,單位是納秒(ns)。往文件中寫入 0 可以重置統計數據
- cpuacct.stat :該 cgroup 中所有任務使用 CPU 的user 和 system 時間,也就是用戶態 CPU 時間和內核態 CPU 時間
- cpuacct.usage_percpu :該 cgroup 中所有任務使用各個 CPU 核數的時間,單位為納秒(ns)
cpuset: cpu 綁定
除了限制 CPU 的使用量,cgroup 還能把任務綁定到特定的 CPU,讓它們只運行在這些 CPU 上,這就是 cpuset 子資源的功能。除了 CPU 之外,還能綁定內存節點(memory node)。
NOTE:在把任務加入到 cpuset 的 task 文件之前,用戶必須設置 cpuset.cpus 和 cpuset.mems 參數。
- cpuset.cpus :設置 cgroup 中任務能使用的 CPU,格式為逗號( , )隔開的列表,減號( - )可以表示范圍。比如, 0-2,7 表示 CPU 第 0,1,2,和 7 核。
- cpuset.mems :設置 cgroup 中任務能使用的內存節點,和 cpuset.cpus 格式一樣
上面兩個是最常用的參數, cpuset 中有很多其他參數,需要對 CPU 調度機制有深入的了解,很少用到,而且我也不懂,所以就不寫了,具體可以參考參考文檔中 RedHat 網站。
memory:限制內存使用
memory 子資源系統能限制 cgroup 中任務對內存的使用,也能生成它們使用數據的報告。
控制內存使用:
- memory.limit_in_bytes :cgroup 能使用的內存上限值,默認為字節;也可以添加 k/K 、 m/M 和 g/G 單位后綴。往文件中寫入 -1 來移除設置的上限,表示不對內存做限制
- memory.memsw.limit_in_bytes :cgroup 能使用的內存加 swap 上限,用法和上面一樣。寫入 -1 來移除上限
- memory.failcnt :任務使用內存量達到 limit_in_bytes 上限的次數
- memory.memsw.failcnt :任務使用內存加 swap 量達到 memsw.limit_in_bytes 上限的次數
- memory.soft_limit_in_bytes :設置內存軟上限。如果內存充足, cgroup 中的任務可以用到 memory.limit_in_bytes 設定的內存上限;當時當內存資源不足時,內核會讓任務使用的內存不超過 soft_limit_in_bytes 中的值。文件內容的格式和 limit_in_bytes 一樣
- memory.swappiness :設置內核 swap out 進程內存(而不是從 page cache 中回收頁) 的傾向。默認值為 60,低于 60 表示降低傾向,高于 60 表示增加傾向;如果值高于 100,表示允許內核 swap out 進程地址空間的頁。如果值為 0 表示傾向很低,而不是禁止該行為。
OOM 操作:
OOM 是 out of memory 的縮寫,可以翻譯成內存用光。cgroup 可以控制內存用完之后應該怎么處理進程,默認情況下,用光內存的進程會被殺死。
memory.oom_control :是否啟動 OOM killer,如果啟動(值為 0,是默認情況)超過內存限制的進程會被殺死;如果不啟動(值為 1),使用超過限定內存的進程不會被殺死,而是被暫停,直到它釋放了內存能夠被繼續使用。
統計內存使用情況:
- memory.stat :匯報內存的使用情況,里面的數據包括:
- cache :頁緩存(page cache)字節數,包括 tmpfs(shmem)
- rss :匿名和 swap cache 字節數,不包括 tmpfs
- mapped_file :內存映射(memory-mapped)的文件大小,包括 tmpfs,單位是字節
- pgpgin : paged into 內存的頁數
- pgpgout :paged out 內存的頁數
- swap :使用的 swap 字節數
- active_anon :活躍的 LRU 列表中匿名和 swap 緩存的字節數,包括 tmpfs
- inactive_anon :不活躍的 LRU 列表中匿名和 swap 緩存的字節數,包括 tmpfs
- active_file :活躍 LRU 列表中文件支持的(file-backed)的內存字節數
- inactive_file :不活躍列表中文件支持的(file-backed)的內存字節數
- unevictable :不可以回收的內存字節數
- memory.usage_in_bytes :cgroup 中進程當前使用的總內存字節數
- memory.memsw.usage_in_bytes :cgroup 中進程當前使用的總內存加上總 swap 字節數
- memory.max_usage_in_bytes :cgroup 中進程使用的最大內存字節數
- memory.memsw.max_usage_in_bytes :cgroup 中進程使用的最大內存加 swap 字節數
net_cls:為網絡報文分類
net_cls 子資源能夠給網絡報文打上一個標記(classid),這樣內核的 tc(traffic control)模塊就能根據這個標記做流量控制。
net_cls.classid :包含一個整數值。從文件中讀取是的十進制,寫入的時候需要是十六進制。比如, 0x100001 寫入到文件中,讀取的將是 1048577 , ip 命令操作的形式為 10:1 。
這個值的格式為 0xAAAABBBB ,一共 32 位,分成前后兩個部分,前置的 0 可以忽略,因此 0x10001 和 0x00010001 一樣,表示為 1:1 。
net_prio:網絡報文優先級
net_prio (Network Priority)子資源能夠動態設置 cgroup 中應用在網絡接口的優先級。網絡優先級是報文的一個屬性值, tc 可以設置網絡的優先級,socket 也可以通過 SO_PRIORITY 選項設置它(但是很少應用會這么做)。
- net_prio.prioidx :只讀文件,里面包含了一個整數值,內核用來標識這個 cgroup
- net_prio.ifpriomap :網絡接口的優先級,里面可以包含很多行,用來為從網絡接口中發出去的報文設置優先級。每行的格式為 network_interface priority ,比如 echo "eth0 5" > /sys/fs/cgroup/net_prio/mycgroup/net_prio.ifpriomap
devices:設備黑白名單
device 子資源可以允許或者阻止 cgroup 中的任務訪問某個設備,也就是黑名單和白名單的作用。
- devices.allow :cgroup 中的任務能夠訪問的設備列表,格式為 type major:minor access ,
- type 表示類型,可以為 a (all), c (char), b (block)
- major:minor 代表設備編號,兩個標號都可以用 * 代替表示所有,比如 *:* 代表所有的設備
- accss 表示訪問方式,可以為 r (read), w (write), m (mknod) 的組合
- devices.deny :cgroup 中任務不能訪問的設備,和上面的格式相同
- devices.list :列出 cgroup 中設備的黑名單和白名單
freezer
freezer 子資源比較特殊,它并不和任何系統資源相關,而是能暫停和恢復 cgroup 中的任務。
- freezer.state :這個文件值存在于非根 cgroup 中(因為所有的任務默認都在根 cgroup 中,停止所有的任務顯然是錯誤的行為),里面的值表示 cgroup 中進程的狀態:
- FROZEN :cgroup 中任務都被掛起(暫停)
- FREEZING :cgroup 中任務正在被掛起的過程中
- THAWED :cgroup 中的任務已經正常恢復
要想掛起某個進程,需要先把它移動到某個有 freezer 的 cgroup 中,然后 Freeze 這個 cgroup。
NOTE:如果某個 cgroup 處于掛起狀態,不能往里面添加任務。用戶可以寫入 FROZEN 和 THAWED 來控制進程掛起和恢復, FREEZING 不受用戶控制。
總結
cgroup 提供了強大的功能,能夠讓我們控制應用的資源使用情況,也能統計資源使用數據,是容器技術的基礎。但是 cgroup 整個系統也很復雜,甚至顯得有些混亂,目前 cgroup 整個在被重寫,新的版本被稱為 cgroup V2,而之前的版本也就被稱為了 V1。
cgroup 本身并不提供對網絡資源的使用控制,只能添加簡單的標記和優先級,具體的控制需要借助 linux 的 TC 模塊來實現。
參考資料
- An introduction to cgroups and cgroupspy
- LXC, Cgroups and Advanced Linux Container Technology Lecture
- redhat doc:Subsystems and Tunable Parameters
- Docker背后的內核知識——cgroups資源限制
- Docker資源管理探秘:Docker背后的內核Cgroups機制
- Linux 內核 cgroups 簡介
- 王喆鋒: Linux Cgroups 詳解
- Linux Cgroups V2 設計
- Understanding the new control groups API
- Resource Management: Linux Kernel Namespaces and cgroups – Rami Rosen
- wikipedia cgroups page
來自:http://cizixs.com/2017/08/25/linux-cgroup