自动化部署 - 如何利用rpm-maven-plugin打包前后端项目

Maven打包成RPM

RPM全称是 Red Hat Package Manager(Red Hat包管理器)。几乎所有的 Linux 发行版本都使用这种形式的软件包管理安装、更新和卸载软件。

在devops链条上,常见的部署方式是通过持续集成工具如jenkins/CI 构建和发布jar包到服务端制定目录。不过随着越来越多的应用趋向于paas 平台,本地化自动部署的需求越来越多,在软件包的安装过程中各种自动控制命令的集成也越来越复杂,jenkins等部署方式已经无法满足需求,这时候将软件包和集成脚本一起打包成rpm包的形式,也是一种另外的思路。对于最终用户来说,使用 RPM所提供的功能来维护系统是比较容易和轻松的。安装、卸载和升级RPM软件包只需一条命令就能搞定。
后续还可以构建yum本地源,通过ambari等其他集成工具,进行对软件包的安装、管理、监控、卸载、升级全生命周期的管控

利用rpm-maven-plugin插件实现rpm构建,以便于RPM软件仓库管理。
包含四个部分:

  • 基本单项目 打包成rpm
  • 多module 项目打包成rpm
  • 纯前端项目 打包成rpm
  • rpm script 应用-通过自动创建软连接实现rpm包自动升级

备注:

rpm-maven-plugin 需要在linux 机器上才能正常运行,运行机器需要

1
yum install rpm-build

一、基础项目打包

pom文件配置

工程信息,声明rpm包要用到的相关信息,例如project.version、rpm.release、rpm.basedir等等

1
2
3
4
5
6
7
8
9
10
11
<modelVersion>4.0.0</modelVersion>
<groupId>io.github.ifengkou</groupId>
<artifactId>web-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
......
<rpm.release>0.3.0</rpm.release>
<rpm.owner>isuhadoop</rpm.owner>
<rpm.basedir>/data/micro-services/9090-web-api-jar</rpm.basedir>
</properties>

pom文件中加入plugin,执行 maven clean package 可以直接打包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1.5</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>rpm</goal>
</goals>
</execution>
</executions>
<configuration>
<prefix>{install_dir}</prefix><!-- 安装目录 -->
<copyright>GPL (c) 2005, SWWDC</copyright>
<distribution>loganshen</distribution>
<group>ifengkou.github.io</group>
<packager>loganshen</packager>
<prefix>${rpm.basedir}</prefix>
<name>web-api</name>
<version>${project.version}</version>
<autoProvides>false</autoProvides>
<autoRequires>false</autoRequires>
<needarch>noarch</needarch>
<targetOS>linux</targetOS>
<release>${rpm.release}</release>
<requires>
<require>java-1.7.0 >= 1.7</require>
</requires>
<mappings>
<mapping>
<directory>${install_dir}/jars</directory><!-- jar存放目录 -->
<username>tomcat</username><!-- 用户提前建,否则用root -->
<groupname>tomcat</groupname>
<sources>
<source>
<location>target/${project.artifactId}-${project.version}</location>
</source>
</sources>
</mapping>
<mapping>
<directory>${install_dir}/bin</directory><!--脚本存放目录 -->
<filemode>755</filemode>
<username>tomcat</username>
<groupname>tomcat</groupname>
<sources>
<source>
<location>${basedir}/scripts</location>
</source>
</sources>
</mapping>
</mappings>
</configuration>
</plugin>

二、多module 打rpm包

加了rpm包插件后,为了在构建时,不影响日常研发测试,所以在打包的rpm插件配置中,不能将其放入maven 的lifecycle

日常构建,打jar包:

1
mvn clean package -U

发布rpm包:

1
mvn clean package rpm:attached-rpm -U

父项目(配置空rpm)
– controller -打最终的rpm 包
– service(配置空rpm)
– dao(配置空rpm)
– model(配置空rpm)

父项目需要配置 空的rpm配置,其他所有子module 都需要配置,否则就会报错。

其中<phase>none</phase> 表示:unbinds rpm creation from maven lifecycle。这样在构建时,只有在显示指定 rpm:attached-rpm时,才会主动构建rpm包,否则不会触发。

其中 <directory>/tmp</directory> 并不存在,所以不会打包任何东西到rpm中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1.5</version>
<inherited>false</inherited>
<executions>
<execution>
<!-- unbinds rpm creation from maven lifecycle -->
<phase>none</phase>
<goals>
<goal>attached-rpm</goal>
</goals>
</execution>
</executions>
<configuration>
<release>1</release>
<copyright>GPL (c) 2005, SWWDC</copyright>
<distribution>loganshen</distribution>
<group>ifengkou.github.io</group>
<packager>loganshen</packager>
<prefix>/opt/soft</prefix>
<mappings>
<mapping>
<directory>/tmp</directory>
</mapping>
</mappings>
</configuration>
</plugin>

