Spring Boot(十六):使用 Jenkins 部署 Spring Boot

news/2025/2/22 15:57:15

Jenkins 是 Devops 神器,本篇文章介绍如何安装和使用 Jenkins 部署 Spring Boot 项目

Jenkins 搭建、部署分为四个步骤;

第一步,Jenkins 安装
第二步,插件安装和配置
第三步,Push SSH
第四步,部署项目
第一步 ,Jenkins 安装
准备环境:

JDK:1.8
Jenkins:2.83
Centos:7.3
maven 3.5

Jdk 默认已经安装完成

配置 Maven
版本要求 Maven3.5.0

软件下载

wget http://mirror.bit.edu.cn/apache/maven/maven-3/3.5.0/binaries/apache-maven-3.5.0-bin.tar.gz

安装

修改环境变量,
在/etc/profile中添加以下几行

## 解压
tar vxf apache-maven-3.5.0-bin.tar.gz
## 移动
mv apache-maven-3.5.0 /usr/local/maven3

MAVEN_HOME=/usr/local/maven3
export MAVEN_HOME
export PATH=${PATH}:${MAVEN_HOME}/bin

记得执行source /etc/profile使环境变量生效。

验证
最后运行mvn -v验证maven是否安装成功

配置防护墙
关闭防护墙

#centos7
systemctl stop firewalld.service
==============================
#以下为:centOS 6.5关闭防火墙步骤
#关闭命令:  
service iptables stop 
#永久关闭防火墙:
chkconfig iptables off

两个命令同时运行,运行完成后查看防火墙关闭状态

service iptables status

Jenkins 安装
下载

cd /opt
wget http://mirrors.jenkins.io/war/2.83/jenkins.war

启动服务

java -jar jenkins.war &

Jenkins 就启动成功了!它的war包自带Jetty服务器

第一次启动 Jenkins 时,出于安全考虑,Jenkins 会自动生成一个随机的按照口令。注意控制台输出的口令,复制下来,然后在浏览器输入密码:

INFO: 

*************************************************************
*************************************************************
*************************************************************

Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

0cca37389e6540c08ce6e4c96f46da0f

This may also be found at: /root/.jenkins/secrets/initialAdminPassword

*************************************************************
*************************************************************
*************************************************************

访问
浏览器访问:http://localhost:8080/

输入:0cca37389e6540c08ce6e4c96f46da0f

进入用户自定义插件界面,建议选择安装官方推荐插件,因为安装后自己也得安装:

接下来是进入插件安装进度界面:

插件一次可能不会完全安装成功,可以点击Retry再次安装。直到全部安装成功

等待一段时间之后,插件安装完成,配置用户名密码:

输入:admin/admin

系统管理-》全局工具配置 jdk路径,

第二步,插件安装和配置
有很多插件都是选择的默认的安装的,所以现在需要我们安装的插件不多,Git plugin 和 Maven Integration plugin,publish over SSH。

插件安装:系统管理 > 插件管理 > 可选插件,勾选需要安装的插件,点击直接安装或者下载重启后安装

配置全局变量
系统管理 > 全局工具配置

JDK

配置本地 JDK 的路径,去掉勾选自动安装

Maven

配置本地maven的路径,去掉

ssh-copy-id -i id_rsa.pub 192.168.0.xx
chmod 644 authorized_keys

勾选自动安装

其它内容可以根据自己的情况选择安装。

使用密钥方式登录目标发布服务器
ssh 的配置可使用密钥,也可以使用密码,这里我们使用密钥来配置,在配置之前先配置好jenkins服务器和应用服务器的密钥认证
Jenkins服务器上生成密钥对,使用ssh-keygen -t rsa命令

输入下面命令 一直回车,一个矩形图形出现就说明成功,在~/.ssh/下会有私钥id_rsa和公钥id_rsa.pub

ssh-keygen -t rsa

jenkins服务器的公钥id_rsa.pub中的内容复制到应用服务器 的~/.ssh/下的 authorized_keys文件

