• cgroup实践-资源控制


    1、Cgroup安装
    安装Cgroups需要libcap-devel和libcgroup两个相关的包

    yum install gcc libcap-devel 
    

    2、Cgroup挂载配置

    Cgroup对应服务名称为cgconfig,cgconfig默认采用“多挂载点”挂载。经过实际测试,发现在CentOS环境中应采用“单挂载点”进行挂载,因此应当卸载原有cgroup文件系统,并禁用cgconfig。
    cgclear或者sudo service cgconfig stop # 停止cgconfig,卸载cgroup目录
    sudo chkconfig cgconfig off          # 禁用cgconfig服务,避免其开机启动
    然后采用“单挂载点”方式重新挂载cgroup。
    可以直接手动挂载,这样仅当次挂载成功。
    mount -t cgroup none /cgroup
    然后编辑/etc/fstab/,输入下列内容。这样每次开机后都会自动挂载。
    none   /cgroup  cgroup  defaults   0 0
    

    3、常用的Cgroup相关命令和配置文件

    service cgconfig status|start|stop|restart    #查看已存在子系统
    lssubsys –am    #查看已存在子系统
    cgclear   # 清除所有挂载点内部文件,相当于service  cgconfig stop
    cgconfigparser -l /etc/cgconfig.conf    #重新挂载
    
    Cgroup默认挂载点(CentOS):/cgroup
    cgconfig配置文件:/etc/cgconfig.conf
    

    4、libcgroup Man Page简介

    man 1 cgclassify -- cgclassify命令是用来将运行的任务移动到一个或者多个cgroup。
    man 1 cgclear -- cgclear 命令是用来删除层级中的所有cgroup。
    man 5 cgconfig.conf -- 在cgconfig.conf文件中定义cgroup。
    man 8 cgconfigparser -- cgconfigparser命令解析cgconfig.conf文件和并挂载层级。
    
    man 1 cgcreate -- cgcreate在层级中创建新cgroup。
    man 1 cgdelete -- cgdelete命令删除指定的cgroup。
    man 1 cgexec -- cgexec命令在指定的cgroup中运行任务。
    man 1 cgget -- cgget命令显示cgroup参数。
    man 5 cgred.conf -- cgred.conf是cgred服务的配置文件。
    man 5 cgrules.conf -- cgrules.conf 包含用来决定何时任务术语某些  cgroup的规则。
    
    man 8 cgrulesengd -- cgrulesengd 在  cgroup 中发布任务。
    man 1 cgset -- cgset 命令为  cgroup 设定参数。
    man 1 lscgroup -- lscgroup 命令列出层级中的  cgroup。
    man 1 lssubsys -- lssubsys 命令列出包含指定子系统的层级。
    

    测试一:限制cpu的资源

    测试后验证了可以做到:

    • 限制进程的cpu占用百分比
    • 限制多个进程组的之间的cpu使用权重
    • 指定进程的使用的cpu和内存组(绑定cpu)

    跑一个耗cpu的脚本

    x=0
    while [ True ];do
        x=$x+1
    done;
    

    top可以看到这个脚本基本占了100%的cpu资源

    top - 15:30:01 up  1:03,  5 users,  load average: 0.30, 0.50, 0.39
    Tasks: 210 total,   2 running, 208 sleeping,   0 stopped,   0 zombie
    Cpu(s):  6.3%us,  0.1%sy,  0.0%ni, 93.5%id,  0.2%wa,  0.0%hi,  0.0%si,  0.0%st
    Mem:  49461228k total, 13412644k used, 36048584k free,    75384k buffers
    Swap:  2097148k total,        0k used,  2097148k free, 12498636k cached
    
        PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    11605 root      20   0  104m 1528 1016 R 99.7  0.0   2:30.48 sh
    105 root      20   0     0    0    0 S  0.3  0.0   0:00.11 kworker/8:1  
    

    创建一个控制组控制这个进程的cpu资源

    mkdir -p /cgroup/cpu/foo	     #新建一个控制组foo
    echo 50000 > /cgroup/cpu/foo/cpu.cfs_quota_us  #将cpu.cfs_quota_us设为50000,相对于cpu.cfs_period_us的100000是50%
    echo 11605 > /cgroup/cpu/foo/tasks
    

    然后top的实时统计数据如下,cpu占用率将近50%,看来cgroups关于cpu的控制起了效果

    top - 15:32:48 up  1:06,  5 users,  load average: 0.80, 0.68, 0.48
    Tasks: 210 total,   2 running, 208 sleeping,   0 stopped,   0 zombie
    Cpu(s):  3.2%us,  0.0%sy,  0.0%ni, 96.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Mem:  49461228k total, 13412276k used, 36048952k free,    75400k buffers
    Swap:  2097148k total,        0k used,  2097148k free, 12498652k cached
    
    PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    11605 root      20   0  104m 1724 1016 R 50.2  0.0   5:09.97 sh
    11639 root      20   0 15200 1200  820 R  0.3  0.0   0:00.03 top    
    

    可以看到,进程的 cpu 占用已经被成功地限制到了 50% 。这里,测试的虚拟机只有一个核心。在多核情况下,看到的值会不一样。另外,cfs_quota_us 也是可以大于 cfs_period_us 的,这主要是对于多核情况。有 n 个核时,一个控制组中的进程自然最多就能用到 n 倍的 cpu 时间。

    这两个值在 cgroups 层次中是有限制的,下层的资源不能超过上层。具体的说,就是下层的 cpu.cfs_period_us 值不能小于上层的值,cpu.cfs_quota_us 值不能大于上层的值。

    另外的一组 cpu.rt_period_us、cpu.rt_runtime_us 对应的是实时进程的限制,平时可能不会有机会用到。

    在 cpu 子系统中,cpu.stat 就是用前面那种方法做的资源限制的统计了。nr_periods、nr_throttled 就是总共经过的周期,和其中受限制的周期。throttled_time 就是总共被控制组掐掉的 cpu 使用时间。

    还有个 cpu.shares, 它也是用来限制 cpu 使用的。但是与 cpu.cfs_quota_us、cpu.cfs_period_us 有挺大区别。cpu.shares 不是限制进程能使用的绝对的 cpu 时间,而是控制各个组之间的配额。比如

    /cpu/cpu.shares : 1024
    /cpu/foo/cpu.shares : 2048
    

    那么当两个组中的进程都满负荷运行时,/foo 中的进程所能占用的 cpu 就是 / 中的进程的两倍。如果再建一个 /foo/bar 的 cpu.shares 也是 1024,且也有满负荷运行的进程,那 /、/foo、/foo/bar 的 cpu 占用比就是 1:2:1 。前面说的是各自都跑满的情况。如果其他控制组中的进程闲着,那某一个组的进程完全可以用满全部 cpu。可见通常情况下,这种方式在保证公平的情况下能更充分利用资源。

    此外,还可以限定进程可以使用哪些 cpu 核心。cpuset 子系统就是处理进程可以使用的 cpu 核心和内存节点,以及其他一些相关配置。这部分的很多配置都和 NUMA 有关。其中 cpuset.cpus、cpuset.mems 就是用来限制进程可以使用的 cpu 核心和内存节点的。这两个参数中 cpu 核心、内存节点都用 id 表示,之间用 “,” 分隔。比如 0,1,2 。也可以用 “-” 表示范围,如 0-3 。两者可以结合起来用。如“0-2,6,7”。在添加进程前,cpuset.cpus、cpuset.mems 必须同时设置,而且必须是兼容的,否则会出错。例如

    # echo 0 >/sys/fs/cgroup/cpuset/foo/cpuset.cpus
    # echo 0 >/sys/fs/cgroup/cpuset/foo/cpuset.mems
    这样, /foo 中的进程只能使用 cpu0 和内存节点0。用
    
    # cat /proc/<pid>/status|grep '_allowed_list'
    

    cgroups 除了用来限制资源使用外,还有资源统计的功能。做云计算的计费就可以用到它。有一个 cpuacct 子系统专门用来做 cpu 资源统计。cpuacct.stat 统计了该控制组中进程用户态和内核态的 cpu 使用量,单位是 USER_HZ,也就是 jiffies、cpu 滴答数。每秒的滴答数可以用 getconf CLK_TCK 来获取,通常是 100。将看到的值除以这个值就可以换算成秒。

    测试二:限制进程的内存资源

    测试后验证了:

    • 限制了资源的占用,达到内存以后,进程直接杀掉

    测试方法:

    跑一个耗内存的脚本,内存不断增长

    x="a"
    while [ True ];do
        x=$x$x
    done;
    

    top看内存占用稳步上升

        PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
        30215 root      20   0  871m 501m 1036 R 99.8 26.7   0:38.69 sh  
    30215 root      20   0 1639m 721m 1036 R 98.7 38.4   1:03.99 sh 
    30215 root      20   0 1639m 929m 1036 R 98.6 49.5   1:13.73 sh
    

    下面用cgroups控制这个进程的内存资源

    mkdir -p /cgroup/memory/foo
    echo 1048576 >  /cgroup/memory/foo/memory.limit_in_bytes   #分配1MB的内存给这个控制组
    echo 30215 > /cgroup/memory/foo/tasks 
    

    发现之前的脚本被kill掉

    [root@localhost ~]# sh /home/test.sh 
    已杀死
    

    因为这是强硬的限制内存,当进程试图占用的内存超过了cgroups的限制,会触发out of memory,导致进程被kill掉。

    实际情况中对进程的内存使用会有一个预估,然后会给这个进程的限制超配50%比如,除非发生内存泄露等异常情况,才会因为cgroups的限制被kill掉。

    也可以通过配置关掉cgroups oom kill进程,通过memory.oom_control来实现(oom_kill_disable 1),但是尽管进程不会被直接杀死,但进程也进入了休眠状态,无法继续执行,仍然无法服务。

    关于内存的控制,还有以下配置文件,关于虚拟内存的控制,以及权值比重式的内存控制等

    [root@localhost /]# ls /cgroup/memory/foo/
    cgroup.event_control  memory.force_empty         memory.memsw.failcnt             
    memory.memsw.usage_in_bytes      memory.soft_limit_in_bytes  memory.usage_in_bytes  tasks
    cgroup.procs          memory.limit_in_bytes      memory.memsw.limit_in_bytes      
    memory.move_charge_at_immigrate  memory.stat                 memory.use_hierarchy
    memory.failcnt        memory.max_usage_in_bytes  memory.memsw.max_usage_in_bytes  
    memory.oom_control               memory.swappiness           notify_on_release
    

    测试三:限制进程的IO资源

    测试验证了:

    • 能够控制io设备的读写速度

    跑一个耗io的脚本

        dd if=/dev/sda of=/dev/null 
    
    通过iotop看io占用情况,磁盘速度到了284M/s
    
    
    30252 be/4 root      284.71 M/s    0.00 B/s  0.00 %  0.00 % dd if=/dev/sda of=/dev/null
    

    下面用cgroups控制这个进程的io资源

    mkdir -p /cgroup/blkio/foo
    
    echo '8:0   1048576' >  /cgroup/blkio/foo/blkio.throttle.read_bps_device
    #8:0对应主设备号和副设备号,可以通过ls -l /dev/sda查看
    echo 30252 > /cgroup/blkio/foo/tasks
    

    再通过iotop看,确实将读速度降到了1M/s

    30252 be/4 root      993.36 K/s    0.00 B/s  0.00 %  0.00 % dd if=/dev/sda of=/dev/null  
    

    对于io还有很多其他可以控制层面和方式,如下

    [root@localhost ~]# ls /cgroup/blkio/foo/
    blkio.io_merged         blkio.io_serviced      blkio.reset_stats                
    blkio.throttle.io_serviced       blkio.throttle.write_bps_device   blkio.weight          cgroup.procs
    blkio.io_queued         blkio.io_service_time  blkio.sectors                    
    blkio.throttle.read_bps_device   blkio.throttle.write_iops_device  blkio.weight_device   notify_on_release
    blkio.io_service_bytes  blkio.io_wait_time     blkio.throttle.io_service_bytes  
    blkio.throttle.read_iops_device  blkio.time                        cgroup.event_control  tasks
    

    blkio 子系统里东西很多。不过大部分都是只读的状态报告,可写的参数就只有下面这几个:

    blkio.throttle.read_bps_device
    blkio.throttle.read_iops_device
    blkio.throttle.write_bps_device
    blkio.throttle.write_iops_device
    blkio.weight
    blkio.weight_device
    
    这些都是用来控制进程的磁盘 io 的。很明显地分成两类,其中带“throttle”的,顾名思义就是节流阀,将流量限制在某个值下。而“weight”就是分配 io 的权重。
    再看看 blkio.weight 。blkio 的 throttle 和 weight 方式和 cpu 子系统的 quota 和 shares 有点像,都是一种是绝对限制,另一种是相对限制,并且在不繁忙的时候可以充分利用资源,权重值的范围在 10 – 1000 之间。
    

    测试权重方式要麻烦一点。因为不是绝对限制,所以会受到文件系统缓存的影响。如在虚拟机中测试,要关闭虚机如我用的 VirtualBox 在宿主机上的缓存。如要测试读 io 的效果,先生成两个几个 G 的大文件 /tmp/file_1,/tmp/file_2 ,可以用 dd 搞。然后设置两个权重

    # echo 500 >/sys/fs/cgroup/blkio/foo/blkio.weight
    # echo 100 >/sys/fs/cgroup/blkio/bar/blkio.weight
    

    测试前清空文件系统缓存,以免干扰测试结果

    sync
    echo 3 >/proc/sys/vm/drop_caches
    

    在这两个控制组中用 dd 产生 io 测试效果。

    # cgexec -g "blkio:foo" dd if=/tmp/file_1 of=/dev/null &
    [1] 1838
    # cgexec -g "blkio:bar" dd if=/tmp/file_2 of=/dev/null &
    [2] 1839
    

    还是用 iotop 看看效果

    TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
    1839 be/4 root       48.14 M/s    0.00 B/s  0.00 % 99.21 % dd if=/tmp/file_2 of=/dev/null
    1838 be/4 root      223.59 M/s    0.00 B/s  0.00 % 16.44 % dd if=/tmp/file_1 of=/dev/null
    

    两个进程每秒读的字节数虽然会不断变动,但是大致趋势还是维持在 1:5 左右,和设定的 weight 比例一致。blkio.weight_device 是分设备的。写入时,前面再加上设备号即可。

    实践记录

    1、假如已经配置好一个资源组,现在想让一个服务按这个组的资源分配来运行服务,而不需要去找到进程号再写入到tasks中

    [root@lab8105 ~]# cgexec -g cpu:daemons/ftp top
    

    这个运行以后有会自动将top进程号写入到tasks当中去

    2、查询一个组里面设置的资源的限制

    设置的值会显示出来,没有设置的就会提示没有找到
    
    [root@lab8105 ~]# cgget  daemons/ftp
    daemons/ftp:
    cgget: cannot find controller 'cpuset' in group 'daemons/ftp'
    cpu.rt_period_us: 1000000
    cpu.rt_runtime_us: 0
    cpu.stat: nr_periods 0
        nr_throttled 0
        throttled_time 0
    cpu.cfs_period_us: 5000
    cpu.cfs_quota_us: -1
    cpu.shares: 1000
    cgget: cannot find controller 'cpuacct' in group 'daemons/ftp'
    cgget: cannot find controller 'memory' in group 'daemons/ftp'
    cgget: cannot find controller 'devices' in group 'daemons/ftp'
    cgget: cannot find controller 'freezer' in group 'daemons/ftp'
    cgget: cannot find controller 'net_cls' in group 'daemons/ftp'
    cgget: cannot find controller 'blkio' in group 'daemons/ftp'
    

    3、需要用两个限制条件对进程进行限制

    [root@lab8105 ~]# cgexec -g cpu:daemons/ftp -g memory:daemons/ftp top
    

    4、默认情况下是一个大根,然后分了几个资源系统,还支持做一个子系统组,即单独组建一个资源组,然后对这个资源组里面进行配置,具体方法如下:

    mount {
            cpu     = /cgroup/cpu_and_mem;
            memory  = /cgroup/cpu_and_mem;
    }
        
    group daemons/ftp {
                            cpu {
                                    cpu.shares = "1000";
                                    cpu.cfs_period_us = "5000";
                            }
                            memory {
                                    memory.swappiness = "20";
                            }
                    }
    

    5、需要创建控制组群,如上的daemons/ftp,想通过命令行的方式创建

    [root@lab8105 ~]# cgcreate -g cpu:/zp -g memory:/zp	
    如上命令使用后会在/cgroup/cpu/中多了zp目录,并且里面是继承的上级的cpu里面的参数,这样就创建了一个zp的组群
    
    删除组群的方式如下(删除cgroup时,其所有任务都移动到了父组群当中):
    [root@lab8105 ~]# cgdelete cpu:/zp memory:/zp
    

    6、设置里面的配置参数

    需要设置
        /cgroup/cpu/daemons/ftp/cpu.shares
    执行
    [root@lab8105 ~]# cgset -r cpu.shares=500 daemons/ftp
    daemons/ftp路径是相对于根的,如果想设置根的这个参数那么就执行
    [root@lab8105 ~]# gset -r cpuacct.usage=0 /
    这里需要注意,只有某些参数是可以修改的,某些参数是不能修改的
    也可以直接echo的方式进行参数的设置
    

    7,移动某个进程到控制组群当中(动态的进行资源的调配)

    移动指定进程到指定的控制组当中,创建两个资源组,使用上面的cpu的脚本,然后运行后,使用top进行监控
    group half {
                            cpu {
                                        cpu.cfs_period_us="100000";
                                        cpu.cfs_quota_us="50000";
                            }
                            memory {
                                    memory.swappiness = "50";
                            }
                    }
    
    group eighty {
                            cpu {
                                    cpu.cfs_period_us = "100000";
                                    cpu.cfs_quota_us="50000";
                            }
                            memory {
                                    memory.swappiness = "80";
                            }
                    }
    [root@lab8105 ~]# cgclassify -g cpu:half 14245
    top监控看到cpu的占用为50%
    [root@lab8105 ~]# cgclassify -g cpu:eighty 14245
    top监控看到cpu的占用为80%
    注意支持多进程,多资源组同时移动
    [root@lab8105 ~]# cgclassify -g cpu,memory:eighty 14245 14565
    备用方法就是直接echo 
    

    8、通过规则对指定的进程进行控制

    我们还可以通过设置规则来让 cgred(cgroup 规则引擎后台程序)自动将进程分配给特定组。cgred 后台程序根据 /etc/cgrules.conf 文件中的设置将任务移到 cgroup 中

    [root@lab8105 ~]# vim /etc/cgrules.conf 
    [root@lab8105 ~]# man cgrules.conf       
    
    # /etc/cgrules.conf
    #The format of this file is described in cgrules.conf(5)
    #manual page.
    #
    # Example:
    #<user>         <controllers>   <destination>
    #@student       cpu,memory      usergroup/student/
    #peter          cpu             test1/
    #%              memory          test2/
    # End of file
    #
    root:cpu.sh           cpu             half/
    root                  cpu             half/
    
    启动监控进程服务
    [root@lab8105 ~]# /etc/init.d/cgred 
    

    效果如下,运行相同的命令,所占用的cpu的资源按指定的比例进行占用

    [root@lab8105 ~]# top
    top - 16:00:40 up 1 day,  1:34,  5 users,  load average: 1.57, 1.13, 0.90
    Tasks: 216 total,   3 running, 213 sleeping,   0 stopped,   0 zombie
    Cpu(s):  6.9%us,  0.0%sy,  0.0%ni, 93.1%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Mem:  49461228k total, 49346312k used,   114916k free, 47374260k buffers
    Swap:  2097148k total,        0k used,  2097148k free,    33116k cached
    
        PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                               
    14648 root      20   0  104m 1716 1012 R 99.7  0.0   4:58.38 cpu1.sh                                                
    14565 root      20   0  104m 1704 1012 R 10.0  0.0   4:30.53 cpu.sh 
    

    如上所述,指定用户,可以指定进程进行控制,也可以指定用户的所有进程进行控制,后台的做的操作就是把进行的号移动到了指定的资源组的task当中去了

  • 相关阅读:
    Tomcat与Spring中的事件机制详解
    Kafka消息系统基础知识索引
    配置SpringBoot-从日志系统配置说起
    支付宝手机网页支付和微信公众号支付接入
    centos下搭建YII环境
    为什么需要 Stream
    基于Django的Rest Framework框架的序列化组件
    基于Django的Rest Framework框架的RESTful规范研究
    web中状态码301和302的区别
    Django初见
  • 原文地址:https://www.cnblogs.com/zphj1987/p/13575294.html
Copyright © 2020-2023  润新知