Hadoop监控管理

2021/02/04 Hadoop

Hadoop监控管理

本章我们将具体介绍如何维护集群以保证其正常运行。毋庸置疑,维护一个大型集群稳定运行是必要的,手段也是多样的。

HDFS文件结构

作为一名合格的系统运维人员,首先要全面掌握系统的文件组织目录。对于Hadoop系统的运维人员来说,就是要掌握HDFS中的NameNode、DataNode、Secondery NameNode是如何在磁盘上组织和存储持久化数据的。只有这样,当遇到问题时,管理人员才能借助系统本身的文件存储机制来快速诊断和分析问题。下面从HDFS的几个方面来分别介绍。

NameNode的文件结构

最新格式化的NameNode会创建以下目录结构:

${dfs.name.dir}/current/VERSION
/edits
/fsimage
/fstime

其中,dfs.name.dir属性是一个目录列表,是每个目录的镜像。VERSION文件是Java属性文件,其中包含运行HDFS的版本信息。

下面是一个典型的VERSION文件包含的内容:

#Wed Mar 23 16:03:27 CST 2011
namespaceID=1064465394
cTime=0
storageType=NAME_NODE
layoutVersion=-18

其中,namespaceID是文件系统的唯一标识符。在文件系统第一次被格式化时便会创建namespaceID。这个标识符也要求各DataNode节点和NameNode保持一致。NameNode会使用此标识符识别新的DataNode。DataNode只有在向NameNode注册后才会获得此namespaceID。cTime属性标记了NameNode存储空间创建的时间。对于新格式化的存储空间,虽然这里的cTime属性值为0,但是只要文件系统被更新,它就会更新到一个新的时间戳。storageType用于指出此存储目录包含一个NameNode的数据结构,在DataNode中它的属性值为DATA_NODE。

layoutVersion是一个负的整数,定义了HDFS持久数据结构的版本。注意,该版本号和Hadoop的发行版本号无关。每次HDFS的布局发生改变,该版本号就会递减(比如-18版本号之后是-19),在这种情况下,HDFS就需要更新升级,因为如果一个新的NameNode或DataNode还处在旧版本上,那么系统就无法正常运行,各节点的版本号要保持一致。

NameNode的存储目录包含edits、fsimage、fstime三个文件。它们都是二进制的文件,可以通过HadoopWritable对象进行序列化。下面将深入介绍NameNode的工作原理,以便使大家更清晰地理解这三个文件的作用。

编辑日志(edit log)及文件系统映像(filesystem image)

当客户端执行写操作时,NameNode会先在编辑日志中写下记录,并在内存中保存一个文件系统元数据,元数据会在编辑日志有所改动后进行更新。内存中的元数据用来提供读数据请求服务。

编辑日志会在每次成功操作之后、成功代码尚未返回给客户端之前进行刷新和同步。对于要写入多个目录的操作,写入流要刷新和同步到所有的副本,这就保证了操作不会因故障而丢失数据。

fsimage文件是文件系统元数据的持久性检查点。和编辑日志不同,它不会在每个文件系统的写操作后都进行更新,因为写出fsimage文件会非常慢(fsimage可能增长到GB大小)。这种设计并不会影响系统的恢复力,因为如果NameNode失败,那么元数据的最新状态可以通过将从磁盘中读出的fsimage文件加载到内存中来进行重建恢复,然后重新执行编辑日志中的操作。事实上,这也正是NameNode启动时要做的事情。一个fsimage文件包含以序列化格式存储的文件系统目录和文件inodes。每个inodes表示一个文件或目录的元数据信息,以及文件的副本数、修改和访问时间等信息。

正如上面所描述的,Hadoop文件系统会出现编辑日志不断增长的情况。尽管在NameNode运行期间不会对系统造成影响,但是,如果NameNode重新启动,它将会花费很长时间来运行编辑日志中的每个操作。在此期间(即安全模式时间),文件系统还是不可用的,通常来说这是不符合应用需求的。

