在编写集群脚本之前,先回顾下 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 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 邮箱会收到一封信
