文档首页/ 应用管理与运维平台 ServiceStage/ 常见问题/ 应用开发问题/ 如何将Java或者Tomcat应用打包成压缩包用于虚拟机部署方式部署组件?
更新时间:2024-12-16 GMT+08:00

如何将Java或者Tomcat应用打包成压缩包用于虚拟机部署方式部署组件?

使用虚拟机部署方式部署组件时,ServiceStage支持将Java或者Tomcat应用打包成zip或者tar.gz压缩包用于部署。

应用压缩包内目录总体结构说明

以Java应用压缩包demoJavaExample.zip为例,应用压缩包内目录总体结构说明如下:

demoJavaExample/
│
├── scripts/
│   │      ├── pre-stop.sh
│   │      ├── stop.sh
│   │      ├── uninstall.sh
│   │      ├── install.sh
│   │      ├── start.sh
│   │      ├── check.sh
│   │      ├── post-start.sh
├── packages/
│   │      ├── weather-1.0.0.jar
├── config/
│   │      ├── system.cfg
├── appspec.yml

压缩包名前缀必须和解压后的文件目录名一致。例如压缩包名为demoJavaExample.zip,解压后文件目录必须为demoJavaExample。

应用压缩包内各个目录及文件的作用说明如下:

  • scripts:必选目录,存储的是应用各个生命周期执行的脚本文件。
  • packages:必选目录,存储的是应用的jar包或者war包。
  • config:必选目录,存储应用的配置信息。Java应用,存储的是system.cfg文件;Tomcat应用,存储的是system.cfg、logging.properties、server.xml文件。
  • appspec.yml:必选文件,记录了生命周期的定义,也可以指定健康检查等信息。

appspec.yml文件说明

如下所示,appspec.yml文件定义了整个部署的流程以及部署过程中使用到的环境变量和健康检查等内容。

spec:
  # 应用运行自定义用户
  deps:
    - name: "@os/linux/user@1.0"
      user: www
      group: www
      home: /home/www

  # 直接引入应用lifecycle脚本环境变量
  env:
  - name: APP_ENV
    value: "{{app.env}}"

  # 以文件形式引入
保存于/opt/application/${appName}/${appVersion}/${instanceId}/servicestage-vmapp/application.conf的
应用lifecycle脚本环境变量
  value:
  - name: APP_VALUE
    value: "{{app.value}}"

  # 应用健康检查
  probes:
    # 接口健康检查
    # health:
    #   exec:
    #     method: GET
    #     request: http://127.0.0.1:8080/healthcheck
    #     timeout: 5

    # 脚本命令健康检查
    liveness:
      exec:
        command:
          - ps -ef | grep ${APP_HOME}/apache-tomcat-*/bin/bootstrap.jar | grep -v grep   # 目前只支持APP_HOME可以取到环境变量
        timeout: 300
        runas: www

  # 应用lifecycle脚本
  lifecycle:
    install:
      - command: scripts/install.sh
        timeout: 300
    check:
      - command: scripts/check.sh
        timeout: 300
        runas: www
    start:
      - command: scripts/start.sh
        timeout: 300
        runas: www
    post-start:
      - command: scripts/post-start.sh
        timeout: 300
        runas: www
    pre-stop:
      - command: scripts/pre-stop.sh
        timeout: 300
        runas: www
    stop:
      - command: scripts/stop.sh
        timeout: 300
        runas: www
    uninstall:
      - command: scripts/uninstall.sh
        timeout: 300
  • deps : 此字段定义了应用运行自定义用户,建议参考如下示例固定。
    deps:
        - name: "@os/linux/user@1.0"
          user: www
          group: www
          home: /home/www
  • env:此字段记录了各脚本运行时的环境变量,在脚本运行时可以直接通过环境引入。
  • value:此字段记录的值会保存在${APP_HOME}/servicestage-vmapp/application.conf中。如果需要脚本中使用这些值,可以在脚本开始执行如下命令:
    #!/bin/bash 
      . ${APP_HOME}/servicestage-vmapp/application.conf
    # dosomething
  • probes:此字段记录了健康检查的命令或者是接口,两者可同时存在。
  • health:访问健康检查接口,查看组件状态。
    • method:查看状态时访问接口使用的HTTP方法。
    • request:访问的地址。
    • body:访问的请求体。
    • timeout:超时时间,单位为秒。
  • liveness:执行指定命令查看组件状态。
    • command:查看状态使用的命令。
    • runas:执行查看状态命令时的Linux用户,建议设置为www或者为空。
    • timeout:超时时间,单位为秒。
  • lifecycle:各个生命周期执行的脚本或者命令。
    • command:指定执行的脚本文件。必须是一个文件相对于${APP_HOME}文件夹所在的相对路径。
    • timeout:超时时间,单位为秒。
    • runas:执行的用户身份,处必须是www或者不填。