为了解决这个问题,Hadoop在NameNode之外的节点上运行一个Secondary NameNode进程,它的任务就是为原NameNode内存中的文件系统元数据产生检查点。其实Secondary NameNode是一个辅助NameNode处理fsimage和编辑日志的节点,它从NameNode中复制fsimage和编辑日志到临时目录并定期合并生成一个新的fsimage,随后它会将新的fsimage上传到NameNode,这样,NameNode便可更新fsimage并删除原来的编辑日志。

下面介绍检查点处理过程的具体步骤。

  • Secondary NameNode首先请求原NameNode进行edits的滚动,这样新的编辑操作就能够进入一个新的文件中了。
  • Secondary NameNode通过HTTP方式读取原NameNode中的fsimage及edits。
  • Secondary NameNode读取fsimage到内存中,然后执行edits中的每个操作,并创建一个新的统一的fsimage文件。
  • Secondary NameNode(通过HTTP方式)将新的fsimage发送到原NameNode。
  • 原NameNode用新的fsimage替换旧的fsimage,旧的edits文件通过步骤1)中的edits进行替换。同时系统会更新fsimage文件到记录检查点记录的时间。

在这个过程结束后,NameNode就有了最新的fsimage文件和更小的edits文件。事实上,对于NameNode在安全模式下的这种情况,管理员可以通过以下命令运行这个过程:

hadoop dfsadmin-saveNamespace

这个过程清晰地表明了Secondary NameNode要有和原NameNode一样的内存需求的原因—要把fsimage加载到内存中,因此Secondary NameNode在集群中也需要有专用机器。

有关检查点的时间表由两个配置参数决定。Secondary NameNode每小时会插入一个检查点(fs.chec-kpoint.period,以秒为单位),如果编辑日志达到64MB(fs.checkpoint.size,以字节为单位),则间隔时间更短,每隔5分钟会检查一次。

Secondary NameNode的目录结构

Secondary NameNode在每次处理过程结束后都有一个检查点。这个检查点可以在一个子目录/previous.checkpoint中找到,可以作为NameNode的元数据备份源,目录如下:

${fs.checkpoint.dir}/current/VERSION
/edits
/fsimage
/fstime
/previous.checkpoint/VERSION
/edits
/fsimage
/fstime

以上这个目录和Secondary NameNode的/current目录结构是完全相同的。这样设计的目的是:万一整个NameNode发生故障,并且没有用于恢复的备份,甚至NFS中也没有备份,就可以直接从Secondary NameNode恢复。具体方式有两种,第一种是直接复制相关的目录到新的NameNode中。第二种是在启动NameNode守护进程时,Secondary NameNode可以使用-importCheckpoint选项,并作为新的NameNode继续运行任务。-importCheckpoint选项将加载fs.checkpoint.dir属性定义的目录中的最新检查点的NameNode数据,但这种操作只有在dfs.name.dir所指定的目录下没有元数据的情况下才进行,这样就避免了重写之前元数据的风险。

DataNode的目录结构

DataNode不需要进行格式化,它会在启动时自己创建存储目录,其中关键的文件和目录如下:

${dfs.data.dir}/current/VERSION
/blk_<id_1>
/blk_<id_1>.meta
/blk_<id_2>
/blk_<id_2>.meta
/……
/subdir0/
/subdir1/
/……
/subdir63/

DataNode的VERSION文件和NameNode的非常类似,内容如下:

#Tue Mar 10 21:32:31 GMT 2010
namespaceID=134368441
storageID=DS-547717739-172.16.85.1-50010-1236720751627
cTime=0
storageType=DATA_NODE
layoutVersion=-18

其中,namespaceID、cTime和layoutVersion值与NameNode中的值都是一样的,namaspaceID在第一次连接NameNode时就会从中获取。stroageID相对于DataNode来说是唯一的,用于在NameNode处标识DataNode。storageType将这个目录标志为DataNode数据存储目录。

