shell脚本实例(八)

【脚本99】域名到期提醒

写一个shell脚本,查询指定域名的过期时间,并在到期前一周,每天发一封提醒邮件。

  • 思路:
    大家可以在linux下使用命令“whois 域名”,如”whois xxx.com”,来获取该域名的一些信息。

  • 提示:
    whois命令,需要安装jwhois包

  • 参考代码:

#!/bin/bash

t1=`date +%s`
is_install_whois()
{
    which whois >/dev/null 2>/dev/null
    if [ $? -ne 0 ]
    then
        yum install -y jwhois
    fi
}

notify()
{
    e_d=`whois $1|grep 'Expiry Date'|awk '{print $4}'|cut -d 'T' -f 1`
    e_t=`date -d "$e_d" +%s`
    n=`echo "86400*7"|bc`
    e_t1=$[$e_t-$n]
    if [ $t1 -ge $e_t1 ] && [ $t1 -lt $e_t ]
    then
        /usr/local/sbin/mail.py aming_test@163.com "Domain $1 will be expire." "Domain $1 expire date is $e_d."
    fi
}

is_install_whois
notify xxx.com

【脚本100】自动增加公钥

写一个shell脚本,当我们执行时,提示要输入对方的ip和root密码,然后可以自动把本机的公钥增加到对方机器上,从而实现密钥认证。

  • 参考代码:
#!/bin/bash

read -p "Input IP: " ip
ping $ip -w 2 -c 2 >> /dev/null

## 查看ip是否可用
while [ $? -ne 0 ]
do
    read -p "your ip may not useable, Please Input your IP: " ip
    ping $ip -w 2 -c 2 >> /dev/null
done
read -p "Input root\'s password of this host: " password

## 检查命令子函数

check_ok() {
if [ $? != 0 ]
then
    echo "Error!."
    exit 1
fi
}

## yum需要用到的包
myyum() {
if ! rpm -qa |grep -q "$1"
then
    yum install -y $1
    check_ok
else
    echo $1  already installed
fi
}

for p in openssh-clients openssh expect
do
    myyum $p
done

## 在主机A上创建密钥对

if [ ! -f ~/.ssh/id_rsa ] || [ ! -f ~/.ssh/id_rsa.pub ]
then
    if [ -d ~/.ssh ]
    then
        mv ~/.ssh/  ~/.ssh_old
    fi
    echo -e "\n" | ssh-keygen -t rsa -P ''
    check_ok
fi

## 传私钥给主机B

if [ ! -d /usr/local/sbin/rsync_keys ]
then
    mkdir /usr/local/sbin/rsync_keys
fi
cd /usr/local/sbin/rsync_keys
if [ -f rsync.expect ]
then
    d=`date +%F-%T`
    mv rsync.expect $d.expect
fi

#创建远程同步的expect文件

cat >  rsync.expect <<EOF
#!/usr/bin/expect
set host [lindex \$argv 0]
#主机B的密码
set passwd [lindex \$argv 1]
spawn rsync -av /root/.ssh/id_rsa.pub root@\$host:/tmp/tmp.txt
expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "\$passwd\r" }
}
expect eof
spawn ssh root@\$host
expect {
"password:" { send "\$passwd\r" }
}
expect "]*"                         
send "\[ -f /root/.ssh/authorized_keys \] && cat /tmp/tmp.txt >>/root/.ssh/authorized_keys \r"
expect "]*"
send "\[ -f /root/.ssh/authorized_keys \] || mkdir -p /root/.ssh/ \r"            
send "\[ -f /root/.ssh/authorized_keys \] || mv /tmp/tmp.txt /root/.ssh/authorized_keys\r"            
expect "]*"
send "chmod 700 /root/.ssh; chmod 600 /root/.ssh/authorized_keys\r"
expect "]*"
send "exit\r"
EOF

check_ok
/usr/bin/expect /usr/local/sbin/rsync_keys/rsync.expect $ip $password
echo "OK,this script is successful. ssh $ip  to test it"

【脚本101】自动封/解封ip

  • 需求背景:

discuz论坛,每天有很多注册机注册的用户,然后发垃圾广告帖子。虽然使用了一些插件但没有效果。分析访问日志,发现有几个ip访问量特别大,所以想到可以写个shell脚本,通过分析访问日志,把访问量大的ip直接封掉。

但是这个脚本很有可能误伤,所以还需要考虑到自动解封这些ip。

  • 思路:

可以每分钟分析1次访问日志,设定一个阈值,把访问量大的ip用iptables封掉80端口
每20分钟检测一次已经被封ip的请求数据包数量,设定阈值,把没有请求的或者请求量很小的解封

-参考代码:

#! /bin/bash

## To block the ip of bad requesting.
## Writen by aming 2017-11-18.

log="/data/logs/www.xxx.com.log"
tmpdir="/tmp/badip"
#白名单ip,不应该被封
goodip="27.133.28.101"

[ -d $tmpdir ] || mkdir -p $tmpdir

t=`date -d "-1 min"  +%Y:%H:%M`