controller 项目是可执行jar包最终打包文件,这个与其他module 不一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1.5</version>
<executions>
<execution>
<id>generate-rpm</id>
<phase>none</phase>
<goals>
<goal>attached-rpm</goal>
</goals>
</execution>
</executions>
<configuration>
<license>GPL (c) 2005, SWWDC</license>
<distribution>loganshen</distribution>
<group>ifengkou.github.io</group>
<packager>loganshen</packager>
<name>rpm-package-name</name>
<prefix>${rpm.basedir}</prefix>
<version>${project.version}</version>
<autoProvides>false</autoProvides>
<autoRequires>false</autoRequires>
<needarch>noarch</needarch>
<targetOS>linux</targetOS>
<release>${rpm.release}</release>
<defineStatements>
<defineStatement>_unpackaged_files_terminate_build 0</defineStatement>
</defineStatements>
<mapping>
<directory>${rpm.basedir}/${rpm.release}/jars</directory>
<sources>
<source>
<location>${basedir}/target/web-api.jar</location>
</source>
</sources>
</mapping>
<mapping>
<directory>${rpm.basedir}/${rpm.release}/bin</directory>
<sources>
<source>
<location>${basedir}/../builder/scripts</location>
</source>
</sources>
</mapping>
</mappings>
</configuration>
</plugin>

三、前端项目打包

前后端分离,前端使用npm run build 构建,生产dist 静态文件,需要在构建时执行shell脚本进行项目构建。构建后需要将生产的目标文件夹倒入到rpm包中

这里用到两个maven plugin:exec-maven-pluginrpm-maven-plugin

先利用exec-maven-plugin 执行shell脚本对工程进行打包生成静态文件,再利用 rpm-maven-plugin 进行rpm打包

日常构建可以通过 mvn clean install 进行构建(需要跳过测试):

1
mvn clean install -Dmaven.test.skip=true

rpm打包可以通过:

1
mvn clean install rpm:attached-rpm -Dmaven.test.skip=true -U

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.github.ifengkou</groupId>
<artifactId>front-web</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<rpm.release>2.9.4</rpm.release>
<rpm.basedir>/data/micro-services/front-web</rpm.basedir>
</properties>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<!-- 覆盖中央仓库的配置 -->
<id>central</id>
<name>alimaven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.5.0</version>
<executions>
<execution>
<id>npm_build</id>
<phase>install</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${basedir}/npmbuild.sh</executable>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1.5</version>
<executions>
<execution>
<id>generate-rpm</id>
<phase>none</phase>
<goals>
<goal>attached-rpm</goal>
</goals>
</execution>
</executions>
<configuration>
<license>GPL (c) 2005, SWWDC</license>
<distribution>loganshen</distribution>
<group>ifengkou.github.io</group>
<packager>loganshen</packager>
<prefix>${rpm.basedir}</prefix> <name>front-web</name>
<version>${project.version}</version>
<autoProvides>false</autoProvides>
<autoRequires>false</autoRequires>
<needarch>noarch</needarch>
<targetOS>linux</targetOS>
<release>${rpm.release}</release> <defineStatements>
<defineStatement>_unpackaged_files_terminate_build 0</defineStatement>
</defineStatements>
<mappings>
<mapping>
<directory>${rpm.basedir}/${rpm.release}</directory>
<username>tomcat</username>
<groupname>tomcat</groupname>
<sources>
<source>
<location>${basedir}/dist</location>
</source>
</sources>
</mapping>
</mappings>
<postinstallScriptlet>
<script>
ln -s ${rpm.basedir}/${rpm.release}/server ${rpm.basedir}/server;
chown -R isuhadoop:isuhadoop ${rpm.basedir};
</script>
</postinstallScriptlet>
<postremoveScriptlet>
<script>
echo "remove symbolic link ";
rm -rf ${rpm.basedir}/server;
echo "uninstall success";
</script>
</postremoveScriptlet>
</configuration>
</plugin>
</plugins>
</build>
</project>

npmbuild.sh为前端工程 build 命令,最终在当前目录生成 dist 的静态文件夹

1
2
3
4
5
#!/bin/sh
echo "start npm install......"
npm install
echo "start npm run build ......"
npm run build

生成 dist 的静态文件夹后,只需要通过rpm plugin 的mapping 将该目录打包到rpm的指定目录,后续可以通过nginx 代理到该目录的index.html

四、rpm script 应用-通过自动创建软连接实现rpm包自动升级

例如web-api服务,通过rpm安装之后的路径是/opt/soft/web-api-1.0。然后rpm那能自动创建一个软链接/opt/soft/web-api连接到/opt/soft/web-api-1.0.0,之后如果要升级到web-api-1.1只需要通过rpm包安装web-api-1.1,rpm包自动将/opt/soft/web-api连接到web-api-1.1上。

