Jenkins入门:流水线方式部署多模块Springboot项目

news2024/11/26 8:31:21

目录

一、环境准备

1. 搭建配置Jenkins  (在上一篇基础上进行)

2. 安装mysql

3. 安装redis

4. 配置docker-componse

 5. 启动docker-componse

二、脚本准备

1. Jenkinsfile

2. deploy.sh

3. Dockerfile

三、Jenkins流水线配置

新增版本号参数

流水线选择代码里面的Jenkinsfile相对路径

选择需要的分支构建任务

四、问题 

1. 多模块打包失败,不会自动打包依赖的其他模块

2. 华为云公网IP访问不通

参考:


一、环境准备

1. 搭建配置Jenkins  (在上一篇基础上进行)

Jenkins入门:从搭建到部署第一个Springboot项目(踩坑记录)-CSDN博客

2. 安装mysql

docker pull mysql:5.7

3. 安装redis

docker pull redis:5.0.14

4. 配置docker-componse

version: "3.8"
services:
  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    restart: always
    privileged: true
    user: root
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/jenkins_home:/var/jenkins_home
      - /root/soft:/root/soft
      - /usr/bin/docker:/usr/bin/docker
      - /etc/docker/daemon.json:/etc/docker/daemon.json
    ports:
      - "8080:8080"
      - "50000:50000"
    environment:
      TZ: Asia/Shanghai
  mysql: # 服务名称
    image: mysql:5.7 # 或其它mysql版本
    container_name: mysql5.7 # 容器名称
    environment:
      - MYSQL_ROOT_PASSWORD=123456 # root用户密码
#      - TZ=Asia/Shanghai # 设置容器时区 我这里通过下面挂载方式同步的宿主机时区和时间了,这里忽略
    volumes:
      - /var/mysql5.7/log:/var/log/mysql # 映射日志目录,宿主机:容器
      - /var/mysql5.7/data:/var/lib/mysql # 映射数据目录,宿主机:容器
      - /var/mysql5.7/conf:/etc/mysql/conf.d # 映射配置目录,宿主机:容器
      - /etc/localtime:/etc/localtime:ro # 让容器的时钟与宿主机时钟同步,避免时间的问题,ro是read only的意思,就是只读。    ports:
      - 3306:3306 # 指定宿主机端口与容器端口映射关系,宿主机:容器
    restart: always # 容器随docker启动自启
  redis: # 服务名称
    image: redis:5.0.14 # redis镜像版本
    container_name: redis5 # 容器名称
    ports:
      - 6379:6379 # 指定宿主机端口与容器端口映射关系,宿主机:容器
    volumes:
      - /var/redis5/conf/redis.conf:/etc/redis/redis.conf # 映射配置文件目录,宿主机:容器
      - /var/redis5/data:/data # 映射数据目录,宿主机:容器
    restart: always # 容器开机自启
    privileged: true # 获取宿主机root权限
    command: ["redis-server","/etc/redis/redis.conf"] # 指定配置文件启动redis-server进程

 5. 启动docker-componse

docker-compose up -d

二、脚本准备

参考项目目录:

1. Jenkinsfile

#!groovy
pipeline {

	agent any

	environment {
		// 应用名称
		APP_NAME = 'xxx-server'
		// 应用部署路径(注意些自己的jenkins挂载目录)
		APP_DEPLOY_BASE_DIR = '/var/jenkins_home/workspace/'
		MODULE_NAME = "$project_name";
	}


	stages {
		stage('打印信息') {      //打印信息
		steps {
			echo '变量打印信息'
			echo "Project_Pipeline_name: $JOB_NAME"
			echo "workspace: $WORKSPACE"
			echo "branch: $branch_name"           //gitlab分支名
			echo "build_id: $BUILD_ID"
		}
	}
	stage('检出') {
		steps {
			git credentialsId: '0d104c96-f1bf-4992-97b2-6b70d6c938e4', url: 'https://gitee.com/xxx/xxx-framework.git',
			branch: "$branch_name"
		}
	}

	stage('构建') {
		steps {
			sh 'mvn clean package -Dmaven.test.skip=true'
		}
	}

	stage('部署') {
		steps {
			sh 'cp -f ' + ' script/shell/deploy.sh ' + "${env.APP_DEPLOY_BASE_DIR}" + "${env.APP_NAME}"+'/'
			sh 'cp -f ' + "${env.APP_NAME}" + '/target/*.jar ' + "${env.APP_DEPLOY_BASE_DIR}" + "${env.APP_NAME}" +'/build/'
			archiveArtifacts "${env.APP_NAME}" + '/target/*.jar'
			sh 'chmod +x ' + "${env.APP_DEPLOY_BASE_DIR}" + "${env.APP_NAME}" + '/deploy.sh'
			sh 'bash ' + "${env.APP_DEPLOY_BASE_DIR}" + "${env.APP_NAME}" + '/deploy.sh'
		}
	}
}
}

