Hadoop 集群脚本

shell

Posted by danner on August 22, 2017

在编写集群脚本之前,先回顾下 shell 知识点

Shell

入门

创建 wc.sh,并在文件写入以下内容:

#!/bin/bash

echo "hello shell"

解释下上面两行代码:

  • 第一行注释表明执行此 shell/bin/bash
  • 第二行是 shell 具体要执行的脚本,输出 hello shell

保存好文件内容后,增加执行权限 chmod u+x wc.sh 后执行 ./wc.sh,输出如下:

hello shell

当然你也可以在首行加 -x 表示 debug 模式,会输出调试信息,整个文件内容:

#!/bin/bash -x

echo "hello shell"

执行后输出

+ echo 'hello shell' 
hello shell

增加了调试信息 + echo 'hello shell' ,代表执行 echo 'hello shell' 语句

变量定义与引用

创建 variable.sh,输入以下内容:

#!/bin/bash 

hello='hello'
date1='date1'
date2=\`date\` 
word=word

echo $hello
echo ${date1}
echo ${date2}
echo $word

执行输出:

hello
date1
Thu Aug 22 23:10:34 CST 2019

关于变量,有几个知识点:

  • $ 后跟随的是变量名
  • `fun` 是表示 fun 的返回值,例子中就是 date 命令返回值
  • 字符串可以用 ""''包裹,也可以不用;但标准用法是用引号

当然也有需要注意点

  • = 前后不能有空格
  • 变量名最好用 {},以免引起歧义
  • 静态变量最好用大写

传递参数

创建 parameter.sh,内容如下:

#!/bin/bash

echo $1
echo $2
echo "$#"
echo "$*"
echo "PID: $$"

执行 ./parameter.sh a b,输出:

a
b
2
a b
PID: 12545
  • 执行 shell 时,传递 a,b 两个参数
  • $1 得到第一个参数
  • $2 得到第二个参数
  • $# 是指参数的个数
  • $* 是指参数的内容
  • $$ 是指当前 shell 进程的 PID

数组

创建 array.sh,内容如下

#!/bin/bash

arr=("john li" wangwu zhangsan)
echo ${arr[@]}
echo ${arr[2]}
echo ${#arr[2]}
echo ${#arr[@]}

执行 ./array.sh,输出:

john li wangwu zhangsan
zhangsan
8
3
  • 数组用 () 表示
  • 数组内容用空格分割
  • @ 符号取数组所以内容
  • # 可以类似 len 函数,#变量 是变量长度,#arr[@] 数组 arr 的元素个数

判断

创建 if.sh,内容如下:

#!/bin/bash


A="abc"
B="john"

if [ ${A} == ${B} ];then
        echo "=="
elif [ ${A} == 'abc' ];then
        echo "=="
else
        echo "!="
fi

执行 ./if.sh,输出

==
  • [] 前后要有空格
  • == 前后要有空格
  • if 结束符号 fi

循环

创建 loop.sh,内容如下

#!/bin/bash

for((i=1;i<5;i++))
do
        echo "i = "${i}
done

​ j=1 while(($j<5)) do echo “j = “${j} let “j++” done

执行 ./loop.sh,输出

i = 1
i = 2
i = 3
i = 4
j = 1
j = 2
j = 3
j = 4
  • 条件是用 (()) 包裹
  • 循环语句是在 do ... done 之内

分割

创建 split.sh,内容如下:

#!/bin/bash

s="rz,j,xx,huhu,yt,co"

OLD_IFS="$IFS"
IFS=","
arr=($s)
IFS="$OLD_IFS"

for x in ${arr[*]}
do
        echo $x
done

执行 ./split.sh,输出

rz
j
xx
huhu
yt
co

AWK

[hadoop@node02 learn_shell]$ cat test.log 
a b c
1 2 3
[hadoop@node02 learn_shell]$ cat test.log | awk '{print $1}'
a
1
[hadoop@node02 learn_shell]$ cat test.log | awk '{print $1,$2}'
a b
1 2
[hadoop@node02 learn_shell]$ cat test.log | awk 'NR==1{print}'      
a b c
[hadoop@node02 learn_shell]$ cat test.log | awk 'NR==1{print $1}'
a
[hadoop@node02 learn_shell]$ cat test.log | awk 'NR>1{print}'     
1 2 3

[hadoop@node02 learn_shell]$ cat test.log 
a;b;c
1;2;3
[hadoop@node02 learn_shell]$ cat test.log | awk -F ";" '{print $2}'
b
2
  • -F 后面紧跟着的是分割点,默认的分割点是空格
  • $index 表示输出 index 的内容
  • NR 表示行操作

sed

sed 命令是利用脚本来处理文本文件,主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。

sed ‘s/要被取代的字串/新的字串/g’

[hadoop@node02 learn_shell]$ cat test.log 
w;w;c
w;2;3
[hadoop@node02 learn_shell]$ sed -i 's/w/f/2' test.log 
[hadoop@node02 learn_shell]$ cat test.log 
w;f;c
w;2;3
[hadoop@node02 learn_shell]$ sed -i 's/w/f/' test.log  
[hadoop@node02 learn_shell]$ cat test.log 
f;f;c
f;2;3
[hadoop@node02 learn_shell]$ sed -i 's/f/m/g' test.log    
[hadoop@node02 learn_shell]$ cat test.log 
m;m;c
m;2;3
  • -i 选项可以直接修改文件内容
  • s替换,可以搭配正规表示法
  • g 代表每行中第几个要被替换的字符串,默认是第一个,g 代表全部

Hadoop

Hadoop 集群的有很多台机器,如果要在检测不同机器的状态,就需要登陆不同的机器;这是一件很麻烦的操作。但我们可以在一台机器上执行 ssh登陆到其他机器来监控,这样就可以在一台机器检测整个集群的状态。

jps

查看集群上的机器每个进程是否运行正常

#!/bin/bash

echo "------------------danner001 process--------------------"
ssh danner001 "$JAVA_HOME/bin/jps"

echo "------------------danner002 process--------------------"
ssh danner002 "$JAVA_HOME/bin/jps"

echo "------------------danner003 process--------------------"
ssh danner003 "$JAVA_HOME/bin/jps"

启动集群

启动 Hadoop 集群

#!/bin/bash 

# zookeeper
ssh danner001 "$ZOOKEEPER_HOME/bin/zkServer.sh start"
ssh danner002 "$ZOOKEEPER_HOME/bin/zkServer.sh start"
ssh danner003 "$ZOOKEEPER_HOME/bin/zkServer.sh start"

sleep 5

# start hdfs + yarn
start-all.sh

sleep 5

# readby resourcemanager
ssh danner002 "$HADOOP_HOME/sbin/yarn-daemon.sh start resourcemanager" 

# jobhistory
mr-jobhistory-daemon.sh start historyserver

exit 0

此刻 ssh 直接远程执行会有环境变量问题,这种方式属于无登陆无交互 shell(详细看参考资料)。此方式执行时只会加载.bashrc,而我们的环境变量都是设置在/etc/proflie.bash_profile里。我们需要在 .bashrc 中添加需要的环境变量。当然我们也可以在执行 shell 时使用 /bin/bash -l zkServer.sh start 命令也可以,这是属于 有登陆 shell,但每个都这么写太麻烦了。

停止集群

停止 Hadoop 集群

#!/bin/bash 

#stop history+yarn+hdfs
mr-jobhistory-daemon.sh stop historyserver
ssh danner002 "$HADOOP_HOME/sbin/yarn-daemon.sh stop resourcemanager"

stop-all.sh

#stop zk
ssh danner001 "$ZOOKEEPER_HOME/bin/zkServer.sh stop"
ssh danner002 "$ZOOKEEPER_HOME/bin/zkServer.sh stop"
ssh danner003 "$ZOOKEEPER_HOME/bin/zkServer.sh stop"

./jps.sh

exit 0

监控 HDFS HA

HDFS 的高可用可以保证挂掉一个 namenode后 ` standby namenode 立刻上线使用。但我们也是需要知道 namenode 什么时候挂了,以便后续的问题排查。本例是在 namenode` 挂掉后立刻发邮件

mail

先配置 mail 465,以下都是在 root 下执行:

先打开 QQ邮箱SMTP服务和开通机子的 485 端口

若没有安装 sendmail 则先安装

yum -y install sendmail

停止服务

systemctl stop sendmail.service
systemctl disable sendmail.service

调整 postfix 参数

vi /etc/postfix/main.cf
inet_interfaces = all

启动 postfix 服务

service postfix start
chkconfig postfix on
postfix check
systemctl status postfix

# 一定要查看下 postfix 状态
postfix.service - Postfix Mail Transport Agent
Loaded: loaded (/usr/lib/systemd/system/postfix.service; enabled; vendor preset: disabled)
Active: active (running) since Sat 2019-08-24 08:32:00 UTC; 1min 49s ago

创建认证

以下用 hadoop 用户执行

mkdir -p ~/.certs/
echo -n | openssl s_client -connect smtp.qq.com:465 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/.certs/qq.crt
certutil -A -n "GeoTrust SSL CA" -t "C,," -d ~/.certs -i ~/.certs/qq.crt
certutil -A -n "GeoTrust Global CA" -t "C,," -d ~/.certs -i ~/.certs/qq.crt
certutil -L -d ~/.certs
cd ~/.certs
certutil -A -n "GeoTrust SSL CA - G3" -t "Pu,Pu,Pu" -d ./ -i qq.crt

# 成功会输出以下信息
Notice: Trust flag u is set automatically if the private key is present.

配置邮件发送

切换回 root 执行

vi /etc/mail.rc
set from=597819425@qq.com
set smtp=smtps://smtp.qq.com:465
set smtp-auth-user=597819425
#授权码
set smtp-auth-password=umiiwawxygqvbbfe
set smtp-auth=login
set ssl-verify=ignore
set nss-config-dir=/home/hadoop/.certs

发送邮件

切换回 hadoop 用户发送

若没有安装 mailx,则先安装 yum -y install mailx

echo hello mail | mail -s " title" 597819425@qq.com

QQ邮箱收到邮件

获取 HDFS 状态

创建 get_hdfs_ha_state.sh,输入以下内容

#!/bin/bash

ACTIVE_HOSTNAME=""
STANDBY_HOSTNAME=""
ACTIVE_SERVICEID=""
STANDBY_SERVICEID=""
ACTIVE_SERVICESTATE=""
STANDBY_SERVICESTATE=""

EMAIL=597819425@qq.com

BIN_HOME=/home/hadoop/app/hadoop/bin

​ ha_name=$(${BIN_HOME}/hdfs getconf -confKey dfs.nameservices) namenode_serviceids=$(${BIN_HOME}/hdfs getconf -confKey dfs.ha.namenodes.${ha_name})

for node in $(echo ${namenode_serviceids//,/ }); do
	state=$(${BIN_HOME}/hdfs haadmin -getServiceState $node)

	if [ "$state" == "active" ]; then
		ACTIVE_SERVICEID="${node}"  
		ACTIVE_SERVICESTATE="${state}" 
		ACTIVE_HOSTNAME=`echo $(${BIN_HOME}/hdfs getconf -confKey dfs.namenode.rpc-address.${ha_name}.${node}) | awk -F ':' '{print $1}'`
		#echo "${ACTIVE_HOSTNAME} : ${ACTIVE_SERVICEID} : ${ACTIVE_SERVICESTATE}"

	elif [ "$state" == "standby" ]; then
		STANDBY_SERVICEID="${node}"
		STANDBY_SERVICESTATE="${state}"
		STANDBY_HOSTNAME=`echo $(${BIN_HOME}/hdfs getconf -confKey dfs.namenode.rpc-address.${ha_name}.${node}) | awk -F ':' '{print $1}'`
		#echo "${STANDBY_HOSTNAME} : ${STANDBY_SERVICEID} : ${STANDBY_SERVICESTATE}"
	else
		echo "hdfs haadmin -getServiceState $node: unkown"
	fi

done

echo "                                                                "
echo "Hostname		Namenode_Serviceid		Namenode_State"
echo "${ACTIVE_HOSTNAME}		${ACTIVE_SERVICEID}		${ACTIVE_SERVICESTATE}"
echo "${STANDBY_HOSTNAME}		${STANDBY_SERVICEID}		${STANDBY_SERVICESTATE}"

​ # save current NN1/2_HOSTNAME state # active namenode 信息固定都是第一行 echo “${ACTIVE_HOSTNAME} ${ACTIVE_SERVICEID} ${ACTIVE_SERVICESTATE}” > HDFS_HA.log echo “${STANDBY_HOSTNAME} ${STANDBY_SERVICEID} ${STANDBY_SERVICESTATE}” » HDFS_HA.log

# 判断当前 active namenode 是否与上次相同,不同则发邮件提醒
if [ -f HDFS_HA_LAST.log ];then
        	HISTORYHOSTNAME=`cat HDFS_HA_LAST.log| awk 'NR==1{print $1}'`

        	if [ "$HISTORYHOSTNAME" != "${ACTIVE_HOSTNAME}"  ];then

                echo "send a mail"
                echo -e "`date "+%Y-%m-%d %H:%M:%S"` : Please to check namenode log." | mail \
                -r "From: alertAdmin <597819425@qq.com>" \
                -s "Warn: CDH HDFS HA Failover!." ${EMAIL}
		
		fi

fi

cat HDFS_HA.log > HDFS_HA_LAST.log

hdfs haadmin -failover nn2 nn1 // 将 active namenode 从 nn2 切换到 nn1

执行 ./get_hdfs_ha_state.sh,输出

Hostname                Namenode_Serviceid              Namenode_State
danner001               nn1             active
danner002               nn2             standby
send a mail 查看 QQ 邮箱会收到一封信

参考资料

Linux sed 命令
ssh连接远程主机执行脚本的环境变量问题