【原創】探索雲端計算容器底層之Cgroup

lightinglei發表於2020-08-30

一、什麼是Cgroup,使用場景?

容器本質上是程式,既然是程式就會消耗掉系統資源,比如:CPU、記憶體、磁碟、網路頻寬等,如果不加以限制,容器在某些情況下就會無限制地吃掉宿主機的系統資源,顯然這不是我們期望發生的,另外當我們的環境中執行了很多容器,且系統資源一定的情況下,我們有優先保證主要容器應用的需求,如何既能夠解決此問題同時又能夠滿足我們的需求呢?答案就是:Linux Cgroup(全程Linux Control Group),在前面的文章中,介紹了namespace為容器這類程式提供了隔離,而Cgroup可以為容器這類程式提供資源使用上限,兩者黃金搭檔,共同為容器應用保駕護航。

二、Cgroup的原理和實踐

CPU的週期控制

Cgroup可以為容器程式使用的CPU、記憶體、磁碟、網路頻寬資源進行限制,具體是如何實現的呢?接下來我們一起來實操下,在 Linux 中,Cgroups 給使用者暴露出來的操作介面是檔案系統,即它以檔案和目錄的方式組織在作業系統的 /sys/fs/cgroup 這個路徑下,我們先去此目錄檢視下

[root@k8s-master /]# cd sys/fs/cgroup/
[root@k8s-master cgroup]# ls
blkio  cpuacct      cpuset   freezer  memory   net_cls,net_prio  perf_event  rdma
cpu    cpu,cpuacct  devices  hugetlb  net_cls  net_prio          pids        systemd

可以看到在cgroup的這個目錄下存在很多子目錄,這些都是cgroup可以限制地資源種類,我們在進一步進入到CPU的子目錄檢視下,裡面有限制資源種類的詳細的限制地指標,比如

1、cpu.cfs_period_us:指定容器對CPU的使用多長時間重新做一次分配
2、cpu.cfs_quota_us:指在cpu.cfs_period_us週期內給分配多少時間給容器
這兩個指標需要一起配合使用來實現CPU的週期控制,我們先手動模擬容器建立的時候,如何完成利用cgroup來實現資源限制,以CPU週期控制為例子,先在/sys/fs/cgroup/cpu目錄下建立1個container_
test的目錄,如下所示我已經建立好(紅色字型)。

[root@k8s-master cgroup]# cd cpu
[root@k8s-master cpu]# ls
cgroup.clone_children  cpuacct.usage_all          cpu.cfs_period_us  docker
cgroup.procs           cpuacct.usage_percpu       cpu.cfs_quota_us   kubepods
cgroup.sane_behavior   cpuacct.usage_percpu_sys   cpu.rt_period_us   notify_on_release
container_test         cpuacct.usage_percpu_user  cpu.rt_runtime_us  release_agent
cpuacct.stat           cpuacct.usage_sys          cpu.shares         system.slice
cpuacct.usage          cpuacct.usage_user         cpu.stat           tasks

 然後進入到此目錄下,ls檢視下,這裡出現了一個神奇的形象,此目錄下自動生成了很多CPU子系統控制的指標,這些指標我們並未進行新增,也就是說在/sys/fs/cgroup/cpu目錄下會給新建的目預設配置CPU子系統資源限制的指標

[root@k8s-master cpu]# cd container_test/
[root@k8s-master container_test]# ls
cgroup.clone_children  cpuacct.usage_percpu       cpu.cfs_period_us  cpu.stat
cgroup.procs           cpuacct.usage_percpu_sys   cpu.cfs_quota_us   notify_on_release
cpuacct.stat           cpuacct.usage_percpu_user  cpu.rt_period_us   tasks
cpuacct.usage          cpuacct.usage_sys          cpu.rt_runtime_us
cpuacct.usage_all      cpuacct.usage_user         cpu.shares

這些指標如何作用呢?為了體現資源的使用情況,我們先寫一個程式來模擬來吃掉系統資源的情況,然後再來檢視指標

[root@k8s-master sh]# cat while.sh 
#!/bin/bash
while : ; do : ; done &

[root@k8s-master sh]# sh while.sh

通過如上程式,寫了一個while無限迴圈的shell指令碼,預設情況下,這個程式之後的程式會佔據掉系統所剩叢集的所有資源,可通過top命令檢視下

[root@k8s-master sh]# ps -ef |grep while
root      14975      1 97 20:29 pts/1    00:02:48 sh while.sh

 

如上圖所示,while迴圈的程式佔據掉了96.3%的CPU資源,在實際的應用中若程式這樣無限制的使用資源,將會給作業系統帶來很大的負擔嗎,那麼如何控制程式資源的使用呢?回到我們之前建立在container_test目錄下