2. deploy.sh

#!/bin/bash
set -e

DATE=$(date +%Y%m%d%H%M)
# 基础路径
BASE_PATH=/var/jenkins_home/workspace/hbintrade-server
# 编译后 jar 的地址。部署时,Jenkins 会上传 jar 包到该目录下
SOURCE_PATH=$BASE_PATH/build
# 服务名称。同时约定部署服务的 jar 包名字也为它。
SERVER_NAME=hbintrade-server
# 环境
PROFILES_ACTIVE=dev
# 健康检查 URL
HEALTH_CHECK_URL=http://xxxx:48080/actuator/health/

# heapError 存放路径
HEAP_ERROR_PATH=$BASE_PATH/heapError
# JVM 参数
JAVA_OPS="-Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$HEAP_ERROR_PATH"

# SkyWalking Agent 配置
#export SW_AGENT_NAME=$SERVER_NAME
#export SW_AGENT_COLLECTOR_BACKEND_SERVICES=192.168.0.84:11800
#export SW_GRPC_LOG_SERVER_HOST=192.168.0.84
#export SW_AGENT_TRACE_IGNORE_PATH="Redisson/PING,/actuator/**,/admin/**"
#export JAVA_AGENT=-javaagent:/work/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar

# 备份
function backup() {
    # 如果不存在,则无需备份
    if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then
        echo "[backup] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过备份"
    # 如果存在,则备份到 backup 目录下,使用时间作为后缀
    else
        echo "[backup] 开始备份 $SERVER_NAME ..."
        cp $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar
        echo "[backup] 备份 $SERVER_NAME 完成"
    fi
}

# 最新构建代码 移动到项目环境
function transfer() {
    echo "[transfer] 开始转移 $SERVER_NAME.jar"

    # 删除原 jar 包
    if [ ! -f "$BASE_PATH/$SERVER_NAME.jar" ]; then
        echo "[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过删除"
    else
        echo "[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成"
        rm $BASE_PATH/$SERVER_NAME.jar
    fi

    # 复制新 jar 包
    echo "[transfer] 从 $SOURCE_PATH 中获取 $SERVER_NAME.jar 并迁移至 $BASE_PATH ...."
    cp $SOURCE_PATH/$SERVER_NAME.jar $BASE_PATH

    echo "[transfer] 转移 $SERVER_NAME.jar 完成"
}

# 停止:优雅关闭之前已经启动的服务
function stop() {
    echo "[stop] 开始停止 $BASE_PATH/$SERVER_NAME"
    PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}')
    # 如果 Java 服务启动中,则进行关闭
    if [ -n "$PID" ]; then
        # 正常关闭
        echo "[stop] $BASE_PATH/$SERVER_NAME 运行中,开始 kill [$PID]"
        kill -15 $PID
        # 等待最大 120 秒,直到关闭完成。
        for ((i = 0; i < 120; i++))
            do
                sleep 1
                PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}')
                if [ -n "$PID" ]; then
                    echo -e ".\c"
                else
                    echo "[stop] 停止 $BASE_PATH/$SERVER_NAME 成功"
                    break
                fi
		    done

        # 如果正常关闭失败,那么进行强制 kill -9 进行关闭
        if [ -n "$PID" ]; then
            echo "[stop] $BASE_PATH/$SERVER_NAME 失败,强制 kill -9 $PID"
            kill -9 $PID
        fi
    # 如果 Java 服务未启动,则无需关闭
    else
        echo "[stop] $BASE_PATH/$SERVER_NAME 未启动,无需停止"
    fi
}

