换种思路解决Linux -> windows的自动部署

场景:

有个项目用到了Windows服务器(运行jar包和.NET代码),如何集成到现有的自动部署平台(基于Linux)面临到两个问题

  1. 如何将资源传从Linux传输到Windows上
  2. 如何在windows上将程作为后台进程并于终端(cmd or powershell)分离,实现类似Linux下nohup命令达到的效果

对于问题1,一开始想寻找一款“windows版的sshd”程序,但是好像没找到比较官方的;后来想到powershell也有Linux版,想通过在Linux上安装powershell通过powershell来在Linux和Windows之间传输文件,运行远程命令。但是Linux版的只是powershell core,只有核心功能,况且powershell网上学习资料太少(吐槽一下:包括windows官网对于powershell的教程也只是在“简介”的程度,完全不够学习使用),遂无奈放弃之。

对于问题2,觉得在Windows上,用powershell应该能试下目标效果吧,但是经过一番研究,只找到了在powershell内后台运行命令的方法,而无法做到脱离该powershell终端。后又想将目标进程“封装”为windows服务,但是经过一番研究,发现不那么好做。至此,对Windows深感痛心。。。

无奈之余“突发奇想”,何不用flask来做一个小接口程序,运行在Windows上,自动部署平台通过http接口上传jar包到目标Windows(解决问题1),至于问题2,也以http接口形式通过python的subprocess库启动一个子进程,这里要注意的是,subprocess.Popen()方法在有个Windows特有的参数creationflags=subprocess.CREATE_NO_WINDOW,通过该参数可以实现进程不依赖窗口(cmd or powershell)运行,相当于达到了Linux下nohup的效果了。

