一种多版本测试解决方案及实现过程

背景

  • 测试人员不太懂技术,测试时nginx配置容易出错。
  • 开发人员在同个项目开发不同分支,每个人开发进度不一致,测试时每次只能测一个小功能点。
  • 每次测完之后的版本需要保留,出了问题能够定位到是哪个版本开始出现的。
  • 开发人员需要一个工具,能够自动化部署测试应用。

功能

  • 支持前端项目多分支部署
  • 支持后端项目多分支部署
  • 支持前后端项目自由组合版本
  • 基于IP的免配置版本切换

实现流程

首先由开发人员配置前后端项目信息,在完成发布后,配置对应的版本信息(包括nginx路由配置及版本号),版本配置完成后,由测试人员切换到响应版本进行测试。

搭建环境要求
linux+mysql+redis+openresty+tomcat+maven

前端部署

配置前端信息

图中可以看到一些必要的配置信息:项目名称 git地址 git分支
通过shell脚本从git上拉取前端代码并生成一个新的目录(项目名称)放置前端的代码(webpack打包压缩等在本地执行)

新建前端项目

mkdir ${项目名称} &&
cd ${项目名称} &&
git clone ${git地址} &&
cd `ls ${项目名称}` && git checkout ${git分支}
  • 更新前端代码
cd /data/front/${项目名称}/`ls /data/front/${项目名称}` && git pull

后端部署

定制tomcat模板

  • 下载tomcat解压后修改tomcat的server.xml配置
    在几个端口配置的位置预留用于替换占位符

    tomcat启动参数也可以在catalina.sh中按自己需求去定制下

  • 然后像配置前端项目一样,我们先填写一些必要的信息 tomcat端口信息以及一些其他的自定义信息。

  • 然后就开始部署一个后端项目
    建立项目目录 -> 拷贝tomcat模板到该目录下 -> git上拉代码到代码目录 -> 使用maven编译生成war包 -> 将war解压到当前项目下tomcat的webapp目录 -> 运行tomcat启动脚本

下面是本人写的一段丑陋的脚本

项目管理脚本
#!/bin/bash
#项目名称
PROJECT_NAME="@projectName@"
PROJECT_GIT_URL="@gitPath@"
GITBRANCH="@branch@"
PROPERTY="@profile@"
TOMCAT_PORT="@tomcatPort@"
DUBBO_PORT="@dubboPort@"
MODULE_NAME="@moduleName@"
FINAL_NAME="@finalName@"
#项目根目录
HOME_ROOT="/data/project/"$PROJECT_NAME
#项目源码
SOURCE_DIR=$HOME_ROOT"/source/"
#项目源码构建目录
PROJECT_DIR=$SOURCE_DIR`ls $SOURCE_DIR`
#tomcat webroot
WEB_ROOT=$HOME_ROOT"/code/"
#tomcat 目录
TOMCAT_HOME=$HOME_ROOT"/tomcat/"
MAVEN_HOME="/data/apache-maven-3.3.9"
update_code(){
        if [ -d $PROJECT_DIR ]
        then
            cd $PROJECT_DIR
           git checkout $GITBRANCH
           git pull
        else
            cd $SOURCE_DIR
            git clone $PROJECT_GIT_URL  
            cd $PROJECT_DIR
            git checkout $GITBRANCH     
        fi
}
install(){
        cd $PROJECT_DIR;
        $MAVEN_HOME/bin/mvn clean; 
        $MAVEN_HOME/bin/mvn -T 1C -Dmaven.test.skip=true  -Dmaven.compile.fork=true -P $PROPERTY install;
}
 
 
deploy(){
        TEMP=$WEB_ROOT"*"
        rm -fR $TEMP
        TEMP=$PROJECT_DIR"/"$MODULE_NAME"/target/"$FINAL_NAME"/*"
        cp -fR $TEMP $WEB_ROOT
}
 
restart(){
	TOMCAT_PID=`jps -v|grep $DUBBO_PORT|awk  '{print $1}' `
	kill -9 $TOMCAT_PID
    sleep 5
    bash $TOMCAT_HOME"bin/catalina.sh" start
}
stop(){
    TOMCAT_PID=`jps -v|grep $DUBBO_PORT|awk  '{print $1}' `
    kill -9 $TOMCAT_PID
    sleep 5
}
 
help(){
        echo $"Usage: $0 {update_code|install|backup|deploy|restart}"
}
 
case "$1" in
stop)
        stop