# 启动:启动后端项目
function start() {
    # 开启启动前,打印启动参数
    echo "[start] 开始启动 $BASE_PATH/$SERVER_NAME"
    echo "[start] JAVA_OPS: $JAVA_OPS"
    echo "[start] JAVA_AGENT: $JAVA_AGENT"
    echo "[start] PROFILES: $PROFILES_ACTIVE"

    echo "[docker] 构建镜像"
    docker stop $SERVER_NAME || true
    docker rm $SERVER_NAME || true
    docker rmi $SERVER_NAME || true
    docker build -t $SERVER_NAME:latest ./hbintrade-server/
    echo "[docker] 构建镜像完成"
    docker run -d -p 48080:48080 $SERVER_NAME

    echo "[docker] 项目启动完成"
    # 开始启动
#    echo "[start] 开始启动 BUILD_ID=dontKillMe nohup java -server $JAVA_OPS -jar $BASE_PATH/$SERVER_NAME.jar --spring.profiles.active=$PROFILES_ACTIVE &"
#    BUILD_ID=dontKillMe nohup java -server $JAVA_OPS -jar $BASE_PATH/$SERVER_NAME.jar --spring.profiles.active=$PROFILES_ACTIVE &
#    echo "[start] 启动 $BASE_PATH/$SERVER_NAME 完成"
}

# 健康检查:自动判断后端项目是否正常启动
function healthCheck() {
    # 如果配置健康检查,则进行健康检查
    if [ -n "$HEALTH_CHECK_URL" ]; then
        # 健康检查最大 120 秒,直到健康检查通过
        echo "[healthCheck] 开始通过 $HEALTH_CHECK_URL 地址,进行健康检查";
        for ((i = 0; i < 120; i++))
            do
                # 请求健康检查地址,只获取状态码。
                result=`curl -I -m 10 -o /dev/null -s -w %{http_code} $HEALTH_CHECK_URL || echo "000"`
                # 如果状态码为 200,则说明健康检查通过
                if [ "$result" == "200" ]; then
                    echo "[healthCheck] 健康检查通过";
                    break
                # 如果状态码非 200,则说明未通过。sleep 1 秒后,继续重试
                else
                    echo -e ".\c"
                    sleep 1
                fi
            done

        # 健康检查未通过,则异常退出 shell 脚本,不继续部署。
        if [ ! "$result" == "200" ]; then
            echo "[healthCheck] 健康检查不通过,可能部署失败。查看日志,自行判断是否启动成功";
            tail -n 10 nohup.out
            exit 1;
        # 健康检查通过,打印最后 10 行日志,可能部署的人想看下日志。
        else
            tail -n 10 nohup.out
        fi
    # 如果未配置健康检查,则 sleep 120 秒,人工看日志是否部署成功。
    else
        echo "[healthCheck] HEALTH_CHECK_URL 未配置,开始 sleep 120 秒";
        sleep 120
        echo "[healthCheck] sleep 120 秒完成,查看日志,自行判断是否启动成功";
        tail -n 50 nohup.out
    fi
}

# 部署
function deploy() {
    cd $BASE_PATH
    # 备份原 jar
#    backup
    # 停止 Java 服务
    stop
    # 部署新 jar
#    transfer
    # 启动 Java 服务
    start
    # 健康检查
#    healthCheck
}

deploy

3. Dockerfile

在server模块下

## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
## 感谢复旦核博士的建议!灰子哥,牛皮!
FROM eclipse-temurin:8-jre

## 创建目录,并使用它作为工作目录
RUN mkdir -p /hbintrade-server
WORKDIR /hbintrade-server
## 将后端项目的 Jar 文件,复制到镜像中
COPY ./target/hbintrade-server.jar app.jar

## 设置 TZ 时区
ENV TZ=Asia/Shanghai
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
ENV JAVA_OPTS="-Xms512m -Xmx512m -Djava.security.egd=file:/dev/./urandom"

## 应用参数
ENV ARGS=""

## 暴露后端项目的 48080 端口
EXPOSE 48080

## 启动后端项目
CMD java ${JAVA_OPTS} -jar app.jar $ARGS --spring.profiles.active=dev

三、Jenkins流水线配置

下载插件Extended Choice Parameter Plugin

新增版本号参数