生命周期执行顺序:

  • 部署的顺序:ServiceStage安装技术栈+install.sh —>start.sh—>check.sh—>post-start.sh
  • 升级/回滚的顺序:ServiceStage安装技术栈+install.sh—>pre-stop.sh—>stop.sh—>uninstall.sh+ServiceStage卸载技术栈—>start.sh—>check.sh—>post-start.sh
  • 删除的顺序:pre-stop.sh—>stop.sh—>uninstall.sh+ServiceStage卸载技术栈

config说明

  • system.cfg文件
    1. 设置config参数

      使用虚拟机部署方式创建并部署组件时,您可以参考添加配置项设置一些配置项,ServiceStage会将设置的配置项以键值对的形式存储在${APP_HOME}/config/system.cfg文件中。您也可以预置一些配置参数在config里面,格式参考示例如下:

      # system.cfg文件中存储内容的格式
      key1=value1
      key2=value2
    2. 引用config参数

      引用config参数的方法示例如下:

      #!/bin/bash 
        . ${APP_HOME}/config/system.cfg 
        . ${APP_HOME}/config/user_config.cfg 
        echo ${key1}
  • server.xml文件

    请参考tomcat的server.xml说明

  • logging.properties文件

    请参考tomcat的logging.properties说明

packages目录说明

您需要将执行的jar包、war包等内容保存在此文件夹中,在脚本中使用命令进行执行。

scripts目录说明

此文件夹用于存储应用各个生命周期执行的脚本。

系统默认配置

系统默认配置均在${APP_HOME}/servicestage-vmapp/application.conf文件中。文件内容如下所示:

export LOG_PATH=/var/log/application/zqb-4-vm-wqd-2-7f6fbc/3c719644-f9f5-46b4-a06a-61fcf163e5b5
export APP_HOME=/opt/application/zqb-4-vm-wqd-2-7f6fbc/2023.1207.11314/3c719644-f9f5-46b4-a06a-61fcf163e5b5
export TOMCAT_STACK_HOME=/opt/application/zqb-4-vm-wqd-2-7f6fbc/2023.1207.11314/3c719644-f9f5-46b4-a06a-61fcf163e5b5/apache-tomcat-8.5.82
export JRE_STACK_HOME=/opt/application/zqb-4-vm-wqd-2-7f6fbc/2023.1207.11314/3c719644-f9f5-46b4-a06a-61fcf163e5b5/jre1.8
export APP_VALUE="{{app.value}}"
export APP_USER=www
export APP_GROUP=www
  • LOG_PATH:日志记录的地址,需要统一将应用日志打印到LOG_PATH下的日志文件中,命名为{lifecycle}_app.log,方便后续的日志上报。
  • APP_HOME:记录了当前应用运行的环境。
  • TOMCAT_STACK_HOME:Tomcat的主目录。
  • JRE_STACK_HOME:Jre的主目录。
  • APP_VALUE:在appspec.yml中指定的环境变量。
  • APP_USER:文件所属的用户。
  • APP_GROUP:文件的属组。

系统默认配置并不会直接出现在环境变量中,可用的脚本环境变量为APP_HOME、在appspec.yml中指定的环境变量和添加组件环境变量时指定的环境变量。

脚本编写说明

Servicestage在执行脚本时的目录结构如下所示:

APP_HOME/
│
├── scripts/
│   │      ├── pre-stop.sh
│   │      ├── stop.sh
│   │      ├── uninstall.sh
│   │      ├── install.sh
│   │      ├── start.sh
│   │      ├── check.sh
│   │      ├── post-start.sh
│   ├──packages/
│   │      ├── my-app.jar/my-app.war
├── config/
│   │      ├── system.cfg
├── servicestage-vmapp/
│   │      ├── application.conf
├── jre1.8
├── apache-tomcat-8.5.82
  • 在编写脚本时,可以先进入${APP_HOME}目录,获取其他文件的位置。
  • 若想获取jre或者tomcat的位置,可以在脚本开始位置执行。${APP_HOME}/servicestage-vmapp/application.conf加载环境变量,再通过${JRE_STACK_HOME}或${TOMCAT_STACK_HOME}引用获取。
  • Servicestage会将${LOG_PATH}目录下的日志上报,因此需要统一将应用日志打印到${LOG_PATH}目录下的日志文件中,命名为{lifecycle}_app.log,方便后续日志上报。
  • 建议在使用时,脚本都是使用如下几行作为开头,用于获取环境变量、用户配置、指定技术栈HOME、指定日志输出路径、指定当前执行地址等。
    #!/bin/bash
    
    # 获取用户参数、指定环境变量
    . ${APP_HOME}/config/system.cfg
    . ${APP_HOME}/servicestage-vmapp/application.conf
    
    # 指定技术栈HOME
    JRE_HOME=${JRE_STACK_HOME}
    TOMCAT_HOME=${TOMCAT_STACK_HOME}
    
    #指定日志输出地址
    installLog="${LOG_PATH}/install_app.log"
    
    #打印日志使用方法
    function writeLog()
    {
        msg="$1\n"
        printf "[`date '+%Y-%m-%d %H:%M:%S'`] $msg" | sudo tee -a ${installLog};
    }
    
    writeLog "-----begin!------"

