2021-05-12 14:32:11
Linux Cgroups 詳解
Namespace是用來實現進程之間的隔離,但是並沒有限制其空間的大小。如果想要限制一個進程可以使用的空間,保證各個進程之間不會互相爭搶就要用到 Cgroups。
Linux Cgroups(Linux Control Groups)提供了對一組進程及將來子進程的資源限制、控制、統計的能力。這些資源包括cpu、記憶體、儲存、網路 等。通過Cgroups,可以方便的控制某個進程佔用的資源,並可以實施監控和統計資訊。
Cgroups中的三個元件
-
cgroup 是對進程分組管理的一種機制,一個cgroup包含一組進程,並可以在這個cgroup上增加Linux subsystem的各種引數設定,將一組進程和一 組subsystem的系統引數關聯起來。
-
subsystem 是一組資源控制的模組。包含以下幾項。
- blkio 設定對塊裝置輸入輸出的存取控制。例如磁碟
- cpu 設定cgroup中進程的cpu被排程策略。
- cpuacct 可以統計cgroup中進程的cpu占用。
- cpuset 在多核機器上,設定cgroup中進程可以使用的cpu和記憶體。此處僅限於NUMA架構。
- devices 控制cgroup對裝置的存取。
- freezer 掛起(suspend)和恢復(resue) cgroup中的進程。
- memory 用於控制cgroup中進程的記憶體占用。
- net_cls 將cgroup中進程產生的網路包分類,便於linux tc(traffic controller)可以根據分類區分出來自某個cgroup包並做監控。
- net_prio 設定cgroup中進程產生的網路流量的優先順序。
- ns 使cgroup中的進程在新的Namespace中fork新進程時,建立一個新的cgroup,這個cgroup包含新的Namespace中的進程。
每個subsystem會關聯到定義的cgroup,並對這個cgroup中的進程做限制和控制。這些subsystem是逐步合併到核心中的,可以安裝apt-get install cgroup-bin 然後通過 lssubsys -a 檢視
- hierarchy 把一組cgroup串成一個柱狀結構,這樣的樹便是一個hierarchy,通過這種結構,Cgroups可以做到繼承。
三個元件的關係
- 系統建立hierarchy 之後,所有的進程都會加入這個hierarchy的cgroup的根節點。在這個cgroup根節點是hierarchy預設建立的。
- 一個subsystem只能附加到一個hierarchy上面。
- 一個進程可以作為多個cgroup的成員,但是cgroup必須在不同的hierarchy中。
- 一個進程fork的子進程和父進程在同一個cgroup中也可以根據需要移到其他cgroup中。
Kernel介面
前面說道Cgroups中的hierarchy是一種樹狀結構,Kernel為了對Cgroups的設定更直觀,也會顯示為樹狀結構。下面進行範例,了解如何操作Cgroups。
-
首先建立並掛在一個hierarchy(cgroup樹),如下.
leon@leon:~$ mkdir cgroup-test
leon@leon:~$ sudo mount -t cgroup -o none,name=cgroup1 cgroup1 ./cgroup-test/
leon@leon:~$ ls ./cgroup-test/
cgroup.clone_children cgroup.procs cgroup.sane_behavior notify_on_release release_agent tasks這些檔案就是這個hierarchy中cgroup根節點的設定項,上面這些檔案含義如下。
- cgroup.clone_children, cpuset的subsystem會讀取這個檔案的設定,如果值是1(預設值0),子cgroup才會繼承父cgroup的cpuset設定。
- cgroup.procs 是樹中當前結點cgroup的行程群組id,現在的位置是在根節點,這個檔案中會有現在系統中所有行程群組的ID。
- notify_on_release和release_agent會在一起使用。notify_on_release標識當這個cgroup最後一個進程退出的時候是否執行了 release_agent;release_agent則是一個路徑,通常用作進程退出後自動清理掉不再使用的cgroup。
- tasks標識該cgroup下面的進程ID,如果把一個進程ID寫到tasks中便會將相應的進程加入到這個cgroup中。
-
然後建立剛才建立的hierarchy上cgroup根節點中擴充套件出的兩個子cgroup。
可以看到建立子資料夾的同時,Kernel會標記這個cgroup的子cgroup,他們會繼承父cgroup的屬性。
- 在cgroup中新增和移動進程 一個進程在Cgroups的hierarchy中,只能在一個cgroup節點上存在,系統所有進程都會預設在根節點上存在,可以將進程移動到其他節點上,只需要將 進程ID移動到cgroup節點的tasks檔案即可。
可以看到當前進程已經被新增到cgroup-1中了。**第一行**
-
通過subsystem限制cgroup進程的資源 上面的hierarchy沒有關係任何的subsystem,所以沒有限制cgroup占用的系統資源。本質系統預設為subsystem建立了hierarchy,比如memory的hierarchy。
可以看到/sys/fs/cgroup/memory目錄掛載在memory subsystem的hierarchy上。下面進入到memory目錄下建立cgroup。限制記憶體。
這樣就建立成功,並新增了記憶體使用的限制。
可以看到9752 使用記憶體最大為100M
Docker是如何使用Cgroups的
Docker是通過Cgroups實現容器資源的限制和監控。
可以看到最大限制是134217728 使用的是1970176.這些都是我們在/sys/fs/cgroup/memory中找到的。由此可見docker本質上也是這樣做的。
Go語言實現Cgroups限制容器資源
在Namespace的基礎之上增加Cgroup的限制,使其具有限制記憶體的功能。
package main
import (
"os"
"os/exec"
"log"
"syscall"
"path"
"fmt"
"io/ioutil"
"strconv"
)
const cgroupMemoryHierarchyMount = "/sys/fs/cgroup/memory" //記憶體掛載點的路徑
func main() {//
if os.Args[0] == "/proc/self/exe"{
fmt.Printf("current pid %d", syscall.Getpid())
fmt.Println()
cmd := exec.Command("sh", "-c" ,"strees --vm-bytes 200m --vm-keep -m 1") // 之前我們通過命令列,這裡命令還是一樣的。
cmd.SysProcAttr = &syscall.SysProcAttr{
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run();err!=nil{
fmt.Println(err)
os.Exit(1)
}
}
cmd :=exec.Command("/proc/self/exe")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags:syscall.CLONE_NEWUTS|syscall.CLONE_NEWPID|syscall.CLONE_NEWNS,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err !=nil{
fmt.Println("error", err)
os.Exit(1)
}else {
//獲取fork的進程pid
fmt.Printf("%v" ,cmd.Process.Pid)
// 在系統中預設建立掛在了memory subsystem的hierarchy上建立Cgroup
os.Mkdir(path.Join(cgroupMemoryHierarchyMount,"testmemorylimit"),0755)
// 將容器加入到這個Cgroup中
ioutil.WriteFile(path.Join(cgroupMemoryHierarchyMount,"testmemorylimit","tasks"),[]byte(strconv.Itoa(cmd.Process.Pid)),0644)
//限制cgroup的使用
ioutil.WriteFile(path.Join(cgroupMemoryHierarchyMount,"testmemorylimit","memory.limit_in_bytes"),[]byte("100m"),0644)
}
cmd.Process.Wait()
}
通過top就可以檢視。
相關文章