Jenkins踩坑系列--你试过linux主机ssh登录windows,启动java进程吗,来试试吧

一、问题概述

在一个多月前,组长让我研究下持续集成。我很自然地选择了jenkins。当时,(包括现在也是),部分服务器用的是windows主机。

我当时想了想,如果我把jenkins装在windows上,在windows上打好包后,要怎么把war包或jar包(针对spring boot项目)传到remote windows主机上呢?

传过去之后,要怎么把这个包运行起来呢(比如war包丢tomcat,重启tomcat;比如怎么用java运行spring boot项目),运行肯定是用脚本(bat),但是我怎么

调用这个脚本呢?

想了想,很头疼。但是我知道linux主机之间,是可以ssh登录的,并且可以ssh远程登录后执行shell的。

所以,最终选择了centos作为持续集成服务jenkins的操作系统。

 

研究jenkins以来,作为一个小菜鸟,找了相关的jenkins QQ群加入,群里气氛很好,大家提问题很多,回答问题的也很多。我就把上述问题在群里问了一下,得到群管理的回复,

针对数据传输部分,回复如下,共4种方案。

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协议。

 

今天我就找时间,自己试了下第一种。整整折腾了一天,本来以为搞不出来了,不过还是勉强解决了。

这边做下记录。

 

 

二(更新)、折腾记录之安装 freesshd

update:

当时写的时候用的是openssh,后来实际情况下,发现部分系统还是执行一些命令会失败。后来转到了freesshd这个软件。

该软件我个人觉得还是挺好用的。

这里记录下。

官网:http://www.freesshd.com/?ctt=download

下面是傻瓜式安装。唯一要注意的是:

1、如果22端口已经被占用,那么只能换端口了,我这边换成了29

2、要添加用户名密码,我这里配了Administrator/1qaz@WSX

 

 

jenkins上配置ssh连接:

二、折腾记录之安装openssh

参考资料:

https://winscp.net/eng/docs/guide_windows_openssh_server

1.下载openssh

https://github.com/PowerShell/Win32-OpenSSH/releases

这边按照自己的机器,选一个版本吧,我看了下,貌似都是beta版,但我感觉还是很稳定的。我选择的是下图箭头所示的64位版本。

2.安装

先解压缩,不一定非要解压缩到下图的地方,自己开心就好。

 

在解压的目录下,按住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服务,配置为开机启动,然后点下面的启动,将服务启动起来。

3、测试一下

1.在自己机器上telnet一下,看看ssh服务的22端口是否是通的。

cmd执行:telnet ip 22

 

2.shell远程工具测试下,是否可以登录。

我这边用的是secureCRT,XShell或者putty都行。

用户名密码就是:远程桌面用的用户名密码。

 4、注意点

ssh进去后,主工作目录在C:\Users\Administrator:

 

 三、Jenkins上配置ssh客户端

1、jenkins配置ssh主机

在jenkins上,在我观察,Publish Over SSH算是一个比较流行的ssh工具,可以传输文件,也可以执行命令(针对linux,可以执行命令和shel;针对windows,cmd里能够进行的操作都可以)。

 在Jenkins管理界面中,依次打开:系统管理--》系统设置--》Publish over SSH部分,按照下图进行配置:

填写完成后,可以点击下面的按钮,测试下连接是否成功。

另外,这里有个填ssh端口的输入框,如果修改了服务端的监听端口的话,记得对应修改。

2、填写注意点

Name随便填,Hostname是ip或者主机名,用户名同远程桌面 用的用户名,点击高级后,填写密码部分。

其中的Remote Directory需要重点关注,该参数表示的是:ssh文件上传后,文件在远端服务器上的保存路径,路径需要预先建立好。

同时,如果上传了文件后,需要执行命令的话,这也是bat、shell等命令的工作目录。

针对windows类型的ssh服务器,这边的填写只能写相对路径,(base 路径为C:\Users\Administrator,如果作为非管理员登录,可能会稍微不一样,可以自己用SecureCRT之类的登进去试试,看看在哪个路径下)

如果不填,就是在base路径;

