• Linux Container Cgroup


    Cgroups是什么?

    Cgroups是control groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(如:cpu,memory,IO等等)的机制。最初由google的工程师提出,后来被整合进Linux内核。Cgroups也是LXC为实现虚拟化所使用的资源管理手段,可以说没有cgroups就没有LXC

    Cgroups可以做什么?

    Cgroups最初的目标是为资源管理提供的一个统一的框架,既整合现有的cpuset等子系统,也为未来开发新的子系统提供接口。现在的cgroups适用于多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化(OS Level Virtualization)。Cgroups提供了一下功能:

    1.限制进程组可以使用的资源数量(Resource limiting )。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOMout of memory)。

    2.进程组的优先级控制(Prioritization )。比如:可以使用cpu子系统为某个进程组分配特定cpu share

    3.记录进程组使用的资源数量(Accounting )。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间

    4.进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。

    5.进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。

    Cgroups相关概念及其关系

    相关概念

    1.任务(task)。在cgroups中,任务就是系统的一个进程。

    2.控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配的资源,同时受到cgroups以控制族群为单位设定的限制。

    3.层级(hierarchy)。控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。

    4.子系统(subsytem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。

    相互关系

    1.每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup ,此cgroup在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员。

    2.一个子系统最多只能附加到一个层级。

    3.一个层级可以附加多个子系统

    4.一个任务可以是多个cgroup的成员,但是这些cgroup必须在不同的层级。

    5.系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务cgroup

    Cgroups子系统介绍

    blkio -- 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等)。

    cpu -- 这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问。

    cpuacct -- 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。

    cpuset -- 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。

    devices -- 这个子系统可允许或者拒绝 cgroup 中的任务访问设备。

    freezer -- 这个子系统挂起或者恢复 cgroup 中的任务。

    memory -- 这个子系统设定 cgroup 中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。

    net_cls -- 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。

    ns -- 名称空间子系统。

    PS:Linux cgroups详解系列是本人毕业论文部分章节的草稿,算是为论文打前站,欢迎方家斧正。

    文章未经本人允许,禁止以任何形式复制,转载;使用在其他任何未经本人允许的地方。若要转载请留言联系。


    在CentOS上安装Linux Container

    1. 准备工作

    先把Linux Container依赖的一些软件包给装上,并启动相关的服务。其中git将在第二步中用来下载源代码。
    [plain] view plain copy
    1. yum install -y automake gcc libvirt libcgroup git  
    2. service cgconfig start  
    3. service libvirtd start   

    2. 从github中下载Linux Container的代码

    [plain] view plain copy
    1. git clone https://github.com/jewes/lxc.git  

    上面的git地址是笔者从官方源代码中fork出来的,对centos的模板做了些小修改,使得创建CentOS的Linux Container更加简单。

    3. 编译

    [plain] view plain copy
    1. ./autogen.sh && ./configure && make && sudo make install  

    4. 设置环境变量,可以考虑将其写到/etc/bashrc中去

    [plain] view plain copy
    1. export LD_LIBRARY_PATH=/usr/local/lib/:$LD_LIBRARY_PATH  

    经过以上几步后,Linux Container就安装到你的机器上了。下面将介绍如何使用Linux Container。

    使用Linux Container

    1. 创建Linux Container

    Linux Container自带了命令lxc-create来创建Container。不过,在构建Hadoop集群的时候,我们有如下需求:
    • 需要创建若干个Container,对应于集群中节点的数量
    • 每个Container要分配一个固定的IP地址
    • 每个Container要配置FQDN,也就是在运行hostname -f的时候,输出的内容要带有域名的,这在配置Kerberos的Hadoop集群中是必须的。
    • Container之间通过机器名能互相访问

    为此,笔者在lxc-create命令的基础上写了小脚本create_containers.sh(该脚本位于源代码的根目录下),使得能用一条命令就创建出我们需要数量的Container。
    使用方法举例:

    [plain] view plain copy
    1. # 需要root权限,因为lxc-create需要root权限  
    2. ./create_containers.sh -n 5 -s 111   

    上述命令将创建5个Container,Container的IP地址从192.168.122.111开始,相关信息如下:
    hostname/container name
    fqdn
    ip address
    lxc111 lxc111.example.com 192.168.122.111
    lxc112 lxc112.example.com 192.168.122.112
    lxc113 lxc113.example.com 192.168.122.113
    lxc114 lxc114.example.com 192.168.122.114
    lxc115 lxc115.example.com 192.168.122.115
    Container默认的root密码是welcome

    2. 常用的Linux Container的管理工具:

    lxc-start: 启动一个Linux Container
    [plain] view plain copy
    1. # -d 表示以daemon来启动该Container  
    2. lxc-start -n <container name> -d  

    lxc-info:显示一个Linux Container的信息
    [plain] view plain copy
    1. lxc-info -n <container name>  

    lxc-stop: 停止一个Container
    [plain] view plain copy
    1. lxc-stop -n <container name>  

    lxc-destroy: 销毁一个Linux Container
    [plain] view plain copy
    1. lxc-destroy -n <container name>  

    你也可以用ssh方式登入Container,这样就和使用一台普通Linux机器没有区别了,你可以在里面用yum来安装需要的软件,当然也可以部署Hadoop了。


    secure container

    对于配置的权限,还是会有要求,container-executor.cfg文件需要root权限,和其父目录等向上拥有者为root,所以,可以把改文件放置到/etc/目录下满足其要求,同时方便配置同步管理,然而,container-executor的默认读取路径是$HADOOP_HOME/etc/hadoop/目录下,所以我们需要传入指定路径去重新编译container-executor,该文件权限可以设置为6050。

    mvn package -Pdist,native -DskipTests -Dtar -Dcontainer-executor.conf.dir=/etc
    

    编译完成后,就可以检查是否成功了

    strings container-executor | grep etc
    

    关于Cgroup

    要开启该功能,在新机器中要确保安装开启和内核支持该功能

    在yarn配置了CGroup的模式下,它是通过cpu.cfs_period_us,cpu.cfs_quota_us,cpu.shares来对container进行CPU的资源控制。

    举个例子:

    假设我们设置yarn.nodemanager.resource.percentage-physical-cpu-limit参数为80和yarn.nodemanager.linux-container-executor.cgroups.strict-resource-usage参数为true(假设测试虚拟机的核数为4),在该模式下所有container能使用的核数为

    yarnProcessors = nodeCpuPercentage * numProcessors = 0.8 * 4 = 3.2
    

    CPU shares为

    cpuShares = CPU_DEFAULT_WEIGHT * containerVCores = 1024 * 1
    

    其中CPU_DEFAULT_WEIGHT=1024代表一个CPU时间

    即containers能使用的核数量为3.2个,然后通过公式:

    containerCPU = (containerVCores * yarnProcessors) / nodeVCores
    

    注:nodeVCore为配置NM的核数为4,containerVCores为应用为每个container申请的核数

    即按照上面的公式得到containerCPU为1*3.2除以4=0.8,然后根据quotaUS = periodUS * containerCPU,即1000*1000*0.8得到800000,即cpu.cfs_quota_us的值,即该进程使用的CPU不会超过80%,当应用申请的container核数为2时,得到shares为2048,意味着其将会获得比1024多2倍的CPU时间,由此类推。

    cgroup控制cpu,/proc/mounts下有一个/sys/fs/cgroup/cpu配置路径,此部分是cgroup用来控制cpu隔离的路径配置。
    在/sys/fs/cgroup/cpu下添加子路径hadoop-yarn,这个路径是yarn用来控制cpu的隔离的根路径
       通过yarn.nodemanager.linux-container-executor.cgroups.hierarchy配置。
    每一个container启动时根据container的名称在根路径下创建子路径来控制这个container的cpu资源

    yarn的cgroup控制:
      cgroups 里,可以用 cpu.cfs_period_us 和 cpu.cfs_quota_us 来限制该组中的所有进程在单位时间里可以使用的 cpu 时间。
      这里的 cfs 是完全公平调度器的缩写。
      cpu.cfs_period_us 就是时间周期(每个cpu core),默认为 1000000,即1秒。
      cpu.cfs_quota_us 就是在这期间内可使用的 cpu 时间,默认 -1,即无限制。
      cpu.cfs_quota_us 的值在单core的情况下小于或等于cpu.cfs_period_us的值,在多core的情况下大于这个值。

      配置项:yarn.nodemanager.resource.percentage-physical-cpu-limit,默认值100,表示不显示cpu资源。
        这个配置用于限制yarn对当前的nodemanager的物理内存的使用百分比,通过这个配置与cpu的逻辑core进行乘法,得到可用的cpu的core.

      如果配置有对cpu使用的阀值时,对yarn的根路径的cgroup的控制:
        按1000*1000微秒为cpu的调度周期除以cpu的可用的core的个数,
          得到yarn的根路径的cpu.cfs_period_us的值,这是每个core的cpu调度周期(最小调度周期为1000微秒)。
          cpu.cfs_period_us与cpu.cfs_quota_us值的计算:
            在yarn中按1000*1000微秒来划分一次cpu的时间片调度周期.
            =>如果可使用的cpu core的个数小于1 core,这个时候cpu.cfs_period_us的值必须大于cpu.cfs_quota_us的值
              cpu.cfs_period_us的值为1000*1000微秒
              cpu.cfs_quota_us的值为(1000*1000微秒)/可用的cpu core的个数,如果这个值小于1000,设置这个值为1000
            =>如果可使用的cpu core的个数大于或等于1 core时,
              cpu.cfs_period_us的值是一个小于或等于cpu.cfs_quota_us的值,由(1000*1000微秒)/可用的cpu core的个数得到
              cpu.cfs_quota_us的值为1000*1000微秒
              如果计算出来cpu.cfs_period_us的值小于1000微秒时,设置cpu.cfs_period_us的值为1000,同时设置cpu.cfs_quota_us的值为-1
            =>最后说下:
              如果cpu core是10时,那么每个cpu core的period_us的周期为100ms,quota_us是1s,
                也就是说这个任务可以在每个cpu core中跑100ms的时间周期

      
      yarn中cgroup针对每个container的cpu控制:
        cpu.shares=>用于分配cpu执行的权重,按1024*container的vcore的个数来得到cpu的执行权重
                    如:一个container分配2core,一个conainer分配3core,那么他们的执行权重为2:3
        cpu.cfs_period_us与cpu.cfs_quota_us的值,默认使用根路径的值.
           =>如果yarn.nodemanager.linux-container-executor.cgroups.strict-resource-usage配置为true时,默认为false.
                 这个配置用于控制是否根据nodemanager配置的vcore来计算container分配的vcore的实际可使用cpu core.
             (实际可用的cpu core的个数)=计算(container分配的vcore的个数*可用的cpu的core的个数)/nm配置的vcore的个数
                 nm配置vcore的配置项:yarn.nodemanager.resource.cpu-vcores,默认值8(可根据实际情况来进行分配).
           
          
             cpu.cfs_period_us与cpu.cfs_quota_us的计算公式
               =>(实际可用的cpu core的个数)小于1 core:
                 cpu.cfs_period_us的值为1000*1000微秒
                 cpu.cfs_quota_us的值为(1000*1000微秒)/(实际可用的cpu core的个数),如果这个值小于1000,设置这个值为1000
               =>(实际可用的cpu core的个数)大于或等于1 core:
                 cpu.cfs_period_us的值是一个小于或等于cpu.cfs_quota_us的值,由(1000*1000微秒)/(实际可用的cpu core的个数)
                 cpu.cfs_quota_us的值为1000*1000微秒
                 =>如果计算出来cpu.cfs_period_us的值小于1000微秒时:
                  设置cpu.cfs_period_us的值为1000,同时设置cpu.cfs_quota_us的值为-1


    正因为当初对未来做了太多的憧憬,所以对现在的自己尤其失望。生命中曾经有过的所有灿烂,终究都需要用寂寞来偿还。
  • 相关阅读:
    【Spark 深入学习 02】- 我是一个凶残的spark
    【Spark 深入学习 01】 Spark是什么鬼?
    【Big Data
    【Netty】通俗地讲,Netty 能做什么?
    【Jetty】Jetty 的工作原理以及与 Tomcat 的比较
    【Tomcat】Tomcat 系统架构与设计模式,第 2 部分: 设计模式分析
    【Tomcat】Tomcat 系统架构与设计模式,第 1 部分: 工作原理
    【Tomcat】Servlet 工作原理解析
    【Tomcat】面向初级 Web 开发人员的 Tomcat
    【Big Data
  • 原文地址:https://www.cnblogs.com/candlia/p/11920252.html
Copyright © 2020-2023  润新知