ssh-copy-id -i id_rsa.pub 192.168.0.xx
chmod 644 authorized_keys

在应用服务器上重启 ssh 服务,service sshd restart现在 Jenkins 服务器可免密码直接登陆应用服务器

之后在用 ssh B尝试能否免密登录 B 服务器,如果还是提示需要输入密码,则有以下原因

a. 非 root 账户可能不支持 ssh 公钥认证(看服务器是否有限制)
b. 传过来的公钥文件权限不够,可以给这个文件授权下 chmod 644 authorized_keys
c. 使用 root 账户执行 ssh-copy-id -i ~/.ssh/id_rsa.pub 这个指令的时候如果需要输入密码则要配置sshd_config

vi /etc/ssh/sshd_config
#内容
PermitRootLogin no

修改完后要重启 sshd 服务

service sshd restart

最后,如果可以 SSH IP 免密登录成功说明 SSH 公钥认证成功。

上面这种方式比较复杂,其实在 Jenk

clean install -Dmaven.test.skip=true -Ptest

ins 后台直接添加操作即可,参考下面方式

使用用户名+密码方式登录目标发布服务器
(1)点击"高级"展开配置

(2)配置SSH的登陆密码

配置完成后可点击“Test Configuration”测试到目标主机的连接,出现”success“则成功连接,如果有多台应用服务器,可以点击”增加“,配置多个“SSH Servers” 点击“保存”以保存配置。

第三步,Push SSH
系统管理 > 系统设置

选择 Publish over SSH

Passphrase 不用设置
Path to key 写上生成的ssh路径:/root/.ssh/id_rsa

下面的 SSH Servers 是重点

Name 随意起名代表这个服务,待会要根据它来选择
Hostname 配置应用服务器的地址
Username 配置 linux 登陆用户名
Remote Directory 不填
点击下方增加可以添加多个应用服务器的地址

第四步,部署项目
首页点击新建:输入项目名称

下方选择构建一个 Maven 项目,点击确定。

勾选丢弃旧的构建,选择是否备份被替换的旧包。我这里选择备份最近的10个

源码管理,选择 SVN,配置 SVN 相关信息,点击 add 可以输入 SVN 的账户和密码

SVN 地址:http://192.168.0.xx/svn/xxx@HEAD,@HEAD意思取最新版本

构建环境中勾选“Add timestamps to the Console Output”,代码构建的过程中会将日志打印出来

在 Build 中输入打包前的 mvn 命令,如:

clean install -Dmaven.test.skip=true -Ptest

意思是:排除测试的包内容,使用后缀为 test 的配置文件。

Post Steps 选择 Run only if build succeeds

点击Add post-build step,选择 Send files or execute commands over SSH

Name 选择上面配置的 Push SSH

Source files配置:target/xxx-0.0.1-SNAPSHOT.jar 项目jar包名
Remove prefix:target/
Remote directory:Jenkins-in/ 代码应用服务器的目录地址,
Exec command:Jenkins-in/xxx.sh 应用服务器对应的脚本。

需要在应用服务器创建文件夹:Jenkins-in,在文件夹中复制一下脚本内容:xxx.sh

DATE=$(date +%Y%m%d)
export JAVA_HOME PATH CLASSPATH
JAVA_HOME=/usr/java/jdk1.8.0_131
PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH
DIR=/root/xxx
JARFILE=xxx-0.0.1-SNAPSHOT.jar

if [ ! -d $DIR/backup ];then
   mkdir -p $DIR/backup
fi
cd $DIR

ps -ef | grep $JARFILE | grep -v grep | awk '{print $2}' | xargs kill -9
mv $JARFILE backup/$JARFILE$DATE
mv -f /root/Jenkins-in/$JARFILE .

java -jar $JARFILE > out.log &
if [ $? = 0 ];then
        sleep 30
        tail -n 50 out.log
fi

cd backup/
ls -lt|awk 'NR>5{print $NF}'|xargs rm -rf

这段脚本的意思,就是 kill 旧项目,删除旧项目,启动新项目,备份老项目。

