Docker cgroup资源限制Linux内核机制深入剖析-Docker商业环境实战

专注于大数据及容器云核心技术解密,可提供全栈的大数据+云原生平台咨询方案,请持续关注本套博客。如有任何学术交流,可随时联系。更多内容请关注《数据云技术社区》公众号。

1 cgroup 核心概念

  • subsystem:基于cgroup的资源管理器,可以看作一种资源(比如CPU,Memory, I/O等),实现对一个cgroup的task的调度和控制
  • cgroup:以某种方式,将某些任务和subsystem进行关联。一个subsystem就是一个内核模块,他被关联到一颗cgroup树之后,就会在树的每个节点(进程组)上做具体的操作。subsystem经常被称作”resource controller”,因为它主要被用来调度或者限制每个进程组的资源。
  • hierarchy:对crgoups和subsystems以某种形式进行的组织,cgroup组织形式是树结构,subsystem会关联连接到hierarchy上。
  • 上面的就是一个1个hierarchy,有两个subsystem(cpu和memory) attach上,其中的cgroups以tree的结构组织。
  • 上面的就是CPU 和 Memory 两个子系统有自己独立的层级系统, 而又通过 Task 取得关联关系, 该task既有CPU的限制又有Memory的限制。
  • 有时我们将进程分组只是为了做一些监控,观察一下他们的状态,比如perf_event subsystem。到目前为止,Linux支持12种subsystem,比如限制CPU的使用时间,限制使用的内存,统计CPU的使用情况,冻结和恢复一组进程等。

1.1 subsystem相关的资源控制

