expect——实现远程服务的自动部署

自动化部署的场景

比如我在本地和服务端都使用 git 来管理代码,当本地代码修改过后,想将改动升级到服务端。这时,我应该先将本地的代码 commit、合并到发布分支、push,然后再 ssh 登录到服务器,在服务器上执行git pull,将代码拉下来。这个过程看似简单,但是如果每天需要执行多次的话,就非常麻烦了。

简单的实现一个 sh 脚本,比如下面

ssh -p 121 root@x.x.x.x
cd /www/wwwroot/xx.com
git pull

执行此脚本,会发现执行到第一行之后,终端会阻塞,因为 ssh 命令是一个阻塞命令,后续的命令将无法执行。

expect 可以实现:开启一个子进程,然后监控这个子进程的输出,按照输出的内容来决定下一步执行的命令。在上面的例子中,就是开启一个子的 ssh 登录的进程,然后监控这个子进程的输出,这样就可以根据输出来决定下面执行的命令啦。

expect 的工作逻辑

先看一个简单的 expect 脚本。

echo '这是一个 shell 脚本'
/usr/bin/expect << eof
set timeout -1
spawn ssh -p 121 root@x.x.x.x
expect "root@*"
send "cd /www/wwwroot/xx/ \r"
expect "root@*"
send "mysqldump -uroot -h localhost -ppassword  dbname > back.sql \r"
expect "root@*"
send "zip db.zip back.sql \r"
expect "root@*"
send "exit \r"
eof
echo 'shell 脚本结束'
  1. expect是一个开发语言,通过/usr/bin/expect进入expect的执行环境。/usr/bin/expect <<eof eof之间是 expect 脚本,而不是shell脚本。
  2. mac预装了expect,这里/usr/bin/expect 是 expect 的安装目录
  3. spawn 指令的含义为开启一个进程,并将这个子进程的标准输入、输出和错误输出重定向到当前 Expect 环境中,从而实现对子进程的监控和控制。这里 spawn 执行的是 ssh 命令,他会将 ssh 进程的 io 结果输出给 expect,所以 expect 才可以实现按照期望输出值来执行命令的能力。
  4. expect 后是期望内容的表达式
  5. send 是发送的命令

通过上面 几点估计你就可以很明白 expect 的工作逻辑了。上面的脚本实现了 ssh 登录一个服务器,然后导出一个数据库并将 导出的 sql 文件压缩。

注意事项

  1. 使用大括号的语法发送的命令将异步执行,不使用大括号发送的命令将同步执行。等待同步执行结果后才执行expect匹配。如:

    expect "root@*"  {send "cd /www/dist/web/ \r"}

    此命令将异步执行,如:

    expect "root@*"  
    send "cd /www/dist/web/ \r"
    expect "root@*"  

    此命令将同步执行,直到cd命令的结果返回才执行下一expect命令。

  2. 使用-d参数可以用调试模式执行命令,这在调试的时候非常有用。如:
    /usr/bin/expect-d<<eof

  3. send执行的命令有超时限制,如果想设定为无限等待阻塞命令,可以使用timeout选项。如:

    echo '🗂 dump 数据 并 zip '
    /usr/bin/expect << eof
    set timeout -1
    spawn ssh -p 1222 root@x.x.x.x
    send "cd /www/wwwroot/xxx.com\r"
    expect "root@*"
    send "zip db.zip db.sql \r"
    expect "root@*"
    send "xxxxx"
    eof
    echo done!
  4. expect是一种编程语言,expect和send是此语言中两个独立的命令。一般来讲一行expect,一行send。

one more thing

服务的自动化部署是一个常见的需求,尤其是当服务较多时。比如一个服务需要部署到多台服务器(负载均衡或者私有化部署)。这时手动部署就成了灾难。麻烦且不稳定,一个不留神就会导致不可预料的结果。此时有两种解决方案。

  • 自己实现一套自动化部署的脚本,实现本地打包->上传到服务器->自动解压等部署操作的自动化;
  • 使用专业的工具比如jenkins;

前者成本较低,但是功能较弱,适合小型项目。后者功能强大,但学习和部署成本较高,适合大型项目(当然如果有jenkens的部署经验,小项目也同样适用)。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注