环境准备
1. 安装JDK
- 查看jdk版本
java -version
[root@sinolee /]# java -version
java version "17.0.2" 2022-01-18 LTS
Java(TM) SE Runtime Environment (build 17.0.2+8-LTS-86)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.2+8-LTS-86, mixed mode, sharing)
2. 安装Git
yum install -y git
- 查看是否安装成功
git version
[root@sinolee /]# git version
git version 1.8.3.1
3. 安装 Maven
yum -y install maven
- 查看maven版本
mvn -version
[root@sinolee /]# mvn -version
Apache Maven 3.8.4 (9b656c72d54e5bacbed989b64718c159fe39b537)
Maven home: /usr/local/apache-maven-3.8.4
Java version: 17.0.2, vendor: Oracle Corporation, runtime: /usr/java/jdk-17.0.2
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-1160.66.1.el7.x86_64", arch: "amd64", family: "unix"
4. 安装jenkins
- yum安装
wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
yum upgrade
异常处理
启动异常 : Jenkins Continuous Integration Server
- 使用
systemctl start jenkins.service
命令启动可能抛异常:
[root@sinolee ~]# systemctl start jenkins.service
Job for jenkins.service failed because the control process exited with error code. See "systemctl status jenkins.service" and "journalctl -xe" for details.
[root@sinolee ~]# systemctl status jenkins.service
● jenkins.service - Jenkins Continuous Integration Server
Loaded: loaded (/usr/lib/systemd/system/jenkins.service; disabled; vendor preset: disabled)
Active: failed (Result: start-limit) since Wed 2022-06-29 15:53:38 CST; 3s ago
Process: 6449 ExecStart=/usr/bin/jenkins (code=exited, status=1/FAILURE)
Main PID: 6449 (code=exited, status=1/FAILURE)
Jun 29 15:53:37 sinolee systemd[1]: Failed to start Jenkins Continuous Integration Server.
Jun 29 15:53:37 sinolee systemd[1]: Unit jenkins.service entered failed state.
Jun 29 15:53:37 sinolee systemd[1]: jenkins.service failed.
Jun 29 15:53:38 sinolee systemd[1]: jenkins.service holdoff time over, scheduling restart.
Jun 29 15:53:38 sinolee systemd[1]: Stopped Jenkins Continuous Integration Server.
Jun 29 15:53:38 sinolee systemd[1]: start request repeated too quickly for jenkins.service
Jun 29 15:53:38 sinolee systemd[1]: Failed to start Jenkins Continuous Integration Server.
Jun 29 15:53:38 sinolee systemd[1]: Unit jenkins.service entered failed state.
Jun 29 15:53:38 sinolee systemd[1]: jenkins.service failed.
- 解决方案:
- 不使用systemctl的方式操作Jenkins, 直接用Jenkins自带的服务启动 (使用 systemctl 最终也是执行的这个命令)
- 切换到 Jenkins 脚本目录
cd /etc/init.d
, 执行以下命令
# 启动
./jenkins start
# 停止
./jenkins stop
# 状态
./jenkins status
执行结果:
[root@sinolee init.d]# ./jenkins restart
Shutting down Jenkins [ OK ]
Starting Jenkins [ OK ]
[root@sinolee init.d]# ./jenkins status
jenkins (pid 19113) is running...
访问 jenkins, 浏览器地址栏输入 主机IP : 端口号, 如:127.0.0.1:8080
访问异常
- 解决方案:
- 安装以下包
yum install dejavu-sans-fonts
yum install fontconfig
yum install xorg-x11-server-Xvfb
环境配置及插件
定制安装所需插件
- 配置路径 : Manage Jenkins >>> Manage Plugins
选择所需插件, 点击安装 ( Install without restart )
安装中文插件
- Available 搜索
Chinese
或Localization: Chinese (Simplified)
- Available 搜索
配置全局工具
1. 配置 Java 工具
- 配置路径 : 系统管理 >>> 全局工具配置 >>> JDK >>> 新增JDK
注 : 若服务器上没有 Java 环境,可以选择自动安装。
建议 : 请预先在服务器上配置好Java环境. 可参考文章 ( 阿里云 ) Linux 安装jdk1.8(rpm方式)
2. 配置 Maven
- 配置路径 : 系统管理 >>> 全局工具配置 >>> Maven 配置
当前配置页下,新增 Maven 安装路径,请手动配置,不选自动安装
settings.xml 文件 一般都是在 Maven 安装路径的 conf 目录下
建议 : 请预先在服务器上配置好Maven环境. 可参考文章 Linux 配置 Maven
基本使用
任务全部使用传统配置模式
CI 的整体逻辑 : 拉取代码 >>> 设置编译命令(方法) >>> 编译 >>> 生成结果(二进制文件或包)
创建一个Java任务
新建任 >>> 输入任务名称 >>> 构建一个Maven项目
Ps : 没有 构建一个Maven项目 选项的处理办法
- 点击
可选插件
输入关键字 :Maven Integration
搜索并安装
- 重启jenkins即可看到选项
源码管理
Ps : Jenkins源码管理无Git选项
- 点击
可选插件
输入关键字 :Github
搜索并安装
配置项目Git账号
需构建的代码分支,此处是固定写死的。 当参数化构建的时候,可以填入指定的分支和tag
构建触发器
构建环境
Build
指定pom.xml的位置,maven类型的任务在编译的时候是根据代码中pom.xml的定义进行代码的编译动作
Post Steps
构建设置
构建后操作
可设置编译成功后的操作,如归档、触发其他的任务、清理工作空间,或其他一些自定义的操作。操作依赖插件的支持。在代码编译成功后增加发布的动作即整体实现CD的功能,从代码到服务器
run.sh
打包脚本
# 项目模块统一变量设置
# 消息队列服务
MQ_CONTAINER="mq"
MQ_IMAGE="mq_server"
MQ_PORT=8081
# 网站服务
WEB_CONTAINER="web"
WEB_IMAGE="web_server"
WEB_PORT=8082
# 用户服务
USER_CONTAINER="user"
USER_IMAGE="user_server"
USER_PORT=8083
echo "========== 删除旧代码 =========="
rm -rf /home/project/demo/*
# 拷贝新代码到应用服务器
echo "========== 拷贝代码 =========="
scp -r ./* /home/project/demo/
# 切换到项目文件夹下
cd /home/project/demo/
echo "========== 项目开始构建打包 =========="
mvn clean package -Dmaven.test.skip=true
echo "========== 拷贝 jar 文件 =========="
mkdir -p /project/demo
\cp -r /home/project/demo/*/target/*server*.jar /project/demo
echo "========== 拷贝 DockerFile 文件 =========="
\cp /home/project/demo/DockerFile /project/demo
# 停止并删除旧的容器
echo "========== 停止并删除旧的容器 =========="
docker ps -a | grep $MQ_CONTAINER && docker stop $MQ_CONTAINER && docker rm $MQ_CONTAINER || echo "mq service not exist"
docker ps -a | grep $WEB_CONTAINER && docker stop $WEB_CONTAINER && docker rm $WEB_CONTAINER || echo "web service not exist"
docker ps -a | grep $USER_CONTAINER && docker stop $USER_CONTAINER && docker rm $USER_CONTAINER || echo "user service not exist"
# 删除旧的镜像
echo "========== 删除旧的镜像 =========="
docker images | grep MQ_IMAGE && docker rmi -f MQ_IMAGE || echo "mq old image not exist"
docker images | grep WEB_IMAGE && docker rmi -f WEB_IMAGE || echo "web old image not exist"
docker images | grep USER_IMAGE && docker rmi -f USER_IMAGE || echo "user old image not exist"
# 构建新的镜像
echo "========== 构建新的镜像 =========="
docker build --build-arg JAR_FILE=mq-server-1.0-SNAPSHOT.jar -t $MQ_IMAGE -f DockerFile /project/demo/
docker build --build-arg JAR_FILE=web-server-1.0-SNAPSHOT.jar -t $WEB_IMAGE -f DockerFile /project/demo/
docker build --build-arg JAR_FILE=user-server-1.0-SNAPSHOT.jar -t $USER_IMAGE -f DockerFile /project/demo/
# 运行镜像容器
echo "========== 运行镜像容器 =========="
docker run -it -d --name $MQ_CONTAINER -p $MQ_PORT:$MQ_PORT $MQ_IMAGE
docker run -it -d --name $WEB_CONTAINER -p $WEB_PORT:$WEB_PORT $WEB_IMAGE
docker run -it -d --name $USER_CONTAINER -p $USER_PORT:$USER_PORT $USER_IMAGE
echo "部署成功"
DockerFile
内容
FROM openjdk:17
ARG JAR_FILE
MAINTAINER cccccc@qq.com # 邮箱
COPY ${JAR_FILE} app.jar
ENV JAVA_OPTS="-Xmx512m -Xms512m -Xmn512m -Xss256K -XX:SurvivorRatio=8 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -XX:+UseConcMarkSweepGC -XX:ConcGCThreads=4 -XX:+CMSClassUnloadingEnabled -Dfile.encoding=UTF-8 -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCTimeStamps -Xloggc:./gc.log"
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
踩坑记录
1. 无法找到 mvn 命令 ( mvn: command not found )
- Jenkins 构建历史 控制台输出
- run.sh 文件, 第46行代码
服务器项目文件夹下执行
mvn clean package -Dmaven.test.skip=true
可正常打包原因分析:
对于java或maven的路径的环境变量是放在/etc/profile中的, 而/etc/profile只有在用户登录的时候才会被load,Jenkins在运行命令时,使用的是Non-login的方式,而这种方式在运行命令时,/etc/profile是不会被load进来的,所以jenkins只能在当前路径下寻找可执行文件解决方案:
jenkins (主界面) Dashboard -> (系统管理) Manage Jenkins -> (系统配置) Configure System -> (全局属性) Global Properties -> (环境变量) Environment variables -> (键值对列表) List of variables
在Jenkins设置全局变量,新增如下内容:- 键
JAVA_HOME
值/usr/java/jdk-17.0.2
- 键
MAVEN_HOME
值/usr/local/apache-maven-3.8.4
- 键
PATH+EXTRA
值$MAVEN_HOME/bin
- 键
JAVA_HOME、MAVEN_HOME 对应的值分别为各自安装目录
PATH+EXTRA 表示 PATH=EXTRA:$PATH, 即扩展当前的PATH变量
2. 运行失败 ( Exception in thread “main” java.lang.NoClassDefFoundError )
[root@sinolee demo]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b7a7addce8bd user_server "java -Djava.securit…" About a minute ago Exited (1) About a minute ago es
[root@sinolee linu]# docker logs b7a7addce8bd
Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
at com.test.demo.UserApplication.main(EsApplication.java:14)
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
... 1 more
- 解决方法 子项目
pom.xml
中新增 build
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.11</version>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
3. 一定要注意 SpringBoot
SpringCloud
SpringCloudAlibaba
之间的版本, 建议别用新版本, 各依赖之间的 jar 包冲突整的人头皮发麻…
本文采用版本
jdk
17
maven
3.8.4
Spring
5.3.20
SpringBoot
2.6.8
SpringCloud
2021.0.3
SpringCloudAlibaba
2021.1
- 父项目
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.6.8</version>
</parent>
<groupId>com.test</groupId>
<artifactId>demo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>user-service</module>
<module>web-service</module>
</modules>
<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringCloudAlibaba 依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
ps:如有错误,欢迎批评指正,谢谢!