jenkins+mqtt实现本地构建和远程自动发版

长期以来,一些本地项目发版不是特别友好,需要在IDE里面打war包,然后远程客户电脑传的客户远程机上,远程机和服务器往往不是同一台机器,还需要多一步传输。所以有了下面的软件结构

jenkins+自建/公共mqtt服务器+文叔叔命令行工具+客户机监听消息执行shell

适用于公司和客户都有公网环境的情况。

本文系统使用Rocky Linux 8,命令同样适用于Centos 7/8

一、jenkins安装

1.jenkin安装

推荐配置 4核/4G内存/50G硬盘

对于Rocky Linux 8有4中安装方式,rpm在线离线安装、docker安装、jar包安装、war包安装,这里使用最通用的war包安装

war包安装先要安装jdk tomcat,具体可以参见Centos 7 安装tomcat并设置开机自启_gsls200808的专栏-CSDN博客

war包从Jenkins download and deployment这个网址下载最新的LTS版本即可

war包下载后放到tomcat的webapp目录,这样出来的路径带/jenkins,如果不想带,解压后放到ROOT目录下也可以

我这里的访问网址是

http://10.24.220.102:8080/jenkins/

初次安装可能会提示

Please wait while Jenkins is getting ready to work

等几分钟即可,等几分钟后如果还没有出现初始化界面就需要改配置了。

初始界面需要填入初始密码,使用cat命令可以查看。

cat /root/.jenkins/secrets/initialAdminPassword
2c259dcd73784792a256cb5c0198f971

之后安装插件的界面点击安装推荐的插件接口

管理员界面设置一个管理员账号。

这样jenkins基本就安装完成了。

2.git maven jdk安装和配置

jdk之前安jenkins安过了,这里不赘述

git安装

yum install git

git安装验证

git --version

maven安装

wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz
cp apache-maven-3.5.4-bin.tar.gz /usr/local/
cd /usr/local/
tar -zxvf apache-maven-3.5.4-bin.tar.gz
rm apache-maven-3.5.4-bin.tar.gz
mv apache-maven-3.5.4 maven

maven环境变量配置

#编辑 /etc/profile 文件
vi /etc/profile
#末尾增加如下内容
# set maven environment
export M2_HOME=/usr/local/maven
export PATH=$M2_HOME/bin:$PATH

#使环境变量生效
source /etc/profile

maven安装验证

mvn -v

jenkins里的配置

Manage Jenkins-->Global Tool Configuration

点击jdk安装/新增jdk填入如下内容

jenkins+mqtt实现本地构建和远程自动发版_第1张图片

 点击maven安装/新增maven,填入如下内容

jenkins+mqtt实现本地构建和远程自动发版_第2张图片

点击git安装/新增git,填入如下内容

jenkins+mqtt实现本地构建和远程自动发版_第3张图片 我这里的git默认识别了就不用填git的了,最后点击下方的保存

需要注意的是,已经配置过的项目重新进入这个页面之后不会默认显示,需要点击安装jdk/maven/git按钮才会显示已经配置的信息

3.gitlab插件GitLab+Gitlab API Plugin安装和配置

安装插件

Manage Jenkins-->Manage Plugins

在可选插件钟搜索GitLab,找到GitLab和Gitlab API Plugin,勾选这两项,然后点击Install without restart

gitlab获取api token配置

登录gitlab-->右上角-->setting-->Access Tokens

jenkins+mqtt实现本地构建和远程自动发版_第4张图片

name填jenkins,socpe勾选api,然后点击Create personal access token 按钮

 jenkins+mqtt实现本地构建和远程自动发版_第5张图片

 点击后生成的token字符串显示在上面,复制下来保存,这个token在这个界面只显示一次。

jenkins里配置token

登录jenkins-->Manage Jenkins-->Configure System

配置里面的Gitlab选项,勾选Enable authentication for '/project' end-point,name填jenkins,gitlab地址填你的gitlab地址,Credentials添加刚才在gitlab里获取的token

jenkins+mqtt实现本地构建和远程自动发版_第6张图片

 在弹出的添加凭据页面,类型选择 Gitlab AP token,API token填你从gitlab复制来的token。

jenkins+mqtt实现本地构建和远程自动发版_第7张图片

 添加凭据后在主页面点test connection,保存。

4.git多分支插件Git Parameter使用

Manage Jenkins-->Manage Plugins

搜索Git Parameter安装

以git项目打包为例说明使用

新建item

任务名称 任意,类型选Freestyle project

填写内容分6块,分别是 General 、源码管理、构建触发器、构建环境、构建、构建后操作

General内容

1.勾选Discard old builds 
   设置 策略Loq Rotation 
      保持构建的天数=1 
      保持构建的最大个数=5
2.GitLab Connection
    选择 jenkins
