Shell帮你掌管上千台服务(多线程)
日常服务器运维时,我们都会批量管理多台服务器。
脚本,批量化,使我们工作中必不可少的。
首先我们需要一台堡垒机,负责免秘钥的登录和分发任务等等
堡垒机到主机
需要一套ssh-keygen来管理
- authorized_keys: 授权密钥
- id_rsa :私钥
- id_rsa.pub :公钥
- known_hosts:连接过机器记录信息,不需要属于“yes”
这些都是前面基础环境和工作。
默认脚本
这个脚本是最初始化的,可以远程过去执行任务,并打印结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#/bin/bash
START_TIME=`date +%s`
for i in `cat /opt/wei/pam_ip.txt` #ip存放的文件
do
cc="ssh deployer@$i \"sudo cat /etc/ssh/sshd_config | grep -Ev '^#' | grep UsePAM\" "
kk=`echo $cc|bash`
if [ ! -n "$kk" ]; then
echo "IP: $i UsePAM no seting"
sudo echo "IP: $i UsePAM no seting" >> /opt/wei/pam_no.txt
else
echo "IP: $i $kk"
sudo echo "IP: $i $kk" >> /opt/wei/pam_yes.txt
fi
done
END_TIME=`date +%s`
EXECUTING_TIME=`expr $END_TIME - $START_TIME`
echo "================end====================="
echo "程序运行时长:$EXECUTING_TIME S"
|
脚本优化(加入线程概念)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#/bin/bash
START_TIME=`date +%s`
for i in `cat /opt/wei/pam_ip.txt`
do
{ cc="ssh deployer@$i \"sudo cat /etc/ssh/sshd_config | grep -Ev '^#' | grep UsePAM\" "
kk=`echo $cc|bash`
if [ ! -n "$kk" ]; then
echo "IP: $i UsePAM no seting"
sudo echo "IP: $i UsePAM no seting" >> /opt/wei/pam_no.txt
else
echo "IP: $i $kk"
sudo echo "IP: $i $kk" >> /opt/we/pam_yes.txt
fi
}&
done
wait
END_TIME=`date +%s`
EXECUTING_TIME=`expr $END_TIME - $START_TIME`
echo "================end====================="
echo "程序运行:$EXECUTING_TIME S"
|
使用**’&’+wait** 实现**“多进程”**实现
运行很快,而且很不老实(顺序都乱了,大概是因为expr运算所花时间不同)
解析:这一个脚本的变化是在命令后面增加了&标记,意思是将进程扔到后台。在shell中,后台命令之间是不区分先来后到关系的。所以各后台子进程会抢夺资源进行运算。
wait命令:
wait [n]
n 表示当前shell中某个执行的后台命令的pid,wait命令会等待该后台进程执行完毕才允许下一个shell语句执行;如果没指定则代表当前shell后台执行的语句,wait会等待到所有的后台程序执行完毕为止。
如果没有wait,后面的shell语句是不会等待后台进程的,一些对前面后台进程有依赖关系的命令执行就不正确了
自定义线程数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
#!/bin/bash
Nproc=20 #最大并发进程数
function PushQue { #将PID值追加到队列中
Que="$Que $1"
Nrun=$(($Nrun+1))
}
function GenQue { #更新队列信息,先清空队列信息,然后检索生成新的队列信息
OldQue=$Que
Que=""; Nrun=0
for PID in $OldQue; do
if [[ -d /proc/$PID ]]; then
PushQue $PID
fi
done
}
function ChkQue { #检查队列信息,如果有已经结束了的进程的PID,那么更新队列信息
OldQue=$Que
for PID in $OldQue; do
if [[ ! -d /proc/$PID ]]; then
GenQue; break
fi
done
}
for i in `cat /opt/wei/pam_ip.txt`
do
{
cc="ssh deployer@$i \"sudo cat /etc/ssh/sshd_config | grep -Ev '^#' | grep UsePAM\" "
kk=`echo $cc|bash`
if [ ! -n "$kk" ]; then
echo "IP: $i UsePAM no seting"
sudo echo "IP: $i UsePAM no seting" >> /opt/wei/pam_no.txt
else
echo "IP: $i $kk"
sudo echo "IP: $i $kk" >> /opt/wei/pam_yes.txt
fi
}&
sleep 0.1 #考虑有序,开启这个参数,速度优先,则注释掉
PID=$!
PushQue $PID
while [[ $Nrun -ge $Nproc ]]; do # 如果Nrun大于Nproc,就一直ChkQue
ChkQue
sleep 0.1
done
done
wait
echo -e "time-consuming: $SECONDS seconds" #显示脚本执行耗时#!/bin/bash
|
使用模拟队列来控制进程数量
要控制后台同一时刻的进程数量,需要在原有循环的基础上增加管理机制。
一个方法是以for循环的子进程PID做为队列元素,模拟一个限定最大进程数的队列(只是一个长度固定的数组,并不是真实的队列)。队列的初始长度为0,循环每创建一个进程,就让队列长度+1。当队列长度到达设置的并发进程限制数之后,每隔一段时间检查队列,如果队列长度还是等于限制值,那么不做操作,继续轮询;如果检测到有并发进程执行结束了,那么队列长度-1,轮询检测到队列长度小于限制值后,会启动下一个待执行的进程,直至所有等待执行的并发进程全部执行完。
定义20线程,速度从原来的311S,降到40S,大大提高效率