本文最后更新于 2023-11-23,文章内容可能已经过时。

自动化部署教程

1.引入:

当我们在日常工作中进行代码开发的时候,经常会遇到这样的问题:

凌晨两点,前端告诉我某一个接口出现了bug,恰好我还没睡,关掉抖音好不容易debug完成,然后发现自己没有服务器的权限,已经部署好的项目无法依旧是无法更新,于是我尝试去联系拥有部署权限的,为数不多的几个负责人,然后发现他们早已经睡觉了.....

在团队开发项目时,这种场景时常可见,而让每一个开发人员都掌握部署的相关知识并给他们响应的权限显然也是不太现实的,那么如何解决这个问题呢?我们很自然的就会想到,能否使用类似"触发器"的工具来解决项目的部署问题,每当有人提交自己新的代码时,触发器就会自动的触发,自动的连接上生成服务器,并且重新拉取,打包,构建,部署项目,这就是自动化部署.

自动化部署可以,节约时间,提高效率,不怕频繁改代码,同时也是devops的重要组成部分.

dac3ee37b168f93a78bb7a623e6fb935

2.环境搭建:

2.1 技术选型:

本次部署的项目为使用maven进行构建的springcloud微服务项目,由于服务器资源不足,所有的微服务模块都在同一台服务器上,搭建自动化部署的程序在另一台服务器.

自动化部署的工具有很多,由于本次项目代码仓库在gitlab上,应用比较多的是gitalab runner和jenkins,考虑到可拓展性和未来重复使用等问题最终采用jenkins.

2.2 安装:

2.2.1 安装java:

1.搜索java安装包 寻找后缀为linux-x64.tar.gz的对应版本,下载下来,发送到服务器上,并执行命令

tar -vzxf jdk-8u161-linux-x64.tar.gz -C /usr/local/java/  //注意文件名可能会有所不同

2.添加java的环境变量

使用vi或者其他编辑方式打开 /etc/profile 然后在文件最底部输入:

export JAVA_HOME=/usr/local/java/jdk1.8.0_161  //注意修改为你自己的安装地址
export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib/
export PATH=$PATH:$JAVA_HOME/bin

最后使用 source /etc/profile使配置文件生效.

3.检查是否安装成功:

java -version

成功情况如下:

image-20231010141817948

更多安装细节以及方式,可参看:

linux安装jdk环境(多种方式)_linux 安装jdk-CSDN博客

2.2.2 安装maven:

首先说明,这一步不是必须的(网上很多教程说是必须).实践证明jenkins只需要依赖jdk就可以运行.

如果你需要在这个服务器上进行打包构建,那么就需要安装,否则就不需要安装.

安装maven之前需要先安装java.

1.搜索maven安装包 寻找后缀为bin.tar.gz的对应版本,下载下来,发送到服务器上,并执行命令

tar -xvf apache-maven-3.6.3-bin.tar.gz   //注意文件名可能会有所不同

2.修改配置文件,配置镜像:

进入maven安装地址下面的/conf,找到settings.xml,把其内容修改为:

<?xml version="1.0" encoding="UTF-8"?>
 
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
 
  <localRepository>/usr/local/maven/repository</localRepository>
  
  <pluginGroups>
  </pluginGroups>
 
  <proxies>    
  </proxies>
 
  <servers>
  </servers>
 
  <mirrors>
    <mirror>  
      <id>alimaven</id>  
      <name>aliyun maven</name>  
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>  
      <mirrorOf>central</mirrorOf>          
    </mirror> 
  </mirrors>
 
  <profiles>
  </profiles>
</settings>

3.添加环境变量:

打开/etc/profile,添加如下内容:

MAVEN_HOME=/usr/local/maven/apache-maven-3.6.3     //注意修改为你自己的安装地址
PATH=$MAVEN_HOME/bin:$PATH
export MAVEN_HOME PATH

最后使配置文件生效 source /etc/profile

4.检查是否成功:

mvn -version

成功情况如下:

image-20231010142916302

2.2.3 安装jenkins:

安装有多种方式.这里推荐两种方式.

2.2.3.1 war包:

进入官网https://jenkins.io/ ,下载自己所需要的版本.

