1、導言:
Linux系統每個進程都可以自由競爭系統資源,有時候會導致一些次要進程佔用了系統某個資源(如CPU)的絕大部分,主要進程就不能很好地執行,從而影響系統效率,重則在linux資源耗盡時可能會引起錯殺進程。因此linux引入了linux cgroups來控制進程資源,讓進程更可控。
2、Linux cgroups基礎知識
Linux Cgroup 可讓您爲系統中所運行任務(進程)的用戶定義組羣分配資源 — 比如 CPU 時間、系統內存、網絡帶寬或者這些資源的組合。您可以監控您配置的 cgroup,拒絕 cgroup 訪問某些資源,甚至在運行的系統中動態配置您的 cgroup。所以,可以將 controll groups 理解爲 controller (system resource) (for) (process)groups,也就是是說它以一組進程爲目標進行系統資源分配和控制。
2.1 它主要提供瞭如下功能:
- Resource limitation: 限制資源使用,比如內存使用上限以及文件系統的緩存限制。
- Prioritization: 優先級控制,比如:CPU利用和磁盤IO吞吐。
- Accounting: 一些審計或一些統計,主要目的是爲了計費。
- Control: 掛起進程,恢復執行進程。
使用 cgroup,系統管理員可更具體地控制對系統資源的分配、優先順序、拒絕、管理和監控。可更好地根據任務和用戶分配硬件資源,提高總體效率。
在實踐中,系統管理員一般會利用CGroup做下面這些事(有點像爲某個虛擬機分配資源似的):
- 隔離一個進程集合(比如:nginx的所有進程),並限制他們所消費的資源,比如綁定CPU的核。
- 爲這組進程分配其足夠使用的內存
- 爲這組進程分配相應的網絡帶寬和磁盤存儲限制
- 限制訪問某些設備(通過設置設備的白名單)
2.2 查看linux是否啓用了linux cgroups
$ uname -r 4.18.0-24-generic $ cat /boot/config-4.18.0-24-generic | grep CGROUP CONFIG_CGROUPS=y CONFIG_BLK_CGROUP=y # CONFIG_DEBUG_BLK_CGROUP is not set CONFIG_CGROUP_WRITEBACK=y CONFIG_CGROUP_SCHED=y CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_RDMA=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_HUGETLB=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_PERF=y CONFIG_CGROUP_BPF=y # CONFIG_CGROUP_DEBUG is not set CONFIG_SOCK_CGROUP_DATA=y CONFIG_NETFILTER_XT_MATCH_CGROUP=m CONFIG_NET_CLS_CGROUP=m CONFIG_CGROUP_NET_PRIO=y CONFIG_CGROUP_NET_CLASSID=y |
對應的CGROUP項爲“y”代表已經打開linux cgroups功能。
2.3 Cgroups 組成
Cgroups主要由task,cgroup,subsystem及hierarchy構成。下面分別介紹下各自的概念。
- Task : 在Cgroups中,task就是系統的一個進程。
- Cgroup : Cgroups中的資源控制都以cgroup爲單位實現的。cgroup表示按照某種資源控制標準劃分而成的任務組,包含一個或多個Subsystems。一個任務可以加入某個cgroup,也可以從某個cgroup遷移到另外一個cgroup。
- Subsystem : Cgroups中的subsystem就是一個資源調度控制器(Resource Controller)。比如CPU子系統可以控制CPU時間分配,內存子系統可以限制cgroup內存使用量。
- Hierarchy : hierarchy由一系列cgroup以一個樹狀結構排列而成,每個hierarchy通過綁定對應的subsystem進行資源調度。hierarchy中的cgroup節點可以包含零或多個子節點,子節點繼承父節點的屬性。整個系統可以有多個hierarchy。
2.4 組織結合和基本規則
主要介紹Subsystems, Hierarchies,Control Group和Tasks之間組織結構和規則:
規則一:
同一個hierarchy能夠附加一個或多個subsystem。如 cpu 和 memory subsystems(或者任意多個subsystems)附加到同一個hierarchy。
規則二:
一個 subsystem 可以附加到多個 hierarchy,當且僅當這些 hierarchy 只有這唯一一個 subsystem。即某個hierarchy(hierarchy A)中的subsystem(如CPU)不能附加到已經附加了其他subsystem的hierarchy(如hierarchy B)中。也就是說已經附加在某個 hierarchy 上的 subsystem 不能附加到其他含有別的 subsystem 的 hierarchy 上。
規則三:
系統每次新建一個hierarchy時,該系統上的所有task默認構成了這個新建的hierarchy的初始化cgroup,這個cgroup也稱爲root cgroup。對於你創建的每個hierarchy,task只能存在於其中一個cgroup中,即一個task不能存在於同一個hierarchy的不同cgroup中,但是一個task可以存在在不同hierarchy中的多個cgroup中。如果操作時把一個task添加到同一個hierarchy中的另一個cgroup中,則會從第一個cgroup中移除
如:
cpu 和 memory subsystem被附加到 cpu_mem_cg 的hierarchy。而 net_cls subsystem被附加到 net_cls hierarchy。並且httpd進程被同時加到了 cpu_mem_cg hierarchy的 cg1 cgroup中和 net hierarchy的 cg2 cgroup中。並通過兩個hierarchy的subsystem分別對httpd進程進行cpu,memory及網絡帶寬的限制。
規則四:
進程(task)在 fork 自身時創建的子任務(child task)默認與原 task 在同一個 cgroup 中,但是 child task 允許被移動到不同的 cgroup 中。即 fork 完成後,父子進程間是完全獨立的。
2.5 hierarchy和cgroup操作
hierarchy
1)新建hierarchy
mkdir cgroup/hy_cpu_mem
2)使用mount命令掛載hierarchy(hy_cpu_mem),並附加cpu、memory到該hierarchy上
mount -t cgroup -o cpu,cpuset,memory hy_cpu_mem cgroup/hy_cpu_mem
3)如果想在已有的hierarchy上attch或detach,使用remount命令detach subsystem
mount -t cgroup -o cpu,cpuset,hy_cpu_mem cgroup/hy_cpu_mem #detach memory
4)卸載hierarchy
umount cgroup/hy_cpu_mem
cgroup
- 創建cgroup
mkdir cgroup/hy_cpu_mem/cgroup1
- 設置cgroup參數
sudo echo 100000 > cpu.cfs_period_us
- 移動task(進程)
只要把對應的進程PID加入到新cgroup的task中即可,如:echo 30167 > newcgroup/tasks
2.6 subsystem介紹
Linxu中爲了方便用戶使用cgroups,已經把其實現成了文件系統,其目錄在/var/fs/cgroup下:
$ ll /sys/fs/cgroup 總用量 0 dr-xr-xr-x 5 root root 0 6月 26 15:52 blkio lrwxrwxrwx 1 root root 11 6月 26 15:52 cpu -> cpu,cpuacct lrwxrwxrwx 1 root root 11 6月 26 15:52 cpuacct -> cpu,cpuacct dr-xr-xr-x 5 root root 0 6月 26 15:52 cpu,cpuacct dr-xr-xr-x 3 root root 0 6月 26 15:52 cpuset dr-xr-xr-x 5 root root 0 6月 26 15:52 devices dr-xr-xr-x 3 root root 0 6月 26 15:52 freezer dr-xr-xr-x 3 root root 0 6月 26 15:52 hugetlb dr-xr-xr-x 5 root root 0 6月 26 15:52 memory lrwxrwxrwx 1 root root 16 6月 26 15:52 net_cls -> net_cls,net_prio dr-xr-xr-x 3 root root 0 6月 26 15:52 net_cls,net_prio lrwxrwxrwx 1 root root 16 6月 26 15:52 net_prio -> net_cls,net_prio dr-xr-xr-x 3 root root 0 6月 26 15:52 perf_event dr-xr-xr-x 5 root root 0 6月 26 15:52 pids dr-xr-xr-x 2 root root 0 6月 26 15:52 rdma dr-xr-xr-x 6 root root 0 6月 26 15:52 systemd dr-xr-xr-x 5 root root 0 6月 26 15:52 unified |
我們可以看到/sys/fs/cgroug目錄下有多個子目錄,這些目錄都可以認爲是收cgroups管理的subsystem資源。每格subsystem對應如下:
- blkio — 這個子系統爲塊設備設定輸入/輸出限制,比如物理設備(磁盤,固態硬盤,USB 等等)。
- cpu — 這個子系統使用調度程序提供對 CPU 的 cgroup 任務訪問。
- cpuacct — 這個子系統自動生成 cgroup 中任務所使用的 CPU 報告。
- cpuset — 這個子系統爲 cgroup 中的任務分配獨立 CPU(在多核系統)和內存節點。
- devices — 這個子系統可允許或者拒絕 cgroup 中的任務訪問設備。
- freezer — 這個子系統掛起或者恢復 cgroup 中的任務。
- memory — 這個子系統設定 cgroup 中任務使用的內存限制,並自動生成內存資源使用報告。
- net_cls — 這個子系統使用等級識別符(classid)標記網絡數據包,可允許 Linux 流量控制程序(tc)識別從具體 cgroup 中生成的數據包。
- net_prio — 這個子系統用來設計網絡流量的優先級
- hugetlb — 這個子系統主要針對於HugeTLB系統進行限制,這是一個大頁文件系統。
2.7 subsystem配置參數介紹
2.7.1 blkio - BLOCK IO 資源控制
限額類 限額類是主要有兩種策略,一種是基於完全公平隊列調度(CFQ:Completely Fair Queuing )的按權重分配各個 cgroup 所能佔用總體資源的百分比,好處是當資源空閒時可以充分利用,但只能用於最底層節點 cgroup 的配置;另一種則是設定資源使用上限,這種限額在各個層次的 cgroup 都可以配置,但這種限制較爲生硬,並且容器之間依然會出現資源的競爭。
- 按比例分配塊設備 IO 資源
- blkio.weight:填寫 100-1000 的一個整數值,作爲相對權重比率,作爲通用的設備分配比。
- blkio.weight_device: 針對特定設備的權重比,寫入格式爲device_types:node_numbers weight,空格前的參數段指定設備,weight參數與blkio.weight相同並覆蓋原有的通用分配比。{![查看一個設備的device_types:node_numbers可以使用:ls -l /dev/DEV,看到的用逗號分隔的兩個數字就是。有的文章也稱之爲major_number:minor_number。]}
- 控制 IO 讀寫速度上限
- blkio.throttle.read_bps_device:按每秒讀取塊設備的數據量設定上限,格式device_types:node_numbers bytes_per_second。
- blkio.throttle.write_bps_device:按每秒寫入塊設備的數據量設定上限,格式device_types:node_numbers bytes_per_second。
- blkio.throttle.read_iops_device:按每秒讀操作次數設定上限,格式device_types:node_numbers operations_per_second。
- blkio.throttle.write_iops_device:按每秒寫操作次數設定上限,格式device_types:node_numbers operations_per_second
- 針對特定操作 (read, write, sync, 或 async) 設定讀寫速度上限
- blkio.throttle.io_serviced:針對特定操作按每秒操作次數設定上限,格式device_types:node_numbers operation operations_per_second
- blkio.throttle.io_service_bytes:針對特定操作按每秒數據量設定上限,格式device_types:node_numbers operation bytes_per_second
- 統計與監控 以下內容都是隻讀的狀態報告,通過這些統計項更好地統計、監控進程的 io 情況。
- blkio.reset_stats:重置統計信息,寫入一個 int 值即可。
- blkio.time:統計 cgroup 對設備的訪問時間,按格式device_types:node_numbers milliseconds讀取信息即可,以下類似。
- blkio.io_serviced:統計 cgroup 對特定設備的 IO 操作(包括 read、write、sync 及 async)次數,格式device_types:node_numbers operation number
- blkio.sectors:統計 cgroup 對設備扇區訪問次數,格式 device_types:node_numbers sector_count
- blkio.io_service_bytes:統計 cgroup 對特定設備 IO 操作(包括 read、write、sync 及 async)的數據量,格式device_types:node_numbers operation bytes
- blkio.io_queued:統計 cgroup 的隊列中對 IO 操作(包括 read、write、sync 及 async)的請求次數,格式number operation
- blkio.io_service_time:統計 cgroup 對特定設備的 IO 操作(包括 read、write、sync 及 async)時間 (單位爲 ns),格式device_types:node_numbers operation time
- blkio.io_merged:統計 cgroup 將 BIOS 請求合併到 IO 操作(包括 read、write、sync 及 async)請求的次數,格式number operation
- blkio.io_wait_time:統計 cgroup 在各設備中各類型IO 操作(包括 read、write、sync 及 async)在隊列中的等待時間(單位 ns),格式device_types:node_numbers operation time
- __blkio.__recursive_*:各類型的統計都有一個遞歸版本,Docker 中使用的都是這個版本。獲取的數據與非遞歸版本是一樣的,但是包括 cgroup 所有層級的監控數據。
2.7.2 cpu - CPU 資源控制
CPU 資源的控制也有兩種策略,一種是完全公平調度 (CFS:Completely Fair Scheduler)策略,提供了限額和按比例分配兩種方式進行資源控制;另一種是實時調度(Real-Time Scheduler)策略,針對實時進程按週期分配固定的運行時間。配置時間都以微秒(µs)爲單位,文件名中用us表示。
CFS 調度策略下的配置
- 設定 CPU 使用週期使用時間上限
- cpu.cfs_period_us:設定週期時間,必須與cfs_quota_us配合使用。
- cpu.cfs_quota_us :設定週期內最多可使用的時間。這裏的配置指 task 對單個 cpu 的使用上限,若cfs_quota_us是cfs_period_us的兩倍,就表示在兩個核上完全使用。數值範圍爲 1000 - 1000,000(微秒)。
- cpu.stat:統計信息,包含nr_periods(表示經歷了幾個cfs_period_us週期)、nr_throttled(表示 task 被限制的次數)及throttled_time(表示 task 被限制的總時長)。
- 按權重比例設定 CPU 的分配
- cpu.shares:設定一個整數(必須大於等於 2)表示相對權重,最後除以權重總和算出相對比例,按比例分配 CPU 時間。(如 cgroup A 設置 100,cgroup B 設置 300,那麼 cgroup A 中的 task 運行 25% 的 CPU 時間。對於一個 4 核 CPU 的系統來說,cgroup A 中的 task 可以 100% 佔有某一個 CPU,這個比例是相對整體的一個值。)
- RT 調度策略下的配置 實時調度策略與公平調度策略中的按週期分配時間的方法類似,也是在週期內分配一個固定的運行時間。
- cpu.rt_period_us :設定週期時間。
- cpu.rt_runtime_us:設定週期中的運行時間。
2.7.3 cpuacct - CPU 資源報告
這個子系統的配置是cpu子系統的補充,提供 CPU 資源用量的統計,時間單位都是納秒。
- cpuacct.usage:統計 cgroup 中所有 task 的 cpu 使用時長
- cpuacct.stat:統計 cgroup 中所有 task 的用戶態和內核態分別使用 cpu 的時長
- cpuacct.usage_percpu:統計 cgroup 中所有 task 使用每個 cpu 的時長
2.7.4 cpuset - CPU 綁定
爲 task 分配獨立 CPU 資源的子系統,參數較多,這裏只選講兩個必須配置的參數,同時 Docker 中目前也只用到這兩個。
- cpuset.cpus:在這個文件中填寫 cgroup 可使用的 CPU 編號,如0-2,16代表 0、1、2 和 16 這 4 個 CPU。
- cpuset.mems:與 CPU 類似,表示 cgroup 可使用的memory node,格式同上
2.7.5 device - 限制 task 對 device 的使用
** 設備黑 / 白名單過濾 **
- devices.allow:允許名單,語法type device_types:node_numbers access type ;type有三種類型:b(塊設備)、c(字符設備)、a(全部設備);access也有三種方式:r(讀)、w(寫)、m(創建)。
- devices.deny:禁止名單,語法格式同上。
統計報告
- devices.list:報告爲這個 cgroup 中的task 設定訪問控制的設備
2.7.6 freezer - 暫停 / 恢復 cgroup 中的 task
只有一個屬性,表示進程的狀態,把 task 放到 freezer 所在的 cgroup,再把 state 改爲 FROZEN,就可以暫停進程。不允許在 cgroup 處於 FROZEN 狀態時加入進程。 * **freezer.state **,包括如下三種狀態: - FROZEN 停止 - FREEZING 正在停止,這個是隻讀狀態,不能寫入這個值。 - THAWED 恢復
2.7.7 memory - 內存資源管理
限額類
- memory.limit_bytes:強制限制最大內存使用量,單位有k、m、g三種,填-1則代表無限制。
- memory.soft_limit_bytes:軟限制,只有比強制限制設置的值小時纔有意義。填寫格式同上。當整體內存緊張的情況下,task 獲取的內存就被限制在軟限制額度之內,以保證不會有太多進程因內存捱餓。可以看到,加入了內存的資源限制並不代表沒有資源競爭。
- memory.memsw.limit_bytes:設定最大內存與 swap 區內存之和的用量限制。填寫格式同上。
報警與自動控制
- memory.oom_control:改參數填 0 或 1, 0表示開啓,當 cgroup 中的進程使用資源超過界限時立即殺死進程,1表示不啓用。默認情況下,包含 memory 子系統的 cgroup 都啓用。當oom_control不啓用時,實際使用內存超過界限時進程會被暫停直到有空閒的內存資源。
統計與監控類
- memory.usage_bytes:報告該 cgroup 中進程使用的當前總內存用量(以字節爲單位)
- memory.max_usage_bytes:報告該 cgroup 中進程使用的最大內存用量
- memory.failcnt:報告內存達到在 memory.limit_in_bytes設定的限制值的次數
- memory.stat:包含大量的內存統計數據。
- cache:頁緩存,包括 tmpfs(shmem),單位爲字節。
- rss:匿名和 swap 緩存,不包括 tmpfs(shmem),單位爲字節。
- mapped_file:memory-mapped 映射的文件大小,包括 tmpfs(shmem),單位爲字節
- pgpgin:存入內存中的頁數
- pgpgout:從內存中讀出的頁數
- swap:swap 用量,單位爲字節
- active_anon:在活躍的最近最少使用(least-recently-used,LRU)列表中的匿名和 swap 緩存,包括 tmpfs(shmem),單位爲字節
- inactive_anon:不活躍的 LRU 列表中的匿名和 swap 緩存,包括 tmpfs(shmem),單位爲字節
- active_file:活躍 LRU 列表中的 file-backed 內存,以字節爲單位
- inactive_file:不活躍 LRU 列表中的 file-backed 內存,以字節爲單位
- unevictable:無法再生的內存,以字節爲單位
- hierarchical_memory_limit:包含 memory cgroup 的層級的內存限制,單位爲字節
- hierarchical_memsw_limit:包含 memory cgroup 的層級的內存加 swap 限制,單位爲字節
linux cgroups應用實例
前提
假設我們編寫了一個死循環程序test
#include <stdio.h> int main(){ long i=0; while(1){ i++; } return 0; } |
佔用cpu達到100%:
例1:顯示CPU使用比例
$ mkdir /sys/fs/cgroup/cpu/test $ cd /sys/fs/cgroup/cpu/ test $ ls cgroup.clone_children cgroup.procs cpuacct.stat cpuacct.usage cpuacct.usage_percpu cpu.cfs_period_us cpu.cfs_quota_us cpu.shares cpu.stat notify_on_release tasks $ cat cpu.cfs_quota_us -1 $ sudo echo 100000 > cpu.cfs_period_us $ sudo echo 20000 > cpu.cfs_quota_us $ cat cpu.cfs_quota_us $ echo 30167 > tasks |
注意:看下cgroups是不是隻讀,如果是隻讀需要重新掛載爲讀寫:sudo mount -o remount,rw /sys/fs/cgroup。另外注意權限,如果出現sudo還是寫不了,那是因爲“>”也是一個命令,sudo只是讓echo具有root權限,而“>”還是普通權限,遇到這種情況可以通過以下方法解決:
1)利用sh –c命令讓bash把字符串當成完整一個命令執行,如:sudo sh –c “echo hello > 1.txt”
2)利用管道和tee命令:
echo a | sudo tee 1.txt;或追加:echo a | sudo tee –a 1.txt
3)進入root用戶,sudo –s
重新查看test佔用CPU率:
可以通過資源控制後,test進程最高佔用20%的CPU資料,也就是我們設置的賦值。另外,同樣的進程共同佔用所分配的資料,如同時啓動兩個test,那麼兩個test共同佔用20%的CPU資源。
例2:限制進程使用memory
$ mkdir test $ cd test $ cat memory.limit_in_bytes $ echo 64k > memory.limit_in_bytes $ echo 30167 > tasks |
這樣就限制了test進程最多可使用64K內存,超過會被殺掉。
例3:現在進程IO資源
啓動壓力測試命令:
sudo dd if=/dev/sda1 of=/dev/null #將sda1整盤數據輸出到/dev/null
查看IO使用情況:
可以看到此時自盤讀取速度爲7.97/M/s。
接下來開始控制IO使用:
$ cd /sys/fs/cgroup/blkio $ mkdir io $ cd io $ ll /dev/sda1 $ brw-rw---- 1 root disk 8, 1 6月 18 13:55 /dev/sda1 $ sudo echo '8:0 1048576' > blkio.throttle.read_bps_device $ sudo echo 30618 > tasks |
這樣,這個進程的IO讀速度被限制在1M/S秒