全文完。

参考:https://blog.csdn.net/LLQ_200/article/details/76921487

资料推荐
最近又赶上跳槽的高峰期(招聘旺季),好多读者都问我要有没有最新面试题,找华为朋友整理一份内部资料《第6版:互联网大厂面试题》并分类 4 份 PDF,累计 926 页!

整个资料包,包括 Spring、Spring Boot/Cloud、Dubbo、JVM、集合、多线程、JPA、MyBatis、MySQL、大数据、Nginx、Git、Docker、GitHub、Servlet、JavaWeb、IDEA、Redis、算法、面试题等几乎覆盖了 Java 基础和阿里巴巴等大厂面试题等、等技术栈!

据说已经有小伙伴通过这套资料,成功的入职了蚂蚁金服、字节跳动等大厂。

而且,这些资料不是扫描版的,里面的文字都可以直接复制,非常便于我们学习:

2.1 基本名词解释
名词 概念
PlatformTransactionManager 事务管理器,管理事务的各生命周期方法,下文简称TxMgr
TransactionAttribute 事务属性, 包含隔离级别,传播行为,是否只读等信息,下文简称TxAttr
TransactionStatus 事务状态,下文简称TxStatus
TransactionInfo 事务信息,内含TxMgr, TxAttr, TxStatus等信息,下文简称TxInfo
TransactionSynchronization 事务同步回调,内含多个钩子方法,下文简称TxSync / transaction synchronization
TransactionSynchronizationManager 事务同步管理器,维护当前线程事务资源,信息以及TxSync集合
2.2 七种事务传播行为
Spring中的Propagation枚举和TransactionDefinition接口定义了7种事务传播行为:

REQUIRED
如果当前无事务则开启一个事务,否则加入当前事务。
SUPPORTS
如果当前有事务则加入当前事务。
MANDATORY
如果当前无事务则抛出异常,否则加入当前事务。
REQUIRES_NEW
如果当前无事务则开启一个事务,否则挂起当前事务并开启新事务。
NOT_SUPPORTED
如果当前有事务,则挂起当前事务以无事务状态执行方法。
NEVER
如果当前有事务,则抛出异常。
NESTED
创建一个嵌套事务,如果当前无事务则创建一个事务。
2.3 套路讲解
这里介绍以下Spring声明式事务的套路。如果能知晓大致套路会对接下来看源码有很大的帮助。文笔不是太好,下面的描述如有不清,还请指出。

2.3.1 事务拦截器
我们给一个bean的方法加上@Transactional注解后,Spring容器给我们的是一个代理的bean。当我们对事务方法调用时,会进入Spring的ReflectiveMethodInvocation#proceed方法。这是AOP的主要实现,在进入业务方法前会调用各种方法拦截器,我们需要关注的拦截器是org.springframework.transaction.interceptor.TransactionInterceptor。
TransactionInterceptor的职责类似于一个“环绕切面”,在业务方法调用前根据情况开启事务,在业务方法调用完回到拦截器后进行善后清理。

事务切面在源码中具体的实现方法是TransactionAspectSupport#invokeWithinTransaction,相信平时大家debug的时候在调用栈中经常能看到此方法。事务切面关注的是TransactionInfo(TxInfo),TxInfo是一个“非常大局观”的东西(里面啥都有:TxMgr, TxAttr, TxStatus还有前一次进入事务切面的TransactionInfo)。

因此事务切面会调用createTransactionIfNecessary方法来创建事务并拿到一个TxInfo(无论是否真的物理创建了一个事务)。如果事务块内的代码发生了异常,则会根据TxInfo里面的TxAttr配置的rollback规则看看这个异常是不是需要回滚,不需要回滚就尝试提交,否则就尝试回滚。如果未发生异常,则尝试提交。