将下载好的war包放在一个文件夹内,切换到该文件夹,启动:

cd c:\jenkins  //你自己的路径
java -jar Jenkins.war
​

当显示 Jenkins is fully up and running 时,表示Jenkins服务器启动成功。

2.2.3.2 docker:

docker   pull   jenkins/jenkins:2.344      #拉取jenkins镜像
docker run -d -p 10240:8080 -p 10241:50000 -v /var/jenkins_mount:/var/jenkins_home   -v  /usr/local/maven/apache-maven-3.8.5:/usr/local/maven   -v /usr/local/git/bin/git:/usr/local/git    -v /etc/localtime:/etc/localtime --name myjenkin 镜像id   #启动jenkins镜像
​

正确启动并配置好服务器安全组端口之后,就可以访问jenkins了.

2.3 配置:

使用浏览器访问ip:port会看到如下页面:

在这里插入图片描述

找到对应的密码,输入点击继续.

在这里插入图片描述

选择安装推荐的插件.然后耐心的等待较长一段时间.

安装完成后,就可以看到类似如下页面:

image-20231015104210472

为了实现所有的功能,第一步推荐安装完成插件.

点击系统管理-插件管理,进行插件的安装.比较重要的有

image-20231015104547882

(如果不使用服务器打包就无需安装和启用)

image-20231015104613715

image-20231015105637410

如果使用gitlab,还需要安装这个插件.

3.建立任务:

建立一个任务,可以是自由风格或者maven.

image-20231015104706043

新建任务之后可以看到任务栏右侧的内容:

image-20231015104820454

可以看到,整个平台的操作都是以构建作为核心,这里现在解释一下什么叫构建:

构建就是以我们编写的 Java 代码、框架配置文件、国际化等其他资源文件、JSP 页面和图片等静态资源作为“原材料”,去“生产”出一个可以运行的项目的过程。构建是把我们在开发环境写的代码,转换成生产环境的代码。

平台的逻辑就是获取代码-执行构建前操作-进行构建-执行构建后操作.

3.1 配置源码:

自动化脚本获取源代码的地方.

如果使用git,截图如下:

image-20231015105331294

其中credentials是一个能正常登陆进入git仓库的账号,如果出现错误会报错:

无法连接仓库:Command "git ls-remote -h http://ruoweiedu.com:7000/elevator/elevator-backend.git HEAD" returned status code 128:
stdout:
stderr: fatal: Authentication failed for 'http://ruoweiedu.com:7000/elevator/elevator-backend.git/'

指定分支为*/master.

3.2 构建触发器:

指的是什么时候会触发自动化构建以及其他操作,jenkins配置了多种构建方式.

使用gitlab hook(钩子)构建的配置如下:

image-20231015105739894

然后需要把gitlab hook的url添加到gitlab仓库中去.

登录gitlab仓库,找到设置-集成-增加web钩子 输入url之后即可添加.

image-20231015105949972

3.3 构建步骤:

这一步实现真正的构建.

如果是自由风格的项目,那么构建的时候不会发生任何事,源代码还是源代码,因为并没有使用任何的构建工具.

如果是maven风格的项目,那么构建时会把项目源代码构建成一个个的jar包.

一般推荐使用maven进行构建,但是当需要构建的服务非常多时,对服务器的性能要求较高,如果服务器性能不够的话也可以把这个步骤放到生产服务器上.

注意,使用这种方法会有一个bug:

如果使用maven风格构建项目的话,每次构建会在jenkins服务器上构建然后把jar包发送到生产服务器,如果出现代码库中有错误文件,这种方式是没有问题的(工作空间会每次拉取时更新),但是如果构建也发生在生产服务器上的话,就会出现错误文件无法删除的情况,

3.4 构建后操作:

构建完成之后的操作,一般为通过ssh发送到对应的生产服务器上,并执行自动化脚本来进行部署.

在构建后操作选择通过ssh发送工件.并进行配置:

image-20231015110819058

image-20231015123357625

这个名字是配置了一个ssh服务.

找到系统管理-system,找到publish over ssh,进行配置.

首先找一台服务器(随便一台).运行:

ssh -keygen -t rsa

