目录
[1. 搭建配置Jenkins (在上一篇基础上进行)](#1. 搭建配置Jenkins (在上一篇基础上进行))
[2. 安装mysql](#2. 安装mysql)
[3. 安装redis](#3. 安装redis)
[4. 配置docker-componse](#4. 配置docker-componse)
[5. 启动docker-componse](#5. 启动docker-componse)
[1. Jenkinsfile](#1. Jenkinsfile)
[3. Dockerfile](#3. Dockerfile)
[1. 多模块打包失败,不会自动打包依赖的其他模块](#1. 多模块打包失败,不会自动打包依赖的其他模块)
[2. 华为云公网IP访问不通](#2. 华为云公网IP访问不通)
一、环境准备
1. 搭建配置Jenkins (在上一篇基础上进行)
Jenkins入门:从搭建到部署第一个Springboot项目(踩坑记录)-CSDN博客
2. 安装mysql
bash
docker pull mysql:5.7
3. 安装redis
bash
docker pull redis:5.0.14
4. 配置docker-componse
bash
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
bash
docker-compose up -d
二、脚本准备
参考项目目录:
1. Jenkinsfile
bash
#!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
bash
#!/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模块下
bash
## 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博客