• 部署方案@应用系统单点切换解决方案


    1 背景介绍

      1. 对于应用主要部署在云虚拟机上,云虚拟机作为IT服务最主要也是最重要的基础设施,其稳定性和可用性对公司业务的影响至关重要。
      2. 当生产云虚拟机发生重大故障时,由于应用节点大都是单节点部署,只能选择处理时间较长的主机恢复方案进行修复,因此导致服务停机长达数小时,部分业务处理中断。因此,提高服务的可用性成为了眼下急需解决的问题。
      3. 对于java应用部署方式为两种:基于tomcat的war部署以及普通的jar部署。不规范不统一的部署,给日常版本发布以及系统运维带来一些额外的成本,而故障恢复也需要一个统一且简单的操作以减少恢复时间,因此,规范应用的部署,并实现自动化是首先要解决的问题。
    

    2 整体方案

    要消灭单点问题,最终需要全部更换为集群部署方案。应用服务的集群部署包括:需针对实时服务、定时任务、工作流以及其他存在前后依赖关系的任务等,制定对应的集群方案,这是一个相对漫长且复杂的过程。但是,集群部署除了需要更多的主机资源之外,还需要对各个系统进行适应性改造。因此,整体方案上,采用循序渐进的方式从单节点部署过渡到集群部署上,结合已有的主机资源,过渡方案采用冷备方式,虽然不能直接提升服务的可用性,但可以大大减少故障恢复的时间。

      1. 对每个应用系统的主机进行克隆冷备,平时不启动,因此只消耗部分硬盘资源,不占用稀缺的内存和CPU资源,故障发生时,先将原来的主机直接下线,然后启动克隆主机,部署最新的应用版本后即可恢复服务,服务恢复时间从原来的数小时降低到半小时以内。
      2. 对于应用的部署,从应用的系统用户、权限、部署目录结构、日志的命名以及日志文件的切换等这几个方面进行规范,力求规格统一。同时,采用Jenkins+maven实现自动发布部署,出于系统安全考虑,测试环境和生产环境需要隔离。
    

    3 部署工作

    3.1 应用部署规范

    • 3.1.1 方案说明

    用户

    组名称 组ID 备注
    staff 600

    用户名称 用户ID 用户目录
    {应用简称} 601 staff /home/{应用简称}
    cxwh 602 staff /home/cxwh

    应用目录结构

    目录 属主/组 备注
    /data/{应用用户名} {应用用户名}:staff 应用存放目录
    /data/{应用用户名}/java {应用用户名}:staff Jdk软件安装包存放目录
    /data/{应用用户名}/tomcat {应用用户名}:staff tomcat安装包存放目录
    /data/{应用用户名}/config {应用用户名}:staff 项目环境相关配置文件,一般是xxx.properties
    /data/{应用用户名}/application {应用用户名}:staff 当前运行版本的版本目录,采用软链接到release对应版本
    /data/{应用用户名}/deploy {应用用户名}:staff 发版时文件存放位置
    /data/{应用用户名}/release {应用用户名}:staff 应用文件发版后存放的位置
    /data/{应用用户名}/tools {应用用户名}:staff 应用相关工具存放位置,例如版本发布脚本
    /data/{应用用户名}/logs {应用用户名}:staff 应用,脚本日志目录
    /data/{应用用户名}/temp {应用用户名}:staff 临时文件目录
    /data/{应用用户名}/resource {应用用户名}:staff 数据文件等资源存放位置
    /data/{应用用户名}/backup {应用用户名}:staff 备份目录

    包部署命名规范

    应用简称-版本号-日期-环境标识.war
    其中,
    应用简称: 一般与应用用户名一致
    版本号: x.x.x,x是整数,可以是两位
    日期: 格式是yyyymmdd
    环境标识: 与maven上的profile的id一致,一般是dev/sit/uat/prod,分别表示开发/集成测试/用户验收测试/生产

    日志规范

      1. 基于tomcat的应用
        Tomcat日志文件名:tomcat.应用简称.YYYYMMDD.log,以天为单位存储切割
        应用日志文件名:应用简称.YYYYMMDD.log,以天为单位存储切割
      2. 其他应用
        由于存在多样性,原则上不做要求
    
    • 3.1.2 实施步骤
      1 按用户与组新建系统组以及用户
      2 按卷组、逻辑卷规划新建/调整文件系统
      3 按应用部署结构规划新建目录并分配权限
      4 根据需要在指定目录下安装java、tomcat等软件
      5 配置java,tomcat等参数,系统用户主目录下的.bash_profile配置,tomcat的server.xml配置
      6 将应用生产版本按目录结构规范分别存放部署
      7 停止旧的应用服务
      8 启动新部署的应用服务
      9 检查验证应用服务
    

    3.2 自动化部署

    • 3.2.1 方案说明

    针对目前产线上,每次投产发布应用时,研发人工部署的情况比较多,使得运维工作开展时,需要研发人员紧密配合。为了提高效率和实时性,搭建一套自动化部署平台,将应用系统的部署发布流程做成自动化模式。这样在产线投产时,避免人工部署的情况,降低出错几率,加速发布流程。同时流程得以标准化,便于系统管理与日常运维。最后在应用系统的部署发布流程中,可以做到即使没有对应的应用系统的研发人员,运维人员也可自行发包,提高运维效率。

    • 3.2.2 实施步骤
      1 申请自动化部署平台服务器,安装常用软件以及本次使用的开源框架Jenkins。
      2 根据应用部署规范,将应用服务器的用户、目录进行标准化统一,编写日常运维脚本,如发布、启停等脚本。
      3 收集各应用系统发版流程,根据项目的特点制定自动化部署流程,最后归档成操作手册。
      4 将操作手册分发到各应用服务负责人,按照操作手册,部署配置Jenkins。
      5 配置用户权限以及Jenkins的网络访问权限,以最小化原则配置Jenkins,使得特定角色和特定环境才可以访问。
      6 选择发版日,应用系统逐个进行自动化发版验证。
    

    4 附录

    4.1 规范目录创建创建脚本

    • 脚本create_app.sh 代码
    
    if [ $# -lt 1 ];then
        echo "usage:$0 user_name"
        echo "其中:"
        echo "user_name 应用系统用户名"
        exit 0
    fi
    
    user_name=$1
    
    change_pswd()
    {
    expect <<EOF
       set timeout 3
       spawn passwd $1
       expect "*新的 密码*"
       send "$2
    "
       expect "*新的 密码*"
       send "$2
    "
       expect "*成功更新*"
    EOF
    }
    
    result=`type expect |grep "expect is"`
    if [ "$result" == "" ];then
        echo "需要安装 expect 工具"
        yum install expect -y
    fi
    
    echo "即将创建系统用户名为[$user_name]的应用部署环境"
    #echo "按任意键继续(ctrl+c 取消)..."
    #read anykey
    
    flag=`cat /etc/group |grep -w staff`
    if [ "$flag" == "" ];then
        echo "新建[staff]组"
        groupadd -g 600 staff
    fi
    
    flag=`cat /etc/passwd |grep -w cxwh`
    if [ "$flag" == "" ];then
        echo "新建[cxwh]用户(默认密码是:cxwh)"
        useradd -g staff cxwh
        change_pswd cxwh cxwh
    fi
    
    flag=`cat /etc/passwd |grep -w ${user_name}`
    if [ "$flag" != "" ];then
        echo "[${user_name}]用户已经存在,不需要创建!"
    else
        echo "新建[${user_name}]用户(默认密码:${user_name})"
        useradd -g staff ${user_name}
        change_pswd ${user_name} ${user_name}
    fi
    
    mkdir -p /data/${user_name}
    mkdir -p /data/${user_name}/java
    mkdir -p /data/${user_name}/tomcat
    mkdir -p /data/${user_name}/application
    mkdir -p /data/${user_name}/config
    mkdir -p /data/${user_name}/deploy
    mkdir -p /data/${user_name}/release
    mkdir -p /data/${user_name}/tools
    mkdir -p /data/${user_name}/logs
    mkdir -p /data/${user_name}/temp
    mkdir -p /data/${user_name}/resource
    mkdir -p /data/${user_name}/backup
    
    chown -R ${user_name} /data/${user_name}
    
    bash_profile="/home/${user_name}/.bash_profile"
    
    cat <<!! > $bash_profile
    # .bash_profile
    
    # Get the aliases and functions
    if [ -f ~/.bashrc ]; then
        . ~/.bashrc
    fi
    
    # User specific environment and startup programs
    
    PATH=$PATH:$HOME/bin
    
    export PATH
    
    export JAVA_HOME=/data/${user_name}/java
    export PATH=.:/data/${user_name}/tools:$JAVA_HOME/bin:$PATH
    
    alias cdd="cd /data/${user_name}/deploy"
    alias cdl="cd /data/${user_name}/logs"
    alias cda="cd /data/${user_name}/application"
    
    set -o vi
    
    !!
    
    echo "创建完成."
    
    
    • 脚本create_app.sh 使用

    1 用root登录应用对应的主机,然后用create_app.sh执行创建用户环境操作就可以了,比如app

      1 用root登录 
      2 把create_app.sh脚本传上去
      3 执行脚本 sh create_app.sh app
      4 验证一下用户,目录是否按规范创建好
    

    2 脚本会执行以下操作

      1 如果没有安装 expect ,执行 yum install expect -y 安装
      2 如果没有 staff 组,执行 groupadd -g 600 staff 创建
      3 如果没有 cxwh 用户,执行 useradd -g staff cxwh 创建,初始密码与用户名一样
      4 假设要创建的用户是 pad,如果系统还没有 pad 用户, 执行 useradd -g staff pad 创建,初始密码与用户名一样
      5 在/data/${user_name}目录下用mkdir新建整套应用部署目录(如果已经存在,不受影响),并授权 chown -R ${user_name} /data/${user_name}
      6 修改 /home/${user_name}/.bash_profile 配置,主要是PATH,JAVA_HOME,alias
    

    4.2 项目规范部署执行脚本

    • 脚本deploy.sh 代码
    #!/bin/bash
    source ~/.bash_profile
    
    app_name=$1
    
    #判断执行脚本是否输入应用名
    if [ $# -lt 1 ];then
        echo "执行请输入: $0 app_name" >>$log_file 2>&1
        echo "其中app_name为应用名称" >>$log_file 2>&1
        cat $log_file
        exit 2
    fi
    
    #获取当前用户名
    user_name=`whoami`
    
    #获取当前时间
    date=`date +%Y%m%d%H%M%S`
    
    #获取当前服务器IP地址
    LOCAL_IP=`ip addr show eth0 | grep global | awk -F ' ' '{print $2}' | sed 's//24//'`
    
    logs_path=/data/${user_name}/logs
    deploy_path=/data/${user_name}/deploy
    release_path=/data/${user_name}/release
    application_path=/data/${user_name}/application
    backup_path=/data/${user_name}/backup/release
    tomcat_path=/data/${user_name}/tomcat
    #部署过程日志
    log_file=${logs_path}/deploy.${app_name}.${date}.log
    
    
    #备份IP信息
    backup_server_list=(
    #192.168.90.11
    192.168.80.13
    )
    BACKUP_USER="appbak"
    BACKUP_PSWD="Appbak%110"
    BACKUP_PORT="22"
    
    #写日志函数
    log()
    {
        echo "[`date +'%Y%m%d %H%M%S'`] $1 $2" >> $log_file
    }
    log_info()
    {
        log "info " "$1"
    }
    log_warn()
    {
        log "warn " "$1"
    }
    log_error()
    {
        log "error " "$1"
    }
    
    #war包部署过程
    echo "发版日志文件: ${log_file}"
    log_info "${LOCAL_IP}应用[${app_name}]开始发版"
    
    cd ${deploy_path}
    log_info "1.在${deploy_path}目录下查找待发布的war包"
    var=`ls *${app_name}*.war`
    if [ "$var" == '' ];then
        log_error "待发布的war包不存在,发布失败!"
        cat ${log_file}
        exit 3
    fi
    release=${var%%.war}
    
    log_info "2.待发布的war包: ${release}.war"
    
    
    log_info "3.本地备份war包: ${release}.war --> ${backup_path}"
    cp ${release}.war ${backup_path}/ >> ${log_file} 2>&1
    
    #判断远程备份IP是否存在
    port_test()
    {
        echo ""|telnet $1 $2 2>/dev/null | grep "^]" | wc -l
    }
    for element in ${backup_server_list[@]}
    do 
        result=`port_test ${element} ${BACKUP_PORT}`
        if [ "${result}" == "1" ];then
        BACKUP_SERVER=${element}
        break
        fi
    done
    if [ "${BACKUP_SERVER}" == "" ];then
        log_error "无可用的远程备份主机,无法远程备份,不能发版!"
        cat $log_file
        exit 4
    fi
    BACKUP_BACK_PATH="application/${BACKUP_SERVER}/${user_name}"
    
    log_info "4.远程备份war包: ${release}.war --> ${BACKUP_USER}@${BACKUP_SERVER}:${BACKUP_BACK_PATH}"
    
    #远程备份war包函数
    sftp_sample()
    {
    expect <<EOF
       set timeout 3                      
       spawn sftp -oPort=${BACKUP_PORT} ${BACKUP_USER}@${BACKUP_SERVER}
       expect {
        "*yes/no*" { send "yes
    "; exp_continue }
        "*password*" { send "${BACKUP_PSWD}
    " }
        }
       expect "sftp>"
       send "cd ${BACKUP_BACK_PATH}
    "
       expect "sftp>"
       send "put ${release}.war
    "
       expect "sftp>"
       send "exit
    "
    EOF
    }
    sftp_sample 2>&1 >/dev/null
    
    log_info "5.删除当前应用版本的软链接"
    rm -rf ${application_path}/${app_name}
    rm -rf ${release_path}/${app_name}*
    
    log_info "6.解压新版本war包,并建立新版本的软链接"
    unzip -o ${release}.war -d ${release_path}/${release} 2>&1 >/dev/null
    ln -s ${release_path}/${release} ${application_path}/${app_name} >>$log_file 2>&1
    
    bin_path=${application_path}/${app_name}/sbin
    tag="java"
    if [ ! -d ${bin_path} ];then
        bin_path=${tomcat_path}/bin/
        tag="tomcat"
    fi
    
    log_info "7.服务启停脚本目录:$bin_path"
    
    log_info "8.停止${tag}服务"
    sh ${bin_path}/shutdown.sh >>$log_file 2>&1
    sleep 10
    for p in `jps | grep Bootstrap | awk '{print $1}'`
    do
           kill -9 $p
    done
    
    jps | grep Bootstrap 2>&1 >/dev/null
    if [ "$?" = "0" ];then	
    	echo "${tag}服务停止失败,请查验!" >>$log_file 2>&1
    	cat ${log_file}
    	exit 5
    else
    	echo "${tag} stoped." >>$log_file 2>&1
    fi
    #for p in `jps | grep Bootstrap | awk '{print $1}'`
    #do
    #	kill -9 $p
    #done
    
    log_info "9.启动${tag}服务"
    sh ${bin_path}/startup.sh >>$log_file 2>&1
    
    sleep 30
    
    jps | grep Bootstrap >>$log_file 2>&1
    if [ "$?" = "0" ];then	
    	log_info "10.发版完成,请查看tomcat日志确保服务正常启动,并做生产验证."
    else
    	log_info "10.${tag}服务启动失败,请查验!"
    	cat ${log_file}
    	exit 6
    fi
    
    rm -rf ${deploy_path}/${release}.war
    cat ${log_file}
    
    
  • 相关阅读:
    Oracle并行操作——从串行到并行
    Log4Net使用指南
    NET开发人员应该要知道
    测试11g压缩性能
    C#不同操作系统下,界面大小不一的原因
    采用nettcp绑定的wcf宿主到iis7
    Packaging Oracle Data Access Components into .Net projects
    Operating System Property Values
    【转】《Effective C#中文版:改善C#程序的50种方法》读书笔记
    解决.svc 无法解析
  • 原文地址:https://www.cnblogs.com/qq438649499/p/12322144.html
Copyright © 2020-2023  润新知