基于Java+Spring Boot开源项目JeeSite的Jenkins持续交付介绍
一、实战项目介绍- JeeSite
- 基于Spring Boot 2.0
- 数据存储MySQL
- 语言:Java
- 规模大小:适中,适合初学者
源码地址:https://gitee.com/thinkgem/jeesite4 ,感谢原作者
本次项目演练地址:https://gitee.com/sfboy/iJeeSite4 ,在原作者的项目上,增加了Pipline持续集成的脚本,便于实战使用
二、环境准备
Spring Boot项目运行配置:MySQL+Java+Maven
MySQL安装参考:https://www.cnblogs.com/feng0815/p/14041558.html
Maven安装参考: https://www.cnblogs.com/feng0815/p/14354226.html
Java安装参考:自行百度。。
三、项目配置、调试、运行
3.1 初始化
- 打开 JeeSite4/web/src/main/resources/config/application.yml,填写数据库配置(地址、IP端口、用户名、密码)
- 进入项目路径 JeeSite4/web/bin,运行初始化脚本程序init-data.sh(Windows下运行init-data.bat)
初始化脚本用来创建初始数据,初始化前要保证数据库中有jeesite数据库,否则会造成无法连接jeesite数据库,初始化失败
开始执行
执行结束
3.2 配置项目
3.2.1 使用IDE编译
- 添加JeeSite4/root/pom.xml Maven根节点到IDE的maven view中,其余依赖module会根据配置一起导入
Maven编译、打包
在Maven view 中选中JeeSite Root下Lifecycle中的clean + install,然后单击运行按钮
3.2.2 在命令行编译
进入到JeeSite4/root目录下,运行mvn clean install
3.3 运行项目demo
3.3.1 在IDE运行
展开JeeSite Web → Plugins → spring-boot → spring-boot:run ,单击运行
运行成功
访问项目:http://localhost:8980/a/login
输入用户名system,密码admin,点击登录
3.3.2 在命令行运行
进入到JeeSite4/web目录下,运行:mvn clean spring-boot:run -Dmaven.test.skip=true
(Dmaven.test.skip=true,不运行单元测试)
运行成功
四、Tomcat部署持续交互实战
4.1 Tomcat运行和配置入门
下载,官网:https://tomcat.apache.org/
直接解压到安装目录即可
Tomcat基本配置
- 启动:bin/startup.sh
- 停止:bin/shutdown.sh
- 配置文件:conf/server.xml(配置端口号)
- 日志:logs/catalina.out
4.2 使用Jenkins Pipline任务部署Spring应用到Tomcat
4.2.1 Jenkins参数配置,设置Pipline脚本中所需的参数
创建Jenkins Pipline任务
设置代码分支,默认分支为master
4.2.2 配置Pipline脚本
运行,构建任务
构建成功,打开项目页面:http://localhost:8080/a/login
Pipline代码如下(Scripted Pipeline):
node('master') { stage('同步源码') { git([url: 'https://gitee.com/sfboy/iJeeSite4.git', branch: '${branch}']) } stage('maven编译打包') { sh ''' . ~/.bash_profile export pwd=`pwd` export os_type=`uname` cd web/src/main/resources/config if [[ "${os_type}" == "Darwin" ]]; then sed -i "" "s/mysql_ip/${mysql_ip}/g" application.yml sed -i "" "s/mysql_port/${mysql_port}/g" application.yml sed -i "" "s/mysql_user/${mysql_user}/g" application.yml sed -i "" "s/mysql_pwd/${mysql_pwd}/g" application.yml else sed -i "s/mysql_ip/${mysql_ip}/g" application.yml sed -i "s/mysql_port/${mysql_port}/g" application.yml sed -i "s/mysql_user/${mysql_user}/g" application.yml sed -i "s/mysql_pwd/${mysql_pwd}/g" application.yml fi cd $pwd/root mvn clean install -Dmaven.test.skip=true cd $pwd/web mvn clean package spring-boot:repackage -Dmaven.test.skip=true -U ''' } stage('停止 tomcat') { sh ''' ## 停止tomcat的函数, 参数$1带入tomcat的路径$TOMCAT_PATH killTomcat() { pid=`ps -ef|grep $1|grep java|awk '{print $2}'` echo "tomcat Id list :$pid" if [ "$pid" = "" ] then echo "no tomcat pid alive" else kill -9 $pid fi } ## 停止Tomcat killTomcat $tomcat_home ''' } stage('清理环境') { sh ''' ## 删除原有war包 rm -f $tomcat_home/webapps/ROOT.war rm -rf $tomcat_home/webapps/ROOT ''' } stage('部署新的war包') { sh ''' cp web/target/web.war $tomcat_home/webapps/ cd $tomcat_home/webapps mv web.war ROOT.war ''' } stage('启动tomcat') { sh ''' JENKINS_NODE_COOKIE=dontkillme cd $tomcat_home/bin sh startup.sh ''' } }
五、docker部署持续交付实战
Pipline脚本(Declarative Pipeline)
pipeline { agent { label 'master' } environment { docker_image_name = 'jeesite4' docker_container_name = 'iJeesite4' PATH = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin" } parameters { string(name: 'branch', defaultValue: 'master', description: 'Git branch') } stages{ stage('同步源码') { steps { git url:'https://gitee.com/sfboy/iJeeSite4.git', branch:'$branch' } } stage('设定配置文件'){ steps{ sh ''' . ~/.bash_profile export os_type=`uname` cd ${WORKSPACE}/web/bin/docker if [[ "${os_type}" == "Darwin" ]]; then sed -i "" "s/mysql_ip/${mysql_docker_ip}/g" application-prod.yml sed -i "" "s/mysql_port/${mysql_port}/g" application-prod.yml sed -i "" "s/mysql_user/${mysql_user}/g" application-prod.yml sed -i "" "s/mysql_pwd/${mysql_pwd}/g" application-prod.yml else sed -i "s/mysql_ip/${mysql_docker_ip}/g" application-prod.yml sed -i "s/mysql_port/${mysql_port}/g" application-prod.yml sed -i "s/mysql_user/${mysql_user}/g" application-prod.yml sed -i "s/mysql_pwd/${mysql_pwd}/g" application-prod.yml fi ''' } } stage('Maven 编译'){ steps { sh ''' . ~/.bash_profile cd ${WORKSPACE}/root mvn clean install -Dmaven.test.skip=true cd ${WORKSPACE}/web mvn clean package spring-boot:repackage -Dmaven.test.skip=true -U ''' } } stage('停止 / 删除 现有Docker Container/Image '){ steps { script{ try{ sh 'docker stop $docker_container_name' }catch(exc){ echo "The container $docker_container_name does not exist" } try{ sh 'docker rm $docker_container_name' }catch(exc){ echo "The container $docker_container_name does not exist" } try{ sh 'docker rmi $docker_image_name' }catch(exc){ echo "The docker image $docker_image_name does not exist" } } } } stage('生成新的Docker Image'){ steps { sh ''' . ~/.bash_profile cd ${WORKSPACE}/web/bin/docker rm -f web.war cp ${WORKSPACE}/web/target/web.war . docker build -t $docker_image_name . ''' } } stage('启动新Docker实例'){ steps { sh ''' docker run -d --name $docker_container_name -p 8981:8980 $docker_image_name ''' } } } }
六、生产与测试环境分离持续交付实战
需要在Pipline脚本中区分环境,具体脚本如下
pipeline { agent { label 'master' } environment { docker_image = 'jeesite4' docker_container = 'iJeesite4' PATH = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin" } parameters { string(name: 'branch', defaultValue: 'master', description: 'Git branch') choice(name: 'env', choices: ['prod', 'qa'], description: 'Environment Type') } stages{ stage('同步源码') { steps { git url:'https://gitee.com/sfboy/iJeeSite4.git', branch:'${branch}' } } stage('设定配置文件'){ steps{ sh ''' . ~/.bash_profile if [[ "${env}" == "prod" ]]; then export mysql_ip=${mysql_prod_ip} export mysql_port=${mysql_prod_port} else export mysql_ip=${mysql_qa_ip} export mysql_port=${mysql_qa_port} fi export os_type=`uname` cd ${WORKSPACE}/web/bin/docker if [[ "${os_type}" == "Darwin" ]]; then sed -i "" "s/mysql_ip/${mysql_ip}/g" application-${env}.yml sed -i "" "s/mysql_port/${mysql_port}/g" application-${env}.yml sed -i "" "s/mysql_user/${mysql_user}/g" application-${env}.yml sed -i "" "s/mysql_pwd/${mysql_pwd}/g" application-${env}.yml sed -i "" "s/<env>/${env}/g" Dockerfile-param else sed -i "s/mysql_ip/${mysql_ip}/g" application-${env}.yml sed -i "s/mysql_port/${mysql_port}/g" application-${env}.yml sed -i "s/mysql_user/${mysql_user}/g" application-${env}.yml sed -i "s/mysql_pwd/${mysql_pwd}/g" application-${env}.yml sed -i "s/<env>/${env}/g" Dockerfile-param fi ''' } } stage('Maven 编译'){ steps { sh ''' . ~/.bash_profile cd ${WORKSPACE}/root mvn clean install -Dmaven.test.skip=true cd ${WORKSPACE}/web mvn clean package spring-boot:repackage -Dmaven.test.skip=true -U ''' } } stage('停止 / 删除 现有Docker Container/Image '){ steps { script{ try{ sh 'docker stop $docker_container-$env' }catch(exc){ echo "The container $docker_container-${params.env} does not exist" } try{ sh 'docker rm $docker_container-$env' }catch(exc){ echo "The container $docker_container-${params.env} does not exist" } try{ sh 'docker rmi $docker_image-$env' }catch(exc){ echo "The docker image $docker_image-${params.env} does not exist" } } } } stage('生成新的Docker Image'){ steps { sh ''' cd ${WORKSPACE}/web/bin/docker rm -f web.war cp ${WORKSPACE}/web/target/web.war . docker build -t $docker_image-$env -f Dockerfile-param . ''' } } stage('启动新Docker实例'){ steps { sh ''' if [[ "$env" == "prod" ]]; then export port="8888" else export port="8811" fi docker run -d --name $docker_container-$env -p $port:8980 $docker_image-$env ''' } } } }
dockerfile
FROM frolvlad/alpine-java:jdk8-slim MAINTAINER ThinkGem@163.com ENV TZ "Asia/Shanghai" ENV LANG C.UTF-8 VOLUME /tmp WORKDIR /app ADD web.war . RUN jar -xvf web.war WORKDIR /app/WEB-INF ENV JAVA_OPTS "-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m" ENV JAVA_OPTS "$JAVA_OPTS -Dspring.profiles.active=<env>" ADD application-<env>.yml ./classes/config ENTRYPOINT java -cp /app $JAVA_OPTS org.springframework.boot.loader.WarLauncher EXPOSE 8980
在Jenkins中配置好Pipline脚本中所需的MySQL变量信息
创建Pipline任务,设置Pipline脚本
由于我们第一次执行时,脚本里的parameter参数无法同步到Jenkins是里,所以第一次执行会报错,当我们刷新页面是就会发现参数已经同步了
然后我们选择测试环境,再次build即可
build完成
打开测试环境:http://127.0.0.1:8811/a/login
然后我们再build成产环境
build完成
打开成产环境:http://localhost:8888/a/login
查看生产环境和测试环境的docker容器
ok,完结,撒花~~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2018-02-03 移动端自动化测试-WTF Appium?