DataNode中current目录下的其他文件都有blk_refix前缀,它有两种类型:

  • HDFS中的文件块本身,存储的是原始文件内容;
  • 块的元数据信息(使用.meta后缀标识)。一个文件块由存储的原始文件字节组成,元数据文件由一个包含版本和类型信息的头文件和一系列块的区域校验和组成。

当目录中存储的块数量增加到一定规模时,DataNode会创建一个新的目录,用于保存新的块及元数据。当目录中的块数量达到64(可由dfs.DataNode.numblocks属性值确定)时,便会新建一个子目录,这样就会形成一个更宽的文件树结构,避免了由于存储大量数据块而导致目录很深,使检索性能免受影响。通过这样的措施,数据节点可以确保每个目录中的文件块数是可控的,也避免了一个目录中存在过多文件。

Hadoop的状态监视和管理工具

对一个系统运维的管理员来说,进行系统监控是必须的。监控的目的是了解系统何时出现问题,并找到问题出在哪里,从而做出相应的处理。管理守护进程对监控NameNode、DataNode和JobTracker是非常重要的。在实际运行中,因为DataNode及TaskTracker的故障可能随时出现,所以集群需要提供额外的功能以应对少部分节点出现的故障。管理员也要隔一段时间执行一些监测任务,以获知当前集群的运行状态。

审计日志

HDFS通过审计日志可以实现记录文件系统所有文件访问请求的功能,其审计日志功能通过log4j实现,但是在默认配置下这个功能是关闭的:log的记录等级在log4j.properties中被设置为WARN:

log4j.logger.org.apache.hadoop.fs.FSNamesystem.audit=WARN

在此处将WARN修改为INFO,便可打开审计日志功能。这样在每个HDFS事件之后,系统都会在NameNode的log文件中写入一行记录。 下面是一个请求/usr/hadoop文件的例子:

INFO org.apache.hadoop.hdfs.server.namenode.FSNamesystem.audit:
ugi=admin, staff, admin ip=/127.0.0.1 cmd=listStatus src=/user/admin=null
perm=null

关于log4j还有很多其他配置可改,比如可以将审计日志从NameNode的日志文件中分离出来等。具体操作可查看Hadoop的Wiki:http://wiki.apache.org/hadoop/HowToConfigure。

监控日志

所有Hadoop守护进程都会产生一个日志文件,这对管理员来说非常重要。下面我们就介绍如何使用这些日志文件。

设置日志级别

当进行故障调试排除时,很有必要临时调整日志的级别,以获得系统不同类型的信息。log4j日志一般包含这样几个级别:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或用户自定义的级别。

Hadoop守护进程有一个网络页面可以用来调整任何log4j日志的级别,在守护进程的网络UI后附后缀/logLevel即可访问该网络页面。按照规定,日志的名称和它所对应的执行日志记录的类名是一样的,可以通过查找源代码找到日志名称。例如,为了调试JobTracker类的日志,可以访问JobTracker的网络UI:http://jobtracker-host:50030/logLevel,同时设置日志名称org.apache.hadoop.mapred.JobTracker到层级DEBUG。当然也可以通过命令行进行调整,代码如下:

hadoop daemonlog-setlevel jobtracker-host:50030\
org.apache.hadoop.mapred.JobTracker DEBUG

通过命令行修改的日志级别会在守护进程重启时被重置,如果想要持久化地改变日志级别,那么只要改变log4j.properties文件内容即可。我们可以在文件中加入以下行:

log4j.logger.org.apache.hadoop.mapred.JobTracker=DEBUG

获取堆栈信息

有关系统的堆栈信息,Hadoop守护进程提供了一个网络页面(在网络UI后附后缀/stacks才可以访问),该网络页面可以为管理员提供所有守护进程JVM中运行的线程信息。可以通过以下链接访问该网络页面:http://jobtracker-host:50030/stacks。

Metrics

事实上,除了Hadoop自带的日志功能以外,还有很多其他可以扩展的Hadoop监控程序供管理员使用。在介绍这些监控工具之前,先对系统的可度量信息(Metrics)进行简单讲解。