tomcat的server.xml说明

  • 在创建tomcat组件时可以提前准备server.xml,将server.xml复制到tomcat的目录下(这一步骤建议在install生命周期中执行)。
    #!/bin/bash
    . ${APP_HOME}/servicestage-vmapp/application.conf 
    cp ${APP_HOME}/conf/server.xml ${TOMCAT_STACK_HOME}/conf/server.xml
  • ServiceStage在${TOMCAT_STACK_HOME}/conf/server.xml有一份预置的server.xml文件,其中有四个占位符,使用时需要将其正确的替换,可以参考如下代码:
    #!/bin/bash
    . ${APP_HOME}/servicestage-vmapp/application.conf
    function replacePara()
    {
        sWord=$1
        dWord=$2
        theFile=$3
        if [[ "$sWord" == "" || "$theFile" == "" ]]
        then
            writeLog "[ERROR] ReplacePara has empty param, \$1:$sWord, \$3:$theFile"
            return 1
        fi
    
        if [[ ! -f ${theFile} ]]
        then
            writeLog "[ERROR] File $theFile does not exist."
            return 1
        fi
        count=`grep -c "$sWord" ${theFile}`
        if [[ "${count}" == "0" ]];then
            return 0
        fi
        sed "s#$sWord#${dWord}#g" ${theFile} > ${theFile}.temp
        if [[ "$?" != "0" ]]
        then
            writeLog "[ERROR] Sed command in replacePara error."
            return 1
        fi
        mv -f ${theFile}.temp ${theFile}
    }
    replacePara "@{LOG_FILE_PATH_APP}" "${LOG_FILE_PATH_APP}" ${TOMCAT_HOME}/conf/server.xml
    replacePara "@{http_port}" "${instance_port_port}" ${TOMCAT_HOME}/conf/server.xml
    replacePara "@{server_port}" "${server_port}" ${TOMCAT_HOME}/conf/server.xml
    replacePara "@{APP_PACKAGE_NAME}" "examples.war" ${TOMCAT_HOME}/conf/server.xml
    • @{server_port}:tomcat服务器的端口号。
    • @{APP_PACKAGE_NAME}:war包相对于${TOMCAT_STACK_HOME}目录的位置。
    • @{http_port}: tomcat的Connector的端口号。
    • @{LOG_FILE_PATH_APP}:tomcat日志打印的路径。

tomcat的logging.properties说明

您需要自行准备logging.properties,将其复制到tomcat的目录下,复制代码示例如下:

#!/bin/bash
. ${APP_HOME}/servicestage-vmapp/application.conf 
cp ${APP_HOME}/conf/logging.properties ${TOMCAT_STACK_HOME}/conf/logging.properties

logging.properties可以参考如下内容,使用时将@{LOG_FILE_PATH_APP}替换为真正的日志目录。

handlers = 1catalina.org.apache.juli.AsyncFileHandler, 2localhost.org.apache.juli.AsyncFileHandler, 3manager.org.apache.juli.AsyncFileHandler, 4host-manager.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler

.handlers = 1catalina.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler

1catalina.org.apache.juli.AsyncFileHandler.level = FINE
1catalina.org.apache.juli.AsyncFileHandler.directory = @{LOG_FILE_PATH_APP}
1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina.

2localhost.org.apache.juli.AsyncFileHandler.level = FINE
2localhost.org.apache.juli.AsyncFileHandler.directory = @{LOG_FILE_PATH_APP}
2localhost.org.apache.juli.AsyncFileHandler.prefix = localhost.

3manager.org.apache.juli.AsyncFileHandler.level = FINE
3manager.org.apache.juli.AsyncFileHandler.directory = @{LOG_FILE_PATH_APP}
3manager.org.apache.juli.AsyncFileHandler.prefix = manager.

4host-manager.org.apache.juli.AsyncFileHandler.level = FINE
4host-manager.org.apache.juli.AsyncFileHandler.directory = @{LOG_FILE_PATH_APP}
4host-manager.org.apache.juli.AsyncFileHandler.prefix = host-manager.

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.AsyncFileHandler

FAQ

  • 如何以root账户运行脚本命令?
    1. 将要以root账户运行的内容编写成一个脚本root-install.sh。
    2. 在真正要执行的install.sh执行如下语句,其中${ROOT_PASSWORD}为root账号的密码,-c后面的双引号中为执行的命令。切换用户后会丢失环境变量,可以在sh前加上所需要的环境变量。
      echo "${ROOT_PASSWORD}" | su - root -c "APP_HOME=${APP_HOME} sh root-install.sh"
    3. 若执行上述语句发生“su: Permission denied”报错,找到/etc/pam.d/su文件,将“ auth required pam_wheel.so use_uid”这一句注释掉。