zoukankan      html  css  js  c++  java
  • Java Spring Boot 打包部署方案

    Java Spring Boot 打包部署方案

    背景

    最近在做一个内网的项目,项目前后端分离,后端用Spring Boot,前端用Ant Design React。部署的环境是学校内网,部署是通过4
    G网卡+VPN连接到内网的机器部署的,考虑到Docker需要连接网络且镜像很大,我们采用最原始的部署方式:后端打成jar包传到服务器上,然后java -jar xxx.jar运行。

    这其中就遇到了一些问题:

    1. 网络慢,打出来的jar包60M左右,要传10分钟。而大部分时候jar包中以来的其他库都是相同的,所以想把依赖的java包分离出来。
    2. 在机器上部署工序繁杂,大部分都是备份、复制、重启等操作。所以想写个shell脚本减少部署的工作量。
    3. 配置文件也要从 Spring Boot 的 jar 包中分离出来,方便在服务器上配置

    打包配置

    项目采用Gradle做依赖管理,我在build.gradle中写了自定义的任务packTarpackTarWithoutLibs,分别可以打出含有依赖库和不含依赖库的tar.gz包,而这个包解压后的结构是这样的:

    app-server-1.0.0
    |-bin/
    | 	|-app.sh # 应用脚本,能用于启动、停止、查看状态、查看日志、安装、升级
    |-config/
    |	|-application.yml #配置文件
    |-lib/
    |	|-a.jar # 依赖的jar
    |	|-b.jar
    |	|-c.jar
    |-app.jar # 应用的jar
    

    以下就是我的Gradle打包配置:

    build.gralde:

    buildscript {
        repositories {
            maven {
                url 'https://maven.aliyun.com/nexus/content/groups/public'
            }
        }
        // 引入 spring boot 插件
        dependencies {
            classpath "org.springframework.boot:spring-boot-gradle-plugin:2.3.3.RELEASE"
        }
    }
    
    group = 'com.xxx'
    version = '1.0.12'
    
    // 插件
    apply plugin: 'java'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
    
    sourceCompatibility = 1.8
    
    configurations {
        compileClasspath {
            extendsFrom annotationProcessor
        }
    }
    
    repositories {
        maven { url 'https://maven.aliyun.com/nexus/content/groups/public' }
    }
    
    // 依赖
    dependencies {
        // ......
        implementation 'org.springframework.boot:spring-boot-starter-web'
    	// ......
    }
    
    // =========================== 打包 ===========================
    
    // 复制依赖的jar
    task copyJar(type: Copy) {
        fileMode 0755
        delete "$buildDir/libs/lib"
        from configurations.default
        into "$buildDir/libs/lib"
    }
    
    // 删除依赖的jar
    task removeLibs(type: Delete) {
        delete "$buildDir/libs/lib"
    }
    
    // 复制配置文件
    task copyConfig(type: Copy) {
        fileMode 0755
        delete "$buildDir/libs/config"
        from('src/main/resources') {
            include 'application-prod.yml'
        }
        rename 'application-prod.yml', 'application.yml'
        into "$buildDir/libs/config"
    }
    
    // 复制脚本
    task copyScript(type: Copy) {
        fileMode 0755
        delete "$buildDir/libs/bin/app.sh"
        from('src/main/resources') {
            include 'app.sh'
        }
        into "$buildDir/libs/bin"
    }
    
    bootJar {
        fileMode 0755
        archiveFileName = 'app-server.jar'
        // 例外所有的jar
        excludes = ["*.jar",'application.yml','application-prod.yml','app.sh']
        // lib目录的清除和复制任务
        dependsOn copyJar
        dependsOn copyConfig
        dependsOn copyScript
        // 指定classpath
        manifest {
            attributes "Manifest-Version": 1.0,
                       "Class-Path": configurations.default.files.collect {"lib/$it.name"}.join(' ')
        }
    }
    
    // 打Tar包
    task packTar(type: Tar) {
        dependsOn bootJar
        archiveFileName = "app-server-${archiveVersion.get()}.tar.gz"
        destinationDirectory = file("$buildDir/dist")
        from "$buildDir/libs"
        fileMode 0755
    }
    
    // 打Tar包
    task packTarWithoutLibs(type: Tar) {
        dependsOn bootJar
        dependsOn removeLibs
        archiveFileName = "app-server-nolib-${archiveVersion.get()}.tar.gz"
        destinationDirectory = file("$buildDir/dist")
        from "$buildDir/libs"
        fileMode 0755
    }
    

    应用部署脚本包含有前后端部署安装的逻辑,假设应用名称是app,app应用有两个模块serverbrowser,shell脚本如下:

    app.sh:

    #!/bin/bash
    
    # 程序名
    APP=app
    # 程序目录
    APP_PATH="/usr/local/iot/$APP"
    SERVER_NAME="$APP-server"
    SERVER_JAR="$APP-server.jar"
    TODAY="`date +%Y%m%d`"
    
    # 启动前需要设置的环境变量
    setEnvVars(){
      # 如果使用默认的glibc的ptmalloc2内存分配器,为避免64M arena问题,应加上这个参数
      # export MALLOC_ARENA_MAX=4
      # 使用jemalloc内存分配器
      export LD_PRELOAD=/usr/local/lib/libjemalloc.so
      # jvm崩溃是导出core文件
      ulimit -c unlimited
    }
    
    # JVM启动参数
    JVM_FLAGS=(
        '-Xms500M'
        '-Xmx1500M'
        '-XX:NativeMemoryTracking=detail'
    )
    
    #使用说明,用来提示输入参数
    usage() {
        echo -e "Usage: ./app.sh <command>"
        echo -e "commands:"
        echo -e "	 status"
        echo -e "	 start"
        echo -e "	 stop"
        echo -e "	 restart"
        echo -e "	 log"
        echo -e "	 install <module> <version>"
        echo -e "	 update <server-part> <version>"
        echo -e "<module>:"
        echo -e "	 server"
        echo -e "	 browser"
        echo -e "<server-part>:"
        echo -e "	 jar"
        echo -e "	 config"
        echo -e "<version>:"
        echo -e "	 when install, it can install sever or browser module tar.gz file like:"
        echo -e "	 - $APP-server-1.0.11.tar.gz"
        echo -e "	 - $APP-browser-1.0.11.tar.gz
    "
        echo -e "	 when update, it can only update server module from tar.gz file like:"
        echo -e "	 - $APP-server-nolib-1.0.11.tar.gz"
        exit 1
    }
     
    ############################### 检查状态 ###############################
    is_exist(){
      pid=`ps -ef|grep $SERVER_JAR|grep -v grep|awk '{print $2}' `
      #如果不存在返回1,存在返回0
      if [ -z "${pid}" ]; then
       return 1
      else
        return 0
      fi
    }
    
    ############################### 重启 ###############################
    restart(){
      stop
      start
    }
    
    ############################### 启动 ###############################
    start(){
      is_exist
      if [ $? -eq "0" ]; then
        echo "${SERVER_JAR} is already running. pid=${pid} ."
      else
        cd "$APP_PATH/server"
        setEnvVars
        nohup java ${JVM_FLAGS[*]} -jar $SERVER_JAR >/dev/null 2>&1 &
        status
      fi
    }
     
    ############################### 停止 ###############################
    stop(){
      is_exist
      if [ $? -eq "0" ]; then
        echo "kill Java process: $pid"
        kill -9 $pid
      else
        echo "${SERVER_JAR} is not running"
      fi
    }
     
    ############################### 查看状态 ###############################
    status(){
      is_exist
      if [ $? -eq "0" ]; then
        echo "${SERVER_JAR} is running. Pid is ${pid}"
      else
        echo "${SERVER_JAR} is NOT running."
      fi
    }
    
    ############################### 查看日志 ###############################
    log(){
      tail -fn 1000 "$APP_PATH/log/$APP.log"
    }
    
    ############################### 更新server模块的jar或config ###############################
    update(){
      serverPart=$1
      version=$2
      if [ -z "$serverPart" ] ;then
        echo "invalid <server-part>, it can't be empty. example: $APP update jar 1.0.11, 'jar' is the <server-part>"
        exit 1
      fi
      if [ "$serverPart" != "jar" ] && [ "$serverPart" != "config" ] ;then
        echo "invalid <server-part>, it must be 'config' or 'jar', like: $APP update jar 1.0.11"
        exit 1
      fi
      if [ -z "$version" ] ;then
        echo "invalid <version>, it can't be empty. example: $APP update jar 1.0.11, '1.0.11' is the <version>"
        exit 1
      fi
      versionFile="$APP_PATH/version/$APP-server-nolib-$version.tar.gz"
      if [ ! -f "$versionFile" ]; then
        echo "invalid <version>, corresponding tar.gz file not exist: $versionFile"
        exit 1
      fi
      if [ "$serverPart" == "jar" ]; then
        updateJar "$version"
      fi
      if [ "$serverPart" == "config" ]; then
        updateConfig "$version"
      fi
    }
    
    ######################### 将version目录下指版本的tar.gz文件copy到temp目录下解压出来 #########################
    extractVersionTagFile(){
      version=$1
      tarFile="$APP_PATH/version/$SERVER_NAME-nolib-$version.tar.gz"
      tempDir="$APP_PATH/temp/$version"
      echo -e "copy to tempdir: $tarFile --> $tempDir"
      rm -rf "$tempDir"
      mkdir -p "$tempDir"
      cp "$tarFile" "$tempDir"
    
      echo -e "extract file:	 $tempDir/$SERVER_NAME-nolib-$version.tar.gz"
      cd "$tempDir"
      tar -xf "$SERVER_NAME-nolib-$version.tar.gz"
      rm "$SERVER_NAME-nolib-$version.tar.gz"
    }
    
    ######################################### 更新jar文件 #########################################
    updateJar(){
      # print version
      version=$1
      echo "update jar of version: $version"
      if  [ -z "$version" ] ;then
        echo "version is empty"
        exit 0
      fi
      # backup old jar
      oldJar="$APP_PATH/server/$SERVER_JAR"
      if [ -f $oldJar  ]; then
        echo -e "backup file:	 $oldJar --> $oldJar.bak"
        mv -f "$oldJar" "$oldJar.bak"
      fi
      # copy tar.gz file in version/ to temp/ and extract it
      extractVersionTagFile $version
      # copy new jar to server/
      echo -e "replace jar:	 $tempDir/$SERVER_JAR --> $APP_PATH/server/$SERVER_JAR"
      cp "$tempDir/$SERVER_JAR" "$APP_PATH/server/$SERVER_JAR"
    }
    
    ######################################### 更新application.yml配置文件 #########################################
    updateConfig(){
      # print version
      version=$1
      echo -e "update config of version: $version"
      if  [ -z "$version" ] ;then
        echo "version is empty"
        exit 0
      fi
      # backup old config file
      oldConfig="$APP_PATH/server/config/application.yml"
      if [ -f $oldConfig  ]; then
        echo -e "backup file:	 $oldConfig --> $oldConfig.$TODAY.bak"
        mv -f "$oldConfig" "$oldConfig.$TODAY.bak"
      fi
      # copy tar.gz file in version/ to temp/ and extract it
      extractVersionTagFile $version
      # copy new jar to server/config/
      echo -e "replace config:	 $tempDir/config/application.yml --> $APP_PATH/server/config/application.yml"
      cp "$tempDir/config/application.yml" "$APP_PATH/server/config/application.yml"
    }
    
    ######################################### 安装 #########################################
    # 语法:
    #   app install <module> <version>
    # 例如:
    #   app install server 1.0.11
    #   app install browser 1.0.8
    install(){
      module=$1
      version=$2
      versionFile="$APP_PATH/version/$APP-$module-$version.tar.gz"
      if [ ! -f "$versionFile" ]; then
          echo "file not exist: $versionFile"
          exit 1
      fi
      # 备份
      oldModuleDir="$APP_PATH/$module"
      if [ -d $oldModuleDir  ]; then
        echo -e "backup module dir:	 $oldModuleDir --> $oldModuleDir-bak"
        rm -r "$oldModuleDir-bak"
        mv -f "$oldModuleDir" "$oldModuleDir-bak"
      fi
      installDir="$APP_PATH/$module" # example: /usr/local/iot/app/server
      mkdir -p "$installDir"
      echo -e "copy tar.gz:	 $versionFile --> $installDir"
      cp "$versionFile" "$installDir"
      # extact
      cd "$installDir"
      echo "extract tar.gz: $APP-$module-$version.tar.gz"
      tar -xf "$APP-$module-$version.tar.gz"
      rm "$APP-$module-$version.tar.gz"
      # install server module
      if [ "$module" == "server" ]; then
        echo "install server completed."
        echo "!!!! remember to restart server module: app restart !!!! "
      fi
      # or install browser module
      if [ "$module" == "browser" ]; then
        echo "install browser completed. change dir permission 755..."
        # change the browser module dir permission
        chmod 755 -R "$installDir"
        echo "!!!! remember to reload nginx: sudo nginx -s reload !!!! "
      fi
    }
    
    
     
    #根据输入参数,选择执行对应方法,不输入则执行使用说明
    case "$1" in
      "start")
        start
        ;;
      "stop")
        stop
        ;;
      "status")
        status
        ;;
      "log")
        log
        ;;
      "restart")
        restart
        ;;
      "update")
        update $2 $3
        ;;
      "install")
        echo "install 2: $2, 3: $3"
        install $2 $3
        ;;
      *)
        usage
        ;;
    esac
    
    
  • 相关阅读:
    SSL JudgeOnline 1194——最佳乘车
    SSL JudgeOnline 1457——翻币问题
    SSL JudgeOnlie 2324——细胞问题
    SSL JudgeOnline 1456——骑士旅行
    SSL JudgeOnline 1455——电子老鼠闯迷宫
    SSL JudgeOnline 2253——新型计算器
    SSL JudgeOnline 1198——求逆序对数
    SSL JudgeOnline 1099——USACO 1.4 母亲的牛奶
    SSL JudgeOnline 1668——小车载人问题
    SSL JudgeOnline 1089——USACO 1.2 方块转换
  • 原文地址:https://www.cnblogs.com/caibh/p/13812460.html
Copyright © 2011-2022 走看看