前言
版本介绍:
- Jenkins :2.504.1 LTS
- JDK:使用JDK21运行jenkins,JDK8跑程序应用
- maven:3.9.9
该文档分为两种模式进行部署
- 使用原始的Linux(Centos)本机上部署Jenkins
- 使用Docker中的docker-compoe部署Jenkins
Linux 部署
文件准备
解压命令
tar -zxvf apache-maven-3.9.9-bin.tar.gz
tar -zxvf openjdk-21.0.1_linux-x64_bin.tar.gz
mv settings.xml ./apache-maven-3.9.9/conf
设置maven环境变量(如果已装请跳过)
# 编辑配置文件
vim /etc/profile
# 在末尾追加
export MAVEN_HOME=/usr/local/jenkins/apache-maven-3.9.9/
export PATH=${PATH}:${MAVEN_HOME}/bin
# 使配置文件生效
source /etc/profile
# 测试
mvn -v
安装git(如果已装请跳过)
yum -y install git-core
启动jenkins
sh run_jenkins.sh
Docker 安装
文件准备
Docker-compose
Dockerfile
# 第一阶段:构建工具镜像
FROM eclipse-temurin:21-jdk as builder
USER root
# 创建工作目录并设置权限
RUN mkdir -p /var/jenkins_home && chmod -R 777 /var/jenkins_home
# 安装JDK8
COPY OpenJDK8U-jdk.tar.gz /tmp/
RUN mkdir -p /usr/lib/jvm && \
tar -xzf /tmp/OpenJDK8U-jdk.tar.gz -C /usr/lib/jvm/ && \
rm /tmp/OpenJDK8U-jdk.tar.gz && \
update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk8u392-b08/bin/java 1000 && \
update-alternatives --set java /usr/lib/jvm/jdk8u392-b08/bin/java
# 安装Maven
COPY apache-maven-3.6.3-bin.tar.gz /tmp/
COPY settings.xml /tmp/
RUN tar -xzf /tmp/apache-maven-3.6.3-bin.tar.gz -C /usr/share/ && \
mv /usr/share/apache-maven-3.6.3 /usr/share/maven && \
mkdir -p /usr/share/maven/conf && \
cp /tmp/settings.xml /usr/share/maven/conf/ && \
rm -rf /tmp/*
# 第二阶段:运行时镜像
FROM eclipse-temurin:21-jdk
USER root
# 复制构建产物
COPY --from=builder /usr/lib/jvm/jdk8u392-b08 /usr/lib/jvm/jdk8
COPY --from=builder /usr/share/maven /usr/share/maven
COPY jenkins.war /opt/jenkins.war
# 安装系统依赖
RUN apt-get update && \
apt-get install -y git curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# 配置环境变量
ENV JAVA_HOME=/usr/lib/jvm/jdk8 \
JAVA21_HOME=/opt/java/openjdk \
MAVEN_HOME=/usr/share/maven \
JENKINS_HOME=/var/jenkins_home \
PATH="/usr/share/maven/bin:/usr/lib/jvm/jdk8/bin:/opt/java/openjdk/bin:$PATH"
# 初始化目录结构(关键修复)
RUN mkdir -p /var/jenkins_home/war/META-INF && \
chown -R root:root /var/jenkins_home && \
chmod -R 755 /var/jenkins_home
# 容器配置
EXPOSE 8080 50000
HEALTHCHECK --interval=30s --timeout=5s \
CMD curl -fsS http://localhost:8080/login > /dev/null || exit 1
# 关键修复:使用绝对路径替代环境变量引用
CMD ["/opt/java/openjdk/bin/java", "-Djava.awt.headless=true", "-jar", "/opt/jenkins.war", "--httpPort=8080", "--webroot=/var/jenkins_home/war"]
docker-compose.yml
version: '3.8'
services:
jenkins:
build:
context: .
dockerfile: Dockerfile
container_name: jenkins
restart: unless-stopped
ports:
- "8080:8080"
- "50000:50000"
volumes:
- ./jenkins_home:/var/jenkins_home
- ./m2_repo:/home/jenkins/.m2
- /var/run/docker.sock:/var/run/docker.sock
# 挂载自定义settings.xml(可覆盖默认配置)
- ./settings.xml:/usr/share/maven/ref/settings.xml
environment:
- TZ=Asia/Shanghai
- MAVEN_CONFIG=/home/jenkins/.m2 # Maven配置路径
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/login"]
interval: 30s
timeout: 5s
retries: 3
volumes:
jenkins_home:
m2_repo:
启动jenkins
docker compose up -d
插件
中文插件
Locale
内置SSH服务器
Publish Over SSH
资源监控
Monitoring
界面美化
Blue Ocean
代码管理
Git、Maven Integration、SCM-Manager
离线安装
plugins.jenkins.io/ui/search/?…
系统配置
配置邮箱
- 系统管理员地址:用户名@163.com
- 邮件通知:
-
- SMTP服务器:smtp.163.com
- SMTP认证
-
-
- 用户名:用户名@163.com
- 密码:XXXXXXXX
-
-
- SMTP端口:25
- 回复地址:用户名@163.com
- 字符集:UTF-8
- 测试发件邮箱:用户名@qq.com
SSH Server
添加服务器
Name:10.0.110.167
Hostname:10.0.110.167
Username:root
Remote Directory:/
Password:XXXXXXXX
全局配置
Maven 配置
默认 settings 提供
/usr/share/maven/conf/settings.xml
MAVEN_HOME
/usr/share/maven
Git配置
Name
Default
Path to Git executable
/usr/bin/git
JDK配置
Name
JDK1.8
JAVA_HOME
/usr/lib/jvm/jdk8
设置全局凭证
Gitee账号
用户名:bdmcom
私人令牌:XXXXXXX
新建任务(基于MAVEN)
- 选择Maven项目
- 源码管理
-
- 仓库地址:gitee.com/bdmcom/spri…
- 添加Giee凭证
- 选择分支:*/master
- 触发器
-
- 选择轮询SCM:*/1 * * * *
- 环境
-
- 在构建开始之前,通过SSH发生文件或者执行命令
-
-
- 选择目标服务器
- 设置远程目录:/usr/local/services/04system/temp
- 执行命令:
- cd /usr/local/services/04system/temp/logs
- echo "[(whoami), host:(basename $0), pid:$$] - 构建开始" >> jenkins.log
-
-
- 在构建开始之后,通过SSH发生文件或者执行命令
-
-
- 选择目标服务器
- 设置远程目录:/usr/local/services/04system/temp
- 执行命令:
- cd /usr/local/services/04system/temp/logs
- echo "[(whoami), host:(basename $0), pid:$$] - 构建结束" >> jenkins.log
-
- 构建
-
- 根POM:pom.xml
- 目标和选择:clean install -Dmaven.test.skip=true
- 高级
-
-
- MAVEN_OPTS:-Dmaven.test.skip=true
- 配置文件路径:/usr/share/maven/conf/settings.xml
-
- 构建成功后运行
-
- 选择目标服务器
- 源文件:**/*.jar
- 删除前缀:/target
- 执行命令:
- cd /usr/local/services/04system/temp
- sh start_application.sh restart
- cd /usr/local/services/04system/temp/logs
- echo "[(whoami), host:(basename $0), pid:$$] - 构建中" >> jenkins.log
- cd /usr/local/services/04system/temp
- sh build_docker.sh
- docker compose up -d
脚本(参考)
启动 java jar 包脚本
config.sh
# config.sh
# JDK所在路径
JAVA_HOME="/usr/local/java"
# jar所在的路径
APP_PATH="/usr/local/services/04system/"
# 加载静态参数
APP_MAINCLASS="dcim-system.jar"
# Java虚拟机启动参数
JAVA_OPTS="-Xms128m -Xmx512m"
# 日志文件路径
LOG_FILE="${APP_PATH}/logs/start_application.log"
start_application.sh
#!/bin/sh
#该脚本为Linux下启动java程序的通用脚本。即可以作为开机自启动service脚本被调用,
#也可以作为启动java程序的独立脚本来使用。
#
#Author: yangpeng
#
#Warning:该脚本stop部分使用系统kill命令来强制终止指定的java程序进程。
#在杀死进程前,未作任何条件检查。在某些情况下,如程序正在进行文件或数据库写操作,
#可能会造成数据丢失或数据不完整。如果必须要考虑到这类情况,则需要改写此脚本,
#增加在执行kill命令前的一系列检查。
#
###################################
#加载配置文件
CONFIG_FILE="./config.sh"
if [ ! -f "$CONFIG_FILE" ]; then
echo "Configuration file not found: $CONFIG_FILE"
exit 1
fi
# 加载配置文件
. "$CONFIG_FILE"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" >> "$LOG_FILE"
}
print_status() {
local message="$1"
local status="$2"
echo "$message"
log "$message"
if [ "$status" -eq 0 ]; then
echo "[OK]"
else
echo "[Failed]"
fi
}
###################################
#(函数)判断程序是否已启动
#
#说明:
#使用JDK自带的JPS命令及grep命令组合,准确查找pid
#jps 加 l 参数,表示显示java的完整包路径
#使用awk,分割出pid ($1部分),及Java程序名称($2部分)
###################################
#初始化psid变量(全局)
psid=0
checkpid() {
javaps="$($JAVA_HOME/bin/jps -l | grep "$APP_MAINCLASS")"
if [ -n "$javaps" ]; then
psid=$(echo "$javaps" | awk '{print $1}')
else
psid=0
fi
}
###################################
#(函数)启动程序
#
#说明:
#1. 首先调用checkpid函数,刷新$psid全局变量
#2. 如果程序已经启动($psid不等于0),则提示程序已启动
#3. 如果程序没有被启动,则执行启动命令行
#4. 启动命令执行后,再次调用checkpid函数
#5. 如果步骤4的结果能够确认程序的pid,则打印[OK],否则打印[Failed]
#注意:echo -n 表示打印字符后,不换行
#注意: "nohup 某命令 >/dev/null 2>&1 &" 的用法
###################################
start() {
checkpid
if [ $psid -ne 0 ]; then
print_status "warn: $APP_MAINCLASS already started! (pid=$psid)" 0
else
log "Starting $APP_MAINCLASS ..."
nohup $JAVA_HOME/bin/java -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9057 -jar $JAVA_OPTS "$APP_PATH/$APP_MAINCLASS" >/dev/null 2>&1 &
checkpid
if [ $psid -ne 0 ]; then
print_status "Started successfully (pid=$psid)" 0
else
print_status "Failed to start" 1
fi
fi
}
###################################
#(函数)停止程序
#
#说明:
#1. 首先调用checkpid函数,刷新$psid全局变量
#2. 如果程序已经启动($psid不等于0),则开始执行停止,否则,提示程序未运行
#3. 使用kill -9 pid命令进行强制杀死进程
#4. 执行kill命令行紧接其后,马上查看上一句命令的返回值: $?
#5. 如果步骤4的结果$?等于0,则打印[OK],否则打印[Failed]
#6. 为了防止java程序被启动多次,这里增加反复检查进程,反复杀死的处理(递归调用stop)。
#注意:echo -n 表示打印字符后,不换行
#注意: 在shell编程中,"$?" 表示上一句命令或者一个函数的返回值
###################################
stop() {
checkpid
if [ $psid -ne 0 ]; then
log "Stopping $APP_MAINCLASS ...(pid=$psid)"
kill -9 $psid
local kill_status=$?
if [ $kill_status -eq 0 ]; then
print_status "Stopped successfully" 0
else
print_status "Failed to stop" 1
fi
checkpid
if [ $psid -ne 0 ]; then
stop
fi
else
print_status "warn: $APP_MAINCLASS is not running" 0
fi
}
###################################
#读取脚本的第一个参数($1),进行判断
#参数取值范围:{start|stop|restart|status|info}
#如参数不在指定范围之内,则打印帮助信息
###################################
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
checkpid
if [ $psid -ne 0 ]; then
print_status "$APP_MAINCLASS is running (pid=$psid)" 0
else
print_status "$APP_MAINCLASS is not running" 0
fi
;;
info)
echo "JAVA_HOME: $JAVA_HOME"
echo "APP_PATH: $APP_PATH"
echo "APP_MAINCLASS: $APP_MAINCLASS"
echo "JAVA_OPTS: $JAVA_OPTS"
;;
*)
echo "Usage: $0 {start|stop|restart|status|info}"
exit 1
;;
esac
exit 0
Dockerfile
# 使用OpenJDK 8的Alpine JRE镜像(仅运行环境,体积更小)
FROM openjdk:8-jre-alpine
# 合并时区设置与清理命令,减少层数[1,2](@ref)
RUN apk add --no-cache tzdata \
&& ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& rm -rf /var/cache/apk/*
# 设置工作目录和统一环境变量
WORKDIR /app
ENV SERVER_PORT=4430 JAVA_OPTS=""
# 分阶段复制文件以利用缓存[1,5](@ref)
COPY application.yml ./config/
COPY dcim-system.jar ./
# 统一暴露端口与应用端口[7,8](@ref)
EXPOSE ${SERVER_PORT}
# 启动命令显式指定端口(覆盖配置)
ENTRYPOINT exec java ${JAVA_OPTS} -Dserver.port=${SERVER_PORT} -jar dcim-system.jar
构建镜像
#!/bin/sh
docker compose down
docker rmi dcim-system:1.0.0
docker build -t dcim-system:1.0.0 .
docker-compose.yml
version: '3.8'
services:
dcim-system:
image: dcim-system:1.0.0
container_name: dcim-system
restart: unless-stopped
ports:
- "4430:4430"
volumes:
- ./logs:/app/logs # 保留 SELinux 标签配置
environment:
- JAVA_OPTS=-Xms256m -Xmx1536m
networks:
- backend_network
healthcheck: # 健康检查机制保留
test: ["CMD", "curl", "-f", "http://localhost:4430/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging: # 日志管理策略保留
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
backend_network: # 网络隔离配置保留
driver: bridge
attachable: true
镜像上传到Harbor
#!/bin/bash
# 设置Harbor仓库配置(根据实际环境修改)
HARBOR_URL="10.0.110.XXX:8858" # Harbor服务器地址
HARBOR_USER="root" # 用户名
HARBOR_PASS="XXXXXXX" # 密码
HARBOR_PROJECT="dcim" # 目标项目名称
# 设置镜像配置
IMAGE_NAME="dcim-system" # 镜像名称(与Dockerfile对应)
TAG_NAME=$(date +%Y%m%d%H%M%S) # 自动生成时间戳标签(避免使用latest)
# 步骤1:构建Docker镜像
echo "正在构建镜像 ${IMAGE_NAME}:${TAG_NAME}..."
docker build -t ${IMAGE_NAME}:${TAG_NAME} .
# 检查构建结果
if [ $? -ne 0 ]; then
echo "[错误] 镜像构建失败,请检查Dockerfile"
exit 1
fi
# 步骤2:登录Harbor仓库
echo "登录Harbor仓库 ${HARBOR_URL}..."
docker login ${HARBOR_URL} -u ${HARBOR_USER} -p ${HARBOR_PASS}
if [ $? -ne 0 ]; then
echo "[错误] Harbor登录失败,请检查:"
echo "- 网络连通性"
echo "- /etc/docker/daemon.json是否配置insecure-registries[8](@ref)"
exit 1
fi
# 步骤3:标记镜像(符合Harbor命名规范[7,11](@ref))
HARBOR_IMAGE="${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${TAG_NAME}"
docker tag ${IMAGE_NAME}:${TAG_NAME} ${HARBOR_IMAGE}
# 步骤4:推送镜像
echo "正在推送 ${HARBOR_IMAGE}..."
docker push ${HARBOR_IMAGE}
if [ $? -eq 0 ]; then
echo "推送成功!"
echo "镜像地址:${HARBOR_IMAGE}"
# 清理临时标签
docker rmi ${HARBOR_IMAGE} 2>/dev/null
echo "已清理临时标签"
else
echo "[错误] 推送失败,可能原因:"
echo "- 项目${HARBOR_PROJECT}不存在[2](@ref)"
echo "- 用户无项目权限"
exit 1
fi