linux-rpm-ls

rpm-plugin 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1.5</version>
<executions>
<execution>
<id>generate-rpm</id>
<phase>none</phase>
<goals>
<goal>attached-rpm</goal>
</goals>
</execution>
</executions>
<configuration>
<license>GPL (c) 2005, SWWDC</license>
<distribution>loganshen</distribution>
<group>ifengkou.github.io</group>
<packager>loganshen</packager>
<prefix>${rpm.basedir}</prefix>
<name>web-api</name>
<version>${project.version}</version>
<autoProvides>false</autoProvides>
<autoRequires>false</autoRequires>
<needarch>noarch</needarch>
<targetOS>linux</targetOS>
<release>${rpm.release}</release>
<defineStatements>
<defineStatement>_unpackaged_files_terminate_build 0</defineStatement>
</defineStatements>
<mappings>
<mapping>
<directory>${rpm.basedir}/${rpm.release}/jars</directory>
<sources>
<source>
<location>${basedir}/target/${project.artifactId}-${project.version}.jar</location>
</source>
</sources>
</mapping>
<mapping>
<directory>${rpm.basedir}/${rpm.release}/bin</directory>
<sources>
<source>
<location>${basedir}/scripts</location>
</source>
</sources>
</mapping>
</mappings>
<preinstallScriptlet>
<scriptFile>install/preinstall.sh</scriptFile>
<fileEncoding>utf-8</fileEncoding>
</preinstallScriptlet>
<postinstallScriptlet>
<script>
ln -s ${rpm.basedir}/${rpm.release}/bin ${rpm.basedir}/bin;
ln -s ${rpm.basedir}/${rpm.release}/jars ${rpm.basedir}/jars;
chown -R ${rpm.owner}:${rpm.owner} ${rpm.basedir};
</script>
</postinstallScriptlet>
<preremoveScriptlet>
<scriptFile>install/preremove.sh</scriptFile>
<fileEncoding>utf-8</fileEncoding>
</preremoveScriptlet>
<postremoveScriptlet>
<scriptFile>install/postremove.sh</scriptFile>
<fileEncoding>utf-8</fileEncoding>
</postremoveScriptlet>
</configuration>
</plugin>

有用到了 scriptFile 和 scrpit,scriptFile 没找到怎么传参数,像${rpm.basedir}之类的没法传进去;script 不适合写复杂的shell。还有一种方式是
<script>sh ${rpm.basedir}/shell/xxx.sh ${param1} ${param2} </script>,不过要提前将脚本通过mapping复制到${rpm.basedir}/shell,所以只能在postinstall 后用

  • install/preinstall.sh 主要是为了初始化用户之类的,比如允许web-api 需要特定的用户,那么可以在这个脚本中检测,如果不存在这个用户则创建
  • install/preremove.sh 主要是为了在卸载rpm 时,检测该安装服务是否在运行,如果在运行,则通过命令先shutdown
  • postinstallScriptlet 主要是为了在安装完成后,创建软连接、对目录或者文件进行chown/chmod,或者是创建service ,自启动等等,根据需求来
  • install/postremove.sh 主要是为了在卸载完成后,主动去删除 安装完成后创建的软连接

以下脚本仅做参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
------preinstall.sh
echo "hi,preinstall"
ROOT="/data/micro-services/9090-web-api"
echo ${ROOT}
id isuhadoop
have_user=$?
if [ ${have_user} -ne 0 ];then
echo "add isuhadoop user"
useradd isuhadoop
fi
if [ -d ${ROOT} ];then
echo "${ROOT} is existed"
else
echo "mkdir -p ${ROOT}"
mkdir -p ${ROOT}
chmod 777 ${ROOT}
fi
------preinstall.sh
#!/bin/bash
echo "hi,pre uninstall"
ROOT="/data/micro-services/9090-web-api"
set +e
if test -f ${ROOT}/bin/lryhis.pid ; then
if test -f ${ROOT}/bin/shutdown.sh ;then
echo "try to shutdown the service: sh bin/shutdown.sh"
sh ${ROOT}/bin/shutdown.sh
kill_state=$?
if [ ${kill_state} -ne 0 ];then
echo "shutdown failed"
fi
fi
fi
set -e
------postremove.sh
#!/bin/bash
echo "hi,after uninstall,remove base dir & symbolic link"
ROOT="/data/micro-services/9090-web-api"
echo "remove symbolic link "
rm -rf ${ROOT}/bin ${ROOT}/jars
echo "uninstall success"

参考

https://www.mojohaus.org/rpm-maven-plugin/adv-params.html

https://www.mojohaus.org/exec-maven-plugin/

坚持原创技术分享,您的支持将鼓励我继续创作!
分享