期望效果

  1. 在CI/CD工具里,通过http请求来进行代码更新,如

    curl 172.16.1.77:5000/stop?project=we-gateway
    curl -XPUT 172.16.1.77:5000/update-we-gateway -F "[email protected]`ls build/libs/*.jar`"
    curl 172.16.1.77:5000/start?project=we-gateway
  2. 在windows上需要手动启停应用时,也可以方便的通过命令行工具来操作(包括flask自身),如下
    PS C:\deploy\win_server_manage> python .\wechat_manager.py
    Usage: wechat_manager.py [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --help  Show this message and exit.
    
    Commands:
      start  Start the specified APP in background
      stop   Stop the specified APP

    其中的wechat_manager.py依赖click(依赖)实现友好的命令行支持。

具体方案

flask项目目录结构如下

依次看代码吧(不复杂也有必要的注释)

app.py 提供flask接口

import os
import subprocess
import logging
from flask import Flask, request, Response
from werkzeug.utils import secure_filename
import we_manager

# 将flask日志输出至指定文件
logger = logging.getLogger()
file_handler = logging.FileHandler(‘C:/deploy/win_server_manage/out.log‘, encoding=‘UTF-8‘)
logger.addHandler(file_handler)
logger.setLevel(logging.DEBUG)

app = Flask(__name__)
app.config.from_pyfile(‘conf.py‘)

def upload_base(project):
    file = request.files.get(‘file‘)
    if not file:
        return ‘No file had uploaded‘, 400
    if file.filename == ‘‘:
        return ‘No selected file‘, 400
    #    return ‘"project" key not in the post data‘, 400
    filename = secure_filename(file.filename)
    file.save(os.path.join(app.config[‘PROJECTS‘][project], filename))
    return ‘successfully upload {}\n‘.format(filename)

# 示例1:通过上传方式更新目标jar包
# 因为无法同时上传文件和标识项目,故而各项目需要要给单独接口
@app.route(‘/update-we-gateway‘, methods=[‘put‘, ‘post‘])
def upload_gateway():
    return upload_base(‘we-gateway‘)

# 示例2:更新步骤不需要传文件,直接在目标目录git pull代码即可
@app.route(‘/update-we-server‘, methods=[‘put‘, ‘post‘])
def upload_server():
    os.chdir(app.config[‘PROJECTS‘][‘we-server‘])
    p = subprocess.Popen([‘git‘, ‘pull‘], stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                         creationflags=subprocess.CREATE_NO_WINDOW)
    return "STDOUT:{}\n STDERR:{}".format(p.stdout.read().decode(‘gb2312‘) if p.stdout else None, p.stderr.read().decode(‘gb2312‘) if p.stderr else None)

@app.route(‘/stop‘)
def stop():
    project = request.values.get(‘project‘)
    if not project:
        return ‘"project" key is necessary in the args‘, 400
    if project not in app.config[‘PROJECTS‘]:
        return ‘wrong project name‘, 400
    return we_manager.stop_(project)

@app.route(‘/start‘)
def start():
    project = request.values.get(‘project‘)
    if not project:
        return ‘"project" key is necessary in the args‘, 400
    if project not in app.config[‘PROJECTS‘]:
        return ‘wrong project name‘, 400
    return we_manager.start_(project)

conf.py 配置项,主要是项目名及其目录的对应关系

WIN_IP = ‘172.16.1.7‘
PROJECTS = {‘we-gateway‘: ‘C:/deploy/we-gateway-artifacts‘,
            ‘flask‘: ‘C:/deploy/win_server_manage‘}

wechat_manager.py 启动和停止项目的主要逻辑,被app.py引用

import os
import subprocess
import glob
import shlex
import conf

def start_(name):
    os.chdir(conf.PROJECTS[name])
    if name == ‘flask‘:
        # flask 作为常驻进程,不能用(stdout=subprocess.PIPE, stderr=subprocess.STDOUT)以及stdout.read()来尝试获取标准输出,会阻塞
        # 在有(stdout=subprocess.PIPE, stderr=subprocess.STDOUT)的时候,必须通过下句获取输出才能真正启动flask,stdout = p.stdout.read().decode(‘gb2312‘) if p.stdout else None
        # #这里有这句,则能启动,阻塞console,无这句,不阻塞console但实际未启动
        # flask的输出通过flask内部定义
        p = subprocess.Popen(shlex.split("flask run --host localhost"), creationflags=subprocess.CREATE_NO_WINDOW)
    else:
        try:
            jarfile = glob.glob(‘*{}*.jar‘.format(name))[0]
        except:
            print("Can‘t find a valid jar file")
            return
        # powershell中的out-file相当于Linux中的>
        # 以powershell.exe/cmd.exe开头的子命令记录的pid应该是powershell/cmd的,直接kill这样的pid不能杀掉java进程
        # 但是由于jar包内部没有处理输出,如果python不捕捉子命令输出的话,就会导致无法获得输出,但是捕捉的话,会阻塞python进程(因为常驻内存进程的输出"没有尽头")
        p = subprocess.Popen(shlex.split("java -jar {}".format(jarfile)))

    with open(‘{}/pid.txt‘.format(conf.PROJECTS[name]), ‘w‘) as f:
        f.write(str(p.pid))
    print(‘start {}, pid {} stored in pid.txt.‘.format(name, p.pid))
    return ‘start {}, pid {} stored in pid.txt.‘.format(name, p.pid)

def stop_(name):
    os.chdir(conf.PROJECTS[name])
    res = []

    with open(‘{}/pid.txt‘.format(conf.PROJECTS[name])) as f:
        pid = f.read()
    p = subprocess.Popen([‘powershell.exe‘, ‘kill‘, pid], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    res.append(p.stdout.read().decode(‘gb2312‘) if p.stdout else None)
    res.append(p.stderr.read().decode(‘gb2312‘) if p.stderr else None)
    p.wait(10)

    try:
        os.remove(‘{}/pid.txt‘.format(conf.PROJECTS[name]))
    except FileNotFoundError:
        pass
    print(‘Stop {}, pid.txt removed.\n  {}‘.format(name, res))
    return ‘Stop {}, pid.txt removed.\n  {}‘.format(name, res)

if __name__ == ‘__main__‘:
    import click

    @click.group()
    def cli():
        pass

    @click.command()
    @click.argument(‘name‘, required=True)
    def start(name):
        """Start the specified APP in background"""
        if name not in conf.PROJECTS:
            click.echo(‘Wrong name of app‘)
            return
        # p = Process(target=start_, args=(name,))
        # p.start() # 不必要了
        start_(name)

    @click.command()
    @click.argument(‘name‘, required=True)
    def stop(name):
        """Stop the specified APP"""
        if name not in conf.PROJECTS:
            click.echo(‘Wrong name of app‘)
            return
        stop_(name)

    cli.add_command(start)
    cli.add_command(stop)
    cli()

最后,希望能为备受Windows摧残的伙伴们,提供一些启发和帮助。

原文地址:https://blog.51cto.com/kaifly/2390554

时间: 2024-10-12 12:12:26

换种思路解决Linux -> windows的自动部署的相关文章

linux下实现自动部署tomcat的脚本

linux下实现自动部署tomcat的脚本 由于经常部署war到tomccat上,经常有一些重复的工作要做:停服务.备份war包.上传新的war包.启动服务.索性就写了一个自动部署的脚本. 脚本如下autoDeploy.sh: 1 #! /bin/sh 2 echo '####################开始自动部署####################' 3 path=`pwd` #当前路径 4 tomcatPath=tomcat-7 #指定tomcat文件目录名称 5 cd ../$t

linux开发脚本自动部署及监控

开发脚本自动部署及监控 1.编写脚本自动部署反向代理.web.nfs: 要求: I.部署nginx反向代理三个web服务,调度算法使用加权轮询: #!/bin/sh ngxStatus=`ps aux | grep -v grep |grep -c nginx` function ngxProxyInstall() { if [ -e /usr/sbin/nginx ];then echo "nginx already installed" exit 110 else yum inst

linux服务器安装jenkins自动部署php项目并发送邮箱通知

在Linux服务器上下载Jenkins下载地址:https://jenkins.io/zh/download/选择你的服务器对应的版本,我的是centos6,选择这个版本点击进去:命令行官方已给出,依次执行以下命令行 sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-

linux下nginx自动部署脚本

请保证系统可以使用yum源,可以访问外网. 变量NGINX_PATH     修改nginx安装路径,默认为/opt/nginx 变量NGINX_VERSION    修改nginx的安装版本,默认为1.10.0 #!/bin/bash # The nginx installation script automatically # author:startuppp LOCATION=$(pwd) NGINX_PATH=/opt/nginx NGINX_VERSION=1.10.0 echo -e

weblogic windows脚本自动部署

@echo off echo *************************************************** echo * Created by * echo *************************************************** set DOMAIN_HOME=D:\weblogic\user_projects\domains\ccs_prod_domain echo 请将最新应用包放入指定应用目录 ping 127.1 -n ul ec

window、linux系统与linux服务器之间使用svn同步及自动部署代码的方法

摘要: 在家用PC,在公司用办公电脑对一个项目的代码进行修改时,会遇到代码同步的问题.本文讲解了代码同步及自动部署的解决办法. 实现方法: 1.首先在linux服务器上和linux上安装svn(sudo yum install svn). 2.然后在服务上创建一个版本库并配置用户及权限,这点网上的方法多的是,自行百度. 3.在linux客户端使用svn命令进行同步,具体如下: svn co svn://(ip地址)  (保存文件的文件夹)    ##检出版本库.第一次要输入用户名和密码. 将需要

在linux服务器上装svn版本管理,自动部署代码到项目

在linux服务器上装svn版本管理,自动部署代码到项目 http://bbs.aliyun.com/read/9715.html?spm=5176.7114037.1996646101.1.W3zw3X&pos=1 http://v5sheji.com/archives/setupsvnonlinux.html 1.安装svn服务器端  yum install subversion 从镜像下载安装svn服务器端 中间会提示是否ok,输入y,确认 安装成功提示:.....complete! 依次

Windows无法自动将IP协议堆栈绑定到网络适配器 的解决办法

实验室的台式机在升级驱动后上不了网了,有线网卡驱动卸载后重装了,还是不行,通过Windows诊断发现"Windows 无法自动将 IP 协议堆栈绑定到网络适配器的解决办法". 解决办法: 打开"控制面板">>"网络和 Internet">>"网络连接",右键点击要查看的网络,选择"属性", 取消勾选"Network LightWeight Filter",点击&q

Linux Shell脚本之远程自动部署java maven项目

脚本功能: 自动从git上获取java maven项目工程源码,在机器A上build,build完成后,将Class文件和配置文件等上传到机器B,重新启动机器B上的服务以便变更生效. 脚本特点: 1.(与之前的自动部署脚本相比)全新优化了脚本代码,更friendly,结构更紧凑 2.Public header删除了无用或者不好用的有色彩显示函数,并修正了WORKDIR不是绝对路径可能导致的bug 3.修正了域名解析判断是否正常的一个bug,该bug可能导致遇到无法解析后不断尝试解析 4.全新的m