流水线选择代码里面的Jenkinsfile相对路径

选择需要的分支构建任务

四、问题 

1. 多模块打包失败,不会自动打包依赖的其他模块

解决:flatten-maven-plugin统一版本打包失败问题记录-CSDN博客

2. 华为云公网IP访问不通

解决:华为云服务器公网ip访问不通解决-CSDN博客

参考:

  • Jenkins流水线--部署多模块maven项目(推荐) - 简书 (jianshu.com)
  • 使用docker-compose部署Redis(单机部署)_docker-compose redis单机-CSDN博客
  • 使用docker-compose 部署 MySQL(所有版本通用)_docker compose mysql-CSDN博客
  • 若依微服务集群搭建及jenkins自动化集群部署_若衣微服务版jekens-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2207391.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ffmpeg面向对象——rtsp拉流探索(1)

目录 1.tcp创建及链接的流程图及对象图2.解析 标准rtsp协议的基石是tcp&#xff0c;本节探索下ffmpeg的rtsp拉流协议tcp的socket创建及链接。 1.tcp创建及链接的流程图及对象图 tcp创建及链接的流程图&#xff0c;如下&#xff1a; tcp创建及链接的对象图&#xff0c;如下&…

QD1-P15 HTML 文本标签(textarea、label)

本节学习 HTML 常用标签&#xff1a;textarea和label ‍ 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p15 ‍ 知识点1&#xff1a;textarea标签的用途 可输入多行文本的控件 cols属性&#xff1a; 文本的可见宽度 rows属性&#xff1a; 文本的可见行数 HTML示例 &l…

Map父接口

通过API可以详细查看Map接口包含的具体方法。重点的方法包括&#xff1a; 案例一&#xff1a;Map接口的基本使用 package com.map;import java.util.HashMap; import java.util.Map; import java.util.Set;/*** Map接口的使用* 特点&#xff1a;存储键值对&#xff1b;键不能重…

【数据结构】零碎知识点(易忘 / 易错)总结回顾

一、数据结构的概念 数据结构&#xff08;Data Structure&#xff09;是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素的集合。 二、算法 算法&#xff08;Algorithm&#xff09;就是定义良好的计算过程&#xff0c;它取一个或一组的值为…

Python、R语言Lasso、Ridge岭回归、XGBoost分析Airbnb房屋数据:旅游市场差异、价格预测|数据分享...

全文链接&#xff1a;https://tecdat.cn/?p37839 分析师&#xff1a;Kefan Yu 在大众旅游蓬勃发展的背景下&#xff0c;乡村旅游已成为推动乡村经济、社会和文化发展的关键力量。当前&#xff0c;乡村旅游接待设施主要以招待所、小宾馆和农家乐等形式存在。然而&#xff0c;一…

二叉树改良版——AVL树

为什么说是“改良”&#xff0c;其实标题的二叉树指的是搜索二叉树&#xff0c;它虽然可以缩短查找的效率&#xff0c;但如果数据已经有序或接近有序的话二叉树就会退化成单支树&#xff0c;这样查找元素的话反而会效率低下。因此&#xff0c;为了解决这个问题&#xff0c;AVL树…

zynq 添加lwip库

在自己的项目属性中. 就是在上一行的下面加了一行配置. 多了个 -llwip4 -Wl,--start-group,-lxil,-llwip4,-lgcc,-lc,--end-group

第十四届单片机嵌入式蓝桥杯

一、CubeMx配置 &#xff08;1&#xff09;LED配置 &#xff08;1&#xff09;LED灯里面用到了SN74HC573ADWR锁存器&#xff0c;这个锁存器有一个LE引脚,这个是我们芯片的锁存引脚&#xff08;使能引脚&#xff09;&#xff0c;由PD2这个端口来控制的 &#xff08;2&#xff…

Qt初识_通过代码创建hello world

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 Qt初识_通过代码创建hello world 收录于专栏【Qt开发】 本专栏旨在分享学习Qt的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1.通过按…

魔珐出席INSIGHT金融洞察力峰会,共探AI内容生成新范式

2024年9月27日&#xff0c;2024INSIGHT金融洞察力在北京举行&#xff0c;来自银行、保险、期货、证券、基金等行业的业界翘楚&#xff0c;共商行业热点议题&#xff0c;为金融行业增进互信、扩大合作搭建闭门平台&#xff0c;贡献价值与力量。 魔珐科技AIGC业务负责人杜子航&a…