3.勾选This project is parameterized
    添加参数 Git Parameter(这里用到的就是刚才的 Git Parameter插件)
      名称 填 branch
      参数类型 选择 分支
4.JDK(只有一个JDK时可能没有这个选项)
    选择JDK1.8

源码管理内容

1.选择Git
    Repositories
      Repository URL 填git地址如http://git.aaa.com/bbb_projects/ccc.git
    Credentials
      这里添加 你的git账号 可以用gitlab的用户名密码,也可以用之前的全局配置的apitoken
    Branches to build
      指定分支(为空时代表any) 填 $branch
    其他选项 默认

构建触发器

不填 这里可以配置一些比如push代码后自动构建的操作等

构建

增加构建步骤-Invoke top-level Maven targets
    Maven 版本 选择 Maven3.5
    目标 填 clean package -Pprod -U -Dmaven.test.skip (具体含义查看maven相关命令)

构建后操作

增加构建后操作步骤--E-mail Notification (这里一般用来做构建后的邮件通知 需要安装postfix)
    Recipients 填邮箱 多个邮箱用空格分隔
    勾选 每次不稳定的构建都发送邮件通知

5.maven打包war项目配置

关键命令

clean package -Pdev -U -Dmaven.test.skip

这个命令上一步已经出现了,用来打包的 -P可以指定参数,-Dmaven.test.skip用来跳过test类编译

二、上传及mqtt消息发送

1.transfer命令工具安装

下载地址:GitHub - Mikubill/transfer: 集合多个API的大文件传输工具.

安装命令

#下载,解压到/usr/local/transfer目录
wget https://github.com/Mikubill/transfer/releases/download/v0.4.14/transfer_0.4.14_linux_amd64.tar.gz
mkdir -p /usr/local/transfer
tar -zxvf  transfer_0.4.14_linux_amd64.tar.gz  -C /usr/local/transfer

#编辑配置环境变量
vi /etc/profile

#末尾添加如下内容
# set transfer env
export TRANSFER_HOME=/usr/local/transfer
export PATH=$TRANSFER_HOME:$PATH


#使环境变量生效
source /etc/profile

验证安装

transfer --version

2.mqtt客户端安装

安装命令

yum install epel-release
yum install mosquitto

3.jenkins上传和发消息shell脚本

完整命令如下

cd ./saas-wis-basic/target
/usr/local/transfer/transfer wss basic.war >wenshushu.txt
linklineNumber=$(sed -n '/Download\ Link/=' wenshushu.txt)
echo $linklineNumber
linklinestr=$(sed -n "${linklineNumber}p" wenshushu.txt)
echo $linklinestr
str=${linklinestr#*Link: }
mosquitto_pub -h mq.tongxinmao.com -t topic/sunwardpord -m "{\"module\":\"saas-wis-basic\",\"link\":\"${str}\"}"

近期发现transfer这个工具下载文叔叔的链接报错,建议上传命令换成奶牛快传

/usr/local/transfer/transfer cow basic.war >wenshushu.txt

transfer这个工具是用go写的,有时间我再看下源码改改给原作提个pr

4.修改pom的module不编译不需要的工程

shell命令如下

#删除下的第一行
sed -i '//{n;d}' pom.xml
#在 下添加 saas-wis-production
sed -i '//a\ saas-wis-production<\/module>\n' pom.xml

三、客户端接收mqtt消息自动备份替换war包重启tomcat发版

用go+go调用shell脚本实现。

完整代码在:gsls200808/jenkins-mqttt-client

1.go接收mqtt消息部分实现

实现如下

// test
package main

import (
	"fmt"
	"os"
	"os/signal"

	mqtt "github.com/eclipse/paho.mqtt.golang"
)

var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
	fmt.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic())
	//读取发布配置,备份war包,替换war包,重启tomcat或者docker
}

var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
	fmt.Println("Connected")
}

var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
	fmt.Printf("Connect lost: %v", err)
}

func main() {

	//合建chan
	c := make(chan os.Signal)
	//监听指定信号 ctrl+c kill
	signal.Notify(c, os.Interrupt, os.Kill)
	//阻塞直到有信号传入
	fmt.Println("启动")

	//执行具体方法
	initmqtt()

	//阻塞直至有信号传入
	s := <-c
	fmt.Println("退出信号", s)

}

func initmqtt() {
	var broker = "mq.tongxinmao.com"
	var port = 1883
	opts := mqtt.NewClientOptions()
	opts.AddBroker(fmt.Sprintf("tcp://%s:%d", broker, port))
	opts.SetClientID("go_mqtt_client")
	opts.SetUsername("emqx")
	opts.SetPassword("public")
	opts.SetDefaultPublishHandler(messagePubHandler)
	opts.OnConnect = connectHandler
	opts.OnConnectionLost = connectLostHandler
	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}

	sub(client)
}

