问题描述
部门的Hadoop集群运行有一个多月了,今天需要做点调整,但是突然发现Hadoop不能正常关闭!
Hadoop版本:2.6.0
具体情况如下:
[root@master ~]# stop-dfs.sh Stopping namenodes on [master] master: no namenode to stop slave2: no datanode to stop slave1: no datanode to stop ...
问题原因
执行jps,发现namenode,datanode等进程都正常运行着。纳了闷!
谁报的错就找谁吧,于是就开始阅读hadoop-daemon.sh脚本文件,结果找出问题原因了。
首先找到报错的位置,在文件的最后几行:
if [ -f $pid ]; then TARGET_PID=`cat $pid` if kill -0 $TARGET_PID > /dev/null 2>&1; then echo stopping $command kill $TARGET_PID sleep $HADOOP_STOP_TIMEOUT if kill -0 $TARGET_PID > /dev/null 2>&1; then echo "$command did not stop gracefully after $HADOOP_STOP_TIMEOUT seconds: killing with kill -9" kill -9 $TARGET_PID fi else echo no $command to stop fi rm -f $pid else echo no $command to stop fi
代码很多,我们只看我们关心的部分:
if [ -f $pid ]; then ....#省略n多行 else echo no $command to stop fi
这样就很明显了,如果pid文件不存在就会打印:no xxx to stop
那么pid是什么文件,为什么会不存在,找到pid变量的声明语句,在脚本文件的第107行:
pid=$HADOOP_PID_DIR/hadoop-$HADOOP_IDENT_STRING-$command.pid #第107行
接着再找HADOOP_PID_DIR变量的声明部分:
首先在脚本注释部分找了很关键的一句话:
# HADOOP_PID_DIR The pid files are stored. /tmp by default.
我们知道了,HADOOP_PID_DIR 变量保存的是pid文件的存储路径。默认存储在/tmp目录中,代码如下:
if [ "$HADOOP_PID_DIR" = "" ]; then //97~99行 HADOOP_PID_DIR=/tmp fi
那么这个pid文件是啥呢。Hadoop启动后,会把进程的PID号存储在一个文件中,这样执行stop-dfs脚本时就可以按照进程PID去关闭进程了。
现在问题原因很明确了,就是/tmp目录下的hadoop-*.pid的文件找不到了。
解决问题
那就看看/tmp目录下还有啥:
[root@slave1 ~]# ll /tmp/ srwxr-x--- 1 root root 0 Mar 26 13:39 Aegis-<Guid(5A2C30A2-A87D-490A-9281-6765EDAD7CBA)> drwxr-xr-x 2 root root 4096 Apr 10 13:55 hsperfdata_root srwxr-x--- 1 root root 0 Mar 26 13:39 qtsingleapp-aegisG-46d2-0 srwxrwxrwx 1 root root 0 Mar 26 13:39 qtsingleapp-aegiss-a5d2-0
额,除了我们需要的,其他啥都有!
我们知道/tmp是临时目录,系统会定时清理该目录中的文件。显然把pid文件放在这里是不靠谱的,pid文件长时间不被访问,早被清理了!
既然Hadoop不知道需要关闭哪些进程了,那我们只能手动关闭了!
先用ps -ef查看namenode\datanode等进程的PID,然后用kill -9干掉即可!
重启Hadoop后再看看/tmp目录的变化,多出了下面几个文件:
[root@master ~]# ll /tmp -rw-r--r-- 1 root root 6 Apr 10 13:39 hadoop-root-namenode.pid-rw-r--r-- 1 root root 6 Apr 10 13:39 hadoop-root-secondarynamenode.pid-rw-r--r-- 1 root root 6 Apr 10 13:55 yarn-root-resourcemanager.pid drwxr-xr-x 4 root root 4096 Apr 10 14:52 Jetty_0_0_0_0_50070_hdfs____w2cu08 drwxr-xr-x 4 root root 4096 Apr 10 14:52 Jetty_0_0_0_0_50090_secondary____y6aanv drwxr-xr-x 5 root root 4096 Apr 10 15:02 Jetty_master_8088_cluster____i4ls4w
前三个文件是存放PID的文件,后三个Jetty_xxx格式的目录是Hadoop的web应用的临时目录,不是我们关心的。
打开一个pid文件看看:
[root@master tmp]# cat hadoop-root-namenode.pid32169
很简单,就保存了namenode进程的PID,再关闭namenode进程时就要从这个文件中读取PID。
到这里问题已经愉快的解决了!
但是,明知道pid文件放这里不安全,还不修改一下就显得我太懒了!
修改pid文件存放目录,只需要在hadoop-daemon.sh脚本中添加一行声明即可:
HADOOP_PID_DIR=/root/hadoop/pid #第25行
记住要先关闭Hadoop再修改,不然你修改完又无法关闭了!同样的道理,你还需要修改yarn-daemon.sh
YARN_PID_DIR=/root/hadoop/pid
然后执行start-dfs.sh \ start-yarn.sh 启动Hadoop。再去/root/hadoop/pid目录下看看:
[root@master pid]# ll-rw-r--r-- 1 root root 5 Apr 10 14:52 hadoop-root-namenode.pid-rw-r--r-- 1 root root 5 Apr 10 14:52 hadoop-root-secondarynamenode.pid-rw-r--r-- 1 root root 5 Apr 10 15:02 yarn-root-resourcemanager.pid
好了,从此再也不用担心出现no xxx to stop的警告了!
/tmp目录的清理策略
除了更换pid文件的保存路径外,我不由会想起另外一种解决思路,不让操作系统删除保存在/tmp目录下的pid文件不就可以了嘛!好的,那我们就来看看操作系统是怎么清理/tmp目录的。
遇到这个问题之前,我也没关系过/tmp目录,度娘一下,得到答案。
我们先来看一个重要的命令:
tmpwatch
tmpwatch指令可删除不必要的暂存文件,你可以设置文件超期时间,单位以小时计算。
常用参数: -m 或–mtime 根据文件被更改时间-c 或–ctime 根据文件更改状态时间-M 或–dirtime 根据文件夹被更改时间-x 或–exclude=path 排除某路径-X 或–exclude-pattern=pattern 排除某规则下的路径
/tmp作为临时文件夹,系统默认每天清理一次该目录。系统通过定时任务每天会执行一次/etc/cron.daily/tmpwatch这个脚本。其原理就是利用tmpwatch指令,设置清理策略。我们来看一下这个脚本内容:
/etc/cron.daily/tmpwatch #! /bin/sh flags=-umc /usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \ -x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \ -X '/tmp/hsperfdata_*' 10d /tmp /usr/sbin/tmpwatch "$flags" 30d /var/tmp for d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; do if [ -d "$d" ]; then /usr/sbin/tmpwatch "$flags" -f 30d "$d" fidone
代码的4~6行是一条语句,设置/tmp目录的清理策略. -x或-X是用来排除不清理的文件或目录,10d表示会删除最近10天没有被访问的文件(有的系统可能是240 ,表示240小时,也是10天)。
好吧 ,10天不用就给删除了,Hadoop集群运行几十天了,当然也就找不到pid文件了。
你有注意第6行代码中,被排除的文件吗 ?/tmp/hsperfdata_*,我们在上面解决问题时,第一次查看/tmp目录就有一个匹配此模式的文件:hsperfdata_root 。
那么,想不让系统删除pid文件,比着榴莲画个瓢就行了。在tmpwatch脚本中增加一个排除条件即可:
-X '/tmp/*.pid'