一.应用场景:
本地开发java应用程序,当部署时,要打包java应用,上传jar包到远程服务器,登录远程服务器并执行指令进行重启java服务。
有时候需要频繁反复进行该操作,每次都这样一步一步的做,有些繁琐并且浪费时间,因此写了一份shell脚本,只需要执行一个脚本指令,便自动做了上述所有的事情,节省了许多事。
二.环境
1.springboot的应用,java -jar跑服务,linux服务器使用账号密码登录(使用私钥等只需要修改对应的scp,ssh命令即可)
2.shell脚本,需要shell运行环境支持。mac下默认支持,windows需要使用powershell,区别于cmd,该程序支持shell脚本运行。当使用密钥时,需要修改id_rsa文件的读写权限,设置为当前用户权限就好,具体百度即可。
三.思路
重现步骤,第一步,mvn打包java应用,第二步,上传jar包和对应的配置文件到服务器,第三步,登录服务器,执行部署脚本
四.实现
1.首先新建打包脚本 package.sh
#!/bin/bash
mvn clean package -Dmaven.test.skip=true
该脚本进行打包指令,需要依赖maven环境,具体需要配置MAVEN_HOME还有PATH,也不多说了
2.写好上传服务器脚本 scp.sh
#! /usr/bin/expect
#目标服务器ip,接收第一个参数
set target_ip [lindex $argv 0]
#目标服务器端口,接收第二个参数
set target_port [lindex $argv 1]
#目标服务器用户名,接收第三个参数
set scpUser [lindex $argv 2]
#目标服务器密码,接收第四个参数
set scpPwd [lindex $argv 3]
#传输到目标服务器的路径
set target_path [lindex $argv 4]
#打印接收到的参数
for {set i 0} {$i < $argc} {incr i} {
#打印参数
puts "arg $i: [lindex $argv $i]"
}
#从第六个参数开始,作为发送的文件参数,通配符时,会把匹配到的所有文件依次传进
for {set i 5} {$i < $argc} {incr i} {
#scp指令 -P是端口,默认ssh端口是22, -r是发送的文件路径, :后面是目标地址,如果使用私钥,可以用 scp -i xxx/id_rsa -P ,把-i放在最前面
spawn scp -P $target_port -r [lindex $argv $i] $scpUser@$target_ip:$target_path
#使用expect的功能,当交互出现,匹配到password时,帮我们输入密码值
expect "*password:"
send "$scpPwd\n"
#交互出现,如果没有interact,则文件传输过程会被打断
interact
}
该脚本是expect脚本,需要使用expect来执行,而不是sh
3.在服务器上运行的部署脚本deploy.sh
#!/bin/bash
#当前路径,使用第一个参数
currentPath=$1
#如果第一个参数为空,则pwd(运行脚本的当前目录)作为当前路径
if [ ! -n "$currentPath" ]; then
currentPath=$PWD
fi
#打印当前路径
echo $currentPath
#存放pid的文件路径配置
pidFile="$currentPath/test.pid"
#jar包的路径配置
jarPath="$currentPath/test/target/test.jar"
#target jar包路径配置
targetPath="$currentPath/test.jar"
#当pid文件存在时,读出pid文件的内容,kill掉该pid的进程
if [ -f $pidFile ]; then
echo "pid file exists!"
PID=$(cat $pidFile) # 将PID从文件中读取,并作为一个变量
#杀掉该进程
kill -9 $PID
echo "exec kill $PID success!"
fi
#判断jarPath的文件路径存不存在,如果存在,则用jarPath路径部署,不存在,则用默认的targetPath部署
if [ -f $jarPath ]; then
echo "jarPath exists"
targetPath=$jarPath
fi
echo "执行启动java程序指令"
#cd进去currentPath目录,在该目录下执行java -jar指令
cd $currentPath
#部署jar包的指令,nohup是指忽略挂起 > /dev/null是把nohup的输入丢到一个空设备,即直接丢弃的意思, 2>&1 即所有类型日志的输出 &表示后台运行,后面>表示把pid保存到当前目录的test.pid文件中
#便于后续杀进程重启
nohup java -jar $targetPath --spring.profiles.active=qa > /dev/null 2>&1 & echo $! >"$currentPath/test.pid"
echo "执行启动java程序指令完成!"
4.远程ssh执行指令脚本
#! /usr/bin/expect
set sshIp [lindex $argv 0]
set sshPort [lindex $argv 1]
set sshUser [lindex $argv 2]
set sshPwd [lindex $argv 3]
set cmd [lindex $argv 4]
set currentPath [lindex $argv 5]
#打印所有参数
for {set i 0} {$i < $argc} {incr i} {
puts "arg $i: [lindex $argv $i]"
}
#ssh执行远程指令,如果使用私钥,使用ssh -i xxx/id_rsa
spawn ssh -p $sshPort $sshUser@$sshIp $cmd $currentPath
expect {
"yes/no" {send "yes\r";exp_continue}
"*password" {send "$sshPwd\r"}
}
#结束expect
expect eof
5.最终一键打包上传部署脚本auto.sh
#!/bin/bash
#获取当前路径
currentPath=$PWD
echo "====================开始打包===================="
#调用前面的打包脚本package.sh,所有的脚本放在同级目录下,方便调用
sh $currentPath/package.sh
echo "====================打包成功!===================="
#配置远程服务器的账号
scpUser='test'
#远程服务器的密码
scpPwd="test123456"
#远程服务器的ip
target_ip="192.168.1.1"
#远程服务器的端口
target_port="22"
#发送的jar包路径
from_path="$currentPath/test/target/test.jar"
#服务器部署的目录,发送到服务器的目录,需要在服务器先建好,不会自动创建目录
target_path="/data/my_project"
#需要发送部署脚本,部署脚本deploy.sh的路径
from_deploy_path="$currentPath/deploy.sh"
#发送配置文件,配置文件application.properties的路径
from_properties_path="$currentPath/bggw/src/main/resources/application.properties"
#发送所有的配置文件,配置文件通配
from_all_properties_path="$currentPath/bggw/src/main/resources/*.properties"
#远程服务器存放配置文件的路径,需要在远程服务器先创建好
target_properties_path="$target_path/config"
#如果配置文件路径application.properties存在,就把配置文件路径所有的properties发送到远程服务器(如果不需要发送配置文件,直接注释该段落即可)
if [ -f $from_properties_path ]; then
echo "====================发送配置文件===================="
#调用我们写的scp.sh脚本文件,传输文件,expect脚本需要用expect调用
expect $currentPath/scp.sh $target_ip $target_port $scpUser $scpPwd $target_properties_path $from_all_properties_path
echo "====================发送配置文件完成===================="
fi
echo "====================发送配置文件到${target_ip}成功!===================="
echo "====================发送jar包到${target_ip}===================="
#调用我们写的scp.sh脚本,发送jar包到远程服务器
expect $currentPath/scp.sh $target_ip $target_port $scpUser $scpPwd $target_path $from_path
echo "====================发送jar包到${target_ip}成功!===================="
echo "====================发送部署脚本===================="
#调用我们写的scp.sh脚本,发送部署脚本deploy.sh到远程服务器
expect $currentPath/scp.sh $target_ip $target_port $scpUser $scpPwd $target_path $from_deploy_path
echo "====================发送部署脚本成功!===================="
#调用deploy.sh的指令配置
sshCmd="sh $target_path/deploy.sh"
echo "====================ssh服务器执行脚本===================="
#调用我们写的ssh.sh脚本,远程执行 sshCmd指令,即调用deploy.sh,即让远程服务器调用部署脚本
expect $currentPath/ssh.sh $target_ip $target_port $scpUser $scpPwd "$sshCmd" "$target_path"
echo "====================ssh服务器执行脚本结束===================="
echo "脚本执行结束"
到此所有的脚本已经写完了,只需要在本地执行
sh ./auto.sh
即可,调用自动部署脚本,把所有的脚本串联起来,自动化部署。
五.最后
之所以拆分成这么多脚本,也是为了每个脚本都可以重新利用,不用重复写scp和ssh的那一串指令,最主要是实验觉得expect脚本和bash脚本貌似不能在同个文件同时使用,会有问题。
如果自己有其他的需求,进行自己的定制即可,里面的一些参数需要自己修改,如远程服务器的信息,文件的路径信息等
java服务器部署一般不会如此简单,会提前配置JAVA_HOME等其他信息,还有JVM运行的一些参数,该脚本仅作为简单的一个部署脚本,不作为生产环境的使用,不过作为测试环境,或者自己的服务器玩玩,还是可以的,生产环境如果没有比较复杂的配置,当然也是没有任何问题