HDFS及MapReduce的守护进程会按照一定的规则来收集系统的度量信息。我们将这种度量规则称为Metrics。例如,DataNode会收集如下度量信息:写入的字节数、被复制的文件块数及来自客户端的请求数等。

Metrics属于一个上下文,当前Hadoop拥有dfs、mapred、rpc、jvm等上下文。Hadoop守护进程会收集多个上下文的度量信息。所谓上下文即应用程序进入系统执行时,系统为用户提供的一个完整的运行时环境。进程的运行时环境是由它的程序代码和程序运行所需要的数据结构以及硬件环境组成的。

这里我们认为,一个上下文定义了一个单元,比如,可以选择获取dfs上下文或jvm上下文。我们可以通过配置conf/hadoopmetrics.properties文件设定Metrics。在默认情况下,会将所有上下文都配置为NullContext类,这代表它们不会发布任何Metrics。下面是配置文件的默认配置情况:

dfs.class=org.apache.hadoop.metrics.spi.NullContext
mapred.class=org.apache.hadoop.metrics.spi.NullContext
jvm.class=org.apache.hadoop.metrics.spi.NullContext
rpc.class=org.apache.hadoop.metrics.spi.NullContext

其中每一行都针对一个不同的上下文单元,同时每一行定义了处理此上下文Metrics的类。这里的类必须是MetricsContext接口的一个实现;在上面的例子中,这些NullContext类正如其名,什么都不做,既不发布也不更新它们的Metrics。

下面我们来介绍MetricsContext接口的实现。

FileContext

利用FileContext可将Metrics写入本地文件。FileContext拥有两个属性:fileName—定义文件的名称,period—指定文件更新的间隔。这两个属性都是可选的,如果不进行设置,那么Metrics每隔5秒就会写入标准输出。

配置属性将应用于指定的上下文中,并通过在上下文名称后附加点“.”及属性名进行标示。比如,为了将jvm导出一个文件,我们会通过以下方法调整它的配置:

jvm.class=org.apache.hadoop.metrics.file.FileContext

jvm.fileName=/tmp/jvm_metrics.log

其中,第一行使用FileContex来改变jvm的上下文,第二行将jvm上下文导出临时文件。

需要注意的是,FileContext非常适合于本地系统的调试,但是它并不适合在大型集群中使用,因为它的输出文件会被分散到集群中,使分析的时间成本变得很高。

GangliaContext