生成一对公钥和私钥,然后把公钥添加到目标服务器的authorrized-keys里面,然后把私钥添加到下面的key中.

image-20231015112152502

image-20231015112202131

在上面的配置中,需要注意的有两点,一是username,是添加公钥的那个用户.不能是其他用户.

remote directory是指需要发布的根仓库.

image-20231015112619081

在下方的port配置中的端口是生产服务器中ssh服务运行的端口.

image-20231015123444647

source files代表需要传输的文件 **/*代表传输所有文件
remote directory 代表根目录之后的最终目录

3.4.1 exec command:

最后就是配置exec command 即ssh发送文件到生产服务器之后进行的操作.

这是能成功的exec command.

image-20231015113053953

这个地方是个超级大坑.

一开始的设想的脚本是这样的

sh /home/elevator/deploy/qwe.sh restart all

这个脚本文件是生产服务器上,用来打包并运行所有微服务文件的脚本. 打开这个选项,可以查看控制台的输出.

image-20231015122748001

但是如果只使用这个命令的话,会报错:

SSH: EXEC: connected
fatal: detected dubious ownership in repository at '/home/elevator/elevator'
To add an exception for this directory, call:
​
    git config --global --add safe.directory /home/elevator/elevator
SSH: EXEC: completed after 200 ms
SSH: Disconnecting configuration [elevator] ...
ERROR: Exception when publishing, exception message [Exec exit status not zero. Status [128]]
Build step 'Send build artifacts over SSH' changed build result to UNSTABLE
Finished: UNSTABLE

一开始的多次尝试不管是修改shell脚本还是修改项目配置都会出现这个问题,看起来似乎是脚本文件执行的时候输出了意料之外的结果.但是又不知道究竟是哪里的问题.

那么遇到这种情况,该如何进行调试呢?

可以模拟jenkin的工作流程,使用一台服务器去用ssh访问生产服务器,然后自己去执行在jenkin里运行的每一个命令,最终定位到问题.

原来是生产服务器的maven部署在elevator用户上,而ssh登录的是root用户,这就导致root用户找不到maven.(mvn --version提示找不到领命).

这样的话需要在每次进行部署的时候在脚本文件上添加环境变量并使之生效,就可以正常运行了.

脚本如下:

#!/bin/sh 
# 电梯维保微服务服务启动脚本
# Author:Azusa&bearsattack
set -e
export AUTH=./elevator/elevator-auth.jar
export EXAMPLE=./elevator/elevator-example.jar
export CUSTOMER=./elevator/elevator-example-customer.jar
export GATEWAY=./elevator/elevator-gateway.jar
export ORDER=./elevator/elevator-order.jar 
export SIGN=./elevator/elevator-sign.jar
export SYSTEM=./elevator/elevator-system.jar
export UNIT=./elevator/elevator-unit.jar
​
​
export AUTH_LIB=./elevator/elevator-auth-lib
export EXAMPLE_LIB=./elevator/elevator-example-lib
export CUSTOMER_LIB=./elevator/elevator-example-customer-lib
export GATEWAY_LIB=./elevator/elevator-gateway-lib
export ORDER_LIB=./elevator/elevator-order-lib 
export SIGN_LIB=./elevator/elevator-sign-lib
export SYSTEM_LIB=./elevator/elevator-system-lib
export UNIT_LIB=./elevator/elevator-unit-lib
​
​
​
export AUTH_port=5246
export EXAMPLE_port=5244
export CUSTOMER_port=5245
export GATEWAY_port=5251
export ORDER_port=5247
export SIGN_port=5248
export SYSTEM_port=5249
export UNIT_port=5250
​
​
# ---------------------------拉取最新代码函数---------------------------
PULL()
{
    ## 拉取最新代码
    cd /home/elevator/elevator/
    
    mvn clean package -Dmaven.test.skip=true -q
    ## 打包
    cp -f /home/elevator/elevator/elevator-auth/auth-api/target/elevator-auth.jar /home/elevator/deploy/elevator/
    cp -f /home/elevator/elevator/elevator-example/example-api/target/elevator-example.jar /home/elevator/deploy/elevator/
    cp -f /home/elevator/elevator/elevator-example-customer/customer-api/target/elevator-example-customer.jar /home/elevator/deploy/elevator/
    cp -f /home/elevator/elevator/elevator-gateway/target/elevator-gateway.jar /home/elevator/deploy/elevator/
    cp -f /home/elevator/elevator/elevator-order/order-api/target/elevator-order.jar /home/elevator/deploy/elevator/
    cp -f /home/elevator/elevator/elevator-system/system-api/target/elevator-system.jar /home/elevator/deploy/elevator/
    cp -f /home/elevator/elevator/elevator-unit/unit-api/target/elevator-unit.jar /home/elevator/deploy/elevator/
    cp -f /home/elevator/elevator/elevator-sign/sign-api/target/elevator-sign.jar /home/elevator/deploy/elevator/
​
    # 复制各个模块的lib
    rm -rf /home/elevator/deploy/elevator/elevator-auth-lib
    rm -rf /home/elevator/deploy/elevator/elevator-example-lib
    rm -rf /home/elevator/deploy/elevator/elevator-example-customer-lib
    rm -rf /home/elevator/deploy/elevator/elevator-gateway-lib
    rm -rf /home/elevator/deploy/elevator/elevator-order-lib
    rm -rf /home/elevator/deploy/elevator/elevator-system-lib
    rm -rf /home/elevator/deploy/elevator/elevator-unit-lib
    rm -rf /home/elevator/deploy/elevator/elevator-sign-lib
    cp -r /home/elevator/elevator/elevator-auth/auth-api/target/elevator-auth-lib/ /home/elevator/deploy/elevator/
    cp -r /home/elevator/elevator/elevator-example/example-api/target/elevator-example-lib/ /home/elevator/deploy/elevator/
    cp -r /home/elevator/elevator/elevator-example-customer/customer-api/target/elevator-example-customer-lib/ /home/elevator/deploy/elevator/
    cp -r /home/elevator/elevator/elevator-gateway/target/elevator-gateway-lib/ /home/elevator/deploy/elevator/
    cp -r /home/elevator/elevator/elevator-order/order-api/target/elevator-order-lib/ /home/elevator/deploy/elevator/
    cp -r /home/elevator/elevator/elevator-system/system-api/target/elevator-system-lib/ /home/elevator/deploy/elevator/
    cp -r /home/elevator/elevator/elevator-unit/unit-api/target/elevator-unit-lib/ /home/elevator/deploy/elevator/
    cp -r /home/elevator/elevator/elevator-sign/sign-api/target/elevator-sign-lib/ /home/elevator/deploy/elevator/
    cd /home/elevator/deploy/
}
​
​
# ---------------------------启动函数---------------------------
AUTH()
{
    echo "-----------开始启动 AUTH-----------"
    export BUILD_ID=DongKillMe1
    nohup java -jar -Xmx1024M -Xms256M -Dspring.profiles.active=prod -Dloader.path=$AUTH_LIB $AUTH >/home/elevator/null 2>&1 &
    echo "-----------AUTH 启动成功-----------"
}
 
EXAMPLE()
{
    echo "-------------开始启动 EXAMPLE-------------"
    export BUILD_ID=DongKillMe2
    nohup java -jar -Xmx1024M -Xms256M -Dspring.profiles.active=prod -Dloader.path=$EXAMPLE_LIB $EXAMPLE >/home/elevator/null 2>&1 &
    echo "-------------EXAMPLE 启动成功-------------"
}
 
CUSTOMER()
{
    echo "--------------开始启动 CUSTOMER-------------"
    export BUILD_ID=DongKillMe3
    nohup java -jar -Xmx1024M -Xms256M -Dspring.profiles.active=prod -Dloader.path=$CUSTOMER_LIB $CUSTOMER >/home/elevator/null 2>&1 &
    echo "--------------CUSTOMER 启动成功-------------"
}
 
GATEWAY()
{
    echo "--------------开始启动 GATEWAY--------------"
    export BUILD_ID=DongKillMe4
    nohup java -jar -Xmx1024M -Xms256M -Dspring.profiles.active=prod -Dloader.path=$GATEWAY_LIB $GATEWAY >/home/elevator/null 2>&1 &
    echo "--------------GATEWAY 启动成功--------------"
}
 
ORDER()
{
    echo "-------------开始启动 ORDER------------"
    export BUILD_ID=DongKillMe5
    nohup java -jar -Xmx1024M -Xms256M -Dspring.profiles.active=prod -Dloader.path=$ORDER_LIB $ORDER >/home/elevator/null 2>&1 &
    echo "-------------ORDER 启动成功------------"
} 
​
SYSTEM()
{
    echo "--------------开始启动 SYSTEM--------------"
    export BUILD_ID=DongKillMe6
    nohup java -jar -Xmx1024M -Xms256M -Dspring.profiles.active=prod -Dloader.path=$SYSTEM_LIB $SYSTEM >/home/elevator/null 2>&1 &
    echo "--------------SYSTEM 启动成功--------------"
}
​
SIGN()
{
    echo "--------------开始启动 SIGN--------------"
    export BUILD_ID=DongKillMe7
    nohup java -jar -Xmx1024M -Xms256M -Dspring.profiles.active=prod -Dloader.path=$SIGN_LIB $SIGN >/home/elevator/null 2>&1 &
    echo "--------------SIGN 启动成功--------------"
}
 
UNIT()
{
    echo "--------------开始启动 UNIT--------------"
    export BUILD_ID=dontKillMe8
    nohup java -jar -Xmx1024M -Xms256M -Dspring.profiles.active=prod -Dloader.path=$UNIT_LIB $UNIT >/home/elevator/null 2>&1 &
    echo "--------------UNIT 启动成功--------------"
}
​
​
# ---------------------------停止函数---------------------------------------
KAUTH()
{
    P_ID=`ps -ef | grep -w $AUTH | grep -v "grep" | awk '{print $2}'`
    if [ "$P_ID" == "" ]; then
        echo "AUTH process not exists or stop success"
    else
        kill -9 $P_ID
        echo "AUTH killed success"
    fi
}
 
KEXAMPLE()
{
    P_ID=`ps -ef | grep -w $EXAMPLE | grep -v "grep" | awk '{print $2}'`
    if [ "$P_ID" == "" ]; then
        echo "EXAMPLE process not exists or stop success"
    else
        kill -9 $P_ID
        echo "EXAMPLE killed success"
    fi
}
 
KCUSTOMER()
{
    P_ID=`ps -ef | grep -w $CUSTOMER | grep -v "grep" | awk '{print $2}'`
    if [ "$P_ID" == "" ]; then
        echo "CUSTOMER process not exists or stop success"
    else
        kill -9 $P_ID
        echo "CUSTOMER killed success"
    fi
}
 
KGATEWAY()
{
    P_ID=`ps -ef | grep -w $GATEWAY | grep -v "grep" | awk '{print $2}'`
    if [ "$P_ID" == "" ]; then
        echo "GATEWAY process not exists or stop success"
    else
        kill -9 $P_ID
        echo "GATEWAY killed success"
    fi
}
 
KORDER()
{
    P_ID=`ps -ef | grep -w $ORDER | grep -v "grep" | awk '{print $2}'`
    if [ "$P_ID" == "" ]; then
        echo "ORDER process not exists or stop success"
    else
        kill -9 $P_ID
        echo "ORDER killed success"
    fi
}
 
KSYSTEM()
{
    P_ID=`ps -ef | grep -w $SYSTEM | grep -v "grep" | awk '{print $2}'`
    if [ "$P_ID" == "" ]; then
        echo "SYSTEM process not exists or stop success"
    else
        kill -9 $P_ID
        echo "SYSTEM killed success"
    fi
}
 
KSIGN()
{
    P_ID=`ps -ef | grep -w $SIGN | grep -v "grep" | awk '{print $2}'`
    if [ "$P_ID" == "" ]; then
        echo "SIGN process not exists or stop success"
    else
        kill -9 $P_ID
        echo "SIGN killed success"
    fi
}
​
KUNIT()
{
    P_ID=`ps -ef | grep -w $UNIT | grep -v "grep" | awk '{print $2}'`
    if [ "$P_ID" == "" ]; then
        echo "UNIT process not exists or stop success"
    else
        kill -9 $P_ID
        echo "UNIT killed success"
    fi
}
​
​
​
case "$1" in 
start)
    case "$2" in
        auth|AUTH)
            ## 1、启动AUTH
            AUTH
        ;;
        example|EXAMPLE)
            ## 2、启动EXAMPLE
            EXAMPLE
        ;;
        customer|CUSTOMER)
            ## 3、启动CUSTOMER
            CUSTOMER
        ;;
        gateway|GATEWAY)
            ## 4、启动GATEWAY
            GATEWAY
        ;;
        order|ORDER)
            ## 5、启动ORDER
            ORDER
        ;;
    sign|SIGN)
            ## 6、启动SIGN
            SIGN
        ;;
    unit|UNIT)
            ## 7、启动UNIT
            UNIT
        ;;
        system|SYSTEM)
            ## 8、启动SYSTEM
            SYSTEM
        ;;
        all|ALL)
        # 拉取最新代码
            PULL
            ## 1、启动AUTH
            AUTH
            ## 2、启动EXAMPLE
            EXAMPLE
            ## 3、启动CUSTOMER
            CUSTOMER
            ## 4、启动GATEWAY
            GATEWAY
            ## 5、启动ORDER
            ORDER
        ## 6、启动SIGN
            SIGN
        ## 7、启动UNIT
            UNIT
            ## 8、启动SYSTEM
            SYSTEM
    export BUILD_ID=DongKillMe9
            echo "===start all services success==="
      
        ;;
    esac
    ;;  
 stop)
 case "$2" in 
        auth|AUTH)
            ## 1、停止KAUTH
            AUTH
        ;;
        example|EXAMPLE)
            ## 2、停止EXAMPLE
            KEXAMPLE
        ;;
        customer|CUSTOMER)
            ## 3、停止CUSTOMER
            KCUSTOMER
        ;;
        gateway|GATEWAY)
            ## 4、停止GATEWAY
            KGATEWAY
        ;;
        order|ORDER)
            ## 5、停止ORDER
            KORDER
        ;;
    sign|SIGN)
            ## 6、停止SIGN
            KSIGN
        ;;
    unit|UNIT)
            ## 7、停止UNIT
            KUNIT
        ;;
        system|SYSTEM)
            ## 8、停止SYSTEM
            KSYSTEM
        ;;
        all|ALL)
            ## 1、停止AUTH
            KAUTH
            ## 2、停止EXAMPLE
            KEXAMPLE
            ## 3、停止CUSTOMER
            KCUSTOMER
            ## 4、停止GATEWAY
            KGATEWAY
            ## 5、停止ORDER
            KORDER
        ## 6、停止SIGN
            KSIGN
        ## 7、停止UNIT
            KUNIT
            ## 8、停止SYSTEM
            KSYSTEM
            echo "===stop all services success==="
        ;;
    esac
    ;; 
restart)
case "$2" in 
        auth|auth)
            ## 1、重启AUTH
            sh $0 stop auth
            sleep 2
            sh $0 start auth
        ;;
        example|EXAMPLE)
            ## 2、重启EXAMPLE
            sh $0 stop EXAMPLE
            sleep 2
            sh $0 start EXAMPLE 
        ;;
        customer|CUSTOMER)
            ## 3、重启CUSTOMER
            sh $0 stop CUSTOMER
            sleep 2
            sh $0 start CUSTOMER
        ;;
        gateway|GATEWAY)
            ## 4、重启GATEWAY
            sh $0 stop gateway
            sleep 2
            sh $0 start gateway 
        ;;
        order|ORDER)
            ## 5、重启ORDER
            sh $0 stop ORDER
            sleep 2
            sh $0 start ORDER 
        ;;
    sign|SIGN)
            ## 6、重启SIGN
            sh $0 stop SIGN
            sleep 2
            sh $0 start SIGN
        ;;
    unit|UNIT)
            ## 7、重启UNIT
            sh $0 stop UNIT
            sleep 2
            sh $0 start UNIT
        ;;
        system|SYSTEM)
            ## 8、重启SYSTEM
            sh $0 stop system
            sleep 2
            sh $0 start system 
        ;;
        all|ALL)
            sh $0 stop all
            sleep 10
            sh $0 start all
            echo "===restart all serivces success==="
        ;;
    esac
    ;;   
esac    
exit 0
sleep 1
​

部署成功的部分控制台输出如下:

Started by GitLab push by 陈俊曦
Running as SYSTEM
Building in workspace /var/lib/jenkins/workspace/电梯后端自动化部署
The recommended git tool is: NONE
using credential 4f2e9980-6e2a-4177-a4b2-3df552daad98
 > git rev-parse --resolve-git-dir /var/lib/jenkins/workspace/电梯后端自动化部署/.git # timeout=10
Fetching changes from the remote Git repository
 > git config remote.origin.url http://ruoweiedu.com:7000/elevator/elevator-backend.git # timeout=10
Fetching upstream changes from http://ruoweiedu.com:7000/elevator/elevator-backend.git
 > git --version # timeout=10
 > git --version # 'git version 1.8.3.1'
using GIT_ASKPASS to set credentials 
 > git fetch --tags --progress http://ruoweiedu.com:7000/elevator/elevator-backend.git +refs/heads/*:refs/remotes/origin/* # timeout=10
skipping resolution of commit remotes/origin/master, since it originates from another repository
 > git rev-parse refs/remotes/origin/master^{commit} # timeout=10
Checking out Revision 2b04fc5935fb5008a9b3866effc4d427c68113d3 (refs/remotes/origin/master)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 2b04fc5935fb5008a9b3866effc4d427c68113d3 # timeout=10
Commit message: "合并分支 'cjx_dev' 到 'master'"
 > git rev-list --no-walk 8fbbd358c5a4067fcdfdbb6fbd022445b46526d7 # timeout=10
SSH: Connecting from host [iZhp3dzy03fommg2rwh8unZ]
SSH: Connecting with configuration [elevator] ...
SSH: Creating session: username [root], hostname [ruoweiedu.com], port [7,122]
SSH: Connecting session ...
SSH: Connected
SSH: Opening SFTP channel ...
SSH: SFTP channel open
SSH: Connecting SFTP channel ...
SSH: Connected
SSH: cd [/home]
SSH: OK
SSH: cd [/home]
SSH: OK
SSH: cd [elevator/elevator]
SSH: OK
SSH: put [README.md]
SSH: OK
SSH: cd [/home]
SSH: OK
SSH: cd [elevator/elevator]
//省略部分ssh传输文件过程
SSH: put [Province.java]
SSH: OK
SSH: cd [/home]
SSH: OK
SSH: cd [elevator/elevator]
SSH: OK
SSH: put [pom.xml]
SSH: OK
SSH: Opening exec channel ...
SSH: EXEC: channel open
SSH: EXEC: STDOUT/STDERR from command [export JAVA_HOME=/home/elevator/java/jdk1.8.0_201
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
export MAVEN_HOME=/home/elevator/maven/apache-maven-3.6.1
source /etc/profile
sh /home/elevator/deploy/qwe.sh restart all
​
] ...
SSH: EXEC: connected
AUTH process not exists or stop success
EXAMPLE process not exists or stop success
CUSTOMER process not exists or stop success
GATEWAY process not exists or stop success
ORDER process not exists or stop success
SIGN process not exists or stop success
UNIT process not exists or stop success
SYSTEM process not exists or stop success
===stop all services success===
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/elevator/maven/apache-maven-3.6.1/lib/logback-classic-1.1.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/elevator/maven/apache-maven-3.6.1/lib/maven-slf4j-provider-3.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
-----------开始启动 AUTH-----------
-----------AUTH 启动成功-----------
-------------开始启动 EXAMPLE-------------
-------------EXAMPLE 启动成功-------------
--------------开始启动 CUSTOMER-------------
--------------CUSTOMER 启动成功-------------
--------------开始启动 GATEWAY--------------
--------------GATEWAY 启动成功--------------
-------------开始启动 ORDER------------
-------------ORDER 启动成功------------
--------------开始启动 SIGN--------------
--------------SIGN 启动成功--------------
--------------开始启动 UNIT--------------
--------------UNIT 启动成功--------------
--------------开始启动 SYSTEM--------------
--------------SYSTEM 启动成功--------------
===start all services success===
===restart all serivces success===
SSH: EXEC: completed after 47,686 ms
SSH: Disconnecting configuration [elevator] ...
SSH: Transferred 586 file(s)
Finished: SUCCESS

4.后续拓展:

4.1 Devops:

img

DevOps(Development和Operations的组合词)是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。

4.2 CI/CD:

持续集成(Continuous Integration)
​
什么是持续集成?
定义:持续频繁的(每天多次)将本地代码“集成”到主干分支,并保证主干分支可用
​
持续集成这个词,起源于极限编程,是当时原始的12种实践之一,持续集成的目的快速反馈问题让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过编译、代码扫描、安全扫描、自动化测试等。只要编译失败、扫描出高级别问题或测试用例失败,就不能集成。
Martin Fowler 说过,“持续集成并不能消除Bug,而是让它们非常容易发现和改正。”
​
好处?
(1)通过自动化手段提高集成速度
在传统的研发过程中,一般通过手动方式执行编译、测试、部署,自动化后,可大幅提高集成频次,同时可以减少维护手工脚本带来的低级问题
(2)将问题前置
在每次 commit 时就触发编译、测试,更快的发现问题
(3)防止本地代码大幅偏离主干
如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。
持续交付(Continuous Delivery)
什么是持续交付?
定义:是持续集成的下一步,持续频繁地将软件的新版本交付到类生产环境(类似于预发),交付给测试、产品验收。
持续交付强调的是“交付”,不管怎么更新,软件是随时随地可以交付的,相比持续集成,持续交付除了交付到类生产环境之外,还会执行一些集成测试、API测试等等,确保交付的产物可以直接交付部署。
​
触发方式?
手动触发,通过研发平台手动触发(如触发LinkE预发部署流水线),一般交付结果是一个二进制包或者镜像
​
面临的问题 & 阻碍?
(1)很难保证和线上环境完全一致
由于持续交付的环境很难和线上环境一致,所以可能导致一些问题无法验证,使得交付的产物具备一定风险
探索方案:仿真环境
​
(2)大量脑壳疼的联调环境问题
经常遇到的场景是虽然本系统开发好了,但是由于各种原因导致上下游系统没有 ready 导致无法持续交付,针对上下游依赖非常多的系统,对联调环境稳定性、效率有非常强的依赖
​
(3)很难保证严格充分的自动化测试
持续交付最终要做到交付的产物可直接部署线上,那么对代码的质量要求就非常高,需要覆盖全场景充分的测试 case
​
持续部署(Continuous Deployment)
什么是持续部署?
定义:是持续交付的下一步,“自动”将代码部署到生产环境
​
持续部署强调的是“部署”,它的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。
​
持续部署和持续交付触发方式的区别是,持续部署是自动完成的,持续交付是手动完成的
​
触发方式?
自动触发,通过研发平台配置定时任务,自动获取交付产物进行自动部署。
​
目前即使在蚂蚁,应该很少有团队和研发平台能够真正做到持续部署,因为持续的部署的条件更加严苛
​
面临的问题 & 阻碍?
(1)发布条件受限
部分场景上线需要严格的审批的流程 & 频繁的封网
(2)低效手工发布流程
发布过程需要人工一次次确认,灰度和线上发布过程多分组场景下,需要一次次人工确定,不放心自动发布
目前蚂蚁解决方案:无人值守
(3)缺乏线上问题快速准确的反馈机制( 精准 OPS 能力)
没有 OPS 或者 OPS 噪音太大都会导致反馈不及时,这样研发就不敢把部署过程直接交给系统,也是上面发布需要人工确认的根因
​
目前公司对线上安全要求非常高,持续部署现阶段很难在蚂蚁大规模落地,特别针对类似金融核心这些安全要求非常高的系统,不过其实也未必要“一刀切”,公司还有很多级别不是特别高的应用,可以尝试小范围针对这些应用试点,同时不断持续改进周边 OPS 工具
​

已经搭建完整的自动化构建,自动化部署流程,如果要搭建完整的devops流程,还需要自动化测试,自动化交付流水线.

另外,还可以尝试后续的分模块部署微服务,而不是只单独部署所有的微服务.