首頁 > 軟體

Linux Cgroups 詳解

2020-06-16 16:42:20

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。

  1. 首先建立並掛在一個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中。
  2. 然後建立剛才建立的hierarchy上cgroup根節點中擴充套件出的兩個子cgroup。

可以看到建立子資料夾的同時,Kernel會標記這個cgroup的子cgroup,他們會繼承父cgroup的屬性。
  1. 在cgroup中新增和移動進程 一個進程在Cgroups的hierarchy中,只能在一個cgroup節點上存在,系統所有進程都會預設在根節點上存在,可以將進程移動到其他節點上,只需要將 進程ID移動到cgroup節點的tasks檔案即可。

可以看到當前進程已經被新增到cgroup-1中了。**第一行**

  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就可以檢視。


IT145.com E-mail:sddin#qq.com