#截取一分钟以前的日志
grep "$t:" $log > $tmpdir/last_min.log

#把一分钟内日志条数大于120的标记为不正常的请求
awk '{print $1}' $tmpdir/last_min.log |sort -n |uniq -c |sort -n |tail |awk '$1>120 {print $2}'|grep -v "$good_ip"> $tmpdir/bad.ip

d3=`date +%M`

#每隔20分钟解封一次ip
if [ $d3 -eq "20" ] || [ $d3 -eq "40" ] || [ $d3 -eq "00" ]
then
        /sbin/iptables -nvL INPUT|grep 'DROP' |awk '$1<10 {print $8}'>$tmpdir/good.ip
        if [ -s $tmpdir/good.ip ]
        then
        for ip in `cat $tmpdir/good.ip`
        do
                /sbin/iptables -D INPUT -p tcp --dport 80 -s $ip -j DROP
                d4=`date +%Y%m%d-%H:%M`
                echo "$d4 $ip unblock" >>$tmpdir/unblock.ip
        done
        fi

        #解封后,再把iptables的计数器清零
        /sbin/iptables -Z INPUT
fi

if [ -s $tmpdir/bad.ip ]
then
    for ip in `cat $tmpdir/bad.ip`
    do
        /sbin/iptables -A INPUT -p tcp --dport 80 -s $ip -j DROP
        d4=`date +%Y%m%d-%H:%M`
        echo "$d4 $ip block" >>$tmpdir/block.ip
    done
fi

【脚本102】单机部署SpringBoot项目

有一台测试服务器,经常需要部署SpringBoot项目,手动部署太麻烦,于是写了个部署脚本

  • 脚本代码:
#!/bin/bash

# git仓库路径
GIT_REPOSITORY_HOME=/app/developer/git-repository
# jar包发布路径
PROD_HOME=/prod/java-back
# 应用列表
APPS=(app1 app2 app3)

if [ ! -n "$1" ]
then
   echo -e "请输入要发布的项目!"
   exit
fi