如果填写内容为“\”,(不含双引号),则路径会是c盘根路径;

如果填"target",则路径会是C:\Users\Administrator\target。

我这边简单起见,先不填。因为其作为后续命令执行的工作路径的原因,会有一些坑。后边我再补充。

 

四、配置job

1、job中,配置构建操作

先展示下我的构建操作,很简单,maven编译,打包

 

在jenkins的服务器上,找到jenkins的home路径,其下的workspace目录中,

 

我这边拿来举例的job是test-deploy。

编译完成后,我这边的目录结构是这样的:

 

 2、配置构建后操作--ssh发布

 

cmd /c call C:\Users\Administrator\deploy.bat

 

以上配置,可以和上一步的构建后文件目录结构,仔细对照下就知道怎么填写了。

我这边也不是最佳实践,可以留言讨论。

 

3、远端windows ssh服务器

在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吧,有兴趣的朋友可以跟我交流。

 

4、终于ok了,测试一下

在job页面,点“立即构建“。

运行过程如下:

 

我们在远程ssh主机上,看看我们的服务启动了没,我这边通过看端口的方式:

netstat -ano|findstr "18083"

其中18083是我这边的spring boot应用的服务端口:

不放心的话,可以看看任务管理器。

 

好了。启动起来了。

我们再看看jenkins中,job运行完了没?

运行完了,但是是超时退出的,我这边还没想到好办法。如果你有好的解决办法,欢迎分享。

 

五、尝试过的失败方案-shell版

我最开始只打算做linux主机之间的持续集成,所以只弄了shell。基于懒,因此看看能不能通过在windows上安装shell环境来执行shell。

1、准备shell

准备shell环境,我是通过安装了一个git。然后将git的cmd和bin,配置到了path中。

我瞎搞的,不知道运维界的最佳实践是啥。(我只是个java开发。。。)

2、windows上手动运行

这边我把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 $@
View Code

_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"
View Code

 

我通过远程桌面,在服务器里,手动执行bdmp-backstage-rest.sh文件,服务(端口18083)是可以启动起来的。

 

3、jenkins ssh启动shell:

job配置:

job的状态是success,日志如下:

 但是实际上,服务并没有启动(或者是启动了,但是ssh退出登录时又关闭了)。

 

4、shell脚本-修改脚本睡眠时间1:

运行:

应该是卡在sleep那一句了。此时,看看windows主机上,进程是否启动。

 

 进程是启动的。

后续不出所料的,jenkins那边,job超时,标记为unstable。然后我继续查看windows主机,发现进程还在。这有点出乎我的意料。

我想是不是因为睡眠的时间太长,可能睡眠结束,这个进程也就被kill了。

 

 

5、shell脚本-修改脚本睡眠时间2:

 

job日志显示,20s后,job结束:

windows上,进程还在:

顺便看看我的日志吧:

 

6、shell脚本-修改脚本睡眠时间3:

接下来,修改为3s,我的程序启动大概要10多秒,这边如果设为小于程序启动并绑定端口的时间的话,会怎样?

 

 这边不截图了,效果和第五步一样。

7、疑惑

但是,为啥一定要sleep呢?昨天我用shell方案的时候,没想到这茬,因此在多次尝试无果后,转向了bat。

 有知道的,麻烦解惑。

 

 

 

六、尝试过的失败方案-bat版

 1、方案1

 

2、方案2

这个是最开始的版本。网上都是这样写的。在远程桌面进去,手动执行,没问题的

start javaw -Xms512m -Xmx512m -Xmn512m -jar target\bdmp-backstage-rest.jar 

 

 但是,用在我这里,(ssh登录进来执行bat的方式),却不行。

后来,我在javaw后,加了pause。

这时,可以查到进程了。但是一旦jenkins那边,ssh退出了。就查不到了。估计是被kill了。

所以我开始查找,windows要怎么忽略nohup信号(像linux那样)

3、结论

参考: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

 

 

 

 

 

posted @ 2018-05-10 11:51  三国梦回  阅读(7305)  评论(9编辑  收藏  举报