2.3.2 提交与回滚
事务切面对于尝试提交会判断是否到了最外层事务(某个事务边界)。举个例子:有四个事务方法依次调用,传播行为分别是 方法1:REQUIRED, 方法2:REQUIRED, 方法3: REQUIRES_NEW, 方法4: REQUIRED。很显然这其中包含了两个独立的物理事务,当退栈到方法4的事务切面时,会发现没有到事务最外层,所以不会有真正的物理提交。而在退栈到了方法3对应的事务切面时会发现是外层事务,此时会发生物理提交。同理,退栈到方法1的事务切面时也会触发物理提交。

那么问题来了,Spring是怎么判断这所谓“最外层事务”的呢。
答案是TxStatus中有个属性叫newTransaction用于标记是否是新建事务(根据事务传播行为得出,比如加入已有事务则会是false),以及一个名为transaction的Object用于表示物理事务对象(由具体TxMgr子类负责给出)。Spring会根据每一层事务切面创建的TxStatus内部是否持有transaction对象以及newTransaction标志位判断是否属于外层事务。

类似的,Spring对于回滚事务也是会在最外层事务方法对应的切面中进行物理回滚。而在非最外层事务的时候会由具体txMgr子类给对应的事务打个的标记用于标识这个事务该回滚,这样的话在所有同一物理事务方法退栈过程中在事务切面中都能读取到事务被打了应该回滚的标记。可以说这是同一物理事务方法之间进行通信的机制。

2.3.3 ThreadLocal的使用
Spring事务代码中用ThreadLocal来进行资源与事务的生命周期的同步管理。

在事务切面层面,TransactionAspectSupport里面有个transactionInfoHolder的ThreadLocal对象,用于把TxInfo绑定到线程。那么这样在我们的业务代码或者其他切面中,我们可以拿到TxInfo,也能拿到TxStatus。拿到TxStatus我们就可以调用setRollbackOnly来打标以手动控制事务必须回滚。

TransactionSynchronizationManager是Spring事务代码中对ThreadLocal使用最多的类,目前它内部含有6个ThreadLocal,分别是:

resources
类型为Map<Object, Object>用于保存事务相关资源,比如我们常用的DataSourceTransactionManager会在开启物理事务的时候把<DataSource, ConnectionHolder>绑定到线程。
这样在事务作用的业务代码中可以通过Spring的DataSourceUtils拿到绑定到线程的ConnectionHolder中的Connection。事实上对于MyBatis来说与Spring集成时就是这样拿的。
synchronizations
类型为Set用于保存transaction synchronization,这个可以理解为是回调钩子对象,内部含有beforeCommit, afterCommit, beforeCompletion等钩子方法。
我们自己如果需要的话也可以在业务方法或者切面中注册一些transaction synchronization对象用于追踪事务生命周期做一些自定义的事情。
currentTransactionName
当前事务名
currentTransactionReadOnly
当前事务是否只读
currentTransactionIsolationLevel
当前事务隔离级别
actualTransactionActive
是否存在物理事务,比如传播行为为NOT_SUPPORTED时就会是false。
2.4 几幅草图
下面是我做的几张图,比较丑陋。举了三个不同的事务传播情况,列一下TxInfo的信息(TxInfo中主要列了TxStatus的一些关键字段以及oldTransactionInfo字段)

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) throws Throwable {

    // 获取TransactionAttribute、PlatformTransactionManager、以及连接点方法信息。
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 根据上面抓取出来的txAttribute, tm, 连接点方法等信息判断是否需要开启事务。
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            // 执行回调,如果没有后续拦截器的话,就进入事务方法了。
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // 事务发生异常。
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            // 把上一层事务的TxInfo重新绑到ThreadLocal中。
            cleanupTransactionInfo(txInfo);
        }
        // 事务未发生异常。
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }

    else {
        try {
            Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
                    new TransactionCallback<Object>() {
                        @Override
                        public Object doInTransaction(TransactionStatus status) {
                            TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                            try {
                                return invocation.proceedWithInvocation();
                            }
                            catch (Throwable ex) {
                                if (txAttr.rollbackOn(ex)) {
                                    if (ex instanceof RuntimeException) {
                                        throw (RuntimeException) ex;
                                    }
                                    else {
                                        throw new ThrowableHolderException(ex);
                                    }
                                }
                                else {
                                    return new ThrowableHolder(ex);
                                }
                            }
                            finally {
                                cleanupTransactionInfo(txInfo);
                            }
                        }
                    });

            if (result instanceof ThrowableHolder) {
                throw ((ThrowableHolder) result).getThrowable();
            }
            else {
                return result;
            }
        }
        catch (ThrowableHolderException ex) {
            throw ex.getCause();
        }
    }
}