- blkio: 这个subsystem可以为块设备设定输入/输出限制,比如物理驱动设备(包括磁盘、固态硬盘、USB等)。
- cpu: 这个subsystem使用调度程序控制task对CPU的使用。
- cpuacct: 这个subsystem自动生成cgroup中task对CPU资源使用情况的报告。
- cpuset: 这个subsystem可以为cgroup中的task分配独立的CPU(此处针对多处理器系统)和内存。
- devices 这个subsystem可以开启或关闭cgroup中task对设备的访问。
- freezer 这个subsystem可以挂起或恢复cgroup中的task。
- memory 这个subsystem可以设定cgroup中task对内存使用量的限定,并且自动生成这些task对内存资源使用情况的报告。
- perf_event 这个subsystem使用后使得cgroup中的task可以进行统一的性能测试。{![perf: Linux CPU性能探测器,详见https://perf.wiki.kernel.org/index.php/Main_Page]}
- *net_cls 这个subsystem Docker没有直接使用,它通过使用等级识别符(classid)标记网络数据包,从而允许 Linux 流量控制程序(TC:Traffic Controller)识别从具体cgroup中生成的数据包。
复制代码

2 cgroup基本操作

2.1 挂载cgroup

#尝试将cpu subsystem重新关联一颗cgroup树并且将这棵树mount到./cpu目录
dev@ubuntu:~/cgroup$ mkdir cpu
dev@ubuntu:~/cgroup$ sudo mount -t cgroup -o cpu new-cpu ./cpu

#由于name=test的cgroup树在系统中不存在,所以这里会创建一颗新的name=test的cgroup树,并且将这棵树mount到./test目录
dev@ubuntu:~$ mkdir -p cgroup/test && cd cgroup
dev@ubuntu:~/cgroup$ sudo mount -t cgroup -o none,name=test test ./test

#挂载一棵cgroup树,但不关联任何subsystem,下面就是systemd所用到的方式
mkdir /sys/fs/cgroup/systemd
mount -t cgroup -o none,name=systemd xxx /sys/fs/cgroup/systemd

#创建和删除子cgroup
root@nicktming:~/cgroup# cd demo/
root@nicktming:~/cgroup/demo# mkdir cgroup1
root@nicktming:~/cgroup/demo# ls cgroup1/
cgroup.clone_children  cgroup.event_control  cgroup.procs  notify_on_release  tasks
root@nicktming:~/cgroup/demo# mkdir cgroup2
root@nicktming:~/cgroup/demo# tree
.
|-- cgroup1
|   |-- cgroup.clone_children
|   |-- cgroup.event_control
|   |-- cgroup.procs
|   |-- notify_on_release
|   `-- tasks
|-- cgroup2
|   |-- cgroup.clone_children
|   |-- cgroup.event_control
|   |-- cgroup.procs
|   |-- notify_on_release
|   `-- tasks
|-- cgroup.clone_children
|-- cgroup.event_control
|-- cgroup.procs
|-- cgroup.sane_behavior
|-- notify_on_release
|-- release_agent
`-- tasks
root@nicktming:~/cgroup/demo# sh -c "echo $$ > cgroup1/tasks"
root@nicktming:~/cgroup/demo# cat cgroup1/tasks 
11749
14172
root@nicktming:~/cgroup/demo# cat cgroup2/tasks
// 删除子cgroup 直接删除其文件夹即可
root@nicktming:~/cgroup/demo# rmdir cgroup2
// 如果子cgroup中tasks中有进程的时候删除不了, 必须把进程移到别的cgroup中才可以删除
root@nicktming:~/cgroup/demo# rmdir cgroup1
rmdir: failed to remove ‘cgroup1’: Device or resource busy
// 将该进程从cgroup1移到demo cgroup中
root@nicktming:~/cgroup/demo# sh -c "echo 11749 > tasks"
root@nicktming:~/cgroup/demo# cat cgroup1/tasks
root@nicktming:~/cgroup/demo# rmdir cgroup1
root@nicktming:~/cgroup/demo# tree
.
|-- cgroup.clone_children
|-- cgroup.event_control
|-- cgroup.procs
|-- cgroup.sane_behavior
|-- notify_on_release
|-- release_agent
`-- tasks
复制代码

2.2 cgroup资源配额限制

参考:https://blog.51cto.com/wzlinux/2046566
参考:https://www.jianshu.com/p/b02bf3b3f265
参考:https://www.jianshu.com/p/262d86b82e4c

root@nicktming:~# cat memory.c 
#include 
#include 
#include 
#include 

#define MB (1024 * 1024)

int main(int argc, char *argv[])
{
    char *p;
    int i = 0;
    while(1) {
        p = (char *)malloc(MB);
        memset(p, 0, MB);
        printf("%dM memory allocated\n", ++i);
        sleep(1);
    }

    return 0;
}
root@nicktming:/sys/fs/cgroup/memory# mkdir test-limit-memory && cd test-limit-memory
root@nicktming:/sys/fs/cgroup/memory/test-limit-memory# sh -c "echo $$ > tasks"
root@nicktming:/sys/fs/cgroup/memory/test-limit-memory# sh -c "echo 5M > memory.limit_in_bytes"
root@nicktming:/sys/fs/cgroup/memory/test-limit-memory# cd /root
// 可以看到在加入5M的限制后 运行程序在申请5M内存时候被killed.
root@nicktming:~# gcc memory.c -o memory
root@nicktming:~# ./memory 
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated
Killed


docker run -itd --name stress polinux/stress:1.0.4 stress --cpu 1 --vm-bytes 200M 
1633f77703ac680c6c9ff77ce5072b6c4d239a546151f945c87f57bb7011e17f

cat /sys/fs/cgroup/cpu/docker/1633f77703ac680c6c9ff77ce5072b6c4d239a546151f945c87f57bb7011e17f/cpu.cfs_period_us 
100000
cat /sys/fs/cgroup/cpu/docker/1633f77703ac680c6c9ff77ce5072b6c4d239a546151f945c87f57bb7011e17f/cpu.cfs_quota_us 
-1
可见没有对stress容器做cpu限制

top命令
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND 
47864 root      20   0     736     36      0 R 99.3  0.0   3:00.90 stress
cpu吃到约100%
复制代码

3 总结

时间因素,暂时总结到此,

专注于大数据及容器云核心技术解密,可提供全栈的大数据+云原生平台咨询方案,请持续关注本套博客。如有任何学术交流,可随时联系。更多内容请关注《数据云技术社区》公众号。