1. 程式人生 > >Linux Cgroups

Linux Cgroups

[toc]

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。

  1. 首先建立並掛在一個hierarchy(cgroup樹),如下.

    [email protected]:~$ mkdir cgroup-test
    [email protected]:~$ sudo mount -t cgroup -o none,name=cgroup1 cgroup1 ./cgroup-test/
    [email protected]:~$ 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就可以檢視。