一、分发系统介绍:expect,也就是一个分发脚本:php
场景:业务会愈来愈大,网站app,后端,服务端所使用的编程语言为php,要想运行这个环境,就须要配置LAMP或者LNMP,最后还须要把代码上传到服务器上去,业务不断的增长,一两台机器还好好说,要是几十台,上百台设备,这时候就须要用到了分发系统了,把天天更新的代码发布到每一台机器上去:shell
expect是一种脚本语言,经过它能够实现传输,上线代码,能够实现远程执行命令,不须要输入密码:编程
首先准备一台模板机器(最新的代码待上线),而后给下面分发代码的多台台机器上线,机器的IP,对应用户的密码,然经过expect,借助rsync来上传代码,能够用expect去登陆执行某些命令:vim
1:expect脚本安装:后端
yum install -y expect (安装包和mkpasswd是一块儿的)安全
示例1:自动远程登陆,并执行一些命令:bash
[root@localhost_01 sbin]# vim 1.expect #! /usr/bin/expect set host "192.168.149.129" set passwd "nihao123!" set port "-p 56888" spawn ssh $port root@$host expect { "yes/no" { send "yes\r"; exp_continue} "password:" { send "$passwd\r" } } ##expect eof 表示登陆远程机器后,暂停一两秒钟后退出: interact #脚本结束
注释:在expect中定义变量须要set host才能够:服务器
注释:在文件里 expect { }这里是保证登陆信息,第一次登陆时候,则须要yes记录登陆信息,下一次则不须要了,须要清空 >/root/.ssh/known_hosts才能够:app
exp_continue表示继续/r换行, interact表示继续停留在这台机器不退出:若是不加的话则登陆后立刻退出:ssh
[root@localhost_01 sbin]# ssh -p 56888 192.168.149.129 root@192.168.149.129's password: #由于咱们之前登陆过,全部不询问为了安全性,是否须要还继续连接: [root@localhost_01 sbin]# > /root/.ssh/ authorized_keys authorized_keys_xshell id_rsa.pub authorized_keys.bakup id_rsa known_hosts [root@localhost_01 sbin]# > /root/.ssh/known_hosts #清空这个文件里面的信息: [root@localhost_01 sbin]# ssh -p 56888 192.168.149.129 #再次登陆时则会询问: The authenticity of host '[192.168.149.129]:56888 ([192.168.149.129]:56888)' can't be established. ECDSA key fingerprint is SHA256:mtOEV1y+2ErXFWqQRL0rYCQGGuVE7z4n1is+2dPQc7E. ECDSA key fingerprint is MD5:d9:f7:03:de:c4:f5:40:89:be:3c:25:6d:70:6a:49:a5. Are you sure you want to continue connecting (yes/no)? yes
加入执行权限后成功登陆: chmod a+x 1.expect
[root@localhost_01 sbin]# ./1.expect -bash: ./1.expect: 权限不够 [root@localhost_01 sbin]# chmod a+x 1.expect [root@localhost_01 sbin]# ./1.expect spawn ssh -p 56888 root@192.168.149.129 The authenticity of host '[192.168.149.129]:56888 ([192.168.149.129]:56888)' can't be established. ECDSA key fingerprint is SHA256:mtOEV1y+2ErXFWqQRL0rYCQGGuVE7z4n1is+2dPQc7E. ECDSA key fingerprint is MD5:d9:f7:03:de:c4:f5:40:89:be:3c:25:6d:70:6a:49:a5. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[192.168.149.129]:56888' (ECDSA) to the list of known hosts. root@192.168.149.129's password: Last login: Thu Oct 4 00:33:35 2018 from 192.168.149.135 [root@localhost_02 ~]#
2:expect远程执行命令:
远程执行完命令,并退出:
[root@localhost_01 sbin]# vim 2.expect #!/usr/bin/expect set user "root" set passwd "nihao123!" set port "-p 56888" spawn ssh $port $user@192.168.180.135 expect { "yes/no" { send "yes\r"; exp_continue} "password:" { send "$passwd\r" } } expect "]*" send "touch /tmp/12.txt\r" expect "]*" send "echo 1212 > /tmp/12.txt\r" expect "]*" send "exit\r"
注释:expect "]*" 星号表示通配,匹配右边的全部字符(不论是root用户普通用户):
注释:send 表示执行这个命令:
3:须要加执行权限并执行这个命令:
[root@localhost_01 sbin]# chmod a+x 2.expect [root@localhost_01 sbin]# ll 2.expect -rwxr-xr-x 1 root root 307 10月 4 16:09 2.expect [root@localhost_01 sbin]# ./2.expect spawn ssh -p 56888 root@192.168.149.129 root@192.168.149.129's password: Last login: Thu Oct 4 06:00:22 2018 from 192.168.149.130 [root@localhost_02 ~]# touch /tmp/12.txt [root@localhost_02 ~]# echo 1212 > /tmp/12.txt
而后在129这台机器上查看是否建立成功:
[root@localhost_02 ~]# ls /tmp/12.txt /tmp/12.txt [root@localhost_02 ~]# cat /tmp/12.txt 1212
四、expect脚本传递参数:
[root@localhost_01 sbin]# vim 3.expect [root@localhost_01 sbin]# cat 3.expect #!/usr/bin/expect set port [lindex $argv 0] #第一个参数,若是端口仍是默认的22,则不须要定义次参数,依次往下定义便可: set user [lindex $argv 1] #第二个参数 set host [lindex $argv 2] #第三个参数 set passwd "nihao123!" set cm [lindex $argv 3] #第四参数 spawn ssh -p $port $user@$host expect { "yes/no" { send "yes\r"} "password:" { send "$passwd\r" } } expect "]*" send "$cm\r" expect "]*" send "exit\r"
1:增长执行权限: chmod +x 3.expect
[root@localhost_01 sbin]# chmod a+x 3.expect [root@localhost_01 sbin]# ll 3.expect -rwxr-xr-x 1 root root 369 10月 4 16:37 3.expect
2:执行此脚本:
./3.expect 第一个参数(port) 第二个参数(user) 第三个参数(host) 第四个参数(command)
[root@localhost_01 sbin]# ./3.expect 56888 root 192.168.149.129 ls spawn ssh -p 56888 root@192.168.149.129 root@192.168.149.129's password: Last login: Thu Oct 4 06:24:37 2018 from 192.168.149.130 [root@localhost_02 ~]# ls 1 anaconda-ks.cfg bash CentOS7-Base-163.repo link shell test test.txt
一样,也支持多条参数: 多条命令最后用双引号括住,而后用分号分割则能够:
[root@localhost_01 sbin]# ./3.expect 56888 root 192.168.149.129 "ls;w" spawn ssh -p 56888 root@192.168.149.129 root@192.168.149.129's password: Last login: Thu Oct 4 06:32:46 2018 from 192.168.149.130 [root@localhost_02 ~]# ls;w 1 anaconda-ks.cfg bash CentOS7-Base-163.repo link shell test test.txt 06:32:57 up 19:42, 2 users, load average: 0.42, 0.23, 0.16 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 192.168.149.130 06:32 1.00s 0.02s 0.01s w root pts/1 192.168.149.135 00:33 26:57 0.03s 0.03s -bash
注释:若是是执行多条命令的话,也能够传递多个参数,若是要是把多个命令当成一个参数来传递的话,则须要用双引号引发来,而且用分号分割才能够:
一样:当咱们传递第四个参数时: vmstat模式超时时间是10秒:
[root@localhost_01 sbin]# ./3.expect 56888 root 192.168.149.129 "w;vmstat 1" spawn ssh -p 56888 root@192.168.149.129 root@192.168.149.129's password: Last failed login: Thu Oct 4 06:56:10 CST 2018 from 192.168.149.130 on ssh:notty There were 2 failed login attempts since the last successful login. Last login: Thu Oct 4 06:37:26 2018 from 192.168.149.130 [root@localhost_02 ~]# w;vmstat 1 07:07:33 up 20:17, 2 users, load average: 0.01, 0.19, 0.20 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 192.168.149.130 07:07 0.00s 0.03s 0.03s w root pts/1 192.168.149.135 00:33 1:01m 0.03s 0.03s -bash procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 3 0 0 1026092 2076 240768 0 0 3 42 61 101 0 0 99 1 0 0 0 0 1026108 2076 240820 0 0 0 0 37 78 0 1 99 0 0 0 0 0 1026108 2076 240820 0 0 0 0 42 84 0 0 100 0 0 0 0 0 1026108 2076 240820 0 0 0 0 42 82 0 0 100 0 0 0 0 0 1026108 2076 240820 0 0 0 10 54 90 0 0 100 0 0 0 0 0 1026108 2076 240820 0 0 0 0 37 78 0 0 100 0 0 0 0 0 1026108 2076 240820 0 0 0 103 43 104 0 1 99 0 0 0 0 0 1026108 2076 240820 0 0 0 0 45 86 0 0 100 0 0 0 0 0 1026108 2076 240820 0 0 0 0 37 74 0 0 100 0 0 0 0 0 1026108 2076 240820 0 0 0 0 34 72 0 0 100 0 0 0 0 0 1026108 2076 240820 0 0 0 0 33 70 0 0 100 0 0 0 0 0 1026108 2076 240820 0 0 0 0 35 80 0 0 100 0 0 十秒后自动超时:
注释:也能够设置永不超时: 须要在执行命令(也就是第四个参数)后面添加 set timeout -1
expect "]*" send "$cm\r" #set timeout -1 #表示永不超时: #set timeout 5 #也能够手动指定具体的秒数: 本次5ms: expect "]*" send "exit\r"
一、expect同步文件: 自动同步文件; 一台机器向一台机器同步文件:
1:在一台机器上,把文件同步到其余机器上;并添加执行权限:
[root@localhost_01 sbin]# vim 4.expect [root@localhost_01 sbin]# cat 4.expect #!/usr/bin/expect set passwd "nihao123!" spawn rsync -av -e "ssh -p 56888" root@192.168.149.129:/tmp/12.txt /tmp/ expect { "yes/no" { send "yes\r"} "password:" { send $passwd\r} } expect eof [root@localhost_01 sbin]# chmod a+x 4.expect
2:执行这个脚本: ./4.expect 并查看同步后的文件内容:
[root@localhost_01 sbin]# ./4.expect spawn rsync -av -e ssh -p 56888 root@192.168.149.129:/tmp/12.txt /tmp/ root@192.168.149.129's password: receiving incremental file list 12.txt sent 43 bytes received 96 bytes 278.00 bytes/sec total size is 5 speedup is 0.04 [root@localhost_01 sbin]# ls /tmp/12.txt /tmp/12.txt [root@localhost_01 sbin]# ll /tmp/12.txt -rw-r--r-- 1 root root 5 Oct 4 06:04 /tmp/12.txt
注释:expect eof表示只有spawn执行命令的结果才会被expect捕捉到,由于spawn会启动一个进程,只有这个进程相关信息才会被捕捉到,注意包括标准输入的提示信息,eof 和timeout:
以下测试,注释掉expect eof,则还没来得及传输了,就退出来了(仅仅到输入密码的那里)
[root@localhost_01 sbin]# vim 4.expect #!/usr/bin/expect set passwd "nihao123!" spawn rsync -av -e "ssh -p 56888" root@192.168.149.129:/tmp/12.txt /tmp/ expect { "yes/no" { send "yes\r"} "password:" { send $passwd\r} } #expect eof #注释掉: [root@localhost_01 sbin]# rm -fr /tmp/12.txt [root@localhost_01 sbin]# ./4.expect spawn rsync -av -e ssh -p 56888 root@192.168.149.129:/tmp/12.txt /tmp/ root@192.168.149.129's password: [root@localhost_01 sbin]#
二、expect指定host和要同步的文件:
[root@localhost_01 sbin]# vim 5.expect [root@localhost_01 sbin]# cat 5.expect #!/usr/bin/expect set passwd "nihao123!" set host [lindex $argv 0] set file [lindex $argv 1] spawn rsync -av -e "ssh -p 56888" $file root@$host:$file expect { "yes/no" { send "yes\r"} "password:" { send "$passwd\r"} } expect eof [root@localhost_01 sbin]# chmod a+x 5.expect
注释:用变量定义的文件所在目录要写绝对路径才可:
如:同步本机目录/tmp/12.txt到远端主机192.168.149.129的/tmp/目录下:
[root@localhost_01 sbin]# ./5.expect 192.168.149.129 "/tmp/12.txt" spawn rsync -av -e ssh -p 56888 /tmp/12.txt root@192.168.149.129:/tmp/12.txt root@192.168.149.129's password: sending incremental file list 12.txt sent 100 bytes received 35 bytes 90.00 bytes/sec total size is 9 speedup is 0.07
注释:由于同步rsync须要借助ssh的端口,若是是默认的22端口或者自定义的,那若是有多台机器的ssh的端口各不相同,那是否能够把port当成一个参数来传递:以下:
[root@localhost_01 sbin]# vim 5.expect #!/usr/bin/expect set passwd "nihao123!" set port [lindex $argv 0] set host [lindex $argv 1] set file [lindex $argv 2] spawn rsync -av -e "ssh -p $port" $file root@$host:$file expect { "yes/no" { send "yes\r"} "password:" { send "$passwd\r"} } expect eof [root@localhost_01 sbin]# ./5.expect 56888 192.168.149.129 "/tmp/12.txt" spawn rsync -av -e ssh -p 56888 /tmp/12.txt root@192.168.149.129:/tmp/12.txt root@192.168.149.129's password: sending incremental file list sent 44 bytes received 12 bytes 37.33 bytes/sec total size is 9 speedup is 0.16
二、构建文件分发系统;
一台机器向多台机器同步文件:
需求背景:对于大公司而言,确定会时不时有网站或者配置文件更新,并且使用的机器也是好多台,少则几台,多则上百台,因此自动同步文件是很重要的:
实现思路:首先须要有一台模板机器,提早把要分发的文件准备好,而后只要使用expect脚本批量把须要同步的文件分发到目标机器便可:
核心命令:rsync -av --filep-from=list.txt / root@host:/
注释:使用rsync 的 --files参数,能够实现调用文件里面的列表(列表里的路径须要绝对路径),进行多个文件远程传输,进而实现文件分发:
1:建立rsync.expect的脚本内容:
[root@localhost_01 sbin]# vim rsync.expect [root@localhost_01 sbin]# cat rsync.expect #!/usr/bin/expect set passwd "nihao123!" set host [lindex $argv 0] set file [lindex $argv 1] spawn rsync -avR -e "ssh -p 56888" --files-from=$file / root@$host:/ #这个地方定义了原目录和目标目录以跟目录开始 expect { "yes/no" { send "yes\r"} "password:" { send "$passwd\r" } } expect eof [root@localhost_01 sbin]# chmod a+x rsync.expect
注释:文件路径放在/tmp/flie.list:同步的路径,咱们要把要同步的文件路径放在/tmp/list.txt里面,这个时候须要保证对方机器上也要有个这个路径(文件有没有无所谓),若是没有路径,则须要使用rsync -avR建立路径:
注释:IP列表 /tmp/ip.list:实现分发系统,远程同步的机器数量大,还须要定义一个IP列表 /tmp/ip.txt;
建立同步文件的列表和建立分发系统的IP地址列表:
注释:同步前提是分发系统的密码须要都同样才能够,可是这也容易形成系统不安全(expect脚本泄露),不过能够也使用秘钥验证(rsync验证),省略掉密码的哪一步;
2:建立同步文件列表(写绝对路径):/tmp/file.list
[root@localhost_01 sbin]# vim /tmp/flie.list /tmp/12.txt /root/shell/test.sh /root/111/222/111.txt
3:建立IP地址列表: /tmp/ip.list
[root@localhost_01 sbin]# vim /tmp/ip.list 192.168.149.129 192.168.149.131
4:建立rsync.sh脚本: 并给予执行权限:
[root@localhost_01 sbin]# vim rsync.sh #!/bin/bash for ip in `cat /tmp/ip.list` do echo $ip ./rsync.expect $ip /tmp/file.list done [root@localhost_01 sbin]# chmod a+x rsync.sh
5:执行rsync.sh脚本: ./rsync.sh
[root@localhost_01 sbin]# sh rsync.sh 192.168.149.129 spawn rsync -avR -e ssh -p 56888 --files-from=/tmp/file.list / root@192.168.149.129:/ root@192.168.149.129's password: building file list ... done root/ root/111/ root/111/222/ root/111/222/111.txt root/shell/ root/shell/test.sh tmp/ sent 415 bytes received 69 bytes 322.67 bytes/sec total size is 118 speedup is 0.24 192.168.149.131 spawn rsync -avR -e ssh -p 56888 --files-from=/tmp/file.list / root@192.168.149.131:/ root@192.168.149.131's password: building file list ... done root/ root/111/ root/111/222/ root/111/222/111.txt root/shell/ root/shell/test.sh tmp/ tmp/12.txt sent 467 bytes received 88 bytes 1,110.00 bytes/sec total size is 118 speedup is 0.21
注释:这个shell脚本的目的,就是遍历一下IP列表中的IP地址;
注释:重要的是expect脚本要加上执行权限,而且两端都要安装rsync服务:
注释:分发系统还有一个重要的关键是,确保同步的机器的密码以及端口号(由于rsync要以来sshd的端口)一致,不然将不能实现同步;因此这就存在一个弊端,一旦脚本暴露,将会让别人知道如何登录你机器;固然也有对应的解决办法,那就是使用密钥认证,这样的话,天然在命令行业省去“输入密码< password:" { send "$passwd\r" } >''”和“定义密码< set passwd "123123a" >”的命令了;
二、批量远程执行命令; 有时候远端机器操做完后,须要远程重启,或者重启服务等,则使用这个方法;
1: 建立exe.expcet脚本: 并给执行权限;
[root@localhost_01 sbin]# vim exe.expect #!/usr/bin/expect set port [lindex $argv 0] set host [lindex $argv 1] set cm [lindex $argv 2] set passwd "nihao123!" spawn ssh $port root@$host expect { "yes/no" { send "yes\r"} "password:" { send "$passwd\r" } } expect "]*" send "$cm\r" expect "]*" send "exit\r" [root@localhost_01 sbin]# chmod a+x exe.expect
2:建立exe.sh执行脚本:
#!/bin/bash for ip in `cat ip.list` do echo $ip ./exe.expect $port -p56888 $ip "hostname" done
3:执行脚本: sh exe.sh
[root@localhost_01 sbin]# sh exe.sh 192.168.149.129 spawn ssh -p56888 root@192.168.149.129 root@192.168.149.129's password: Last login: Thu Oct 4 07:12:29 2018 from 192.168.149.130 [root@localhost_02 ~]# hostname localhost_02 [root@localhost_02 ~]# 192.168.149.131 spawn ssh -p56888 root@192.168.149.131 root@192.168.149.131's password: Last login: Thu Oct 4 20:19:06 2018 from 192.168.149.135 [root@localhost_03 ~]# hostname localhost_03