为了解决调试不便的问题,先是同步工具由 nc 转到 rsync,再修改 rsync 源码添加回调参数,最后添加 docker 重启完成的通知信息,成功完成一键部署系统。

* { color: rgba(62, 62, 62, 1) }
body { font-family: “Helvetica Neue”, Helvetica, “Hiragino Sans GB”, “Microsoft YaHei”, Arial, sans-serif; font-size: 15px }
p { line-height: 25.6px; box-sizing: border-box; word-wrap: break-word; text-align: justify; margin: 23.7px 0 }
blockquote { border-left: 2px solid rgba(128, 128, 128, 0.07); background-color: rgba(128, 128, 128, 0.05); padding: 10px -1px; margin: 0 }
blockquote p { color: rgba(137, 137, 137, 1); margin: 0 }
strong { font-weight: 700; color: rgba(62, 62, 62, 1) }
pre { background-color: rgba(248, 248, 248, 1); border-radius: 3px; word-wrap: break-word; overflow: scroll; padding: 12px 13px; font-size: 13px; color: rgba(137, 137, 137, 1) }
h1, h2, h3, h4, h5, h6 { word-break: break-all; text-align: left; font-weight: bold }
hr { border: 1px solid rgba(221, 221, 221, 1) }
h1 { font-size: 170%; ng-top: .5em; margin-topborder-top: 4px solid #aaa; paddi: 1.5em }
h1:first-child { margin-top: 0; padding-top: 0.25em; border-top: none }
table { padding: 0; border-collapse: collapse }
table tr { border-top: 1px solid rgba(204, 204, 204, 1); background-color: rgba(255, 255, 255, 1); margin: 0; padding: 0 }
table tr:nth-child(2n) { background-color: rgba(248, 248, 248, 1) }
table tr th { font-weight: bold; border: 1px solid rgba(204, 204, 204, 1); margin: 0; padding: 6px 13px }
table tr td { border: 1px solid rgba(204, 204, 204, 1); margin: 0; padding: 6px 13px }
table tr th :first-child, table tr td :first-child { margin-top: 0 }
table tr th :last-child, table tr td :last-child { margin-bottom: 0 }
a { color: rgba(65, 131, 196, 1); text-decoration: none }
ul, ol { padding-left: 30px }
li { line-height: 24px }
hr { height: 2px; padding: 0; margin: 16px 0; background-color: rgba(231, 231, 231, 1); border-top: 0 none; border-right: 0 none; border-bottom: 1px solid rgba(221, 221, 221, 1); border-left: 0 none; overflow: hidden; box-sizing: content-box }
pre { background: rgba(242, 242, 242, 1); padding: 12px 13px }
code { padding: 2px 4px; color: rgba(199, 37, 78, 1); background: rgba(249, 242, 244, 1); border-radius: 4px }
code[class*=”language-“], pre[class*=”language-“] { color: rgba(0, 0, 0, 1); background: none; font-family: Consolas, Monaco, “Andale Mono”, “Ubuntu Mono”, monospace; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; word-wrap: normal; line-height: 1.5; -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; -webkit-hyphens: none; -moz-hyphens: none; -ms-hyphens: none; hyphens: none }
pre[class*=”language-“] { position: relative; margin: 0.5em 0; -webkit-box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf; -moz-box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf; box-shadow: -1px 0 rgba(53, 140, 203, 1), 0 0 1px rgba(223, 223, 223, 1); border-left: 10px solid rgba(53, 140, 203, 1); background-color: rgba(253, 253, 253, 1); background-image: linear-gradient(rgba(0, 0, 0, 0) 50%, rgba(69, 142, 209, 0.04) 50%); background-size: 3em 3em; background-origin: content-box; overflow: visible; padding: 0 }
code[class*=”language”] { max-height: inherit; height: 100%; padding: 0 1em; display: block; overflow: auto }
:not(pre)>code[class*=”language-“], pre[class*=”language-“] { background-color: rgba(253, 253, 253, 1); -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; margin-bottom: 1em }
:not(pre)>code[class*=”language-“] { position: relative; padding: 0.2em; -webkit-border-radius: 0.3em; -moz-border-radius: 0.3em; -ms-border-radius: 0.3em; -o-border-radius: 0.3em; border-radius: 0.3em; color: rgba(201, 44, 44, 1); border: 1px solid rgba(0, 0, 0, 0.1); display: inline; white-space: normal }
pre[class*=”language-“]:before, pre[class*=”language-“]:after { content: “”; z-index: -2; display: block; position: absolute; bottom: 0.75em; left: 0.18em; width: 40%; height: 20%; max-height: 13em; -webkit-box-shadow: 0px 13px 8px #979797; -moz-box-shadow: 0px 13px 8px #979797; box-shadow: 0 13px 8px rgba(151, 151, 151, 1); -webkit-transform: rotate(-2deg); -moz-transform: rotate(-2deg); -ms-transform: rotate(-2deg); -o-transform: rotate(-2deg); transform: rotate(-2deg) }
:not(pre)>code[class*=”language-“]:after, pre[class*=”language-“]:after { right: 0.75em; left: auto; -webkit-transform: rotate(2deg); -moz-transform: rotate(2deg); -ms-transform: rotate(2deg); -o-transform: rotate(2deg); transform: rotate(2deg) }
.token.comment, .token.block-comment, .token.prolog, .token.doctype, .token.cdata { color: rgba(125, 139, 153, 1) }
.token.punctuation { color: rgba(95, 99, 100, 1) }
.token.property, .token.tag, .token.boolean, .token.number, .token.function-name, .token.constant, .token.symbol, .token.deleted { color: rgba(201, 44, 44, 1) }
.token.selector, .token.attr-name, .token.string, .token.char, .token.function, .token.builtin, .token.inserted { color: rgba(47, 156, 10, 1) }
.token.operator, .token.entity, .token.url, .token.variable { color: rgba(166, 127, 89, 1); background: rgba(255, 255, 255, 0.5) }
.token.atrule, .token.attr-value, .token.keyword, .token.class-name { color: rgba(25, 144, 184, 1) }
.token.regex, .token.important { color: rgba(238, 153, 0, 1) }
.language-css .token.string, .style .token.string { color: rgba(166, 127, 89, 1); background: rgba(255, 255, 255, 0.5) }
.token.important { font-weight: normal }
.token.bold { font-weight: bold }
.token.italic { font-style: italic }
.token.entity { cursor: help }
.namespace { opacity: 0.7 }
@media screen and (max-width: 767px) { pre[class*=”language-“]:before, pre[class*=”language-“]:after { bottom: 14px; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none } }
.token.tab:not(:empty):before, .token.cr:before, .token.lf:before { color: rgba(224, 215, 209, 1) }
pre[class*=”language-“].line-numbers { padding-left: 0 }
pre[class*=”language-“].line-numbers code { padding-left: 3.8em }
pre[class*=”language-“].line-numbers .line-numbers-rows { left: 0 }
pre[class*=”language-“][data-line] { padding-top: 0; padding-bottom: 0; padding-left: 0 }
pre[data-line] code { position: relative; padding-left: 4em }
pre .line-highlight { margin-top: 0 }
pre.line-numbers { position: relative; padding-left: 3.8em; counter-reset: linenumber 0 }
pre.line-numbers>code { position: relative }
.line-numbers .line-numbers-rows { position: absolute; pointer-events: none; top: 0; font-size: 100%; left: -3.8em; width: 3em; letter-spacing: -1px; border-right: 1px solid rgba(153, 153, 153, 1); -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none }
.line-numbers-rows>span { pointer-events: none; display: block; counter-increment: linenumber 1 }
.line-numbers-rows>span:before { content: counter(linenumber); color: rgba(153, 153, 153, 1); display: block; padding-right: 0.8em; text-align: right }

前言


之前的文章说过 由 PHP 转到 Java 之后,非常不适应的一点就是代码部署过程耗时长,调试不便,虽然可以使用 debug,但有时候还是需要修改代码,重新部署测试机系统,整个流程需要:

  • 使用 mvn 命令将项目打成 war 包,耗时 1 min;
  • 从开发机向测试机上传 war 包,公司内使用无线局域网,上传速度峰值只有 1M 不到,而且很不稳定,面对 100M+ 的 war 包,有点力不从心,此步骤耗时 2.5 min;
  • 服务端重启 docker 进程,耗时 1 min;

再加上需要两台机器切换操作,步骤之间不连贯,需要在边上看着进度,以及时操作下一步。可以说,等到想要的代码上传到测试机运行,花儿都谢了。

作为一个懒人,迫切地需要简化一下流程,虽然可能达不到像 PHP 一样秒传文件立即生效,也要尽量快且方便地部署测试包,别操这么多心。本文就介绍我是怎么一步步优化测试部署流程的。

文章欢迎转载,请尊重作者劳动成果,带上原文链接:http://www.cnblogs.com/zhenbianshu/p/8733103.html

nc 时代


刚入职时,对 Java 的部署相关一脸懵逼,有同事给了一个脚本和两条命令,是为最原始的“自动部署系统”:

  1. 先在测试机上执行脚本,脚本会启用一个 nc 接收进程,监听某一个端口,命令为 nc -4l xxPort > ROOT.war
  2. 自己在开发机上执行一条 mvn 命令,将项目打包,命令为 mvn clean package project
  3. 再在开发机上执行 nc 上传命令,连接测试机 IP 和端口,以打好的 war 包为输入流 nc testIp xxPort < test-1.0.0.war
  4. 传输完 war 包后,脚本会自动重启 docker 机,重启完成后就可以进行测试了。

nc 是 NetCat 的简称,这个小工具用于同步两台服务器间的文件,使用时,先在接收端监听一个端口并指定输出文件,再在发送端连接 IP 和端口,并指定输入流, nc 命令很简单,网络上资料也很多,这里不再多提了。

这个脚本虽然比全部手动好了一些,能帮我少输两个命令(nc 服务端、重启命令),可是时间上并没有缩短,可是乌龟似的上传速度真的不能忍,这时我开始想着怎么加速上传。

rsync “加速”上传


其实一开始我是想从硬件方面解决这个问题的,即使用网线。为此,买了一个网线转接头和一段网线,可是通过同事的设备测试发现转接头和网线都没问题,可是接到一块就不匹配(围笑)。

穷则思变,接着我考虑从软件方面解决这个问题。问了几个同事后,发现有的同事在用 rsync 同步文件,可是 rsync 同步文件的单位不是文件 吗?看了同事演示的上传后,感觉心态崩塌,不好好读文档的后果啊,走了好多弯路。

这里简要介绍一下 rsync 的使用:

服务端

服务端需要启动一个 rsync daemom 进程监听某一端口,默认配置文件在 /etc/rsyncd.conf,以 module 为单位进行用户认证、权限校验、目标文件夹等配置,一个常见的 rsyncd 配置如下:

# general conf
port=873 # 监听端口
max connections=500
log file=/var/log/rsyncd.log
pid file=/var/run/rsyncd.pid # pid 文件

# module 可多个
[zbs]
path = /data1/zbs # zbs模块的根目录
read only=no
use chroot=no
uid=root
gid=root
auth users=zbs // 要进行用户认证的用户名
secrets file=/etc/rsyncd.scrt # 用户名对应的密码存放文件,每行一个,都是以 "zbs:password" 的形式
ignore errors
exclude = .git/ # 排除掉 .git 文件夹

客户端

而在客户端,我们只需要使用 rsync [-option] fileOrDir rsync://{user}@{host}:{port}/{moduleName}/dir 就可以将本地文件同步到服务端了。

至于密码,可以使用 --password-file=/path/to/pwdFile 的形式,也可以在调用 rsync 命令之前设置环境变量:export RSYNC_PASSWORD=XXXX

至于 rsync 的同步算法, 推荐陈皓大神的文章:RSYNC 的核心算法

rsync 解决了上传速度的问题,但是又引入了新的问题:我必须等着上传结束,并且上传结束后还要登陆测试机手动重启 docker 服务,挺不方便的。

修改 rsync,添加回调选项


这时我开始打 rsync 源码的主意了,rsync 是一个开源软件,我考虑帮它加一个参数,让它帮我在文件上传结束后自动执行一些命令。

说做就做,从 rsync官网 下载到 rsync 的源码开始查看并动手修改。rsync 的源码代码量还是挺大的,不过修改它我们不需要通读,只修改读取参数并使用就行了。我将这个问题分为两个步骤:

  • 读取到 callback 参数的值;
  • 上传结束后调用 callback 参数的值;

首先在 proto.h 文件里添加函数声明: char *lp_callback(int module_id);

读取参数的相关代码在 load_param.c 文件内,首先添加变量声明、设置默认值,最后添加参数调用函数。

服务端文件同步的代码在 clientserver.c 文件内,主体是 rsync_module 函数,前面的一系列操作如用户认证、权限校验等我们可以不必管,找到最后一步,在其调用下一次同步函数前 添加如下代码(解释在注释中):

    char * callback = lp_callback(i); // 读取 callback 参数
    if (callback != NULL && strlen(callback) != 0) {
        char cmd[strlen(callback) + 2];
        strcpy(cmd, callback);
        strcat(cmd, " &"); // system 命令会阻塞,需要在命令上添加 & 让它后台执行
        system(cmd); // 使用 stdlib 的 system 执行 callback 命令
    }

修改后的源码见:Github-zhenbianshu-rsyncCallback

这样,我给自己上传用的 module 添加一个脚本作为 callback,在每次上传完后,都会执行这个 callback 脚本,脚本里我可以配置上服务的重启,自动部署就实现了。

docker-compose tomcat 自动部署


其实 tomcat 是可以自动部署的,需要配置 server.xml的 Host 元素,将 autoDeploy 属性置为 true,文档:Tomcat Web Application Deployment

可是我们的服务是基于 docker-compose 进行部署的,如果修改 server.xml 还需要将文件映射到 docker image 里。

其中 docker 可以这么配置:

FROM tomcat:7-jre8
COPY server.xml /usr/local/tomcat/conf/

docker-compose 可以在 yml 配置文件里添加如下配置:

image:
    tomcat-base
volumes:
   - ./path/server.xml:/usr/local/tomcat/conf/server.xml
   - ./path/webapps:/data1/project/webapps

这样,每当上传了新的 war 包,tomcat 就会自动监测到并重新部署服务;

此时,还有一个需求, war 包同步完成,重启完成后我不知道,得随时关注 tomcat 的服务日志,以尽快得知重启结果,及时测试,如果服务重启完就立即告诉我就最好了。

添加通知


此时,我修改的 rsync 就有了作用了,使用 callback 参数在测试机启动一个脚本以监测 tomcat 的服务日志,服务重启完成后会输出 Server startup in xxx ms,如果监测到有新的 log 输出,则发送一个通知告诉我。

callback 参数配置的脚本类似于:

#!/bin/bash

docker-compose stop -t 0
`rm /data1/project/webapps/ROOT -rf`
sleep 1
docker-compose start

sleep 5 # 这里等待一会,使大量 tomcat 日志覆盖掉上一次重启的结果日志
date=`date "+%Y-%m-%d"`
catalina_log="/data1/project/logs/catalina.$date.log"

while : # 重复检测日志最后一行,直到输出了重启成功的标识
do
    finish=`tail -n 1 $catalina_log | grep \'Server startup\'`
    if [ -n "$finish" ]
    then
        break
    fi
    sleep 0.1
done

`curl -u "user:password" -d "uid=5715965217&text=succ" "messages/new.json"` # 调用接口发送通知

其实在测试机启动一个守护进程用来实时监测日志也是可以的,但是需要处理日志的新旧逻辑。

至于通知,有很多工具可以使用,微博、QQ、微信、短信等通讯工具都提供有对外的 http 接口,这个可以依各人喜好选择使用。

小结


最后把开发机上的 mvn 打包命令和 rsync 同步命令也包装成脚本,如下所示:

#!/bin/bash
mvn clean  -DskipTests=true package -Pwar -am -pl project
file=`find /path/to/project/target -name "*.war"`
export RSYNC_PASSWORD=123456
rsync -avz $file rsync://zbs@IP:PORT/zbs/ROOT.war

再给脚本添加一个 alias 别名 alias test="myshell.sh",真正的一键部署就完成了,在部署测试环境时,只需要在项目目录下输入一条命令 test 就开始自动部署了,这时候可以放手去喝杯水或做些其他事,等收到消息通知后,回来继续测试即可。

部门正在搭配 git 系统做自动部署测试系统,非常期待 push 过代码后就可以迅速测试的场景。果然,懒才是第一生产力啊~

关于本文有什么问题可以在下面留言交流,如果您觉得本文对您有帮助,可以点击下面的 推荐 支持一下我,博客一直在更新,欢迎 关注

版权声明:本文为zhenbianshu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zhenbianshu/p/8733103.html