掘金 后端 ( ) • 2024-04-15 15:53

什么是cgroup

cgroup 全称是Controller Group 中文直译是控制组,用于控制单个或者多个进程对系统各种资源的使用,比如CPU、内存、网络、磁盘IO。Docker 就是使用cgroup 来对容器进行资源限制

概念

sub system (子系统)

cgroups 为每种可以控制的资源定义了一个子系统,典型的子系统如下:

  • cpu 子系统
  • cpuacct 子系统,可以统计cgroups 中的进程的cpu 使用报告
  • cpuset 子系统,可以为cgroup中的进程分配单独的cpu 节点或者内存节点
  • memory子系统,可以限制进程的memory使用量
  • blkio子系统,可以限制进程块的设备io
  • devices 子系统,可以控制进程能够访问某些设备
  • net_cls可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
  • freezer 可以挂起或者恢复 cgroups 中的进程
  • ns 不同cgroups 下面的进程使用不同的namespace

层级架构(Hierarchy)

内核使用cgroup 结构体来表示一个control group 对某一个或某几个cgroups子系统的资源显示。cgroup 结构体可以组织成一棵树的形式,每一棵cgroup 结构体组成的树称之为一个 cgroups 层级结构。cgroups层级结构可以 attach 一个或者几个 cgroups 子系统,当前层级结构可以对其 attach 的 cgroups 子系统进行资源的限制。每一个 cgroups 子系统只能被 attach 到一个 cpu 层级结构中。

image.png

cgroups 与进程

在创建了cgroups 层级结构中的节点(cgroups结构体)之后,可以把进程加入到某一个节点的控制任务列表中,一个节点的控制列表中所有的进程都会收到当前节点的资源限制。同时某一个进程也可以被加入到不同的cgroups 层级结构的节点中,因为不同的cgroups 层级结构可以负责不同的系统资源。所以进程和cgroup结构体是一个多对多的关系

image.png

上面这个图从整体结构上描述了进程与 cgroups 之间的关系。最下面的P代表一个进程。每一个进程的描述符中有一个指针指向了一个辅助数据结构css_set(cgroups subsystem set)。 指向某一个css_set的进程会被加入到当前css_set的进程链表中。一个进程只能隶属于一个css_set,一个css_set可以包含多个进程,隶属于同一css_set的进程受到同一个css_set所关联的资源限制。

上图中的”M×N Linkage”说明的是css_set通过辅助数据结构可以与 cgroups 节点进行多对多的关联。但是 cgroups 的实现不允许css_set同时关联同一个cgroups层级结构下多个节点。 这是因为 cgroups 对同一种资源不允许有多个限制配置。

一个css_set关联多个 cgroups 层级结构的节点时,表明需要对当前css_set下的进程进行多种资源的控制。而一个 cgroups 节点关联多个css_set时,表明多个css_set下的进程列表受到同一份资源的相同限制。

cgroups 文件系统

linux 内核有个模块vfs(Virtual File System). VFS 能够把具体文件系统的细节隐藏起来,给用户进程提供一个统一的文件系统API接口,cgroups 也是通过vfs 把功能暴露给用户态的,cgroups 与vfs 之间的衔接部分被称为cgroups 文件系统

vfs

vfs 是一个内核抽象层,使用了一种通用的文件系统设计,具体的文件系统只要实现了vfs 的设计接口,就能注册到vfs 中,从而使内核可以直接读写这种文件系统。这很像面向对象设计中的抽象类与子类之间的关系,抽象类负责对外接口的设计,子类负责具体的实现。其实,VFS本身就是用 c 语言实现的一套面向对象的接口。

cgroup与docker

我们从docker 的核心容器管理工具libcontainer(现在好像叫runc)的代码中可以看出 ps:(关于libcontainer|runc docker containerd 的关系可以看我之前的文章docker和containerd的关系)

// github.com/docker-archive/libcontainer/configs/cgroup.go
type Cgroup struct {
	Name string `json:"name"`

	// name of parent cgroup or slice
	Parent string `json:"parent"`

	// If this is true allow access to any kind of device within the container.  If false, allow access only to devices explicitly listed in the allowed_devices list.
	AllowAllDevices bool `json:"allow_all_devices"`

	AllowedDevices []*Device `json:"allowed_devices"`

	DeniedDevices []*Device `json:"denied_devices"`

	// Memory limit (in bytes)
	Memory int64 `json:"memory"`

	// Memory reservation or soft_limit (in bytes)
	MemoryReservation int64 `json:"memory_reservation"`

	// Total memory usage (memory + swap); set `-1' to disable swap
	MemorySwap int64 `json:"memory_swap"`

	// Kernel memory limit (in bytes)
	KernelMemory int64 `json:"kernel_memory"`

	// CPU shares (relative weight vs. other containers)
	CpuShares int64 `json:"cpu_shares"`

	// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
	CpuQuota int64 `json:"cpu_quota"`

	// CPU period to be used for hardcapping (in usecs). 0 to use system default.
	CpuPeriod int64 `json:"cpu_period"`

	// How many time CPU will use in realtime scheduling (in usecs).
	CpuRtRuntime int64 `json:"cpu_quota"`

	// CPU period to be used for realtime scheduling (in usecs).
	CpuRtPeriod int64 `json:"cpu_period"`

	// CPU to use
	CpusetCpus string `json:"cpuset_cpus"`

	// MEM to use
	CpusetMems string `json:"cpuset_mems"`

	// IO read rate limit per cgroup per device, bytes per second.
	BlkioThrottleReadBpsDevice string `json:"blkio_throttle_read_bps_device"`

	// IO write rate limit per cgroup per divice, bytes per second.
	BlkioThrottleWriteBpsDevice string `json:"blkio_throttle_write_bps_device"`

	// IO read rate limit per cgroup per device, IO per second.
	BlkioThrottleReadIOpsDevice string `json:"blkio_throttle_read_iops_device"`

	// IO write rate limit per cgroup per device, IO per second.
	BlkioThrottleWriteIOpsDevice string `json:"blkio_throttle_write_iops_device"`

	// Specifies per cgroup weight, range is from 10 to 1000.
	BlkioWeight int64 `json:"blkio_weight"`

	// Weight per cgroup per device, can override BlkioWeight.
	BlkioWeightDevice string `json:"blkio_weight_device"`

	// set the freeze value for the process
	Freezer FreezerState `json:"freezer"`

	// Hugetlb limit (in bytes)
	HugetlbLimit []*HugepageLimit `json:"hugetlb_limit"`

	// Parent slice to use for systemd TODO: remove in favor or parent
	Slice string `json:"slice"`

	// Whether to disable OOM Killer
	OomKillDisable bool `json:"oom_kill_disable"`

	// Tuning swappiness behaviour per cgroup
	MemorySwappiness int64 `json:"memory_swappiness"`

	// Set priority of network traffic for container
	NetPrioIfpriomap []*IfPrioMap `json:"net_prio_ifpriomap"`

	// Set class identifier for container's network packets
	NetClsClassid string `json:"net_cls_classid"`
}

从代码中可以看出来docker 通过cgroup 主要支持了这些功能

  • 设备(device)docker 支持让用户选择容器可以使用的设备
  • 内存(memory)支持为容器的运行设定用量限额
  • cpu 支持容器进程间拥有相对独立的运行时间片
  • Freezed 支持容器挂起,节省cpu 资源

打个广告

[云原生容器技术] docker和containerd的关系

容器技术的发展

容器技术相关系列文章一起看效果更好哦qaq