http://www.niftyadmin.cn/n/5862490.html

相关文章

ubuntu部署小笔记-采坑

ubuntu部署小笔记 搭建前端控制端后端前端nginx反向代理使用ubuntu部署nextjs项目问题一 如何访问端口号配置后台运行该进程pm2 问题二 包体过大生产环境下所需文件 问题三 部署在vercel时出现的问题需要魔法访问后端api时&#xff0c;必须使用https协议电脑端访问正常&#xf…

DuodooBMS源码解读之 odoo_phoenix_alarm模块

Odoo18 扩展模块声光报警器用户使用手册 一、模块概述 本扩展模块是基于 Odoo18 原生系统进行开发的&#xff0c;主要用于实现与上位声光报警设备的通讯功能。通过该模块&#xff0c;用户可以方便地向设备发送指令&#xff0c;控制设备的声音、灯光等操作。本手册将详细介绍该…

DeepSeek接入Siri(已升级支持苹果手表)完整版硅基流动DeepSeek-R1部署

DeepSeek接入Siri&#xff08;已升级支持苹果手表&#xff09;完整版硅基流动DeepSeek-R1部署 **DeepSeek** 是一款专注于深度学习和人工智能的工具或平台&#xff0c;通常与人工智能、机器学习、自动化分析等领域有关。它的主要功能可能包括&#xff1a;深度学习模型搜索&…

Python爬虫实战:获取12306特定日期、城市车票信息,并做数据分析以供出行参考

注意:以下内容仅供技术研究,请遵守目标网站的robots.txt规定,控制请求频率避免对目标服务器造成过大压力! 1. 核心思路 需求:获取明天(2025 年 2 月 21 日)从北京到上海的车次、票价、出发时间、硬卧二等卧信息,并保存到 CSV 文件,然后分析出价格最低的 10 趟车次。目…

Java四大框架深度剖析:MyBatis、Spring、SpringMVC与SpringBoot

目录 前言&#xff1a; 一、MyBatis框架 1. 概述 2. 核心特性 3. 应用场景 4. 示例代码 二、Spring框架 1. 概述 2. 核心模块 3. 应用场景 4. 示例代码 三、SpringMVC框架 1. 概述 2. 核心特性 3. 应用场景 4. 示例代码 四、SpringBoot框架 1. 概述 2. 核心…

基于COSTAR模型的内容创作:如何用框架提升写作质量

目录 前言1. Context&#xff08;上下文&#xff09;&#xff1a;理解背景&#xff0c;奠定写作基础1.1 何为上下文1.2 上下文的作用1.3 案例解析 2. Objective&#xff08;目标&#xff09;&#xff1a;明确写作方向&#xff0c;避免跑题2.1 确立目标2.2 如何设定目标2.3 案例…

【电子通知】案例:26AWG*3C OD=3.8线缆的含义是什么?

在看线缆图纸时&#xff0c;有时候会发现标识&#xff1a;26AWG*3C OD3.8&#xff0c;那这个参数是什么意思呢&#xff1f; 首先我们要先知道线缆规格的表示方法&#xff0c;一般来说&#xff0c;线缆规格按以下方式标注&#xff1a; 线规&#xff1a;表示导线的直径或截面积&a…

DeepSeek掘金——SpringBoot 调用 DeepSeek API 快速实现应用开发

Spring Boot 实现 DeepSeek API 调用 1. 项目依赖 在 pom.xml 中添加以下依赖: <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>&l…