;;
update_code)
        update_code
;;
install)
        install
;;
deploy)
        deploy
;;
restart)
        restart
;;
-h)
        help
;;
--help)
        help
;;
*)
        update_code
        install
        deploy
        restart
;;
 
esac
 
exit 0
项目初始化脚本
#!/bin/bash
echo "project name $1"
echo "tomcat port  $2"
echo "dubbo port   $3"
echo "git path     $4" 
echo "git branch   $5" 
echo "git profile  $6" 
echo "moduleName   $7" 
echo "finalName    $8" 
#检查项目是否创建
if [ ! -d "/data/project/$1" ]; then
	mkdir /data/project/$1;
	mkdir /data/project/$1/code ;
	mkdir /data/project/$1/source ;
	cp -R /data/project/tomcat /data/project/$1/;
	cp  /data/project/publish.sh /data/project/$1/publish.sh;
	sed -ig "s/@projectName@/$1/" /data/project/$1/publish.sh;
	sed -ig "s/@tomcatPort@/$2/" /data/project/$1/publish.sh;
	sed -ig "s/@dubboPort@/$3/" /data/project/$1/publish.sh;
	sed -ig "s?@gitPath@?$4?" /data/project/$1/publish.sh;
	sed -ig "s/@branch@/$5/" /data/project/$1/publish.sh;
	sed -ig "s/@profile@/$6/" /data/project/$1/publish.sh;
	sed -ig "s/@moduleName@/$7/" /data/project/$1/publish.sh;
	sed -ig "s/@finalName@/$8/" /data/project/$1/publish.sh;
	cd /data/project/$1/source && git clone $4 && cd /data/project/$1/source/`ls /data/project/$1/source` && git checkout origin/$5
	sed -ig "s/@dubboPort@/$3/" /data/project/$1/tomcat/bin/catalina.sh
	sed -ig "s/@tomcatPort@/$2/" /data/project/$1/tomcat/conf/server.xml
    sed -ig "s/@ajpPort@/$(($2-1))/" /data/project/$1/tomcat/conf/server.xml
    sed -ig "s/@shutdownPort@/$(($2-2))/" /data/project/$1/tomcat/conf/server.xml
fi

基于上述脚本可以完成后端项目的创建更新及重新编译启动

前后端组合版本

基于nginx配置

在后台配置好nginx路由规则后 生成nginx配置文件 替换原有的配置后reload nginx即可

免配置版本切换

将host绑定到nginx所在的机器后执行版本切换操作

点击切换版本后将操作人的IP及切换的版本号写入redis中,openresty中配置如下脚本将请求路由到该版本对应的前后端项目

在openresty配置文件的http模块中配置    
init_by_lua_file /root/init.lua;
init.lua
#获取请求的IP对应的版本信息
function getVersion()
	local redis = require "resty.redis"
	local red = redis:new()
	red:set_timeout(1000) -- 1 sec
	local ok, err = red:connect("127.0.0.1", 6379)
	if not ok then
	    ngx.say("failed to connect: ", err)
	    return
	end
	local headers=ngx.req.get_headers()
	local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
	local version = red:get(ip)
	red:close()
	return version
end
server模块处配置 将请求转发到各个版本对应的前后端
location / {
    content_by_lua '
       local version = getVersion()
       ngx.exec("@" .. version) 
    ';
}
location @4.8.3{
            proxy_pass http://127.0.0.1:port;#通过这个port再转发到对应的前后端
            proxy_redirect              off;
            proxy_set_header            Host $host;
            proxy_set_header            X-Real-IP $remote_addr;
            proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
            client_max_body_size 10m;
            client_body_buffer_size 128k;
            proxy_connect_timeout 90;
            proxy_send_timeout 120;
            proxy_read_timeout 120;
            proxy_buffer_size 4k;
            proxy_buffers 4 32k;
            proxy_busy_buffers_size 64k;
            proxy_temp_file_write_size 64k;
            add_header Pragma "no-cache";
            add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
        }

这里主要就是一个 openresty+lua 的玩法

最后通过web界面将这些流程串起来就是一个兼发布及AB测试于一体的系统了

最后

本文主要提供一个实现思路,抛个转。基于此思路继续延展可以做的事还很多,比如通过一些运维技术如ansible可以实现远程部署,基于openresty接入自己的业务系统又可以完成灰度任务。如果讲的有什么不会的地方欢迎大家指正。有什么问题也可以加我QQ一起讨论。

# nginx 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×