• YARN中内存和CPU两种资源的调度和隔离实现详解


    Hadoop Yarn的资源隔离是指为运行着不同任务的“Container”提供可独立使用的计算资源,以避免它们之间相互干扰。目前支持两种类型的资源隔离:CPU和内存,对于这两种类型的资源,Yarn使用了不同的资源隔离方案。
    对于CPU而言,它是一种“弹性”资源,使用量大小不会直接影响到应用程序的存亡,因此CPU的资源隔离方案采用了Linux Kernel提供的轻量级资源隔离技术Cgroup;对于内存而言,它是一种“限制性”资源,使用量大小直接决定着应用程序的存亡,Cgroup会严格限制应用程序的内存使用上限,一旦使用量超过预先定义的上限值,就会将该应用程序“杀死”,因此无法使用Cgroup进行内存资源隔离,而是选择了线程监控的方式。
    需要解释一下:为什么应用程序的内存会超过预先定义的上限值?Java程序(Container)为什么需要内存资源隔离?
    (1)为什么应用程序的内存会超过预先定义的上限值?
    这里的应用程序特指Yarn Container,它是Yarn NodeManager通过创建子进程的方式启动的;Java创建子进程时采用了“fork() + exec()”的方案,子进程启动瞬间,它的内存使用量与父进程是一致的,然后子进程的内存会恢复正常;也就是说,Container(子进程)的创建过程中可能会出现内存使用量超过预先定义的上限值的情况(取决于父进程,也就是NodeManager的内存使用量);此时,如果使用Cgroup进行内存资源隔离,这个Container就可能会被“kill”。
    (2)Java程序(Container)为什么需要内存资源隔离?
    对于MapReduce而言,各个任务被运行在独立的Java虚拟机中,内存使用量可以通过“-Xms、-Xmx”进行设置,从而达到内存资源隔离的目的。然而,Yarn考虑到用户应用程序可能会创建子进程的情况,如Hadoop Pipes(或者Hadoop Streaming),编写的MapReduce应用程序中每个任务(Map Task、Reduce Task)至少由Java进程和C++进程两个进程组成,这难以通过创建单独的虚拟机达到资源隔离的效果,因此,即使是通过Java语言实现的Container仍需要使用内存资源隔离。
    Yarn Container支持两种实现:DefaultContainerExecutor和LinuxContainerExecutor;其中DefaultContainerExecutor不支持CPU的资源隔离,LinuxContainerExecutor使用Cgroup的方式支持CPU的资源隔离,两者内存的资源隔离都是通过“线程监控”的方式实现的。
    基于线程监控的内存隔离方案
    1.配置参数
    (1)应用程序配置参数
    不同的应用程序对内存的需求不同,可以根据具体情况定义自己的参数,以MapReduce为例:
    mapreduce.map.memory.mb:MapReduce Map Task需要使用的内存量(单位:MB);
    mapreduce.reduce.memory.mb:MapReduce Reduce Task需要使用的内存量(单位:MB);
    (2)Hadoop Yarn NodeManager配置参数
    yarn.nodemanager.pmem-check-enabled:NodeManager是否启用物理内存量监控,默认值:true;
    yarn.nodemanager.vmem-check-enabled:NodeManager是否启用虚拟内存量监控,默认值:true;
    yarn.nodemanager.vmem-pmem-ratio:NodeManager Node虚拟内存与物理内存的使用比例,默认值2.1,表示每使用1MB物理内存,最多可以使用2.1MB虚拟内存;
    yarn.nodemanager.resource.memory-mb:NodeManager Node最多可以使用多少物理内存(单位:MB),默认值:8192,即8GB;
    2.实现原理
    Yarn NodeManager Container的内存监控是由ContainersMonitorImpl(org.apache.hadoop.yarn.server.nodemanager.containermanager.monitor.ContainersMonitorImpl)实现的,内部的MonitoringThread线程每隔一段时间就会扫描所有正在运行的Container进程,并按照以下步骤检查它们的内存使用量是否超过其上限值。
    2.1构造进程树
    如前所述,Container进程可能会创建子进程(可能会创建多个子进程,这些子进程可能也会创建子进程),因此Container进程的内存(物理内存、虚拟内存)使用量应该表示为:以Container进程为根的进程树中所有进程的内存(物理内存、虚拟内存)使用总量。
    在Linux /proc目录下,有大量以整数命名的目录,这些整数是某个正在运行的进程的PID,而目录/proc/<PID>下面的那些文件分别表示着进程运行时的各方面信息,这里我们只关心/proc/<PID>/stat文件即可。
    文件/proc/<PID>/stat仅仅包含一行(多列)文本,可以通过正则表达式从中抽取进程的运行时信息,包括:进程名称、父进程PID、父进程用户组ID、Session ID、用户态运行的时间(单位:jiffies)、核心态运行的时间(单位:jiffies)、占用虚拟内存大小(单位:page)和占用物理内存大小(单位:page)等。
    ContainersMonitorImpl内部维护着每个Container进程的PID,通过遍历/proc下各个进程的stat文件内容(父进程PID、占用虚拟内存大小和占用物理内存大小),我们可以构建出每个Container的进程树,从而得出每个进程树的虚拟内存、物理内存使用总量。
    2.2判断Container进程树的内存使用量(物理内存、虚拟内存)是否超过上限值
    虽然我们已经可以获得各个Container进程树的内存(物理内存、虚拟内存)使用量,但是我们不能仅凭进程树的内存使用量(物理内存、虚拟内存)是否超过上限值就决定是否“杀死”一个Container,因为“子进程”的内存使用量是有“波动”的,为了避免“误杀”的情况出现,Hadoop赋予每个进程“年龄”属性,并规定刚启动进程的年龄是1,MonitoringThread线程每更新一次,各个进程的年龄加一,在此基础上,选择被“杀死”的Container的标准如下:如果一个Contaier对应的进程树中所有进程(年龄大于0)总内存(物理内存或虚拟内存)使用量超过上限值的两倍;或者所有年龄大于1的进程总内存(物理内存或虚拟内存)使用量超过上限值,则认为该Container使用内存超量,可以被“杀死”。(注意:这里的Container泛指Container进程树)
    综上所述,Yarn的内存资源隔离实际是内存使用量监控。
    3.源码分析
    3.1MonitoringThread
    线程监控的核心工作主要是由MonitoringThread(org.apache.hadoop.yarn.server.nodemanager.containermanager.monitor.ContainersMonitorImpl.MonitoringThread)完成的,内部就是一个“while”循环,以指定的时间间隔进行监控:
    其中,时间间隔monitoringInterval由参数yarn.nodemanager.container-monitor.interval-ms指定,默认值:3000,单位:ms。
    下面介绍“while”循环的处理逻辑。
    3.2 将新启动的Container加入监控列表以及将已完成的Container移出监控列表;
    每次监控开始之前都需要更新监控列表:trackingContainers,将新启动的Container加入监控列表,由containersToBeAdded表示;将已完成的Container移出监控列表,由containersToBeRemoved表示。
    containersToBeAdded和containersToBeRemoved都是通过“事件”由org.apache.hadoop.yarn.server.nodemanager.containermanager.monitor.ContainersMonitorImpl.handle负责更新的,如下:
    对于事件START_MONITORING_CONTAINER,它表示有新的Container进程,为其构建一个ProcessTreeInfo实例,用于保存Container的进程树信息,也就是说,这里考虑的不仅仅是Container进程,而是以Container进程为父进程的整个进程树,构造函数参数含义依次如下:
    containerId:Container ID;
    pid:Container进程的PID;
    pTree:Container进程树内存使用量计算器实例,不同的Hadoop运行平台(Windows、Linux)因为统计内存使用量的方式不同,因此需要不同的计算器实例;通过该计算器实例,可以获得当前Container进程树的内存使用量;
    vmemLimit:Container进程树可使用的虚拟内存上限值;
    pmemLimit:Container进程树可使用的物理内存上限值;
    注意:pid、pTree的初始值为Null。
    更新监控列表trackingContainers之后,下一步就是对监控列表中的Container进程树的内存使用量进行监控。
    3.3遍历监控列表trackingContainers,逐个处理其中的进程树;
    可以看出,监控列表trackingContainers中的每一个进程树元素是由ContainerId和ProcessTreeInfo共同表示的。
    下面介绍单独一个进程树的内存监控过程。
    3.4初始化进程树信息ProcessTreeInfo;
    如3.2所述,进程树监控列表trackingContainers是被不断更新的,而新加入监控的Container进程树信息是由ProcessTreeInfo表示的,
    其中pid、pTree的初始值为Null,因此监控过程中如果发现进程树信息ProcessTreeInfo的pid、pTree为Null,要对其进行初始化。
    (1)获取进程树元素,由containerId和ptInfo表示;
    (2)判断如果ptInfo(进程树信息)中的pId(Container进程的PID)为null,则表示需要初始化ptInfo;
    (3)获取ProcessTreeInfo pid,将其保存至pId;
    Container进程PID(pid)可以通过ContainerId(ptInfo.getContainerId())从ContainerExecutor(containerExecutor)中获取;如果获取不到相应的PID,可能是因为Container进程尚没有被启动或者ContainerExecutor已将其移除,也意味着此进程树无需监控。
    (4)获取ProcessTreeInfo pTree,将其保存至pt;
    这里需要介绍一下ResourceCalculatorProcessTree(org.apache.hadoop.yarn.util.ResourceCalculatorProcessTree)的作用。
    每一次对ProcessTreeInfo进行监控时,我们都必须获取该进程树内所有进程的运行状态(这里我们仅关心物理、虚拟内存使用情况等),也就是说,我们需要一个“计算器”,能够将进程树内所有进程的运行状态计算出来,ResourceCalculatorProcessTree就是用来充当“计算器”角色的,如下注释所示:
    ResourceCalculatorProcessTree是一个抽象类,也就意味着它可以有多种实现,具体选取哪一种实现取决于ResourceCalculatorProcessTree.getResourceCalculatorProcessTree:
    其中,processTreeClass由参数yarn.nodemanager.container-monitor.process-tree.class指定,默认值为null。
    因为传入的参数clazz值为null,所以我们仅仅关注上图红色箭头所指的逻辑即可。
    ProcfsBasedProcessTree和WindowsBasedProcessTree分别对应着ResourceCalculatorProcessTree在Linux平台和Windows平台的实现,通常我们关注ProcfsBasedProcessTree即可,也就是说,Linux平台下pTree的实例类型为ProcfsBasedProcessTree。
    (5)将pId、pt更新至ptInfo,初始化过程完成;
    3.5根据ResourceCalculatorProcessTree(ProcfsBasedProcessTree)更新进程树的运行状态(这里仅关注物理、虚拟内存),并获取相关的监控信息;
    (1)获取当前进程树的ResourceCalculatorProcessTree实例pTree,并更新其内部状态updateProcessTree(),实际就是更新进程树中的进程信息(详细处理逻辑见后);
    (2)获取当前进程树中所有进程的虚拟内存使用总量(currentVmemUsage)、物理内存使用总量(currentPmemUsage);
    (3)获取当前进程树中所有年龄大于1的进程的虚拟内存使用总量(curMemUsageOfAgedProcesses)、物理内存使用总是(curRssMemUsageOfAgedProcesses);
    (4)获取当前进程树的虚拟内存使用总量上限值(vmemLimit)、物理内存使用总量上限值(pmemLimit);
    3.6判断进程树的内存使用量是否超过上限值,虚拟内存与物理内存需要分别处理;
    isMemoryOverLimit的值用于表示进程树的内存使用量是否超过上限值,值为true表示超量(虚拟内存或物理内存两者至少有其一超量);值为false表示未超量(虚拟内存和物理内存两者均未超量);初始值设置为false。
    (1)如果开启虚拟内存监控,则判断进程树虚拟内存使用总量是否超过其上限值;
    (2)如果开启物理内存监控,则判断进程树物理内存使用总量是否超过其上限值;
    虚拟、物理内存监控选项的开启分别由参数yarn.nodemanager.vmem-check-enabled、yarn.nodemanager.pmem-check-enabled指定,默认值均为true,表示两者均开启监控。
    判断虚拟、物理内存使用总量是否超过上限值由isProcessTreeOverLimit()(详细处理逻辑见后)统一处理,两者仅传入的参数值不同,参考上图代码。
    3.7如果isMemoryOverLimit值为true,则表示进程树的内存使用量超量(或者虚拟内存、或者物理内存),执行“kill”并从监控列表移除;
    至此,进程树内存使用总量监控处理逻辑完成。
    3.8ResourceCalculatorProcessTree(ProcfsBasedProcessTree) updateProcessTree
    updateProcessTree用于更新当前Container进程的进程树:
    (1)获取所有的进程列表;
    其中,procfsDir的值为/proc/,numberPattern表示的正则表达式为[1-9][0-9]*(用于匹配进程PID)。对于Linux系统而言,所以运行着的进程都对应着目录“/proc/”下的一个子目录,子目录名称即为进程PID,子目录中包含着进程的运行时信息。所谓的进程列表,实际就是Linux目录“/proc/”下的这些进程子目录名称。
    进程列表processList包含的信息:1、10、100、...。
    (2)更新进程树processTree;
    因为Container进程树中的进程随时都可能启动或停止,因此每次监控开始之前都需要更新该Container进程的进程树;而且为了方便处理进程的年龄(加一),将该Container进程“旧”的进程树processTree缓存至oldProcs,然后清空processTree(详情见后)。
    (3)遍历(1)中进程列表,为每一个进程构建ProcessInfo,并将其保存至allProcessInfo;
    ProcessInfo的构建过程由方法constructProcessInfo()完成,处理逻辑很简单:
    a.读取“procfsDir/<pid>/stat”(即“/proc/<pid>/stat”)的文件内容,实际内容只有一行;
    b.通过正则表达式抽取其中的信息,并更新至pInfo;
    可以看出,ProcessInfo保存着一个进程的以下信息:
    name:进程名称;
    ppid:父进程PID;
    pgrpId:父进程所属用户组ID;
    session:进程所属会话组ID;
    utime:进程用户态占用时间;
    stime:进程内核态占用时间;
    vsize:进程虚拟内存使用量;
    rss:进程物理内存使用量;
    遍历构建的过程中,如果发现“我”进程(即当前的Container进程),则将“我”保存至进程树processTree,因为当前的Container进程必须是此Container进程树中的一员;如果没有发现“我”进程,则表示Container进程(树)已经运行结束,无需监控。
    (4)维护进程之间的父子关系;
    allProcessInfo中保存着所有的进程信息,其中key为PID,value为对应的ProcessInfo,我们通过ProcessInfo的ppid(父进程PID),即可以维护出这些进程之间的父子关系。
    对于每一个ProcessInfo(进程)pInfo:
    a.根据pInfo ppid找出其父进程的ProcessInfo:parentPInfo;
    b.将pInfo加入parentPInfo的子进程列表中(ProcessInfo addChild);
    (5)构建当前Container进程(即(3)中的me)的进程树;
    a.将pInfoQueue初始化为me;
    b.如果pInfoQueue不为空,执行以下操作:
    b1.取出pInfoQueue的头元素pInfo,将其加入进程树processTree(注意重复检测);
    b2.将pInfo的所有子进程加入pInfoQueue;
    c.执行b;
    上述流程执行完毕之后,processTree中保存着当前Container进程的进程树。
    (6)更新当前Container进程的进程树中所有进程的年龄;
    处理逻辑很简单:遍历进程树,对于其中的每一个ProcessInfo,如果它是一个“老”进程(即出现在“老”进程树oldInfo中),则将其年龄加一。(注:ProcessInfo age初始值为一)
    到此,进程树更新完毕。
    我们以虚拟内存为例说明进程树的虚拟内存使用总量是如何计算的,如下:
    其实就是根据进程年龄做过滤,然后叠加ProcessInfo中的相关值(虚拟内存:vmem)。
    3.9ContainersMonitorImpl.isProcessTreeOverLimit
    isProcessTreeOverLimit用于判断内存使用量是否超过上限值,虚拟内存和物理内存共用此方法。
    currentMemUsage:进程树中所有进程的虚拟或物理内存使用总量;
    curMemUsageOfAgedProcesses:进程树中所有年龄大于1的进程的虚拟或物理内存使用总量;
    vmemLimit:进程树虚拟或物理内存使用上限;
    满足以下二个条件之一,则认为进程树内存使用超过上限:
    (1)currentMemUsage大于vmemLimit的两倍,这样做的目录主要是为了防止误判(见本文开篇所述);
    (2)curMemUsageOfAgedProcesses大于vmemLimit(年龄大于一的进程可以认内存使用比较“稳定”);

    至此,Hadoop Yarn基于线程监控的内存隔离方案介绍完毕


    yarn默认只管理内存资源,虽然也可以申请cpu资源,但是在没有cpu资源隔离的情况下效果并不是太好.在集群规模大,任务多时资源竞争的问题尤为严重.
    还好yarn提供的LinuxContainerExecutor可以通过cgroup来隔离cpu资源

    cgroup

    cgroup是系统提供的资源隔离功能,可以隔离系统的多种类型的资源,yarn只用来隔离cpu资源

    安装cgroup

    默认系统已经安装了cgroup了,如果没有安装可以通过命令安装
    CentOS 6

    yum install -y libcgroup

    CentOS 7

    yum install -y libcgroup-tools

    然后通过命令启动
    CentOS 6

    /etc/init.d/cgconfig start

    CentOS 7

    systemctl start cgconfig.service

    查看/cgroup目录,可以看到里面已经创建了一些目录,这些目录就是可以隔离的资源

    drwxr-xr-x 2 root root 0 3月  19 20:56 blkio
    drwxr-xr-x 3 root root 0 3月  19 20:56 cpu
    drwxr-xr-x 2 root root 0 3月  19 20:56 cpuacct
    drwxr-xr-x 2 root root 0 3月  19 20:56 cpuset
    drwxr-xr-x 2 root root 0 3月  19 20:56 devices
    drwxr-xr-x 2 root root 0 3月  19 20:56 freezer
    drwxr-xr-x 2 root root 0 3月  19 20:56 memory
    drwxr-xr-x 2 root root 0 3月  19 20:56 net_cls

    如果目录没有创建可以执行

    cd /
    mkdir cgroup
    mount -t tmpfs cgroup_root ./cgroup
    mkdir cgroup/cpuset
    mount -t cgroup -ocpuset cpuset ./cgroup/cpuset/
    mkdir cgroup/cpu
    mount -t cgroup -ocpu cpu ./cgroup/cpu/
    mkdir cgroup/memory
    mount -t cgroup -omemory memory ./cgroup/memory/

    通过cgroup隔离cpu资源的步骤为

    1. 在cpu目录创建分组
      cgroup以组为单位隔离资源,同一个组可以使用的资源相同
      一个组在cgroup里面体现为一个文件夹,创建分组直接使用mkdir命令即可.
      组下面还可以创建下级组.最终可以形成一个树形结构来完成复杂的资源隔离方案.
      每当创建了一个组,系统会自动在目录立即创建一些文件,资源控制主要就是通过配置这些文件来完成
      --w--w--w- 1 root root 0 3月  19 21:09 cgroup.event_control
      -rw-r--r-- 1 root root 0 3月  19 21:09 cgroup.procs
      -rw-r--r-- 1 root root 0 3月  19 21:09 cpu.cfs_period_us
      -rw-r--r-- 1 root root 0 3月  19 21:09 cpu.cfs_quota_us
      -rw-r--r-- 1 root root 0 3月  19 21:09 cpu.rt_period_us
      -rw-r--r-- 1 root root 0 3月  19 21:09 cpu.rt_runtime_us
      -rw-r--r-- 1 root root 0 3月  19 21:09 cpu.shares
      -r--r--r-- 1 root root 0 3月  19 21:09 cpu.stat
      -rw-r--r-- 1 root root 0 3月  19 21:09 notify_on_release
      -rw-r--r-- 1 root root 0 3月  19 21:09 tasks
      yarn默认使用hadoop-yarn组作为最上层,任务运行时yarn会为每个container在hadoop-yarn里面创建一个组
      yarn主要使用cpu.cfs_quota_us cpu.cfs_period_us cpu.shares3个文件
      yarn使用cgroup的两种方式来控制cpu资源分配
      1. 严格按核数隔离资源
        可使用核数 = cpu.cfs_quota_us/cpu.cfs_period_us
        在yarn中cpu.cfs_quota_us被直接设置为1000000(这个参数可以设置的最大值)
        然后根据任务申请的core来计算出cpu.cfs_period_us
      2. 按比例隔离资源
        按每个分组里面cpu.shares的比率来分配cpu
        比如A B C三个分组,cpu.shares分别设置为1024 1024 2048,那么他们可以使用的cpu比率为1:1:2
    2. 将进程id添加到指定组的tasks文件
      创建完分组后只需要将要限制的进程的id写入tasks文件即可,如果需要解除限制,在tasks文件删除即可

    yarn配置

    启动cgroup需要配置几个配置文件

    etc/hadoop/yarn-site.xml配置

    可以参考http://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/NodeManagerCgroups.html配置
    这些配置大部分都是固定配置

    <property>
        <name>yarn.nodemanager.container-executor.class</name>
      <value>org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor</value>
    </property>
    <property>
        <name>yarn.nodemanager.linux-container-executor.resources-handler.class</name>
        <value>org.apache.hadoop.yarn.server.nodemanager.util.CgroupsLCEResourcesHandler</value>
    </property>
    <property>
        <description>yarn使用的cgroup组,默认为/hadoop-yarn</description>
        <name>yarn.nodemanager.linux-container-executor.cgroups.hierarchy</name>
        <value>/hadoop-yarn</value>
    </property>
    <property>
        <description>是否自动挂载cgroup</description>
        <name>yarn.nodemanager.linux-container-executor.cgroups.mount</name>
        <value>true</value>
    </property>
    <property>
        <description>cgroup挂载目录, /sys/fs/cgroup 或者是 /cgroup,目录和系统有关</description>
        <name>yarn.nodemanager.linux-container-executor.cgroups.mount-path</name>
        <value>/cgroup</value>
    </property>
    <property>
        <name>yarn.nodemanager.linux-container-executor.group</name>
        <value>hadoop</value>
    </property>
    <property>
        <description>配置nodemanager使用多少物理cpu资源,比如24核服务器配置90的话,最近使用21.6核</description>
        <name>yarn.nodemanager.resource.percentage-physical-cpu-limit</name>
        <value>90</value>
    </property>
    <property>
        <description>是控制是否严格限制cpu,即按任务申请的core限制,还是非严格限制,即按core的比率限制</description>
        <name>yarn.nodemanager.linux-container-executor.cgroups.strict-resource-usage</name>
        <value>true</value>
    </property>
    <property>
        <description>非安全模式将会以这里设置的用户运行container,比如配置hadoop用户则以hadoop运行container</description>
        <name>yarn.nodemanager.linux-container-executor.nonsecure-mode.local-user</name>
        <value>hadoop</value>
    </property>

    etc/hadoop/container-executor.cfg配置

    这个配置文件每项都需要填,要不然会报错

    yarn.nodemanager.linux-container-executor.group=hadoop
    banned.users=root
    min.user.id=1000
    allowed.system.users=hadoop

    权限设置

    在配置中文件的权限有特殊要求

    chown root:hadoop bin/container-executor
    chmod 6050 bin/container-executor

    系统还要求etc/hadoop/container-executor.cfg 的所有父目录(一直到/ 目录) owner 都为 root
    这个路径是默认${HADOOP_HOME}/etc/hadoop/container-executor.cfg,如果不方便修改所有父级目录为root权限,可以重新编译代码到其他目录,比如/etc/hadoop/目录

    mvn clean package -Dcontainer-executor.conf.dir=/etc/hadoop/ -DskipTests -Pnative

    配置好以后检测是否配置成功

    ./bin/container-executor --checksetup

    如果没有任何输出表示配置成功
    如果一切顺利就可以启动集群了

    测试cgroup

    可以运行测试脚本测试系统

    ./bin/spark-submit   
    --class org.apache.spark.examples.SparkPi   
    --master yarn-cluster   
    --deploy-mode cluster   
    --num-executors 5 
    --executor-cores 3 
    --executor-memory 4G 
    --driver-memory 4G 
    --driver-cores 2 
    lib/spark-examples-1.6.0-hadoop2.6.0.jar   10000

    查看系统是否生效只能登录到服务器查看
    通过top查看信息



    查看是否创建了cgroup分组,ll /cgroup/hadoop-yarn/

    --w--w--w- 1 root root 0 3月  17 15:44 cgroup.event_control
    -rw-r--r-- 1 root root 0 3月  17 15:44 cgroup.procs
    drwxr-xr-x 2 root root 0 3月  17 16:06 container_1489736876249_0003_01_000011
    drwxr-xr-x 2 root root 0 3月  17 16:06 container_1489736876249_0003_01_000026
    drwxr-xr-x 2 root root 0 3月  17 16:06 container_1489736876249_0003_01_000051
    drwxr-xr-x 2 root root 0 3月  17 16:06 container_1489736876249_0003_01_000076
    drwxr-xr-x 2 root root 0 3月  17 16:06 container_1489736876249_0003_01_000101
    drwxr-xr-x 2 root root 0 3月  17 16:06 container_1489736876249_0003_01_000123
    drwxr-xr-x 2 root root 0 3月  17 16:06 container_1489736876249_0003_01_000136
    drwxr-xr-x 2 root root 0 3月  17 16:06 container_1489736876249_0003_01_000155
    drwxr-xr-x 2 root root 0 3月  17 16:30 container_1489736876249_0004_01_000008
    -rw-r--r-- 1 root root 0 3月  17 15:47 cpu.cfs_period_us
    -rw-r--r-- 1 root root 0 3月  17 15:47 cpu.cfs_quota_us
    -rw-r--r-- 1 root root 0 3月  17 15:44 cpu.rt_period_us
    -rw-r--r-- 1 root root 0 3月  17 15:44 cpu.rt_runtime_us
    -rw-r--r-- 1 root root 0 3月  17 15:44 cpu.shares
    -r--r--r-- 1 root root 0 3月  17 15:44 cpu.stat
    -rw-r--r-- 1 root root 0 3月  17 15:44 notify_on_release
    -rw-r--r-- 1 root root 0 3月  17 15:44 tasks

    查看container_*目录下 cpu.cfs_period_us,计算cpu.cfs_quota_us/cpu.cfs_period_us即可知道分配的核数

    [root@- ~]# cat /cgroup/cpu/hadoop-yarn/container*/cpu.cfs_period_us
    462962
    462962
    462962
    462962
    462962
    462962
    462962
    462962
    308641

    问题处理

    配置的过程中免不了会碰上一些问题,以下是我碰到的问题

    spark任务申请了core,node manager分配不正确,都是分配1个核

    这个是由于目前使用的capacity scheduler的资源计算方式只考虑了内存,没有考虑CPU
    这种方式会导致资源使用情况统计不准确,比如一个saprk程序启动命令资源参数如下

    --num-executors 1 --executor-cores 3 --executor-memory 4G --driver-memory 4G --driver-cores 1

    DefaultResourceCalculator 统计占2核
    DominantResourceCalculator 统计占4核
    修改配置文件即可解决

      <property>
        <name>yarn.scheduler.capacity.resource-calculator</name>
        <value>org.apache.hadoop.yarn.util.resource.DominantResourceCalculator</value>
        <description>
          The ResourceCalculator implementation to be used to compare
          Resources in the scheduler.
          The default i.e. DefaultResourceCalculator only uses Memory while
          DominantResourceCalculator uses dominant-resource to compare
          multi-dimensional resources such as Memory, CPU etc.
        </description>
      </property>

    container-executor运行时报缺少GLIBC_2.14库

    container-executor: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by bin/container-executor)

    这个和系统版本有关,只能通过重新编译container-executor来解决

    mvn clean package -Dcontainer-executor.conf.dir=/etc/hadoop/ -DskipTests -Pnative

    centos 7系统container启动报错,不能写入/cgroup/cpu

    这个是yarn在centos 7下的一个bug,hadoop 2.8以后的版本才会解决
    这个bug主要是因为centos 7下cgroup的目录和centos 6不一致导致,centos 7 cpu目录合并成cpu,cpuacct, 这个,导致的错误,需要打补丁后编译https://issues.apache.org/jira/browse/YARN-2194

     private String findControllerInMtab(String controller,
                                          Map<String, List<String>> entries) {
        for (Entry<String, List<String>> e : entries.entrySet()) {
    //      if (e.getValue().contains(controller))
    //        return e.getKey();
    
          if (e.getValue().contains(controller)) {
            String controllerKey = e.getKey();
            // In Redhat7, the controller is called "/sys/fs/cgroup/cpu,cpuacct"
            controllerKey = controllerKey.replace("cpu,cpuacct", "cpu");
            if (new File(controllerKey).exists()) {
              return controllerKey;
            }
          }
        }
    
        return null;
      }

    升级的风险

    由于改变了资源的隔离方式,升级可能有几个方面的影响

    任务资源分配问题

    升级cgroup后单个任务如果以前资源分配不合理可能会出现计算延时情况,出现资源问题时需要调整任务资源
    在集群规模小的时候可能没有资源可以调整,那么可以修改为非严格模式,非严格模式不能按配置限制资源,只能保证资源不被少数进程全部占用

    <property>
        <name>yarn.nodemanager.linux-container-executor.cgroups.strict-resource-usage</name>
        <value>false</value>
    </property

    spark driver资源问题

    spark任务的driver在集群模式deploy-mode cluster时,如果没有配置driver-cores的话默认分配1核,1核在任务规模大时有可能资源会紧张.采用deploy-mode client模式的不受cgroup限制


    正因为当初对未来做了太多的憧憬,所以对现在的自己尤其失望。生命中曾经有过的所有灿烂,终究都需要用寂寞来偿还。
  • 相关阅读:
    第一次团队作业
    第二次结对作业
    第一次结对作业
    制作简易的中文编译器
    第一次博客作业
    个人总结
    第三次个人作业
    第二次结对作业
    第一次结对作业
    第二次编程作业
  • 原文地址:https://www.cnblogs.com/candlia/p/11920128.html
Copyright © 2020-2023  润新知