# cd dir
for((i=0;i<${#APPS[@]};i++))
do
   echo $1 ${APPS[i]}
   if [ $1 = ${APPS[i]} ]
   then
      echo -e ===========Enter $1=============
      cd ${GIT_REPOSITORY_HOME}/$1
      break
   fi
done

if [ `pwd` != ${GIT_REPOSITORY_HOME}/$1 ]
then
    echo -e "输入的项目名没有找到!"
    exit
fi

echo "==========git切换分之到master==============="
git checkout master

echo "==================git fetch======================"
git fetch

echo "==================git pull======================"
git pull

echo "===========选择线上环境编译并跳过单元测试===================="
mvn clean package -Dmaven.test.skip=true -Pprod

jar_path=${GIT_REPOSITORY_HOME}/$1/target/*-0.0.1-SNAPSHOT.jar
echo ${jar_path}
if [ -f ${jar_path} ]
then
    # backup dest
    echo -e "===========jar backup============="
    mv ${PROD_HOME}/$1/*-0.0.1-SNAPSHOT.jar ${PROD_HOME}/$1/$1-0.0.1-SNAPSHOT.jar.back

    # copy
    echo "======拷贝编译出来的jar包拷贝到$PROD_HOME======="
    if [ -d ${PROD_HOME}/$1 ]
    then
        /bin/cp ${GIT_REPOSITORY_HOME}/$1/target/*-0.0.1-SNAPSHOT.jar  ${PROD_HOME}/$1
    else
        mkdir ${PROD_HOME}/$1
        /bin/cp ${GIT_REPOSITORY_HOME}/$1/target/*-0.0.1-SNAPSHOT.jar  ${PROD_HOME}/$1
    fi

    echo "============停止项目============="
    jar_name=`jps |awk -F" " '{ print $2 }'| egrep ^$1.*jar$`
    pid=`jps |grep ${jar_name} | awk -F" " '{ print $1 }'`
    echo ${pid}
    kill -15 ${pid}

    echo "================sleep 10s========================="
    for i in 1 2 3 4 5 6 7 8 9 10
    do
        echo ${i}"s"
        sleep 1s
    done

    echo "============启动项目============="
    nohup java -jar ${PROD_HOME}/$1/*-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 &
    echo -e "===========Deploy Success============="
else
    echo -e "===========Deploy Error============="
fi

【脚本103】利用shell脚本监控verdaccio服务

我们在公司内部通过verdaccio创建了私有npm仓库,但是verdaccio不太稳定,每周都可能会挂掉一次两次的,在网上也没有找到好的解决方法。随着挂掉次数越来越多,痛定思痛,决定写个脚本主动监控verdaccio并在其挂掉以后自动重启。

判断服务是否正常运行

怎么判断verdaccio是否正常正常呢?如果是我肉眼判断的话,我直接打开verdaccio的主页,比如http://example:4873,如果能看到页面内容则说明服务正常,如果看到”无法访问次网站“等字样则说明服务挂掉了。为了实现自动化的监控和重启,我们必须通过脚本程序来做,在此我们可以通过curl命令来获取http://example:4873页面的响应码,如果返回200则说明服务正常,否则服务挂掉了。我们使用curl命令的参数-I只显示响应头。然后通过判断响应头中是否包含HTTP/1.1 200 OK字样来检查服务是否正常。因此就有了以下程序:

header=`curl -I http://example:4873`
if [[ $header =~ 'HTTP/1.1 200 OK' ]]; then
    echo 'ok'
else
    echo 'not ok'
fi

通过以上代码我们就可以监控服务是否正常了,我们再来拓展一下思路,其实我们还可以通过检查verdaccio服务进程是否存在来判断verdaccio服务正常与否。怎么判断进程是否存在呢?这里不再详细介绍,请参考一下代码:

# 查询有几个进程占用了`4873`端口
count=`lsof -ti :4873 | wc -l`
if [[ $count -ne 0 ]]; then
    echo 'ok'
else
    echo 'not ok'
fi

第二种方案不如第一种直观,我仍然把它写出来主要是提醒各位多多思考,不要拘泥于某一种方案。接下来我们仍然采用第一种更加直观的方案继续进行。

服务挂了以后自动重启

在上一节中我们已经能够检查服务是否正常了,在这一节中我们要实现的是自动重启服务,这个就非常简单了,想想我们是怎么启动verdaccio的,嗯,我们是在控制台中输入一下命令,nohup verdaccio &,其中nohup让命令永远执行下去,即使用户退出也没有关系,&让程序在后台运行。两者结合起来就可以程序永久地在后台运行。

header=`curl -I http://example:4873`
if [[ $header =~ 'HTTP/1.1 200 OK' ]]; then
    echo 'ok';
else
    # 重启服务
    nohup verdaccio &
fi

定期监控服务运行状况

通过前面两个步骤我们已经写出一个脚本,它会检查服务是否正常运行,如果服务挂掉了就会重启。这还是这个检查工作是一次性的,需要我们不停地执行脚本。当然可以通过crontab命令(以后再介绍)实现,不过在这里我们将通过写一个死循环逻辑来实现每隔一段时间来检查服务是否正常运行。

while true
do
    header=`curl -I http://example:4873`
    if [[ $header =~ 'HTTP/1.1 200 OK' ]]; then
        echo 'ok';
    else
        # 重启服务
        nohup verdaccio &
    fi
    # 每个10秒检查一次
    sleep 10s
done

sleep命令会让程序暂停一段时间,很适合用在循环方式运行的监控脚本中,它有一个参数表示要暂停的时间,时间单位可以是s秒,m分钟,h小时和d天。默认为秒。我们也可以传入小数0.1s来实现毫秒级的睡眠,但是sleep命令只能保证10ms的睡眠,如果你对时间精度要求特别高的话,sleep命令就无能为力了。

重启服务以后记录日志

当服务挂了以后除了重启以外,还需要将重启行为记入到日志文件中,方便以后我们查看什么时候服务被重启了,最终代码如下:

#!/bin/sh

# 获取脚本目录
shell_folder=$(cd `dirname $0`; pwd)

while true
do
    header=`curl -I http://example:4873`
    if [[ $header =~ 'HTTP/1.1 200 OK' ]]; then
        echo 'ok';
    else
        # 重启服务并记录日志
        nohup verdaccio &
        echo `date +%Y-%m-%d\ %H:%M:%S` "restart" >> $shell_folder/verdaccio.restart.log
    fi
    # 每个10秒检查一次
    sleep 10s
done

执行命令nohup sh verdaccio.sh &就启动了一个守护进程,实现了每隔10秒检查一次服务是否运行正常,如果挂掉就会重启的功能,并且还会记录到日志中。

如果你是在Ubuntu服务器上运行该命令,可能会遇到这个错误[[: not found,这是因为sh只是一个符号链接,最终指向是一个叫做dash的程序,自Ubuntu 6.10以后,系统的默认shell /bin/sh被改成了dashdash(the Debian Almquist shell) 是一个比bash小很多但仍兼容POSIX标准的shell,它占用的磁盘空间更少,执行shell脚本比bash更快,依赖的库文件更少,当然,在功能上无法与bash相比。所以在Ubuntu上我们需要指定使用bash,即nohup bash verdaccio.sh &

如何取消服务自动重启

如果有一天,你想关闭verdaccio服务,守护进程就会检测到该服务挂掉,并自动重启该服务。导致你想关闭该服务也不行了。所以我们首先要先关闭守护进程,如何关闭呢?我们可以使用jobs命令查看守护进程ID,然后杀掉该进程。

jobs -l
kill -9 <id>

针对verdaccio的特殊方案

其实verdaccio是用Node写的,因此可以通过pm2让其达到自动重启的功能,命令如下:

# 启动
pm2 start which verdaccio 
# 停止
pm2 stop which verdaccio 
# 查看日志
pm2 show verdaccio

   转载规则


《shell脚本实例(八)》 helen 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录