背景
Jenkins 所在的 Tomcat 总是莫名挂掉,虽然任务配置了 NOKILLME
参数,而且并不是总是发生在编译完成后才挂的。怀疑是机器资源不足导致的,没有依据。最简单的办法是创建一个定时任务,检测 Tomcat 状态,不见了就拉起它。
本文记录这个简单的过程,很简单的一个脚本,里面确包含着 Linux shell 的几个基本知识点,一起温故一下。
拉起脚本
编写拉起脚本,平时写了很多 stop.sh
脚本,随便找到一个参考,拷贝过来的,命名为 pull-tomcat.sh
。 Tomcat 的特殊关键字是进程启动的 class 类名称 Bootstrap
,编写完整脚本如下:
#!/bin/sh
basePath=$(cd `dirname $0`; pwd)
scriptName=$(basename "$0")
cd $basePath
cd ..
dataDir=`pwd`
date=`echo $(date "+%m%d%H%M%S")`
tomcatInfo=`ps -ef|grep -w Bootstrap|grep -v grep|grep -v "$scriptName"|awk '{print $2}'`
echo $date","$tomcatInfo
if [ "$tomcatInfo" == "" ]; then
echo 'Tomcat is missing and start it.'
cd /opt/apache-tomcat-9.0.82/bin
sh startup.sh
fi
脚本很简单,但是有几个关键点:
- 第一行脚本编辑器设置必须正确,区分不同操作系统默认生成的 shell 文件该行不一样。
- 使用 ps 查看进程时,需要考虑脚本的执行方式,为了保证过滤结果正确,需要过滤掉当前脚本自己
grep -v "$scriptName"
。 - crontab 定时器执行时依赖环境变量和直接执行脚本或者在命令行执行命令不一样,所以不能环境变量
PATH
路径下的命令,如jps
,否则会导致crontab
命令执行失败。 - if 分支如果编写 else 分支,则该分支里面必须包含语句,否则会报错。这点跟 Java 不一样,Java 可以包含空分支的。
脚本解释器问题
MacOs 下用 IDEA 创建 .sh
后缀的文件时,自动会在第一行加上解释器信息为:
#!/sh
但是当放到 Linux 系统下,通过 ./文件名
的方式执行时,会报错误:
-bash: ./test.sh: /sh: 坏的解释器: 没有那个文件或目录
注意事项:
./文件名
的方式执行时,必须有执行权限,否则会报“权限不够”的错误,执行失败;且会以开头第一行设置的 Shell 解释器进行执行。Mac 下默认的解释器是#!/sh
,在 Linux 下应该设置为#!/bin/bash
sh 文件名的方式
,即使没有执行权限,也能执行。通过子进程的方式执行,而子进程的执行路径字段为当前脚本文件路径,且会被 grep 过滤器留下,因此得到的结果不准确。
上图是目标主机上在脚本中执行 ps -ef|grep -w Bootstrap|grep -v grep
的结果,里面包含了当前脚本名称。诡异的是,这种情况还跟机器有关,以前都没有碰到过这种情况。可能跟 shell 解释器的什么特殊配置有关吧。
cron 环境变量问题
jps 命令,当 crontab 定时执行时,报异常,命令不存在。脚本中最初想用 jps 命令查找 Bootstrap 进程的,但是定时调度时总是报命令不存在。
猜测是 /root/.bash_profile
里面没有配置 JAVA_HOME/bin
到 PATH
,但是配置后,定时任务执行时依旧报这个错误。
编写定时任务,执行 crontab -e
添加一条每隔 10分钟执行一次的脚本,并将脚本输出内容写入某文件:
*/10 * * * * /home/pull-tomcat.sh >> /home/pull-tomcat.log
启示录
最后再汇总一下 Shell 的基本知识:
- 以后编写 stop 脚本时,用 ps 查找目标进程时,需要考虑过滤掉当前脚本名称。
- 第一行的脚本默认解释器,当使用
./文件名
执行时会生效,所以必须保证对应系统中存在指定的解释器。 - 环境变量问题,搞不清楚环境变量,使用命令时写全路径。
- crontab 定时任务添加时,可以追加命令执行结果到某个文件,便于查看任务执行情况。cron 自身的调度日志只能看到调度过程,而看不到脚本执行内容。Linux 下 cron 语法是 「分 时 日 月 周」五部分,跟 Quartz 的调度表达式少了头尾的 分和年。
- crontab 服务名称是 crond ,修改定时任务后,任务配置自动生效。