XUbuntu安装OpenSSH远程连接服务器

目录 打开终端。更新你的包索引安装OpenSSH服务器。在终端中输入以下命令&#xff1a;安装完成后&#xff0c;OpenSSH服务器会自动启动。查看主机 IP测试连接打开 cmd 终端SSH 连接虚拟机确认连接输入连接密码发现问题修改用户&#xff0c;尝试连接 打开终端。 更新你的包索引 …

候机时间计算(数学小题目,练习时间字符串“解析”)

时间字符串的简单处理&#xff0c;可自行解析也可以调库。 (笔记模板由python脚本于2024年10月10日 18:06:42创建&#xff0c;本篇笔记适合有基本编程逻辑的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“…

MinIO 学习订阅服务

MinIO 的入门非常简单 — 只需几个简单的命令和一个 100 MB 的小二进制文件&#xff0c;您就可以立即启动并运行一个功能性开发环境。但是&#xff0c;为了在生产规模上利用 MinIO 的全部功能&#xff0c;我们鼓励专业人士更多地了解 MinIO 的广泛功能。我们推出了 MinIO 学习订…

Spring Boot课程问答:技术难题专家解答

摘要 随着信息互联网信息的飞速发展&#xff0c;无纸化作业变成了一种趋势&#xff0c;针对这个问题开发一个专门适应师生交流形式的网站。本文介绍了课程答疑系统的开发全过程。通过分析企业对于课程答疑系统的需求&#xff0c;创建了一个计算机管理课程答疑系统的方案。文章介…

企业远控私有化部署解决方案-内信互联

内信互联&#xff08;DoLink&#xff09;&#xff0c;是点量软件新推出的企业私有化远程控制系统解决方案。很多朋友对这个产品还不是很了解&#xff0c;今天点量小编就对其基础功能做一些详细说明&#xff0c;如果您想快速拥有自己的企业私有远程控制系统&#xff0c;欢迎联系…

xavier 在tensorflow pytorch中的应用,正太分布和均匀分布的计算公式不一样

Xavier初始化&#xff0c;也被称为Glorot初始化&#xff0c;是一种用于深度神经网络的权重初始化方法。这种方法是由Xavier Glorot和Yoshua Bengio在2010年的论文《Understanding the difficulty of training deep feedforward neural networks》中提出的。Xavier初始化的主要目…

bpmn-js 元素与布局渲染

BPMN-JS 是基于 BPMN 2.0来定义元素关联关系,并通过Diagram-js库来实现web可视化的显示和编辑工作。Diagram-js 也是由BPMN.IO组织开发的一个专门用于业务流程建模符号(BPMN)的可视化开源 JavaScript 库。 元素(Elements) BPMN 2.0(Business Process Model and Notation…

Windows docker 部署MiGPT+ 本地Ollama

1. 下载 MiGPT https://github.com/idootop/mi-gpt https://github.com/idootop/mi-gpt/releases/tag/v4.2.0 2. 运行 Ollama qwen模型 3.配置Mi GPT .env .migpt.js 运行docker 运行 需要上网 docker run -d --env-file D:\LLM\mi-gpt-4.2.0\.env -v D:\LLM\mi-gpt-4.2.0…

【读书笔记·VLSI电路设计方法解密】问题12:制造MOSFET晶体管的主要工艺步骤是什么

VLSI芯片是在半导体材料上制造的,这种材料的导电性介于绝缘体和导体之间。通过一种称为掺杂的工艺引入杂质,可以改变半导体的电气特性。能够在半导体材料的细小且定义明确的区域内控制导电性,促使了半导体器件的发展。结合更简单的无源元件(电阻、电容和电感),这些器件被…

3D汽车动画:技术、应用与行业影响

3D汽车动画&#xff0c;凭借其逼真的可视化效果和动态功能&#xff0c;已成为汽车行业展示创新和技术实力的重要工具。它不仅能够细致地呈现产品功能&#xff0c;还能模拟复杂的驾驶场景&#xff0c;帮助客户全面了解汽车的性能和设计。3D汽车动画的应用不仅加强了汽车设计展示…