func sub(client mqtt.Client) {
	topic := "topic/sunwardpord"
	token := client.Subscribe(topic, 1, nil)
	token.Wait()
	fmt.Printf("Subscribed to topic: %s\n", topic)
}

2.客户端发布json定义

定义moudule名、war包目录、替换war前后执行的脚本,这个json放到go文件的同级目录。

jenkinsmqtt.json

[{
	"module": "saas-wis-basic",
	"warpath": "/data/webrv/tomcat/basic/webapps",
	"warname": "basic.war",
	"backpath": "/data/webrv/backwar",
	"stop": "docker stop wis-basic",
	"start": "docker start wis-basic",
	"restart": "docker restart wis-basic",
	"transfer": "/usr/local/transfer/transfer"
}]

3.客户端解析json调用shell脚本实现

定义读取文件,执行shell,解析json的几个方法

// readFile 使用ioutil.ReadFile 直接从文件读取到 []byte中
func readFile(fileName string) string {
	f, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Printf("读取文件失败:%#v", err)

		return ""
	}
	return string(f)
}

//读取消息的发布模块名和链接
func readIssueModule(issuejson string) (string, string) {
	buf := bytes.NewBuffer([]byte(issuejson))
	js, _ := simplejson.NewFromReader(buf)

	fmt.Println(js)

	var each_map = make(map[string]interface{})
	each_map, _ = js.Map()

	module := each_map["module"].(string)
	link := each_map["link"].(string)

	return module, link
}

//根据模块名和模块匹配本地json模块发布
func issueModuleLocalJson(imodule string, link string, localjson string) {
	//读本地配置 war包路径 存储命令等
	fmt.Println(localjson)

	buf := bytes.NewBuffer([]byte(localjson))
	js, _ := simplejson.NewFromReader(buf)

	fmt.Println(js)
	//获取json字符串中的  数组
	rows, _ := js.Array()
	fmt.Println(rows)

	//遍历rows数组
	for _, row := range rows {
		each_map := row.(map[string]interface{})
		module := each_map["module"].(string)
		warpath := each_map["warpath"].(string)
		warname := each_map["warname"].(string)
		backpath := each_map["backpath"].(string)
		stop := each_map["stop"].(string)
		start := each_map["start"].(string)
		transfer := each_map["transfer"].(string)
		// restart := each_map["restart"].(string)

		if module == imodule {
			fmt.Println("找到对应模块")
			exec_shell("cd " + backpath)
			pwd, _ := os.Getwd()
			fmt.Println("当前目录" + pwd)
			exec_shell(transfer + " " + link)
			exec_shell(stop)
			exec_shell("cp -r" + " " + warname + " " + warpath)
			exec_shell(start)
		} else {
			break
		}
	}

}

//阻塞式的执行外部shell命令的函数,等待执行完毕并返回标准输出
func exec_shell(s string) (string, error) {
	//函数返回一个*Cmd,用于使用给出的参数执行name指定的程序
	cmd := exec.Command("/bin/bash", "-c", s)

	//读取io.Writer类型的cmd.Stdout,再通过bytes.Buffer(缓冲byte类型的缓冲器)将byte类型转化为string类型(out.String():这是bytes类型提供的接口)
	var out bytes.Buffer
	cmd.Stdout = &out

	//Run执行c包含的命令,并阻塞直到完成。  这里stdout被取出,cmd.Wait()无法正确获取stdin,stdout,stderr,则阻塞在那了
	err := cmd.Run()
	checkErr(err)

	return out.String(), err
}

//错误处理函数
func checkErr(err error) {
	if err != nil {
		fmt.Println(err)
		panic(err)
	}
}

接收消息的方法添加如下调用

//读取发布配置,备份war包,替换war包,重启tomcat或者docker
	issuejson := string(msg.Payload())
	fmt.Println(issuejson)

	//读本地配置 war包路径 存储命令等
	pwd, _ := os.Getwd()
	localjson := readFile(pwd + "/" + "jenkinsmqtt.json")
	fmt.Println(localjson)

	//发布模块解析
	module, link := readIssueModule(issuejson)
	fmt.Println(module)
	fmt.Println(link)

	//模块发布
	issueModuleLocalJson(module, link, localjson)

4.交叉编译

通常情况下,我们一般是windows开发,linux部署,所以有必要把go工程编译成linux下的可执行文件

安装TDM-GCC

下载地址:tdm-gcc

安装后进windows的cmd,进入go文件所在目录,输入以下命令

SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build main.go

编译后的linux的可执行文件没有后缀,复制到linux改下权限就能使用。

你可能感兴趣的:(分享学习,jenkins,服务器,运维)