在一个多月前,组长让我研究下持续集成。我很天然地选择了jenkins。当时,(包括如今也是),部分服务器用的是windows主机。java
我当时想了想,若是我把jenkins装在windows上,在windows上打好包后,要怎么把war包或jar包(针对spring boot项目)传到remote windows主机上呢?linux
传过去以后,要怎么把这个包运行起来呢(好比war包丢tomcat,重启tomcat;好比怎么用java运行spring boot项目),运行确定是用脚本(bat),可是我怎么git
调用这个脚本呢?github
想了想,很头疼。可是我知道linux主机之间,是能够ssh登陆的,而且能够ssh远程登陆后执行shell的。spring
因此,最终选择了centos做为持续集成服务jenkins的操做系统。shell
研究jenkins以来,做为一个小菜鸟,找了相关的jenkins QQ群加入,群里气氛很好,你们提问题不少,回答问题的也不少。我就把上述问题在群里问了一下,获得群管理的回复,windows
针对数据传输部分,回复以下,共4种方案。centos
1。1 在win上装sshd,在linux上用scp,sftp传文件到win。
1。2在linux上装samba客户端,映射win共享磁盘符到linux。而后cp。
1。3 在linux上装powershell,而后装一个第三方模块。而后在win上开启。winrm,数据走的是winrm协议端口,即powershell服务器的端口。
1。4 在win上装ftpd,和道理1。3同样。数据走的是ftp协议。
今天我就找时间,本身试了下第一种。整整折腾了一天,原本觉得搞不出来了,不过仍是勉强解决了。tomcat
这边作下记录。bash
update:
当时写的时候用的是openssh,后来实际状况下,发现部分系统仍是执行一些命令会失败。后来转到了freesshd这个软件。
该软件我我的以为仍是挺好用的。
这里记录下。
官网:http://www.freesshd.com/?ctt=download
下面是傻瓜式安装。惟一要注意的是:
一、若是22端口已经被占用,那么只能换端口了,我这边换成了29
二、要添加用户名密码,我这里配了Administrator/1qaz@WSX
jenkins上配置ssh链接:
参考资料:
https://winscp.net/eng/docs/guide_windows_openssh_server
https://github.com/PowerShell/Win32-OpenSSH/releases
这边按照本身的机器,选一个版本吧,我看了下,貌似都是beta版,但我感受仍是很稳定的。我选择的是下图箭头所示的64位版本。
先解压缩,不必定非要解压缩到下图的地方,本身开心就好。
在解压的目录下,按住shift,同时鼠标右键,打开cmd或者powershell。
执行:
powershell.exe -ExecutionPolicy Bypass -File install-sshd.ps1
继续右键打开powershell(在我这,cmd不行,可是在powershell窗口能够成功),
执行:(这句意思是加防火墙规则,放行22端口)
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
若是提示New-NetFirewallRule不被认识,找个powershell吧,或者查一下,或者手动添加入站规则。
接下来,去到计算机管理-服务里面,找到sshd服务,配置为开机启动,而后点下面的启动,将服务启动起来。
1.在本身机器上telnet一下,看看ssh服务的22端口是不是通的。
cmd执行:telnet ip 22
2.shell远程工具测试下,是否能够登陆。
我这边用的是secureCRT,XShell或者putty都行。
用户名密码就是:远程桌面用的用户名密码。
ssh进去后,主工做目录在C:\Users\Administrator:
在jenkins上,在我观察,Publish Over SSH算是一个比较流行的ssh工具,能够传输文件,也能够执行命令(针对linux,能够执行命令和shel;针对windows,cmd里可以进行的操做均可以)。
在Jenkins管理界面中,依次打开:系统管理--》系统设置--》Publish over SSH部分,按照下图进行配置:
填写完成后,能够点击下面的按钮,测试下链接是否成功。
另外,这里有个填ssh端口的输入框,若是修改了服务端的监听端口的话,记得对应修改。
Name随便填,Hostname是ip或者主机名,用户名同远程桌面 用的用户名,点击高级后,填写密码部分。
其中的Remote Directory须要重点关注,该参数表示的是:ssh文件上传后,文件在远端服务器上的保存路径,路径须要预先创建好。
同时,若是上传了文件后,须要执行命令的话,这也是bat、shell等命令的工做目录。
针对windows类型的ssh服务器,这边的填写只能写相对路径,(base 路径为C:\Users\Administrator,若是做为非管理员登陆,可能会稍微不同,能够本身用SecureCRT之类的登进去试试,看看在哪一个路径下)
若是不填,就是在base路径;
若是填写内容为“\”,(不含双引号),则路径会是c盘根路径;
若是填"target",则路径会是C:\Users\Administrator\target。
我这边简单起见,先不填。由于其做为后续命令执行的工做路径的缘由,会有一些坑。后边我再补充。
先展现下个人构建操做,很简单,maven编译,打包
在jenkins的服务器上,找到jenkins的home路径,其下的workspace目录中,
我这边拿来举例的job是test-deploy。
编译完成后,我这边的目录结构是这样的:
cmd /c call C:\Users\Administrator\deploy.bat
以上配置,能够和上一步的构建后文件目录结构,仔细对照下就知道怎么填写了。
我这边也不是最佳实践,能够留言讨论。
在C:\Users\Administrator\下的目录文件结构:
deploy.bat的内容:
echo hello java -version javaw -Xms512m -Xmx512m -Xmn512m -jar target\bdmp-backstage-rest.jar echo bye :end
重点关注的是上面标红的地方:
这里,必须是javaw。不信能够多试试。最终是怎么选定这个方案的,后面说。只关注结果的话,复制上面的就能够。
我这边由于bat水平有限,只写了启动程序的部分。
按理说,是须要上传后,拷贝jar或war到别的目录,先杀掉之前的进程,(避免端口冲突而启动失败),再开启新进程。
这块留做todo吧,有兴趣的朋友能够跟我交流。
在job页面,点“当即构建“。
运行过程以下:
咱们在远程ssh主机上,看看咱们的服务启动了没,我这边经过看端口的方式:
netstat -ano|findstr "18083"
其中18083是我这边的spring boot应用的服务端口:
不放心的话,能够看看任务管理器。
好了。启动起来了。
咱们再看看jenkins中,job运行完了没?
运行完了,可是是超时退出的,我这边还没想到好办法。若是你有好的解决办法,欢迎分享。
我最开始只打算作linux主机之间的持续集成,因此只弄了shell。基于懒,所以看看能不能经过在windows上安装shell环境来执行shell。
准备shell环境,我是经过安装了一个git。而后将git的cmd和bin,配置到了path中。
我瞎搞的,不知道运维界的最佳实践是啥。(我只是个java开发。。。)
这边我把shell共享下(我也是在前人基础上改的,自己shell水平堪忧)。
bdmp-backstage-rest.sh:
#!/bin/bash source /etc/profile export SERVICE_NAME=bdmp-backstage-rest export JAR_ARTIFACT_NAME=./target/bdmp-backstage-rest.jar export DEBUG_PORT=2222 export JAVA_OPTS="-Xms1024m -Xmx1024m -Xmn512m" cd `dirname $0` echo "CURRENT DIRECTORY:"`pwd` export SCRIPT_NAME=$0 echo "SCRIPT_NAME:"$SCRIPT_NAME" parameters:"$@ echo "Invoke _server.sh now!" ./_server.sh $@
_server.sh:
#!/bin/bash #date 2018-04-12 #author caokunliang #version 1.0 #desc: echo "_server.sh is called! paramters is "$@ echo "JAR_ARTIFACT_NAME:"$JAR_ARTIFACT_NAME echo "SERVICE_NAME:"$SERVICE_NAME echo "JAVA_OPTS:"$JAVA_OPTS if [ -z "$JAR_ARTIFACT_NAME" ]; then echo "请不要直接调用本脚本,并确保调用脚本中设置了JAVA_MAIN_CLASS" exit 1 fi if [ -z "$SERVICE_NAME" ]; then echo "请在调用脚本中设置SERVICE_NAME" exit 2 fi if [ -z "$JAVA_OPTS" ]; then JAVA_OPTS="-Xms1024m -Xmx1024m -Xmn512m" fi #服务进程的pid pids="" #调试模式下的参数 DEBUG_OPTS="" #============================================================================== #根据启动类类名搜索服务进程的pid #============================================================================== function get_pids() { echo "(${FUNCNAME[@]})" #默认使用JDK自带的jps echo "jps -l:" jps -l JPS_STATUS=$? if [ $JPS_STATUS -ne 0 ]; then pids=`ps -e -o pid -o command | grep -vi ' grep ' | grep -i 'java' | grep "$JAR_ARTIFACT_NAME" | awk '{print $1}'` else pids=`jps -l|grep "$JAR_ARTIFACT_NAME" | awk '{print $1}'` fi echo "" if [ -z "$pids" ]; then echo "the process of "$JAR_ARTIFACT_NAME " is not found" fi } #============================================================================== #终止服务进程 #============================================================================== function svr_stop() { get_pids if [ -n "$pids" ]; then for p in $pids do echo "kill -9 "$p kill -9 $p done echo "" echo "the process are stopped" fi } #============================================================================== #启动服务前进行环境准备 #============================================================================== function svr_test() { echo "" java -version > /dev/null JAVA_STATUS=$? if [ $JAVA_STATUS -ne 0 ]; then echo "java not found under the path,please make sure jdk is installed" exit 3 fi cd `dirname $0` if [ "$1" = "debug" ]; then DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address="$DEBUG_PORT" " fi if [ -n "$SERVICE_NAME" ]; then echo "SERVICE_NAME = "$SERVICE_NAME fi echo "JAR_ARTIFACT_NAME = "$JAR_ARTIFACT_NAME echo "JAVA_OPTS = "$JAVA_OPTS echo "DEBUG_OPTS = "$DEBUG_OPTS echo "CUSTOM_PARAM = "$@ echo "HOME_DIR = "`pwd` echo "LAUNCH_COMMAND = nohup java "$JAVA_OPTS" "$DEBUG_OPTS" "-jar" "$JAR_ARTIFACT_NAME" >/dev/null 2>&1 &" echo "" } #============================================================================== #启动服务 #============================================================================== function svr_start() { #nohup java $JAVA_OPTS $DEBUG_OPTS -jar $JAR_ARTIFACT_NAME > nohup.out & nohup java $JAVA_OPTS $DEBUG_OPTS -jar $JAR_ARTIFACT_NAME >/dev/null 2>&1 & } #============================================================================== #根据参数调用对应的服务 #============================================================================== if [ "$1" = "stop" ]; then svr_stop elif [ "$1" = "start" ]; then svr_test $@ svr_start $@ elif [ "$1" = "restart" ]; then svr_stop sleep 2 svr_test $@ svr_start $@ elif [ "$1" = "debug" ]; then svr_stop sleep 2 svr_test $@ svr_start $@ elif [ "$1" = "test" ]; then svr_test $@ get_pids if [ -n "$pids" ]; then for p in $pids do echo "当前服务进程PID = "$p done fi elif [ "$1" = "dump" ]; then get_pids if [ -n "$pids" ]; then for p in $pids do echo "当前服务进程PID = "$p echo "" echo "jinfo "$p" > "$SERVICE_NAME"."$p".jinfo.log" jinfo $p > $SERVICE_NAME.$p.jinfo.log echo "" echo "jstack "$p" > "$SERVICE_NAME"."$p".jstack.log" jstack $p > $SERVICE_NAME.$p.jstack.log echo "" echo "jmap -heap "$p" > "$SERVICE_NAME"."$p".heap.log" jmap -heap $p > $SERVICE_NAME.$p.heap.log echo "" echo "jmap -histo "$p" > "$SERVICE_NAME"."$p".histo.log" jmap -histo $p > $SERVICE_NAME.$p.histo.log echo "" echo "jmap -dump:format=b,file="$SERVICE_NAME"."$p".dump "$p jmap -dump:format=b,file=$SERVICE_NAME.$p.dump $p echo "" TAR_NAME=$SERVICE_NAME".dump."$p"."`date +%Y%m%d.%H%M`".tar.gz" echo "tar --remove-files --exclude=\"*.gz\" -zcvf "$TAR_NAME" "$SERVICE_NAME"."$p".*" tar --remove-files --exclude="*.gz" -zcvf $TAR_NAME $SERVICE_NAME.$p.* echo "" echo "生成打包文件 "`du -h $TAR_NAME | awk '{print $1}'` echo `pwd`"/"$TAR_NAME done fi else echo "" echo "用法: "$SCRIPT_NAME" [参数]" echo "" echo " start 正常启动服务,服务器首次启动时使用,绑定端口时若是端口已经被占用" echo " 会直接报错退出" echo "" echo " stop 使用直接终止进程的方式关闭服务" echo "" echo "" echo " restart 重启服务,先将原来的服务进程关闭,而后启动服务" echo "" echo " debug 关闭当前的服务并用debug模式重启,用于远程调试" echo "" echo "" echo " dump 保存服务进程的内存堆栈等信息,用于排查问题" echo "" echo " test 查看服务启动配置信息,不会启动服务" fi echo "success"
我经过远程桌面,在服务器里,手动执行bdmp-backstage-rest.sh文件,服务(端口18083)是能够启动起来的。
job配置:
job的状态是success,日志以下:
可是实际上,服务并无启动(或者是启动了,可是ssh退出登陆时又关闭了)。
运行:
应该是卡在sleep那一句了。此时,看看windows主机上,进程是否启动。
进程是启动的。
后续不出所料的,jenkins那边,job超时,标记为unstable。而后我继续查看windows主机,发现进程还在。这有点出乎个人意料。
我想是否是由于睡眠的时间太长,可能睡眠结束,这个进程也就被kill了。
job日志显示,20s后,job结束:
windows上,进程还在:
顺便看看个人日志吧:
接下来,修改成3s,个人程序启动大概要10多秒,这边若是设为小于程序启动并绑定端口的时间的话,会怎样?
这边不截图了,效果和第五步同样。
可是,为啥必定要sleep呢?昨天我用shell方案的时候,没想到这茬,所以在屡次尝试无果后,转向了bat。
有知道的,麻烦解惑。
这个是最开始的版本。网上都是这样写的。在远程桌面进去,手动执行,没问题的
start javaw -Xms512m -Xmx512m -Xmn512m -jar target\bdmp-backstage-rest.jar
可是,用在我这里,(ssh登陆进来执行bat的方式),却不行。
后来,我在javaw后,加了pause。
这时,能够查到进程了。可是一旦jenkins那边,ssh退出了。就查不到了。估计是被kill了。
因此我开始查找,windows要怎么忽略nohup信号(像linux那样)
参考:https://stackoverflow.com/questions/3382082/whats-the-nohup-on-windows
仔细看了这个问题下的每一个回答,我总算知道了缘由。
在ssh到windows的状况下,只要ssh退出,ssh启动的任务都会被kill掉。惟一可行的办法,就是使用javaw。
因此,linux经过ssh远程到windows上,执行sh或者bat,去启动java子进程。
目前我只知道这两种方式,一种shell,(须要加sleep,具体缘由未明),一种就是bat,必须使用javaw