[root@k8s-master container_test]# cat cpu.cfs_quota_us 
-1
[root@k8s-master container_test]# cat cpu.cfs_period_us
100000

 預設建立的目錄下cfs_quota_us 若為-1,則表示還未啟用quota,即還未實行資源限制,cfs_period_us預設為100000us=100ms=0.1s(秒),接下來我們向cpu.cfs_quota_us 輸入30ms,cfs_period_us值維持不變還是為100ms,在前面關於這2個概念有介紹,cpu.cfs_quota_us表示的是cfs_period_us的週期內,分配30/100的時間,即30%,接下來驗證下

[root@k8s-master container_test]# echo 30000 > /sys/fs/cgroup/cpu/container_test/cpu.cfs_quota_us

  [root@k8s-master container_test]# cat cpu.cfs_quota_us
  30000

設定已完成,但是此時還不會立即生效,還需要將程式ID輸入到資源限制地task裡

[root@k8s-master container_test]# echo 14975 > /sys/fs/cgroup/cpu/container_test/tasks

接下來我們在通過top檢視下資源使用情況,如下圖所示,可以看到CPU的資源使用上限由原來的96.3%已經降到29.9%了,表明此while程式的CPU的資源使用上限已經設定成功。

 以上整個過程為手動設定模擬容器建立的過程中CPU份額控制的過程,實際上在容器建立的過程中,並不需要上面這般步驟,我們只需要在run容器的時候指定指標引數即可,如下所示

[root@k8s-master container_test]# docker run -it -d --cpu-period=100000 --cpu-quota=30000 nginx /bin/bash

上面的命令是後臺守護程式的方式執行了1個nginx的容器,且指定CPU的每隔100000us=100ms做一次分配,且每次分配給容器的時間為30ms,可以看到這個分配和前面手動分配是一致的,值得注意的是這裡需要加上-d來建立容器,若不加上的話會進入到終端互動介面,一旦提出終端互動介面後,容器這個程式也將會退出,而我們希望容器程式保持後臺執行,因此需要加上-d,容器執行成功後,將會在docker目錄下新建一個以容器ID命名的目錄,這個目錄和前面手動建立的目錄以上,系統會預設配置資源限制的引數,我們可以如下看下:

[root@k8s-master container_test]# docker run -it -d --cpu-period=100000 --cpu-quota=30000 nginx /bin/bash
16f51f6780685be9c83b1684515005f30aed91916fdd6573b28eaf56be201e4a
[root@k8s-master docker]# ls
01a0fd62d2110e54b0c3635b2897e7c18e6b78f026fa57b4214d7662dd3b38ba  cpuacct.usage_sys
16f51f6780685be9c83b1684515005f30aed91916fdd6573b28eaf56be201e4a  cpuacct.usage_user
cgroup.clone_children                                             cpu.cfs_period_us
cgroup.procs                                                      cpu.cfs_quota_us
cpuacct.stat                                                      cpu.rt_period_us
cpuacct.usage                                                     cpu.rt_runtime_us
cpuacct.usage_all                                                 cpu.shares
cpuacct.usage_percpu                                              cpu.stat
cpuacct.usage_percpu_sys                                          notify_on_release
cpuacct.usage_percpu_user                                         tasks

如上紅色部分為docker目錄下依據容器的名稱預設建立的目錄,我們進入到這個目錄,然後輸出下之前我們在建立的時候指定的cpu.cfs_quota_us和cfs_period_us值

[root@k8s-master 16f51f6780685be9c83b1684515005f30aed91916fdd6573b28eaf56be201e4a]# cat cpu.cfs_period_us 
100000
[root@k8s-master 16f51f6780685be9c83b1684515005f30aed91916fdd6573b28eaf56be201e4a]# cat cpu.cfs_quota_us
30000

可以看到我們之前設定的值已經生效了,也就是說這個nginx的容器最多可以支援使用到30%左右的CPU頻寬。

相類似的我們可以對容器獲取CPU的資源的優先順序進行設定,通過--cpu-share這個引數,其指定的值並非是給容器具體的份額,其實是個權重,在需要對容器資源進行限制時才會生效,權重大的,可以優先得到CPU的資源;另外還可以對使用的核數進行限制,針對多核的伺服器,可以控制容器執行限定使用哪些CPU核心和記憶體節點,即使用-cpuset-cpus和-cpuset-mens引數,比如:我們可以指定建立的容器只能用0、1、2三核。

 

三、總結

本文以CPU中週期控制限制某程式的CPU資源使用為例子,介紹了其手動設定引數和容器自動設定引數,每新建1個容器,在/sys/fs/cgroup/cpu/docker目錄下都會自動以容器的ID為名字建立1個目錄,且在此目錄下支援對CPU、記憶體、網路頻寬、磁碟的資源使用進行限制,而其限制地處理與CPU的週期控制是類似的,這裡就未做過多介紹,感興趣的讀者可以自己實踐驗證下,感謝您的閱讀!


作者簡介:雲端計算容器\Docker\K8s\Serverless方向產品經理,學點技術,為更好地設計產品。

相關文章