精华内容
下载资源
问答
  • cgroup
    2022-02-06 20:16:28

    一、Cgroup基本信息

    ​ docker中对资源进行控制的方式是使用Cgroup,开控制资源,K8S中也有limit来控制资源

    ​ docker通过Cgroup来控制勇气使用的资源配额,包括CPU、内存、磁盘三大方面,基本覆盖常见的资源配额和使用量控制

    ​ Cgroup是Control group的简写,是linux内核提供的一种显示所使用物力资源的机制,包括CPU、内存、I/O,基本覆盖了常见的资源配额和使用量控制

    ​ Cgroup是一种资源控制手段,也是容器隔离的6个名称空间的一种实现手段

    ​ Cgroup的主要作用还是linux内核提供的一种限制、记录、隔离进程组所使用的的物理资源(如CPU、内存、磁盘I/O等)的机制,被Lxc,docker等很多项目用于实现进程资源控制。Cgroup本身是提供将进程实行分组化,管理的功能和接口的基础结构,I/O或内存的分配控制等具体的资源管理是通过该功能来实现的。这些具体的资源功能成为Cgroup子系统,子系统如下:

    ​ blkio:设置限制每个块设备的输入输出的控制,例如:磁盘、光盘等

    ​ CPU:使用调度程序为Cgroup人物提供CPU的访问

    ​ cpuacct:产生Cgroup任务的CPU资源报告

    ​ CPUset:如果是多核心的CPU,这个子系统会为Cgroup任务分配一个单独的CPU和内存。

    ​ devices:允许或拒绝cgroup 任务对设备的访问

    ​ freezer:暂停和恢复cgroup任务

    ​ memory:设置每个cgroup 的内存限制以及产生内存资源报告

    ​ net_cls:标记每个网络包以供cgroup方便使用

    ​ ns:―命名空间子系统

    ​ perf_event:增加了对每个group的监测跟踪的能力,可以监测属于某个特定的 group的所有线程以及运行在特定CPU上的线程

    二、CPU使用率控制

    1、CPU使用率基本信息

    CPU的使用率衡量单位是以秒为单位,每秒的参数值是100000。

    ​ CPU是以每一秒为一个周期,控制其使用率则是将一个周期以逻辑的方式进行切片,分割成数个时间片,每个时间片可以进行分配任务,来处理不同的线程

    ​ 容器中,可以选择在运行的时候,该容器最大可以使用多少的时间片,例如你想该容器使用5%的CPU性能,则是可以直接进行配置,直接以每秒100000为单位进行换算即可,5%即为5000的参数值。具体怎么配置,下面我再介绍。

    ​ 再设置该数值之后,CPU中的每个周期,都会分配出5%的周期时间0.05秒的时间来运转这个进程,因为每个容器都是一个进程来负责运行。

    ​ CPU在同一个时刻中,只能被一个进程进行占用,所以需要对CPU的使用时间进行限制,来达到“雨露均沾”,维持服务器上的项目能够正常运行。

    2、CPU使用率控制的方式

    CPU在分配时间片的时候,是根据上面的参数值来进行分配的,例如有两个容器A、B,参数值分别是10000和1000,在cpu每个周期分配时间片的时候,容器A参数值10000,容器B是1000。

    ​ 假如这台服务器在只有这两进程工作的时候,CPU在一个周期内分恒数个时间片,容器A获取时间片的几率是容器B的十倍(理论值,实际场合中,考虑各个因素,可能有浮动),比如容器A在不工作的时候,只有容器B工作的时候,CPU的整个周期都是属于容器B的。所以设置的参数值只是在同时运转多个进程的时候,CPU根据参数值来决定每个周期分配多少时间片出去。

    但是一些场合也是不适合进行设置资源限制,例如在一个容器中,只运行这一个程序,而这个程序非常的吃资源,例如ps、游戏等等这些大型程序。本来就读取麻烦,如果再进行限制其cpu等资源。更加的延缓了对该设备的处理动作所以还是在一个容器中,处理大量小型文件的时候并且这些文件并不对传输速率有需求的时候进行限制才是合理的

    3、利用stress压力测试工具来进行测试

    手动安装一个stress的压力测试镜像文件,然后再创建容器查看

    FROM centos:7
    RUN yum -y install wget && \
        wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo && \
        yum -y install stress && \
        yum clean all
    

    写好之后编译成镜像

    docker build -t stress:1 .
    ......
    
    docker images
    REPOSITORY                TAG       IMAGE ID       CREATED          SIZE
    stress                    1         05dd238d656b   52 seconds ago   229MB
    

    使用这个镜像创建两个容器来进行测试:

    A:

    docker run -itd --name cpu500 --cpu-shares 500 stress:1 stress -c 10
    

    B:

    docker run -itd --name cpu1000 --cpu-shares 1000 stress:1 stress -c 10
    

    对比:

    CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O     BLOCK I/O   PIDS
    18b2013f0db2   cpu1000   266.98%   340KiB / 3.702GiB   0.01%     648B / 0B   0B / 0B     11
    87280cb5e0af   cpu500    131.64%   336KiB / 3.702GiB   0.01%     648B / 0B   0B / 0B     11
    

    这个是我截取的一段的占用对比,可以看到cpu1000的占用率是cpu500的二倍,

    这是相对的,如果还有别的,或者cpu1000此时不在运行,则是不会这么分配

    4、CPU周期

    docker提供了–cpu-period、–cpu-quota两个参数来控制容器可以分配到的CPU时钟周期

    –CPU-period是限制CPU周期

    –CPU-quota是限制CPU单位的时间内,容器会被分配到多少时间比。

    ​ 可以指定CPU一个周期内有多少时间用来运转指定的容器。–CPU-period和–CPU-quota的单位都是微秒,CPU-period的最小值是为1000微秒,最大值是1秒。默认值是0.1秒(数值是10000)CPU-quota默认值是-1,代表的意思是不做控制,cpu-period和cpu-quota参数一般联合使用

    –CPU-shares是一个时间片分配权重,相对的随机时间片,容器对CPU资源的使用绝对不会超过配置的值

    假设:一个容器,在每秒需要使用单个CPU的0.2秒的时间,可以将CPU-period的权重值设置为100000(即为1秒),CPU-quota设置为20000(即为0.2秒)。

    docker run -itd --cpu-period 100000 --cpu-quota 20000 stress:1
    容器ID
    #设置完成之后,会有一个容器ID的信息,等下查看需要用到,复制下
    

    查看该容器的两个时间权重值

    cat /sys/fs/cgroup/cpu/docker/容器ID/cpu.cfs_quota_us
    20000
    
    cat /sys/fs/cgroup/cpu/docker/容器ID/cpu.cfs_period_us
    100000
    

    文件路径:/sys/fs/cgroup/ cpu/ docker

    #系统自带的资源控制目录

    sys #系统

    fs #系统文件

    5、限制CPU内核的使用

    限制CPU内核的使用,可以通过配置,使得某些程序独享CPU内核,以提高其处理速度

    对多核CPU的服务器来说,Docker还可以控制容器运行时使用那些CPU的内核,即使用–cpu-cpus参数。这对于具有多CPU的服务器尤为重要,可以对需要高性能计算的容器进行性能最优的配置。

    docker run -itd --cpuset-cpus 0-1 stress:1
    容器ID
    
    cat /sys/fs/cgroup/cpuset/docker/容器ID/cpuset.cpus
    0-1
    

    三、内存限额

    在容器中对内存限额,与操作系统类似

    容器可以使用的内存包括两部分:物理内存和Swap

    docker通过下面两组参数来控制容器内存的使用量

    -m或者–memory:设置内存的使用限额,例如:100M、200M

    –memory-swap:设置内存+swap的使用限额

    执行下面命令,可以允许该容器最多可以使用200M的内存和300M的swap

    docker run -it -m 200M --memory-swap=300M centos:stress --vm 1 --vm-bytes 280M

    其中:–vm 1 :代表的是启动一个内存工作线程

    ​ --vm-bytes 280M:表示的是,每个线程最多分配到280内存

    在默认情况下,容器可以使用主机上所有空闲状态的内存,与cpu的cgroup类似,docker会自动为容器在目录中创建响应的cgroup配置文件

    如果线程分配的内存,超过内存的限额,stress线程会报错,容器退出运行

    例如:执行下面命令可以运行该容器最多使用200M的内存和300M的swap

    docker run -itd -m 200m --memory-swap=300m stress:1 --vm 1 --vm-bytes 280m
    #如果需要让工作线程分配的内存超过300M,分配的内存超额,stress线程报错,容器退出
    docker run -itd -m 200m --memory-swap=300m progrium/stress --vm 1 --vm-bytes 310m
    

    创建完成之后,会自动在目录/sys/fs/cgroup/memory/docker/中创建相对应的cgroup配置文件

    小结:

    cgroup对于内存的限制,主要体现在以下两个部分:

    1. 内存使用上限(硬限制)

    2. 内存+swap使用上限(硬限制)

      1. 类比于k8s中的资源限制手段,也是使用cgroups的技术,体现为limit

        respones:

        ​ request:分配的初始内存的大小,初始即为100M

        ​ cpu:100m

        ​ memory:100m

        ​ limits:分配的内存最大使用量,最多使用200M

        ​ cpu:200m

        ​ memory:200m

    四、Block I/O的限制

    默认情况下,所有的容器能平等的读写磁盘,可以通过设置-blkio-weight参数来设置容器的block I/O的优先级,-blkio-weight与–cpu-shares类似,是一种设置相对权重值的方式,默认值为500

    例如:将容器A的读写性能是容器B的两倍:

    docker run -itd --name testA --blkio-weight 600 stress:1
    容器ID
    
    docker run -itd --name testB --blkio-weight 300 stress:1
    容器ID
    

    创建完成之后,查看两个容器的相关配置信息

    A:
    cat /sys/fs/cgroup/blkio/docker/容器ID/blkio.weight
    600
    B:
    cat /sys/fs/cgroup/blkio/docker/容器ID/blkio.weight
    300
    

    五、bps和iops的限制

    bps是byte per second:是每秒读写的数据量

    iops是io per second:是每秒io的次数(流)

    可以通过下面参数控制容器的bps和iops

    –device-read-bps:可以限制读某个设备的bps(每秒读写数据量)

    –device-write-bps:可以限制写某个设备的bps

    –device-read-iops:可以限制读某个设备的iops

    –device-write-iops:可以限制写某个设备的iops

    例如:限制容器对于写/dev/sda下数据的速率为3MB/s

    docker run -it --device-write-bps /dev/sda:3MB stress:1
    
    dd if=/dev/zero of=test1 bs=100M count=1 oflag=direct
    1+0 records in
    1+0 records out
    104857600 bytes (105 MB) copied, 33.3938 s, 3.1 MB/s
    #可见,平均速度为每秒3.1MB左右。退出容器,再进行测试
    dd if=/dev/zero of=test1 bs=100M count=1 oflag=direct
    记录了1+0 的读入
    记录了1+0 的写出
    104857600字节(105 MB)已复制,0.261808 秒,401 MB/秒
    #没有被限制速度为每秒400左右的传输速率
    
    更多相关内容
  • CGROUP-METRICS 用于读取指标的节点模块。 从/sys/fs/cgroup/读取。 内存指标: 从路径/sys/fs/cgroup/memory/memory读取: 原始值: stat.rss :匿名和交换高速缓存内存的字节数 kmem.usage_in_bytes :当前...
  • Docker cgroup 信息不赞成这样做。用于检查 docker 容器的最小工具用法 usage: docker_info.py [-h] [--cgroup_name CGROUP_NAME] [--verbose VERBOSE] [--list LIST] [--url URL]一般安装 virtualenv env. env/bin/...
  • docker cgroup 资源监控的详解 1.cgroup术语解析: blkio: 这个subsystem可以为块设备设定输入/输出限制,比如物理驱动设备(包括磁盘、固态硬盘、USB等)。 cpu: 这个subsystem使用调度程序控制task对CPU的使用...
  • mkdir /cgroup/memory/test/ echo 50M > /cgroup/memory/test/memory.limit_in_bytes echo 50M > /cgroup/memory/test/memory.memsw.limit_in_bytes cgexec -g memory:test mongod -port 27017 --bind_ip 127.0.0.1 ...
  • 用于管理cgroup的Python库 该库提供了一种管理和表示cgroup的Python方式。 它提供了将python对象转换为cgroups兼容字符串,反之亦然的接口。 树木 cgroupspy有两种表示cgroups文件系统的方法 作为树-这是表示它们的...
  • 这是cinf ,是容器信息的缩写,是一种用于查看名称空间和cgroup的命令行工具,这些工具组成了Linux容器,例如Docker,rkt / appc或OCI / runc。 当您需要了解幕后情况时,它对于底层容器探测可能很有用。 在这里...
  • cgroups -fs用于管理Linux控制组(cgroup)的Native Rust库。 当前,此板条箱仅支持原始的V1层次结构。 您是cgroups-fs Native Rust库,用于管理Linux控制组(cgroups)。 当前,此板条箱仅支持原始的V1层次结构。 ...
  • 控制群组(control group)(在此指南中简写为 cgroup)是 Linux kernel 的一项功能:在一个系统中运行的层级制进程组,您可对其进行资源分配(如 CPU 时间、系统内存、网络带宽或者这些资源的组合)。通过使用 ...
  • CGroup操作

    2021-06-24 20:00:31
    一、cgroup简介 1.1、cgroup子系统 cgroup支持多种维度的限制,具体如下: 子系统 说明 blkio 这​​​个​​​子​​​系​​​统​​​为​​​块​​​设​​​备​​​设​​​定​​​输​​​入​...

    一、cgroup简介

    1.1、cgroup子系统

    cgroup支持多种维度的限制,具体如下:

    子系统说明
    blkio这​​​个​​​子​​​系​​​统​​​为​​​块​​​设​​​备​​​设​​​定​​​输​​​入​​​/输​​​出​​​限​​​制​​​,比​​​如​​​物​​​理​​​设​​​备​​​(磁​​​盘​​​,固​​​态​​​硬​​​盘​​​,USB 等​​​等​​​)
    cpu这​​​个​​​子​​​系​​​统​​​使​​​用​​​调​​​度​​​程​​​序​​​提​​​供​​​对​​​ CPU 的​​​ cgroup 任​​​务​​​访​​​问
    cpuacct这​​​个​​​子​​​系​​​统​​​自​​​动​​​生​​​成​​​ cgroup 中​​​任​​​务​​​所​​​使​​​用​​​的​​​ CPU 报​​​告​​​
    cpuset这​​​个​​​子​​​系​​​统​​​为​​​ cgroup 中​​​的​​​任​​​务​​​分​​​配​​​独​​​立​​​ CPU(在​​​多​​​核​​​系​​​统​​​)和​​​内​​​存​​​节​​​点​​​
    devices这​​​个​​​子​​​系​​​统​​​可​​​允​​​许​​​或​​​者​​​拒​​​绝​​​ cgroup 中​​​的​​​任​​​务​​​访​​​问​​​设​​​备​​​
    freezer这​​​个​​​子​​​系​​​统​​​挂​​​起​​​或​​​者​​​恢​​​复​​​ cgroup 中​​​的​​​任​​​务
    memory这​​​个​​​子​​​系​​​统​​​设​​​定​​​ cgroup 中​​​任​​​务​​​使​​​用​​​的​​​内​​​存​​​限​​​制​​​,并​​​自​​​动​​​生​​​成​​​​​内​​​存​​​资​​​源使用​​​报​​​告​​​。​​​
    net_cls — 这​​​个​​​子​​​系​​​统​​​使​​​用​​​等​​​级​​​识​​​别​​​符​​​(classid)标​​​记​​​网​​​络​​​数​​​据​​​包​​​,可​​​允​​​许​​​ Linux 流​​​量​​​控​​​制​​​程​​​序​​​(tc)识​​​别​​​从​​​具​​​体​​​ cgroup 中​​​生​​​成​​​的​​​数​​​据​​​包​​​
    net_prio这个子系统用来设计网络流量的优先级
    hugetlb这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统

    1.2、cgroup目录

    通常安装完系统之后,会自动挂载这些目录,可以发现这些目录与上面的子系统是一一对应的

    root@localhot:/sys/fs/cgroup/cpu$ mount -t cgroup
    cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
    cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
    cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
    cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
    cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
    cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
    cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
    cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
    cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
    cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
    cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
    root@localhost:/sys/fs/cgroup/cpu$ 

    cgroup限制,常用的子系统是cpu和memory。下面将通过cpu和内存限制进行说明,注意实验在cgroup-v1版本实验

    二、cpu限制
    2.1、创建测试程序

    我们启动一个进程,代码如下:

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/syscall.h>
    #include <pthread.h>
    #include <sys/types.h>
    
    void* run(void *data) {
        printf("sub-thread-id:%d\n", syscall(SYS_gettid));
        while(1);//子线程
    }
    
    int main(){
        printf("main-thread-id:%d\n", syscall(SYS_gettid));
        pthread_t thread;
        pthread_attr_t attr;
        pthread_attr_init( &attr );
        pthread_attr_setdetachstate(&attr,1);
        pthread_create(&thread, &attr, run, 0); //第二个参数决定了分离属性
        while(1);//主线程
        return 0;
    }
    [root@localhost ~]# gcc a.c -lpthread
    [root@localhost ~]# ./a.out 
    main-thread-id:18867
    sub-thread-id:18868
    
    top - 15:31:30 up 36 min,  1 user,  load average: 2.89, 1.72, 1.27
    Threads:   2 total,   2 running,   0 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 62.8 us, 18.3 sy,  0.3 ni, 18.6 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem :  4772156 total,  2041864 free,  1462712 used,  1267580 buff/cache
    KiB Swap:  4980732 total,  4980732 free,        0 used.  2866188 avail Mem 
    
      PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                             
     9288 root      20   0   14700    388    304 R 98.7  0.0   2:43.01 a.out                                                               
     9289 root      20   0   14700    388    304 R 97.0  0.0   2:42.36 a.out 

     可以看到占用了两个core,200%

     2.2、在系统cgroup目录直接创建

    root@localhost:/sys/fs/cgroup/cpu$ mkdir tmp-cgroup
    root@localhost:/sys/fs/cgroup/cpu$ ls tmp-cgroup/
    cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release
    cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks
    root@localhost:/sys/fs/cgroup/cpu$ 

    创建完目录,目录中内容是自动创建出来的,参数说明可以参考《3.2. cpu Red Hat Enterprise Linux 6 | Red Hat Customer Portal》

    配置说明备注
    cpu.cfs_period_us默认值100000(即100ms), 最小值1000us和最大值1s详细举例
    cpu.cfs_quota_us默认值-1,表示不限制
    tasks被限制的task id,实际是线程id(通过top -H -p 查看线程id)按照线程维度进行限制
    cgroup.procs被限制的process id,进程id按照进程维度进行限制

    举例说明:

    cpu.cfs_period_us = 100000 (100ms)

    cpu.cfs_quota_us = 50000 (50ms)

    在这100ms调度周期内,最多占用50ms,换句话说,占用50%cpu,也就是0.5core。

    在举例:

    cpu.cfs_period_us = 100000 (100ms)

    cpu.cfs_quota_us = 200000 (200ms)

    在这100ms调度周期内,最多占用200ms,换句话说,占用200%cpu,也就是2core。

    cpu核心数 = cpu.cfs_quota_us / cpu.cfs_period_us

    2.3、按照线程维度限制cpu

    [root@localhost cpu]# cd tmp-cgroup/         
    [root@localhost tmp-cgroup]# echo 50000 > cpu.cfs_quota_us 
    [root@localhost tmp-cgroup]# echo 18868 > tasks 
    [root@localhost tmp-cgroup]# 
    top - 16:12:42 up 33 min,  1 user,  load average: 1.99, 1.52, 1.21
    Threads:   2 total,   2 running,   0 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 39.3 us,  0.5 sy,  0.0 ni, 60.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem :  4772156 total,  2173732 free,  1345920 used,  1252504 buff/cache
    KiB Swap:  4980732 total,  4980732 free,        0 used.  3023376 avail Mem 
    
      PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                             
    18867 root      20   0   14700    388    304 R 99.7  0.0   3:12.76 a.out                                                               
    18868 root      20   0   14700    388    304 R 49.8  0.0   3:06.64 a.out                                                               
    

    通过上面操作,我们对线程18868进行了限制,cpu使用率为50%。

    如何解除限制呢?只需要将线程id,写入父级cgroup tasks文件中即可

    [root@localhost tmp-cgroup]# echo 18868 > ../tasks 
    [root@localhost tmp-cgroup]#
    top - 16:19:57 up 40 min,  1 user,  load average: 2.40, 2.22, 1.66
    Threads:   2 total,   2 running,   0 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 61.2 us,  4.2 sy,  0.8 ni, 33.4 id,  0.5 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem :  4772156 total,  2099132 free,  1378992 used,  1294032 buff/cache
    KiB Swap:  4980732 total,  4980732 free,        0 used.  2952392 avail Mem 
    
      PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                             
    18868 root      20   0   14700    388    304 R 99.0  0.0   7:56.57 a.out                                                               
    18867 root      20   0   14700    388    304 R 98.3  0.0  10:23.67 a.out  

     由于一个进程内的线程,是不固定的,尤其是java程序。因此我们需要按照整个进程级别进行限制,就要用到cgroup.procs。

    2.4、按照进程维度限制cpu

    按照刚才实现的代码,main-thread-id就是进程id。所以通过如下命令,可以对整个进程进行限制

    [root@localhost tmp-cgroup]# 
    [root@localhost tmp-cgroup]# echo 18867 > cgroup.procs 
    [root@localhost tmp-cgroup]# 
    [root@localhost ~]# top -H -p 18867
    top - 16:25:46 up 46 min,  2 users,  load average: 1.23, 1.92, 1.74
    Threads:   2 total,   2 running,   0 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 15.5 us,  1.4 sy,  0.0 ni, 83.1 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem :  4772156 total,  2110104 free,  1389992 used,  1272060 buff/cache
    KiB Swap:  4980732 total,  4980732 free,        0 used.  2966200 avail Mem 
    
      PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                             
    18867 root      20   0   14700    388    304 R 24.9  0.0  15:28.42 a.out                                                               
    18868 root      20   0   14700    388    304 R 24.9  0.0  13:01.60 a.out                                                               
    

    tasks和procs区别,重点内容:

    【tasks】

    比如当前进程A,存在线程:主线程id-100(进程id也是100),子线程101后,我们手动将进程id-100,写入到tasks文件中后,会发现:

    a、cgroup.procs也会出现100

    b、子线程101,并不会加入tasks中

    c、当在创建一个新的线程102后,发现tasks文件会出现102

    【procs】

    比如当前进程A,存在线程:主线程id-100(进程id也是100),子线程101后,我们手动将进程id-100,写入到cgroup.procs文件中后,会发现:

    a、tasks也会出现100

    b、子线程101,会自动加入tasks中

    c、当在创建一个新的线程102后,发现tasks文件会出现102

    主要区别在b)

    如果写入task文件,是子线程id(不是进程id),会出现什么结果呢?

    a)将子线程id写入task文件后,cgroup.procs文件是进程id(主线程id)

    b)后续新建线程不会添加到task文件中

    c)如果后续将进程id(主线程id),写入task文件,后续在创建新的线程仍然会自动添加到task中   

    测试代码:

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/syscall.h>
    #include <pthread.h>
    #include <sys/types.h>
    
    void* run(void *data) { 
        printf("sub-thread-id:%d\n", syscall(SYS_gettid));
        while(1);//子线程
    } 
    
    int main(){ 
        int i = 5;
        printf("main-thread-id:%d\n", syscall(SYS_gettid));
        pthread_t thread; 
        pthread_attr_t attr; 
        pthread_attr_init( &attr ); 
        pthread_attr_setdetachstate(&attr,1); 
        while(i) {//主线程
          pthread_create(&thread, &attr, run, 0); //第二个参数决定了分离属性
          i--;
          sleep(30);
        }
        while(1);
        return 0; 
    }

    2.5、cpu绑定

    在某些特殊场景,希望进程能够独占cpu而不被置换出去,这就需要进行cpu绑定。我们可以通过代码方式进行cpu绑定,可以参考我之前写的博客《Linux进程、线程绑定CPU以及独占CPU》。这里介绍cgroup cpuset实现的。 

    通常家用电脑至少4个核心了,我们使用02号cpu( 编号是00开始,)。cpu绑定,是在/sys/fs/cgroup/cpuset目录中,我们在这个目录中创建一个目录tmp-cgroup-cpuset并且进入该目录。查看如下两个文件,默认都是空

    [root@localhost tmp-cgroup-cpuset]# cat cpuset.cpus
    
    [root@localhost tmp-cgroup-cpuset]# cat cpuset.mems
    
    [root@localhost tmp-cgroup-cpuset]#
    [root@localhost tmp-cgroup-cpuset]# echo 2-2 >> cpuset.cpus 
    [root@localhost tmp-cgroup-cpuset]# cat cpuset.cpus
    2
    [root@localhost tmp-cgroup-cpuset]# echo 0 >> cpuset.mems
    [root@localhost tmp-cgroup-cpuset]# cat cpuset.mems
    0
    [root@localhost tmp-cgroup-cpuset]#

    仍然使用上面那个测试程序并且重新启动一次

    [root@localhost ~]# 
    [root@localhost ~]# ./a.out 
    main-thread-id:20951
    sub-thread-id:20952

    通过htop -p PID 命令进行常看,如果htop命令不存在需要自己安装一下,注意:htop显示cpu序号是从1开始

    
      1  [####################################################100.0%]   Tasks: 156, 498 thr; 3 running
      2  [#***                                                  4.6%]   Load average: 1.41 0.83 0.99 
      3  [#*                                                    1.9%]   Uptime: 01:17:26
      4  [####################################################100.0%]
      Mem[|||||||||||||||||||||#**************           1.64G/4.55G]
      Swp[                                                  0K/4.75G]
    
      PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command                                                                  
    20951 root       20   0 14700   388   304 R 200.  0.0  2:29.19 ./a.out                                                                  
    20952 root       20   0 14700   388   304 R 100.  0.0  1:14.43 ./a.out
    

    可以观察一段时间,发现cpu 100%,核心数总是在变化,不是固定的。接下来是重点,我们将进程id写入到配置文件(cpuset中的cgroup.procs)中:

    [root@localhost tmp-cgroup-cpuset]# cat cgroup.procs
    [root@localhost tmp-cgroup-cpuset]# echo 20951 > cgroup.procs
    [root@localhost tmp-cgroup-cpuset]# 
      1  [#**                                                   3.3%]   Tasks: 156, 497 thr; 3 running
      2  [##*                                                   3.3%]   Load average: 2.15 1.48 1.22 
      3  [####################################################100.0%]   Uptime: 01:20:14
      4  [*                                                     0.7%]
      Mem[|||||||||||||||||||||#**************           1.65G/4.55G]
      Swp[                                                  0K/4.75G]
    
      PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command                                                                  
    20951 root       20   0 14700   388   304 R 100.  0.0  7:05.96 ./a.out                                                                  
    20952 root       20   0 14700   388   304 R 49.7  0.0  3:32.95 ./a.out

    我们发现,只有02号cpu(htop中是3)cpu使用率为100%,说明我们设置的是生效的

    三、内存限制

    下面对内存的限制,内存限制,在/sys/fs/memory目录中,我们仍然创建一个目录并将虚拟内存和swap内存设置相同值30M, 如下:

    [root@localhost ~]# cd /sys/fs/cgroup/memory/
    [root@localhost memory]# 
    [root@localhost memory]# mkdir tmp-cgroup-memory
    [root@localhost memory]# cd tmp-cgroup-memory/
    [root@localhost tmp-cgroup-memory]# 
    [root@localhost tmp-cgroup-memory]# echo "30M" >> memory.limit_in_bytes 
    [root@localhost tmp-cgroup-memory]# echo "30M" >> memory.memsw.limit_in_bytes 
    [root@localhost tmp-cgroup-memory]# 

    测试代码如下:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(){
        printf("main-pid=%d\n", getpid());
        int i = 100;
        int mem_size=5*1024*1024; // 5mb
        char *p = NULL;
        int size = 0;
        while(i) {//主线程
          p = malloc(sizeof(char)*mem_size); //有内存泄露,没有关系,进程结束后就释放了
          i--;
          if (i > 80) {
             sleep(3);
          }
          memset(p, 1, mem_size);
          size += mem_size;
          printf("[%d] - memory is allocated [%8d] bytes \n", getpid(), size);
        }
        printf(" malloc over\n");
        while(1);
        return 0;
    }
    [root@localhost tmp-cgroup-memory]# 
    [root@localhost tmp-cgroup-memory]# cat memory.limit_in_bytes 
    31457280
    [root@localhost tmp-cgroup-memory]# cat memory.max_usage_in_bytes 
    31457280
    [root@localhost tmp-cgroup-memory]# echo 8488 > cgroup.procs 
    [root@localhost tmp-cgroup-memory]#
    [root@localhost ~]# ./memory-cgroup 
    main-pid=8488
    [8488] - memory is allocated [ 5242880] bytes 
    [8488] - memory is allocated [10485760] bytes 
    [8488] - memory is allocated [15728640] bytes 
    [8488] - memory is allocated [20971520] bytes 
    [8488] - memory is allocated [26214400] bytes 
    [8488] - memory is allocated [31457280] bytes 
    已杀死
    [root@localhost ~]# 
    [root@localhost tmp-cgroup-memory]# dmesg | grep 8488
    [   68.148488] wlp6s0b1: RX AssocResp from 64:64:4a:d5:a1:0f (capab=0x431 status=0 aid=3)
    [  644.945348] CPU: 2 PID: 8488 Comm: memory-cgroup Kdump: loaded Tainted: G        W      ------------   3.10.0-957.el7.x86_64 #1
    [  644.945723] [ 8488]     0  8488    10020     9075      25        0             0 memory-cgroup
    [  644.945729] Memory cgroup out of memory: Kill process 8488 (memory-cgroup) score 1149 or sacrifice child
    [  644.945736] Killed process 8488 (memory-cgroup) total-vm:40080kB, anon-rss:35884kB, file-rss:416kB, shmem-rss:0kB
    [root@localhost tmp-cgroup-memory]# 
    [root@localhost tmp-cgroup-memory]#

    测试在死循环中每次申请5MB空间,当超过30MB的时候,就会被kill掉。通过终端以及dmesg命令可以查看出,说明memory限制生效了。 

    四、线程数限制

    工作中,大部分都是java程序,在pass平台中,某些场景需要对线程数进行限制。

    [root@localhost 8743]# cd /sys/fs/cgroup/pids
    [root@localhost pids]# 
    [root@localhost pids]# 
    [root@localhost pids]# mkdir tmp-cgroup-pids
    [root@localhost pids]# cd tmp-cgroup-pids/
    [root@localhost tmp-cgroup-pids]# 
    [root@localhost tmp-cgroup-pids]# ls
    cgroup.clone_children  cgroup.event_control  cgroup.procs  notify_on_release  pids.current  pids.max  tasks
    [root@localhost tmp-cgroup-pids]# 
    [root@localhost tmp-cgroup-pids]# cat pids.max 
    max
    [root@localhost tmp-cgroup-pids]# 
    [root@localhost tmp-cgroup-pids]# 
    
    #include <stdio.h>
    #include <unistd.h>
    #include <pthread.h>
    
    void* run(void *data) {
        printf("sub-thread-id:%d\n", *(int*)data);
        while(1);//子线程
    }
    
    int main(){
        int i = 100;//创建最多100线程
        printf("processor-id:%d, main-thread-id:0\n", getpid());
        pthread_t thread;
        pthread_attr_t attr;
        pthread_attr_init( &attr );
        pthread_attr_setdetachstate(&attr,1);
        int j = 1;
        while(i) {
          pthread_create(&thread, &attr, run, &j); //第二个参数决定了分离属性
          i--;
          sleep(3);
          j++;
        }
        return 0;
    }
    [root@localhost ~]# ./thread-cgroup 
    processor-id:9646, main-thread-id:0
    sub-thread-id:1
    sub-thread-id:2
    sub-thread-id:3
    [root@localhost tmp-cgroup-pids]# 
    [root@localhost tmp-cgroup-pids]# echo 9400 > cgroup.procs
    [root@localhost tmp-cgroup-pids]# 

     最终当到了20个线程,再继续创建线程就会失败,被kill掉,如下所示:

    sub-thread-id:15
    sub-thread-id:16
    sub-thread-id:17
    sub-thread-id:18
    sub-thread-id:19
    段错误(吐核)
    [root@localhost ~]# 

    六、总结

    cgroup大体介绍就这样了,常用的限制基本介绍了一遍。对于其他隔离,需要自己查阅相关资料。但是可能细心的同僚,可能会说,有没有相关命令直接操作,这样操作下来的确不太方便?以及docker是如何实现的?ok,其实是有相关命令的,libcgroup,在下一篇将介绍libcgroup以及docker的实现。

    展开全文
  • Cgroup概述

    千次阅读 2021-12-04 07:29:28
    一、Cgroup的目的 Cgroup和namespa类似,也是将进程进程分组,但是目的与namespace不一样,namespace是为了隔离进程组之前的资源,而Cgroup是为了对一组进程进行统一的资源监控和限制。 二、为什么需要Cgroup ...

    一、Cgroup的目的    

        Cgroup和namespa类似,也是将进程进程分组,但是目的与namespace不一样,namespace是为了隔离进程组之前的资源,而Cgroup是为了对一组进程进行统一的资源监控和限制。

    二、为什么需要Cgroup

        在Linux里,对进程进程分组,比如Session group、process group等,后来需要追踪一组进程的内存和IO使用情况,出现了cgroup,用来统一对进程进行分组,并在分组的基础上对进程进程监控和资源控制管理等。

    三、Cgroup中名词概念

    任务(Task)在Cgroup中,任务就是系统的一个进程
    控制族群(control group)控制族群就是按照一组某种标准划分的进程。Cgroup中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也可以从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配资源,同时受到cgroups以控制族群为单位设定限制。
    层级(hierachy)控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定属性
    子系统(subsystem)一个子系统就是一个资源控制器

        Cgroup可以指整个cgrop技术,也可以指一个具体的进程组。

        Cgroup是Linux下一种将进程按组管理的机制,在用户层面看,Cgroup技术就是可以把系统中的所有进程组织成一颗独立的树,每颗树都包含系统所有进程,树的每个节点就是一个进程组,而每颗树又和一个或者多个subsystem关联,树的作用是对进程分组,而subsystem作用是对这些组进行操作。

    • subsystem:一个subsystem就是一个内核模块,它被关联到一颗Cgroup树之后,就会对树的每个节点(进程组)上做具体操作。subsystem也被称为resource controller,因为它主要用来调度和限制每个进程组资源,有时也可以将进程分组只是为了做监控,观察进程组的状态。例如,perf_event subsystem。目前Linux支持12中system,比如限制CPU的使用时间,限制使用内存,统计CPU使用情况,冻结和恢复一组进程等。
    • 下面每个子系统都需要与内核的其它模块配合来完成资源控制,比如对CPU资源的限制是通过进程调度模块根据CPU子系统的配置来完成;对内存资源的限制是内存模块根据memoty子系统的配置来完成,对网络数据包的控制则需要Traffic Control子系统配合完成。
      subsystem作用
      cpu主要限制进程CPU使用率
      cpuacct统计cgroups中进程的cpu使用率
      cpuset为cgroups中进程分配单独的CPU节点(多核)或内存节点
      blkio限制进程的块设备IO
      memory限制进程的memory使用量
      devices可以控制进程能够访问某些设备
      freezer挂起或者恢复cgrops中进程
      net_cls可以标记cgroups中进程的网络数据包,然后可以使用tc(traffic control)对数据包进程控制
      ns可以使不同cgroups下面的进程使用不同的namespace

    四、cgroups层级结构(Hierachy)

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

      上图表示两个cgroups层级结构,每一个层级结构中是一颗树形结构,树的每一个节点是一个cgroups结构体(比如cpu_cgrp,memory_cgrp)。第一个cgroups层级结构attach了cpu子系统和cpuacct子系统,当前

    cgroups层级结构中的cgroups层级结构中的cgroup结构体就可以对cpu资源进进行限制,并且对进程的cpu使用情况进行统计。

        第二个cgroups层级结构中attach了memory子系统,当前cgroups层级结构中的cgroup结构体就可以对memory的资源进行限制。在每一个cgroups层级结构中,每一个节点(cgroup结构体)可以设置对资源不同的限制权重。比如上图中cgrp1组中的进程可以使用60%cpu时间片,而cgrp2组中的进程可以使用20%的cpu时间片。

     

     cgroup层级关系显示,CPU和Memory两个子系统有自己独立的层级系统而又通过Task group取得关联关系

    相互关系:

    每次在系统中创建新层级时,该系统中所有任务都是那个层级默认cgroup(root cgroup,此cgoup在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员;

    • 同一个hierarchy可以附加到一个或者多个subsystem
    • 一个subsystem可以附加到多个hierarchy,当且仅当这个hierarchy只有这一个subsystem,如下图,hierarchy B已经有了一个subsystem Memory,所以hierarchy不能attach到CPU这个subsystem中

     

    • 同一个task不能属于同一个hierarchy的不同的cgroup:
    • fork出的子进程在初始化状态与其父进程处于同一个cgroup。进程(task)在fork出自身时创建子任务(child task)默认与原task在同一个cgroup,但之后这个子任务可以被移动到其它cgroup中去,可以理解为这个子任务是一个独立的进程:

    五、cgroups与进程

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

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

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

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

    六、cgroups文件系统

        Linux使用多种数据结构在内核中实现了cgroups的配置,关联了进程和cgroups节点,那么Linux如何让用户态的进程使用到cgroups的功能呢?

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

        VFS:

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

        VFS通用文件模型中包含以下四种元数据结构:

        1. 超级块对象(superblock object) ,用于存放已经注册的文件系统信息。比如ext2,ext3等这些基础的磁盘文件系统,还用于读写socket的socket文件系统,以及当前用于读写cgroups文件系统等。

        2. 索引节点对象(inode object),用于存放具体文件的信息。对于一般的磁盘文件系统而言,inode节点中一般会存放文件在硬盘中的存储块信息;对于socket文件系统,inode会存放socket的相关属性,而对于cgoups这样的特殊文件系统,inode会存放与cgroup节点相关的属性信息。这里比较重要的一部分叫做inode_operations的结构体,这个结构体定义了在具体文件系统中创建文件,删除文件等具体操作。

       3. 文件对象(file object),一个文件对象表示一个进程打开的一个文件,文件对象是存放在进程文件描述符表里面的。同样这个文件中比较重要的部分是file_operations的结构体,这个结构体描述了具体的文件系统读写实现。当进程在某一个文件描述符上调用读写操作时,实际调用的时file_operations中定义的方法。对于普通的磁盘文件系统,file_operations中定义的就是普通块设备读写操作;对于socket文件系统,file_operations中定义的就是socket对应的send/recv等操作;而对于cgroups这样特殊的文件系统,file_operations中定义的就是操作cgroup结构体等具体实现。

    4. 目录项对象(dentry object):在每个文件系统中,内核在查找一个路径的文件时,会为内核路径上的每一个分量都生成一个目录项对象,通过目录项对象找到对应的indode对象,目录项对象一般会被缓存,从而提供内核查找速度。

    基于VFS实现的文件系统,都必须实现VFS通用文件模型定义这些对象,并实现这些对象中定义的部分函数。cgroup文件系统也不例外:

    static struct file_system_type cgroup_fs_type = {
        .name = "cgroup",
        .mount = cgroup_mount,       //安装cgroup文件系统要执行的函数
        .kill_sb = cgroup_kill_sb,  //卸载cgroup文件系统要执行的函数
    }

     

       每次把一个cgroups子系统安装到某个转载点时,cgroup_mount方法都会被调用,这个方法生成一个cgroup_root(cgroup层级结构的根)并封装成超级块对象。

        cgroups超级块对象定义的操作:

    static file_system_type cgroup_fs_type = {
        .statfs = simple_statfs,
        .drop_inode = generic_delelte_inode,
        .show_option = cgroup_show_options,
        .remount_fd = cgroup_remount,
    }

     cgroups文件系统对inode对象和file对象定义的特殊实现:

    static const struct inode_operations cgroup_dir_inode_operations = {
            .lookup = cgroup_lookup,
            .mkdir = cgroup_mkdir,
            .rmdir = cgroup_rmdir,
            .rename = cgroup_rename,
    };
    
    static const struct file_operations cgroup_file_operations = {
            .read = cgroup_file_read,
            .write = cgroup_file_write,
            .llseek = generic_file_llseek,
            .open = cgroup_file_open,
            .release = cgroup_file_release,
    };

     

        cgroups通过实现VFS的通用文件系统模型,把cgroups层级结构的细节,隐藏在cgroups文件系统的这些实现函数中。从令一个方面说,用户在用户态对cgroups文件系统的操作,通过VFS转化为对cgroups层级结构的维护。通过这样的方式,内核把cgroups的功能暴露给用户态进程。

    七、cgroups使用方法

    #################cgroups文件系统挂载###########################

    Linux用户使用mount命令挂载cgroup文件系统,

    mount -t cgroup -o subsystems name /cgroup/name  #其中subsystems表示要挂载的cgroups子系统, /cgroup/name表示挂载点,这条命令同时在内核中创建了一个cgroups层级结构

     比如挂载cpu_set,cpu, cpuacct,memory 4个subsystem到cgroup/cpu_and_mem目录下,

    mount -t cgroup -o remount, cpu,cpuset, cpu_acct,memory cpu_and mem /cgroup/cup_and_mem

    在centos下,使用yum install libcgroup安装了cgroups模块之后,在/etc/cgfong.conf文件中自动生成cgroups子系统的挂载点:

    mount {
        cpuset  = /cgroup/cpuset;
        cpu = /cgroup/cpu;
        cpuacct = /cgroup/cpuacct;
        memory  = /cgroup/memory;
        devices = /cgroup/devices;
        freezer = /cgroup/freezer;
        net_cls = /cgroup/net_cls;
        blkio   = /cgroup/blkio;
    }

    上面的每一条配置都等于展开的mount命令,例如

    mount -t cgroup -o cpuset cpuset /cgroup/cpuset  #这样系统启动之后会自动把这些子系统挂载到相应的挂载点

    #########子节点和进程############

        挂载某个cgroups子系统到挂载点后,就可以通过在挂载点下面建立文件夹或者使用cgcreate命令创建cgroups层级结构中的节点,比如通过命令

    cgcreate -t sankkuai:sankuai -g cpu: test #就可以在cpu子系统下面建立一个名为test节点
    [root@idx cpu]# ls
    cgroup.event_control  cgroup.procs  cpu.cfs_period_us  cpu.cfs_quota_us  cpu.rt_period_us   cpu.rt_runtime_us  cpu.shares  cpu.stat  lxc  notify_on_release  release_agent  tasks  test

      然后可以通过写入需要的值到test下面不同文件,来配置需要限制的资源。每个子系统下面都可以进行多种不同的配置,需要配置的参数各不相同,详细的参数配置参考cgroups手册。可以使用cgset命令设置cgroups子系统参数,格式为

    cgset -r parameter=value path_to_cgroup

      当删除某一个cgroup节点时候,可以使用cgdelete命令,比如要删除test节点,

    cgdelete -r cpu:test #删除test节点

     把进程加入到cgroups子节点有很多方法,可以把pid写入到子节点下面的task文件中,也可以通过cgclassify添加进程

    cgclassify -g subsystem:path_to_cgroup pidlist

       也可以直接使用cgexec在某个cgroups下启动进程

    cgexec -g subsystem:path_to_cgoup command aguments

    八、cgroups实践

       Docker在实现不同的Container之间资源隔离和控制的时候,可以创建复杂的cgroups节点和配置文件完成,对于同一个Container中的进程,可以把这些进程的PID添加到同一组cgroups子节点中达到对这些进程进行同样的资源限制。

    九、cgroups优点

        1. 在cgroups引入内核之前,想要完成对某一个进程CPU使用率进行限制,只能通过nice命令调整进程的优先级,或者cpulimit命令限制进程使用进程的CPU使用率,缺点是无法限制一个进程组的资源使用限制,也无法完成Docker或者其它云平台所需的这一类轻型容器资源限制。

       2. 在cgroups之前,想要完成一个或者一组进程的物理内存使用率的限制,几乎不可能完成。使用cgroups提供的功能,可以轻易的限制限制系统内某一组物理内存占用率、对于网络包,设备访问或者io资源控制,cgroups同样提供了之前无法完成的精细化控制。

    展开全文
  • 在本篇文章里小编给大家整理的是关于Docker底层技术Namespace Cgroup应用的相关知识点,需要的朋友们学习下。
  • 松果 通过cgroup将ceph osd守护程序自动固定到numa节点的脚本 用法: 将prz-pincpus.conf放入/ etc /并根据自己的喜好进行编辑 运行pincpus :-)
  • linux cgroup

    2020-09-13 10:42:03
    了解cgroup 1.描述   Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。这个项目最早是由Google的工程师在2006年发起...
    • 了解cgroup

    1.cgroup描述

    官方描述:

       DESCRIPTION   
           Control groups, usually referred to as cgroups, are a Linux kernel
           feature which allow processes to be organized into hierarchical
           groups whose usage of various types of resources can then be limited
           and monitored.  The kernel's cgroup interface is provided through a
           pseudo-filesystem called cgroupfs.  Grouping is implemented in the
           core cgroup kernel code, while resource tracking and limits are
           implemented in a set of per-resource-type subsystems (memory, CPU,
           and so on).
       
       Terminology
           A cgroup is a collection of processes that are bound to a set of
           limits or parameters defined via the cgroup filesystem.
    
           A subsystem is a kernel component that modifies the behavior of the
           processes in a cgroup.  Various subsystems have been implemented,
           making it possible to do things such as limiting the amount of CPU
           time and memory available to a cgroup, accounting for the CPU time
           used by a cgroup, and freezing and resuming execution of the
           processes in a cgroup.  Subsystems are sometimes also known as
           resource controllers (or simply, controllers).
    
           The cgroups for a controller are arranged in a hierarchy.  This
           hierarchy is defined by creating, removing, and renaming
           subdirectories within the cgroup filesystem.  At each level of the
           hierarchy, attributes (e.g., limits) can be defined.  The limits,
           control, and accounting provided by cgroups generally have effect
           throughout the subhierarchy underneath the cgroup where the
           attributes are defined.  Thus, for example, the limits placed on a
           cgroup at a higher level in the hierarchy cannot be exceeded by
           descendant cgroups
    

      Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。这个项目最早是由Google的工程师在2006年发起(主要是Paul Menage和Rohit Seth),最早的名称为进程容器(process containers)。在2007年时,因为在Linux内核中,容器(container)这个名词太过广泛,为避免混乱,被重命名为cgroup,并且被合并到2.6.24版的内核中去。

      Linux Cgroup ​​​系​​​统​​​中​​​所​​​运​​​行​​​任​​​务​​​(进​​​程​​​)的​​​用​​​户​​​定​​​义​​​组​​​群​​​分​​​配​​​资​​​源​​​ — 比​​​如​​​ CPU 时​​​间​​​、​​​系​​​统​​​内​​​存​​​、​​​网​​​络​​​带​​​宽​​​或​​​者​​​这​​​些​​​资​​​源​​​的​​​组​​​合​​​。​​​可​​​以​​​监​​​控​​​配​​​置​​​的​​​ cgroup,拒​​​绝​​​ cgroup 访​​​问​​​某​​​些​​​资​​​源​​​,甚​​​至​​​在​​​运​​​行​​​的​​​系​​​统​​​中​​​动​​​态​​​配​​​置​​​cgroup。

      cgroup 是 Linux 下的一种将进程按组进行管理的机制,在用户层看来,cgroup 技术就是把系统中的所有进程组织成一颗一颗独立的树,每棵树都包含系统的所有进程,树的每个节点是一个进程组,而每颗树又和一个或者多个 subsystem 关联,树的作用是将进程分组,而 subsystem 的作用就是对这些组进行操作。cgroup 主要包括下面两部分:

    • subsystem : 一个 subsystem 就是一个内核模块,它被关联到一颗 cgroup 树之后,就会在树的每个节点(进程组)上做具体的操作。subsystem 经常被称作 resource controller,因为它主要被用来调度或者限制每个进程组的资源,但是这个说法不完全准确,因为有时我们将进程分组只是为了做一些监控,观察一下他们的状态,比如 perf_event subsystem。到目前为止,Linux 支持 12 种 subsystem,比如限制 CPU 的使用时间,限制使用的内存,统计 CPU 的使用情况,冻结和恢复一组进程等。

    • hierarchy : 一个 hierarchy 可以理解为一棵 cgroup 树,树的每个节点就是一个进程组,每棵树都会与零到多个 subsystem 关联。在一颗树里面,会包含 Linux 系统中的所有进程,但每个进程只能属于一个节点(进程组)。系统中可以有很多颗 cgroup 树,每棵树都和不同的 subsystem 关联,一个进程可以属于多颗树,即一个进程可以属于多个进程组,只是这些进程组和不同的 subsystem 关联。目前 Linux 支持 12 种 subsystem,如果不考虑不与任何 subsystem 关联的情况(systemd 就属于这种情况),Linux 里面最多可以建 12 颗 cgroup 树,每棵树关联一个 subsystem,当然也可以只建一棵树,然后让这棵树关联所有的 subsystem。当一颗 cgroup 树不和任何 subsystem 关联的时候,意味着这棵树只是将进程进行分组,至于要在分组的基础上做些什么,将由应用程序自己决定,systemd 就是一个这样的例子。

    2.原理

      cgroups的实现本质上是给系统进程挂上钩子(hooks),当task运行的过程中涉及到某个资源时就会触发钩子上所附带的subsystem进行检测,最终根据资源类别的不同使用对应的技术进行资源限制和优先级分配。

      cgroups的目的是将任意进程进行分组化管理。

      cgroups本身提供将进程进行分组化管理的功能,而IO或内存的分配等具体的资源管理功能则是通过subsystem来实现的。可以通过/proc/cgroup查看可以使用的subsystem。

      cgroups提供了一个名为cgroup的虚拟文件系统,作为进行分组管理和各子系统设置的用户接口.要使用cgroups,必须挂载cgroup文件系统,此时可以通过挂载参数指定使用的subsystem。

      在linux中,cgroups默认挂载在/sys/fs/cgroup目录,其中包含了所有的子系统以及每个子系统的层级。

    3.cgroups相关概念

      cgroups主要由task,cgroup,subsystem以及hierarchy组成.

    • Task(任务) : 在 linux 系统中,内核本身的调度和管理并不对进程和线程进行区分,只是根据 clone 时传入的参数的不同来从概念上区分进程和线程。这里使用 task 来表示系统的一个进程或线程。
    • cgroup(控制族群): 控制族群是一组按照某种标准划分的进程.cgroups中的资源控制都是以控制族群为单位实现的.一个进程可以加入某个控制族群,也可以迁移至另一个控制族群。
    • Hierarchy(层级) :层级有一系列 cgroup 以一个树状结构排列而成,每个层级通过绑定对应的子系统进行资源控制。层级中的 cgroup 节点可以包含零个或多个子节点,子节点继承父节点挂载的子系统。一个操作系统中可以有多个层级。
    • subsystem(子系统): 通常是一个资源控制器.例如CPU子系统可以控制CPU时间分配.一个子系统必须附加到控制族群树上才能起作用,该树上的所有控制族群都受到该子系统的控制

    3.1.子系统简介

      subsystem实际上就是cgroups的资源控制系统,每种subsystem独立地控制一种资源

    • blkio (since Linux 2.6.33; CONFIG_BLK_CGROUP): 这个subsystem可以为块设备设定输入/输出限制,比如物理驱动设备(包括磁盘,固态硬盘,USB等),限制cgroup访问块设备的IO速度。
    • cpu (since Linux 2.6.24; CONFIG_CGROUP_SCHED): 这个subsystem使用调度程序控制task对CPU的使用,用来限制cgroup的CPU使用率。
    • cpuacct (since Linux 2.6.24; CONFIG_CGROUP_CPUACCT): 这个subsystem自动生成cgroup中task对CPU资源使用情况的报告,统计cgroup的CPU的使用率
    • cpuset (since Linux 2.6.24; CONFIG_CPUSETS): 这个subsystem可以为cgroup中的task分配独立的CPU(此处针对多处理器系统)和内存,绑定cgroup到指定CPUs和NUMA节点。
    • devices (since Linux 2.6.26; CONFIG_CGROUP_DEVICE) : 这个subsystem可以开启或关闭cgroup中task对设备的访问,限制cgroup创建(mknod)和访问设备的权限。
    • freezer (since Linux 2.6.28; CONFIG_CGROUP_FREEZER): 这个subsystem可以挂起或恢复cgroup中的task,suspend和restore一个cgroup中的所有进程。
    • memory (since Linux 2.6.25; CONFIG_MEMCG): 这个subsystem可以设定cgroup中task对内存使用量的限定,并且自动生成这些task对内存资源使用情况的报告,统计和限制cgroup的内存的使用率,包括process memory, kernel memory, 和swap。
    • perf_event (since Linux 2.6.39; CONFIG_CGROUP_PERF): 这个subsystem使用后使得cgroup中的task可以进行统一的性能测试,对cgroup进行性能监控。
    • net_cls (since Linux 2.6.29; CONFIG_CGROUP_NET_CLASSID) : 将一个cgroup中进程创建的所有网络包加上一个classid标记,用于tc和iptables。 只对发出去的网络包生效,对收到的网络包不起作用。
    • net_prio (since Linux 3.3; CONFIG_CGROUP_NET_PRIO) : 针对每个网络接口设置cgroup的访问优先级。
    • hugetlb (since Linux 3.5; CONFIG_CGROUP_HUGETLB) : 限制cgroup的huge pages的使用量。
    • pids (since Linux 4.3; CONFIG_CGROUP_PIDS) : 限制一个cgroup及其子孙cgroup中的总进程数。

      上面这些subsystem,有些需要做资源统计,有些需要做资源控制,有些即不统计也不控制。对于cgroup树来说,有些subsystem严重依赖继承关系,有些subsystem完全用不到继承关系,而有些对继承关系没有严格要求。

    xxx:$ cat /proc/cgroups
    #subsys_name    hierarchy       num_cgroups     enabled
    cpuset  3       1       1
    cpu     2       70      1
    cpuacct 2       70      1
    blkio   9       70      1
    memory  7       127     1
    devices 8       70      1
    freezer 10      1       1
    net_cls 6       1       1
    perf_event      4       1       1
    net_prio        6       1       1
    hugetlb 11      1       1
    pids    5       71      1
    

    3.2.cgroups规则

      在任务,控制族群,层级,子系统间有着一些规则:

    • 同一个hierarchy能够附加一个或多个subsystem
    • 一个subsystem只能附加到一个hierarchy上
    • 每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认cgroup(我们称之为root cgroup,此cgroup在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员
    • 一个任务可以是多个cgroup的成员,但是这些cgroup必须在不同的层级
    • 进程创建子进程时,该子进程自动成为其父进程所在的cgroup的成员.可根据需要将其移至其它cgroup中

    3.3.cgroups 的主要作用

      实现 cgroups 的主要目的是为不同用户层面的资源管理提供一个统一化的接口。从单个任务的资源控制到操作系统层面的虚拟化,cgroups 提供了四大功能:

    • 资源限制:cgroups 可以对任务是要的资源总额进行限制。比如设定任务运行时使用的内存上限,一旦超出就发 OOM。
    • 优先级分配:通过分配的 CPU 时间片数量和磁盘 IO 带宽,实际上就等同于控制了任务运行的优先级。
    • 资源统计:cgoups 可以统计系统的资源使用量,比如 CPU 使用时长、内存用量等。这个功能非常适合当前云端产品按使用量计费的方式。
    • 任务控制:cgroups 可以对任务执行挂起、恢复等操作。

    3.4.cgroups 的文件系统接口

      cgroups 以文件的方式提供应用接口,我们可以通过 mount 命令来查看 cgroups 默认的挂载点:

    $ mount | grep cgroup
    >tmpfs on /sys/fs/cgroup type tmpfs (rw,mode=755)
    cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
    cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
    cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children)
    cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event)
    cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids)
    cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
    cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
    cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
    cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
    cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
    cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb)
    
    • 第一行的 tmpfs 说明 /sys/fs/cgroup 目录下的文件都是存在于内存中的临时文件。
    • 第二行的挂载点 /sys/fs/cgroup/systemd 用于 systemd 系统对 cgroups 的支持。
    • 其余的挂载点则是内核支持的各个子系统的根级层级结构。

    Note: 在使用 systemd 系统的操作系统中,/sys/fs/cgroup 目录都是由 systemd 在系统启动的过程中挂载的,并且挂载为只读的类型。换句话说,系统是不建议在 /sys/fs/cgroup 目录下创建新的目录并挂载其它子系统的。

    4.如何查看当前进程属于哪些cgroup

      可以通过查看/proc/[pid]/cgroup知道指定进程属于哪些cgroup。

    dev@ubuntu:~$ cat /proc/777/cgroup
    11:cpuset:/
    10:freezer:/
    9:memory:/system.slice/cron.service
    8:blkio:/system.slice/cron.service
    7:perf_event:/
    6:net_cls,net_prio:/
    5:devices:/system.slice/cron.service
    4:hugetlb:/
    3:cpu,cpuacct:/system.slice/cron.service
    2:pids:/system.slice/cron.service
    1:name=systemd:/system.slice/cron.service
    

    每一行包含用冒号隔开的三列:

    • cgroup树的ID, 和/proc/cgroups文件中的ID一一对应。

    • 和cgroup树绑定的所有subsystem,多个subsystem之间用逗号隔开。这里name=systemd表示没有和任何subsystem绑定,只是给他起了个名字叫systemd。

    • 进程在cgroup树中的路径,即进程所属的cgroup,这个路径是相对于挂载点的相对路径。

    以 memory 子系统为例

    xxx:/sys/fs/cgroup/memory$ ls
    cgroup.clone_children           memory.kmem.tcp.failcnt             memory.soft_limit_in_bytes
    cgroup.event_control            memory.kmem.tcp.limit_in_bytes      memory.stat
    cgroup.procs                    memory.kmem.tcp.max_usage_in_bytes  memory.swappiness
    cgroup.sane_behavior            memory.kmem.tcp.usage_in_bytes      memory.usage_in_bytes
    init.scope                      memory.kmem.usage_in_bytes          memory.use_hierarchy
    memory.failcnt                  memory.limit_in_bytes               notify_on_release
    memory.force_empty              memory.max_usage_in_bytes           release_agent
    memory.kmem.failcnt             memory.move_charge_at_immigrate     system.slice
    memory.kmem.limit_in_bytes      memory.numa_stat                    tasks
    memory.kmem.max_usage_in_bytes  memory.oom_control                  user.slice
    memory.kmem.slabinfo            memory.pressure_level
    

    Brief summary of control files.

    
     tasks				 # attach a task(thread) and show list of threads
     cgroup.procs			 # show list of processes
     cgroup.event_control		 # an interface for event_fd()
     memory.usage_in_bytes		 # show current usage for memory
    				 (See 5.5 for details)
     memory.memsw.usage_in_bytes	 # show current usage for memory+Swap
    				 (See 5.5 for details)
     memory.limit_in_bytes		 # set/show limit of memory usage
     memory.memsw.limit_in_bytes	 # set/show limit of memory+Swap usage
     memory.failcnt			 # show the number of memory usage hits limits
     memory.memsw.failcnt		 # show the number of memory+Swap hits limits
     memory.max_usage_in_bytes	 # show max memory usage recorded
     memory.memsw.max_usage_in_bytes # show max memory+Swap usage recorded
     memory.soft_limit_in_bytes	 # set/show soft limit of memory usage
     memory.stat			 # show various statistics
     memory.use_hierarchy		 # set/show hierarchical account enabled
     memory.force_empty		 # trigger forced page reclaim
     memory.pressure_level		 # set memory pressure notifications
     memory.swappiness		 # set/show swappiness parameter of vmscan
    				 (See sysctl's vm.swappiness)
     memory.move_charge_at_immigrate # set/show controls of moving charges
     memory.oom_control		 # set/show oom controls.
     memory.numa_stat		 # show the number of memory usage per numa node
    
     memory.kmem.limit_in_bytes      # set/show hard limit for kernel memory
     memory.kmem.usage_in_bytes      # show current kernel memory allocation
     memory.kmem.failcnt             # show the number of kernel memory usage hits limits
     memory.kmem.max_usage_in_bytes  # show max kernel memory usage recorded
    
     memory.kmem.tcp.limit_in_bytes  # set/show hard limit for tcp buf memory
     memory.kmem.tcp.usage_in_bytes  # show current tcp buf memory allocation
     memory.kmem.tcp.failcnt            # show the number of tcp buf memory usage hits limits
     memory.kmem.tcp.max_usage_in_bytes # show max tcp buf memory usage recorded
    
    

    refer to

    • https://lwn.net/Articles/604609/
    • https://www.cnblogs.com/sparkdev/p/8296063.html
    • Documentation/admin-guide/cgroup-v2.rst
    • https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/cgroup-v1/cgroups.txt?h=v4.18-rc1
    • https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
    • https://man7.org/linux/man-pages/man7/cgroups.7.html
    展开全文
  • cgroup

    2021-07-15 18:02:42
    cgroup介绍 cgroup是control group的简称,它为Linux内核提供了一种任务聚集和划分的机制,通过一组参数集合将一些任务组织成一个或多个子系统。 Cgroups是control groups的缩写,最初由Google工程师提出,后来编进...
  • CGroup V11. CGroup 概念Task: 任务,也就是进程,但这里的进程和我们通常意义上的 OS 进程有些区别,在后面会提到。CGroup: 控制组,一个 CGroup 就是一组按照某种标准划分的Tasks。这里的标准就是 Subsystem 配置...
  • 最近因为项目原因,发现对于cgroup的知识严重匮乏,所以恶补了一下cgroup的相关知识。cgroup指对进程进行分组,然后控制让他们的cpu,io以及memory的使用,和系统的性能息息相关。一、首先是cgroup的整体框架图:...
  • Linux CGroup 基础

    2021-05-17 11:13:37
    CGroup V11. CGroup 概念Task: 任务,也就是进程,但这里的进程和我们通常意义上的 OS 进程有些区别,在后面会提到。CGroup: 控制组,一个 CGroup 就是一组按照某种标准划分的Tasks。这里的标准就是 Subsystem 配置...
  • 一个任务可以加入某个 cgroup,也可以从某个 cgroup 迁移到另外一个 cgroup subsystem(子系统) cgroups 中的 subsystem 就是一个资源调度控制器(Resource Controller)。比如 CPU 子系统可以控制 CPU 时间分配,...
  • CGroup 全称是 Control Group,顾名思义,它是用来做“控制”的。控制什么东西呢?当然是资源的使用了。那它都能控制哪些资源的使用呢?本章主要进行总结,主要涉及到以下方面 what:什么是cgroup以及cgroup的内核...
  • Cgroup框架的实现

    千次阅读 2021-05-17 22:13:43
    CGoup核心主要创建一系列sysfs文件,用户空间可以通过这些节点控制CGroup各子系统行为,以及各子系统模块根据参数。在执行过程中或调度进程到不同CPU上,或控制CPU占用时间,或控制IO带宽等等。另外,在每个系统的...
  • 获取task关联的cgroup 获取cgroup中的task
  • cgroup 原理分析

    2021-04-29 14:09:52
    cgroup 相关概念解释 Cgroups提供了以下功能: 限制进程组可以使用的资源(Resource limiting ):比如memory子系统可以为进程组设定一个memory使用上限,进程组使用的内存达到限额再申请内存,就会出发OOM(out ...
  • Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。防止进程间不利的资源抢占。 一般在较大的公司中会经常用到,例如多个...
  • docker 使用cgroup控制资源,K8S 里面也有 limit (使用上限) Docker通过 Cgroup 来控制容器使用的资源配额,包括 CPU、内存、磁盘三大方面, 基本覆盖了常见的资源配额和使用量控制。 Cgroup 是 Control Groups 的...
  • ss, css, css_set, cgroup, cgroup hierarchy的简单定义描述及他们的关系梳理
  • 《容器三把斧之 | cgroup原理与实现》 《容器三把斧之 | namespace原理与实现》 目录 容器三把斧之 | cgroup原理与实现 cgroup结构体 cgroup_subsys_state结构体 css_set结构体 cgroup_subsys结构 CGroup的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 66,311
精华内容 26,524
关键字:

cgroup