ssh远程迭代拷贝、执行操作、退出ssh操作

一、背景:

如果需要实现这样一个功能:

有一个存储机器列表的文件,里面按TAB键分割,依次存储了“IP”,“_USER(能执行ssh后的操作)”,“PORT(ssh协议端口)”这几列,依次登录到每台机器上,拷贝可执行业务代码包,并自动设置该用户(_USER)的crontab定时调度任务,以使任务可以按规定在每台server上启动起来。

则将需求分解,可得到如下操作步骤:

1、读取server list文件(已TAB分割),从里面获取每一行记录,解析出“IP”,“_USER(能执行ssh后的操作)”,“PORT(ssh协议端口)”;
‘’
2、根据1中解析出的server信息,遍历每一台server,ssh远程登录过去,执行如下操作 list:
A). 拷贝业务代码包(biz.tar.gz)到该server,修改文件宿主(由com_admin -> _USER)。
B). 判断该用户的crontab文件(_USER_CRON_FILE)是否已存在,不存在,则由root创建之并将文件所属关系重新修改为_USER。
C). 添加crontab调度业务逻辑到该用户的crontab文件(_USER_CRON_FILE)中。
D). 切换到_USER下,解压业务代码包(biz.tar.gz)。
E). 退出当前shell环境,释放连接

Pre-Requirements:
1、脚本所在的server(如跳板机),需要能通过指定的PORT免密登陆到目标主机;
2、目标主机需要登录root的sudo权限,如本例中的com_admin用户,必须要能通过"sudo su"命令切换为root;

二、代码逻辑

1、服务器列表文件

以TAB作为分隔符,存贮“IP”,“_USER(能执行ssh后的操作)”,“PORT(ssh协议端口)”这几列。

server_list.txt

#111.111.111.111	mysql	22
111.111.111.112	oracle	22
111.111.111.113	com_admin	22222
111.111.111.114	zhangsan	22223

2、业务逻辑 Shell 脚本

biz_runwith_noroot.sh

#!/bin/bash

set -x

# 获取当前路径
dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
SERVER_LIST_FILE=$dir/server_list.txt

echo 'Current dir='$dir

PRESSURE_CODE=biz.tar.gz

# 操作数量
cnt=1
all_cnt=`cat $SERVER_LIST_FILE | wc -l`

# 遍历每一台server
while read line
do
#sleep 10
echo ">>当前正在操作第 " $cnt " 台主机,共有 "$all_cnt " 台主机"

let cnt=cnt+1

