Hadoop Yarn内存资源隔离实现原理-基于线程监控的内存隔离方案

Hadoop Yarn 的资源隔离是指为运行着不同任务的 “Container” 提供可独立使用的计算资源,以避免它们之间相互干扰。目前支持两种类型的资源隔离: CPU 和内存,对于这两种类型的资源, Yarn 使用了不同的资源隔离方案。

注:Hadoop Yarn内存资源隔离实现原理-基于线程监控的内存隔离方案,本文以 hadoop-2.5.0-cdh5.3.2 为例进行说明。

 

hadoop 的资源隔离是指为运行着不同任务的 “Container” 提供可独立使用的计算资源,以避免它们之间相互干扰。目前支持两种类型的资源隔离: CPU 和内存,对于这两种类型的资源, Yarn 使用了不同的资源隔离方案。

对于 CPU 而言,它是一种 “ 弹性 ” 资源,使用量大小不会直接影响到应用程序的存亡,因此 CPU 的资源隔离方案采用了 Linux Kernel 提供的轻量级资源隔离技术Cgroups ;对于内存而言,它是一种 “ 限制性 ” 资源,使用量大小直接决定着应用程序的存亡, Cgroups 会严格限制应用程序的内存使用上限,一旦使用量超过预先定义的上限值,就会将该应用程序 “ 杀死 ” ,因此无法使用 Cgroups 进行内存资源隔离,而是选择了线程监控的方式。

 

Hadoop

 

需要解释一下:为什么应用程序的内存会超过预先定义的上限值? Java程序(Container )为什么需要内存资源隔离?

( 1 )为什么应用程序的内存会超过预先定义的上限值?

这里的应用程序特指 Yarn Container ,它是 Yarn NodeManager 通过创建子进程的方式启动的; Java 创建子进程时采用了 “fork() + exec()” 的方案,子进程启动瞬间,它的内存使用量与父进程是一致的,然后子进程的内存会恢复正常;也就是说,Container (子进程)的创建过程中可能会出现内存使用量超过预先定义的上限值的情况(取决于父进程,也就是 NodeManager 的内存使用量);此时,如果使用 Cgroups进行内存资源隔离,这个 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 是否启用虚拟内存量监控,默认值: false ;

yarn.nodemanager.vmem-pmem-ratio : NodeManager Node 虚拟内存与物理内存的使用比例,默认值 2.1 ,表示每使用 1MB 物理内存,最多可以使用 2.1MB 虚拟内存;

yarn.nodemanager.resource.memory-mb : NodeManager Node 最多可以使用多少物理内存(单位: MB ),默认值: 8192 ,即 8GB ;

2. 实现原理

Yarn NodeManagerContainer 的内存监控是由 ContainersMonitorImpl (org.apache.hadoop.yarn.server.nodemanager.containermanager.monitor.ContainersMonitorImpl )实现的,内部的 MonitoringThread 线程每隔一段时间就会扫描所有正在运行的 Container 进程,并按照以下步骤检查它们的内存使用量是否超过其上限值。

2.1 构造进程树

如前所述, Container 进程可能会创建子进程(可能会创建多个子进程,这些子进程可能也会创建子进程),因此 Container 进程的内存(物理内存、虚拟内存)使用量应该表示为:以 Container 进程为根的进程树中所有进程的内存(物理内存、虚拟内存)使用总量。

在 Linux /proc 目录下,有大量以整数命名的目录,这些整数是某个正在运行的进程的 PID ,而目录 /proc/ 下面的那些文件分别表示着进程运行时的各方面信息,这里我们只关心 /proc//stat 文件即可。

文件 /proc//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 的内存资源隔离实际是内存使用量监控。

在这里我还是要推荐下我自己建的大数据学习交流qq裙:522189307 , 裙 里都是学大数据开发的,如果你正在学习大数据 ,小编欢迎你加入,大家都是软件开发党,不定期分享干货(只有大数据开发相关的),包括我自己整理的一份最新的大数据进阶资料和高级开发教程,欢迎进阶中和进想深入大数据的小伙伴。上述资料加群可以领取

猜你喜欢

转载自blog.csdn.net/Yukioog/article/details/90289642