什么是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 层级结构中。
cgroups 与进程
在创建了cgroups 层级结构中的节点(cgroups结构体)之后,可以把进程加入到某一个节点的控制任务列表中,一个节点的控制列表中所有的进程都会收到当前节点的资源限制。同时某一个进程也可以被加入到不同的cgroups 层级结构的节点中,因为不同的cgroups 层级结构可以负责不同的系统资源。所以进程和cgroup结构体是一个多对多的关系
上面这个图从整体结构上描述了进程与 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的关系)
go
// 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