背景
- 测试人员不太懂技术,测试时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 webrootWEB_ROOT=$HOME_ROOT"/code/"#tomcat 目录TOMCAT_HOME=$HOME_ROOT"/tomcat/"MAVEN_HOME="/data/apache-maven-3.3.9"update_code(){if [ -d $PROJECT_DIR ]thencd $PROJECT_DIRgit checkout $GITBRANCHgit pullelsecd $SOURCE_DIRgit clone $PROJECT_GIT_URLcd $PROJECT_DIRgit checkout $GITBRANCHfi}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 $TEMPTEMP=$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_PIDsleep 5bash $TOMCAT_HOME"bin/catalina.sh" start}stop(){TOMCAT_PID=`jps -v|grep $DUBBO_PORT|awk '{print $1}' `kill -9 $TOMCAT_PIDsleep 5}help(){echo $"Usage: $0 {update_code|install|backup|deploy|restart}"}case "$1" instop)stop;;update_code)update_code;;install)install;;deploy)deploy;;restart)restart;;-h)help;;--help)help;;*)update_codeinstalldeployrestart;;esacexit 0项目初始化脚本#!/bin/bashecho "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" ]; thenmkdir /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/$5sed -ig "s/@dubboPort@/$3/" /data/project/$1/tomcat/bin/catalina.shsed -ig "s/@tomcatPort@/$2/" /data/project/$1/tomcat/conf/server.xmlsed -ig "s/@ajpPort@/$(($2-1))/" /data/project/$1/tomcat/conf/server.xmlsed -ig "s/@shutdownPort@/$(($2-2))/" /data/project/$1/tomcat/conf/server.xmlfi
基于上述脚本可以完成后端项目的创建更新及重新编译启动
前后端组合版本
基于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 seclocal ok, err = red:connect("127.0.0.1", 6379)if not ok thenngx.say("failed to connect: ", err)returnendlocal 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 versionendserver模块处配置 将请求转发到各个版本对应的前后端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一起讨论。