#正则表达式,匹配以#开始的行
if [[ "$line" =~ ^#.* ]];then
	echo '正在处理的行是:'$line
	continue
fi

#!如果处理的数据文件,字段间分割符不是空白字符(如tab、空白)。
#    以csv文件为例,以下注释需要放开,并需临时设置当前IFS=',',并在数据处理完毕后后改回来
# --------------------------------------------------------------------------
#OLD_IFS=$IFS
#IFS='	'
#arr=($line)

# 将每一行按默认IFS(一般指代空白,如tab,或空格)分割,并转化为数组
arr=($line)
#IFS=$OLD_IFS

# 提取IP/_USER属性
IP=${arr[0]}
_USER=${arr[1]}
PORT=${arr[2]}
echo 'IP='${arr[0]}', _USER='${arr[1]}', PORT='${arr[2]}

# 传输加压文件至每一台server上
scp -P $PORT $dir/$PRESSURE_CODE com_admin@${IP}:~

# 每一台server的Home目录
HOME_DIR=/home/${_USER}

# 每一台server上的cron文件,该文件存在于/var/spool/cron/目录下,且以用户名命名
# 如:/var/spool/cron/zhangsan
_USER_CRON_FILE=/var/spool/cron/$_USER

# 创建指定用户(如com_admin、mysql、oracle)的crontab定时调度任务
# 这里使用EOF方式,在ssh登录到主机后,执行一连串操作。
# EOF表示END OF FILE,只是界定处于EOF块之前的操作语句,会被整体执行。
# EOF只是个标识,也可以使用其他成对出现的字符。
ssh -p $PORT $IP  << EOF 

# 以下操作以root身份执行
sudo su root
cd
pwd
mv /home/com_admin/$PRESSURE_CODE $HOME_DIR/

sleep 3

chown -R $_USER:$_USER $HOME_DIR/${PRESSURE_CODE}

ls -l $HOME_DIR/${PRESSURE_CODE}

# 校验$_USER 用户的cron文件是否已存在。
# 已存在表明之前根据"cron -e"创建过,则cron文件无须更改权限;否则表明用户的cron为手工创建,需修改用户的 cron为系统默认的600
# ------------------------------------------------------------------------------
if [ ! -f "$_USER_CRON_FILE" ];then
echo '不存在文件: '$_USER_CRON_FILE',将创建之,并把该文件权限修改为cron默认的600'
touch $_USER_CRON_FILE
sleep 3

chmod 600 $_USER_CRON_FILE
sleep 3

chown -R $_USER:$_USER $_USER_CRON_FILE
sleep 3
else
echo '已存在文件: '$_USER_CRON_FILE',不做任何操作,维持原文件权限'
fi

cat >> /var/spool/cron/${_USER} << DSG
# 添加crontab调度业务逻辑
0 0 * * * sh /home/${_USER}/biz/start_new.sh main >> /home/${_USER}/biz/start_new.sh.out 2>&1 &
0 5 * * * sh /home/${_USER}/biz/start_new.sh stopAll >> /home/${_USER}/biz/start_new.sh.stopall_out 2>&1 &
DSG

# 切换回 $_USER,较为安全的执行命令
su $_USER
cd ~
tar -xzvf $HOME_DIR/$PRESSURE_CODE

# 如下exit语句,将依次退出各个用户的shell环境,最终关闭整个ssh进程,不空占句柄数,是一种良好的编程习惯。
# 退出当前用户shell(如oracle)
pwd
exit

# 退出当前用户shell(如root)
pwd
exit

# 退出当前用户shell(如com_admin)
pwd
exit
EOF

done < $SERVER_LIST_FILE

echo '已拷贝的服务器个数为:' $(($cnt - 1))

set +x

biz_runwith_comadmin.sh

#!/bin/bash

echo "The numbers of input parameters is: "$#
echo "The script's input parameters is: "$@
echo ""

if [[ $# -ne 4 ]];then
	echo "USAGE: $0 <SSH_PORT> <start|stop> <vm|phy> <1|2|3>" 
	echo "Please confirm your input parameters."
	exit
else
        server_type=$3
        mem_type=$4
        if [ $4 = 1 ];then
                mem_type="group_23g"
        elif [ $4 = 2 ];then
                mem_type="group_30g"
        elif [ $4 = 3 ];then
                mem_type="group_38g"
        fi
fi

dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
SERVER_LIST_FILE=$dir/server_list.txt.${server_type}.${mem_type}

echo $dir

PRESSURE_CODE=my_order.tar.gz
PRESSURE_PROJECT_NAME=my_order

# 操作数量
cnt=1
all_cnt=`cat $SERVER_LIST_FILE | wc -l`
# 遍历每一台server
while read line
do

echo ">>当前正在操作第 " $cnt " 台主机,共有 "$all_cnt " 台主机"

#echo $line

#过滤掉#开头的IP机器
if [[ "$line" =~ ^#.* ]];then
        echo '正在处理的行是:'$line
        continue
fi

#OLD_IFS=$IFS
#IFS='	'
#arr=($line)

# 将每一行按默认IFS(一般指代空白,如tab,或空格)分割,并转化为数组
arr=($line)
#IFS=$OLD_IFS

# 提取IP/USER属性
IP=${arr[0]}
USER=${arr[1]}
#PORT=${arr[2]}
PORT=$1
COMMAND=$2
#echo 'IP='${arr[0]}', USER='${arr[1]}', PORT='${arr[2]}
echo 'IP='$IP', USER='$USER', PORT='$PORT

# transfer PRESSURE file to each server
scp -P $PORT $dir/$PRESSURE_CODE com_admin@${IP}:~

# create user's crontab(eg. mysql、oracle、bi_admin)
ssh -p $PORT $IP  << EOF 

sudo su root
cd
pwd

# Copy my_order add pression program to root's home directory.
mv /home/com_admin/$PRESSURE_CODE ~

ls -l $PRESSURE_CODE

tar -xzvf $PRESSURE_CODE

#cat >> /var/spool/cron/root << DSG
#50 19 * * * sh /root/my_order/start_new.sh main >> /root/my_order/start_new.sh.out 2>&1 &
#59 19 * * * sh /root/my_order/start_new.sh stopAll >> /root/my_order/start_new.sh.stopall_out 2>&1 &
#DSG

#chmod 600 /var/spool/cron/root

chown -R root. $PRESSURE_PROJECT_NAME

cd $PRESSURE_PROJECT_NAME

#Decompression my_order/jdk-8u191-linux-x64.tar.gz
#rm -rf jdk1.8.0_191
JDK_DIR=/root/jdk1.8.0_191

#不存在和有执行权限的话,从my_order文件夹中拷贝到root根下,并解压;也可以用"-d"直接判断是否为folder
if [ ! -x $JDK_DIR ];then
	cp jdk-8u191-linux-x64.tar.gz /root
	tar -xzvf /root/jdk-8u191-linux-x64.tar.gz -C /root/
fi

if [[ ${COMMAND} =~ "stop" ]];then
	nohup sh /root/my_order/start_new.sh stopAll >> /root/my_order/start_new.sh.out 2>&1 &
elif [[ ${COMMAND} =~ "start" ]];then	
	nohup sh /root/my_order/start_new.sh main >> /root/my_order/start_new.sh.out 2>&1 &
fi

exit
EOF

let cnt=cnt+1
done < $SERVER_LIST_FILE

echo '已拷贝的BI服务器个数为:' $(($cnt - 1))

#Exit this program
exit

三、总结

1、避开系统变量为变量命名。
如USER替换为_USER

2、shell中获取一个变量讲过加减乘除后的值,可以使用如下语法:
①、let cnt=cnt+1
②、$(($cnt - 1))

3、精准获取脚本执行的当前上下文路径:

dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)

4、遍历并读取文件中的每一行,可以使用如下语法:

while read line
do
。。。
done < $SERVER_LIST_FILE

5、判断字符串是否以某指定字符开头,以下以#开头为例说明:

if [[ "$line" =~ ^#.* ]];then
        continue 
fi

continue:退出本次操作,进入下一个循环操作;
break:退出本层循环,如果循环有多层,可以使用break n语法。

6、正确使用IFS进行字符分割
如果字符串分割符不是默认的空白(如tab,或空格),则需要使用这种语法进行字符串分割,操作步骤如下:
①、保存系统IFS;
②、IFS赋值为新的字段分隔符,如解析csv文件,需指定为逗号分割符;
③、使用()语法将字符串按指定分隔符转换成数组array;
④、遍历数组语法:
A)、遍历每一个元素:${arr[0]},${arr[1]},${arr[2]}
B)、遍历全部元素:${arr[@]}

OLD_IFS=$IFS
IFS=' ,'

# 将每一行按默认IFS(一般指代空白,如tab,或空格)分割,并转化为数组
arr=($line)

IFS=$OLD_IFS

7、默认ssh端口不是22时ssh和scp命令的用法:
①、scp (大P)
scp -P $PORT d i r / dir/ dir/PRESSURE_CODE com_admin@${IP}:~

②、ssh(小p)
ssh -p $PORT $IP << EOF

8、判断文件存在 vs 判断字符以指定字符开头语法的不同
①、判断文件存在(一个中括号
if [ ! -f "$USER_CRON_FILE" ];then

②、判断字符以指定字符开头(两个中括号
if [[ "$line" =~ ^#.* ]];then

9、ssh免密配置操作
①、目标主机 “ssh-keygen -t rsa”
②、拷贝跳板机上.ssh中的id_rsa.pub到目标主机的authorized_keys(权限600)
M1:拷贝跳板机id_rsa.pub内容,黏贴到目标主机的authorized_keys文件中。
M2:ssh-copy-id -i ~/.ssh/id_rsa.pub 目标机器ip地址/主机名

10、目标主机上普通用户添加sudo权限操作
①、root身份打开/etc/sudoers文件;
②、"## Same thing without a password" 这行下添加如下语句:

%com_admin ALL=(ALL) NOPASSWD: ALL

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值