Ganglia(http://ganglia. info/)是一个开源的分布式监控系统,主要应用于大型分布式集群的监控。通过它可以更好地监控和调整集群中每个机器节点的资源分配。Ganglia本身会收集一些监控信息,包括CPU和内存使用率等。通过使用GangliaContext我们可以非常方便地将Hadoop的一些测量内容注入Ganglia中。此外,GangliaContext有一个必须的属性—servers,它的属性值是通过空格或逗号分隔的Ganglia服务器主机地址:端口。

NullContextWithUpdateThread

通过前面的介绍,我们会发现FileContext和GangliaContext都将Metrics推广到外部系统。而Hadoop内部度量信息的获取需要另外的工具,比如著名的Java管理扩展(Java Management Extensions, JMX),JMX中的NullContextWithUpdateThread就是用来解决这个问题的(我们将在后面进行详细讲解)。和NullContext相似,它不会发布任何Mertics,但是它会运行一个定时器周期性地更新内存中的Metrics,以保证另外的系统可以获得最新的Metrics。

除NullContextWithUpdateThread外,所有MetricsContext都会执行这种在内存中定时更新的方法,所以只有当不使用其他输出进行Metrics收集时,才需要使用NullContext-WithUpdateThread。举例来说,如果之前正在使用GangliaContext,那么随后只要确认Metrics是否被更新,而且只需要使用JMX,不用进一步对Metrics系统进行配置。

CompositeContext

CompositeContext允许我们输出多个上下文中的相同的Metrics,比如下面的这个例子:

jvm.class=org.apache.hadoop.metrics.spi.CompositeContext
jvm.arity=2
jvm.sub1.class=org.apache.hadoop.metrics.file.FileContext
jvm.fileName=/tmp/jvm_metrics.log
jvm.sub2.class=org.apache.hadoop.metrics.ganglia.GangliaContext
jvm.servers=ip-10-70-20-111.ec2.internal:8699

其中arity属性用来定义子上下文数量,在这里它的值为2。所有子上下文的属性名称都可以使用下面的句子设置:jvm.sub1.class=org.apache.hadoop.metrics.file.FileContext。

Java管理扩展

Java管理扩展(JMX)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活地开发无缝集成的系统、网络和服务管理应用。Hadoop包含多个MBean(Managed Bean,管理服务,它描述一个可管理的资源),它可以将Hadoop的Metrics应用到基于JMX的应用程序中。当前MBeans可以将Metrics展示到dfs和rpc上中文中,但不能在mapred及jvm上下文中实现。

JDK中的Jconsole工具可以帮助我们查看JVM中运行的MBeans信息,使我们很方便地浏览Hadoop中的监控信息。很多第三方监控和调整系统(Nagios和Hyperic等)可用于查询MBeans,这样JMX自然就成为我们监控Hadoop系统的最好工具。但是,需要设置支持远程访问的JMX,并且设置一定的安全级别,包括密码权限、SSL链接及SSL客户端权限设置等。为了使系统支持远程访问,JMX要求对一些选项进行更改,其中包括设置Java系统的属性(可以通过编辑Hadoop的conf/hadoop-env.sh文件实现)。

下面的例子展示了如何通过密码远程访问NameNode中的JMX(在SSL不可用的条件下):

export HADOOP_NameNode_OPTS="-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.password.file=$HADOOP_CONF_DIR/jmxremote.password
-Dcom.sun.management.jmxremote.port=8004$HADOOP_NameNode_OPTS"

jmxremote. password文件以纯文本的格式列出了所有的用户名和密码。JMX文档有关于jmxremote.password文件的更进一步的格式信息。

通过以上的配置,我们可以使用JConsole工具浏览远程NameNode中的MBean监控信息。事实上,我们还有很多其他方法实现这个功能,比如通过jmxquery(一个命令行工具,具体信息可查看http://code.google.com/p/jmxquery/)来检索低于副本要求的块:

./check_jmx-U service:jmx:rmi:///jndi/rmi://NameNode-host:8004/jmxrmi-O\
hadoop:service=NameNode, name=FSNamesystemState-A UnderReplicatedBlocks\
-w 100-c 1000-username monitorRole-password secret
JMX OK-UnderReplicatedBlocks is 0

通过jmxquery命令创建一个JMX RMI链接,链接到NameNode主机地址上,端口号为8004。它会读取对象名为hadoop:service=NameNode, name=FSNamesystemState的UnderReplicatedBlocks属性,并将读出的值写入终端。-w、-c选项定义了警告和数值的临界值,这个临界值的选定要在我们运行和维护集群一段时间以后才能选出比较合适的经验值。

需要注意的是,尽管我们可以通过JMX的默认配置看到Hadoop的监控信息,但是它们不会自动更新,除非更改MetricContext的具体实现。如果JMX是我们使用的监控系统信息的唯一方法,那么就可以把MetricContext的实现更改为NullContextWithUpdateThread。

通常大多数人会使用Ganglia和另外一个可选的系统(比如Nagios)来进行Hadoop集群的检测工作。Ganglia可以很好地完成大数据量监控信息的收集和图形化工作,而Nagios及类似的系统则更擅长处理小规模的监控数据,并且在监控信息超出设定的监控阈值时发出警告。管理者可以根据需求选择合适的工具。

Hadoop集群的维护

安全模式

当NameNode启动时,要做的第一件事情就是将映像文件fsimage加载到内存,并应用edits文件记录编辑日志。一旦成功重构和之前文件系统一致且居于内存的文件系统元数据,NameNode就会创建一个新的fsimage文件(这样就可以更高效地记录检查点,而不用依赖于Secondary NameNode)和一个空的编辑日志文件。只有全部完成了这些工作,NameNode才会监听RPC和HTTP请求。然而,如果NameNode运行于安全模式下,那么文件系统只能对客户端提供只读模式的视图。

文件块的位置信息并没有持久化地存储在NameNode中,这些信息都存储在各DataNode中。在文件系统的常规操作期间,NameNode会在内存中存储一个块位置的映射。在安全模式下,需要留给DataNode一定的时间向NameNode上传它们存储块的列表,这样NameNode才能获得充足的块位置信息,才会使文件系统更加高效。如果NameNode没有足够的时间来等待获取这些信息,那么它就会认为该块没有足够的副本,进而安排其他DataNode复制。这在很多情况下显然是没有必要的,还浪费系统资源。在安全模式下,NameNode不会处理任何块复制和删除指令。

当最小副本条件达到要求时,系统就会退出安全模式,这需要延期30秒(这个时间由dfs.safe-mode.extension属性值确定,默认为30,一些小的集群(比如只有10个节点),可以设置该属性值为0)。这里所说的最小副本条件是指系统中99.9%(这个值由dfs.safemode.threshold.pct属性确定,默认为0.999)的文件块达到dfs.replication.min属性值所设置的副本数(默认为1)。

当格式化一个新的HDFS时,NameNode不会进入安全模式,因为此时系统中还没有任何文件块。

使用以下命令可以查看NameNode是否已进入安全模式:

hadoop dfsadmin-safemode get
Safe mode is ON

在有些情况下,需要在等待NameNode退出安全模式时执行一些命令,这时我们可以使用以下命令:

hadoop dfsadmin-safemode wait
#command to read or write a file

作为管理员,也应掌握使NameNode进入或退出安全模式的方法,这些操作有时也是必需的,比如在升级完集群后需要确认数据是否仍然可读等。这时我们可以使用以下命令:

hadoop dfsadmin-safemode enter
Safe mode is ON

当NameNode仍处于安全模式时,也可以使用以上命令以保证NameNode没有退出安全模式。要使系统退出安全模式可执行以下命令:

hadoop dfsadmin-safemode leave
Safe mode is OFF

Hadoop的备份

元数据的备份

如果NameNode中存储的持久化元数据信息丢失或遭到破坏,那么整个文件系统就不可用了。因此元数据的备份至关重要,需要备份不同时期的元数据信息(1小时、1天、1周……)以避免突然宕机带来的破坏。

备份的一个最直接的办法就是编写一个脚本程序,然后周期性地将Secondary NameNode中previous.checkpoint子目录(该目录由fs.checkpoint.dir属性值确定)下的文件归档到另外的机器上。该脚本需要额外验证所复制的备份文件的完整性。这个验证可以通过在NameNode的守护进程中运行一个验证程序来实现,验证其是否成功地从内存中读取了fsimage及edits文件。

数据的备份

HDFS的设计目标之一就是能够可靠地在分布式集群中储存数据。HDFS允许数据丢失的发生,所以数据的备份就显得至关重要了。由于Hadoop可以存储大规模的数据,备份哪些数据、备份到哪里就成为一个关键。在备份过程中,最优先备份的应该是那些不能再生的数据和对商业应用最关键的数据。而对于那些可以通过其他手段再生的数据或对于商业应用价值不是很大的数据,可以考虑不进行备份。

这里需要强调的是,不要认为HDFS的副本机制可以代替数据的备份。HDFS中的Bug也会导致副本丢失,同样硬件也会出现故障。尽管Hadoop可以承受集群中廉价商用机器故障,但是有些极端情况不能排除在外,特别是系统有时还会出现软件Bug和人为失误的情况。

通常Hadoop会设置用户目录的策略,比如,每个用户都有一个空间配额,每天晚上都可进行备份工作。但是不管设置什么样的策略,都需要通知用户,以避免客户反映问题。

Search

    微信好友

    博士的沙漏

    Table of Contents