
使用pipeline在docker容器中部署SpringBoot项目
Jenkins创建项目
-
创建一个流水线项目
-
配置代码仓库为gitee仓库
-
配置gitee webhook
-
先保存jenkins的配置,然后去gitee上进行配置
-
-
配置流水线
jenkins上的配置到这里就完成了,现在准备springboot项目的Jenkinsfile配置。
Jenkinsfile配置
实现Jenkinsfile语法提示
在springboot项目的项目根目录下新建Jenkinsfile文件
然后配置一下Jenkinsfile的语法提示:
-
配置IDEA识别Jenkinsfile文件为Groovy语法。
-
请求jenkins接口获取jenkins pipeline的一些语法规则
http://192.168.181.105:10880/job/spring-boot-template/pipeline-syntax/gdsl
-
在任意一个模块src.main.java目录下新建
pipeline.gdsl
文件,然后把刚才从接口请求的内容copy进去。
注意:从 Jenkins 获取的 gdsl
文件可能存在一些未自动补全的字段
编写Jenkinsfile文件
首先贴一份Jenkinsfile配置
//file:noinspection GroovyAssignabilityCheck
pipeline {
// 指定任务在哪个集群节点中执行
agent any
// 声明全局变量,方便后面使用
environment {
// 应用名称
APP_NAME = 'SpringBootTemplate'
// 应用版本
APP_VERSION = "1.0.0"
// 打包后,需要部署服务的jar名称
JAR_NAME = 'module-template'
// JAR URL
JAR_URL = 'module-template/target'
// 启动参数
// SERVER_OPTS ="-Xmx512m -Xms64m"
// SERVER_PARAMS ="--spring.profiles.active=docker --server.port=8080"
SERVER_OPTS = ''
SERVER_PARAMS = ''
}
stages {
// 构建jar
stage('Build') {
agent {
docker {
image 'maven:3.6.3-slim'
args '-v /root/.m2:/root/.m2'
}
}
steps {
// 运行打包脚本
sh 'sh ./jenkins/scripts/build.sh'
// 暂存jar包,避免不同agent获取不到文件
stash includes: "${env.JAR_URL}/*.jar", name: 'jar'
}
}
// 测试阶段
stage('Test') {
steps {
sh 'sh ./jenkins/scripts/test.sh'
}
}
// 部署容器
stage('Deploy') {
environment {
IMAGE_NAME = 'spring_boot_template'
IMAGE_VERSION = '1.0.0'
MAIN_SERVER_PORT = '8899'
EMS_SERVER_PORT = '9898'
}
steps {
// 获取Build stage构件的Jar包
unstash 'jar'
sh 'sh ./jenkins/scripts/deploy.sh'
}
post {
failure {
echo '部署失败'
}
}
}
}
post {
always {
echo 'Always'
}
success {
echo "Success"
}
failure {
echo 'Failure'
}
}
}
Jenkinsfile实现流程大致为,首先进行 Jar 包的构建,并将其打包成 Docker 镜像,然后将镜像运行在宿主机的 Docker 容器上
编写构建阶段的Build Stage
-
首先定义两个环境变量
JAR_NAME,JAVA_URL
,用于 Maven 进行打包时使用environment { JAR_NAME = 'module-template' JAR_URL = 'module-template/target' }
-
定义Buidl Stage,使用Maven容器进行项目构建
// 构建jar stage('Build') { agent { docker { image 'maven:3.6.3-slim' // 将maven仓库目录挂载到宿主机中,方便复用。 args '-v /root/.m2:/root/.m2' } } }
由于我们的 Jenkins 是运行于宿主机的 Docker 上的,并且在运行时指定了
docker.sock
文件的映射,因此构建阶段运行的 Maven 容器是运行在宿主机上的,相当于在宿主机上运行docker run -v /root/.m2:/root/.m2 maven:3.6.3-slim
,同时映射宿主机的本地 Maven 仓库,以便于复用依赖。 -
指定运行时步骤
steps
,定义build.sh
脚本,把所有指令放在脚本里执行。steps { // 运行打包脚本 sh 'sh ./jenkins/scripts/build.sh' // 暂存jar包,避免不同agent获取不到文件 stash includes: "${env.JAR_URL}/*.jar", name: 'jar' }
-
编写
build.sh
脚本。# 构建 Jar 包,跳过测试 mvn -B -DskipTests clean package
build.sh
进行的工作很简单,只是对项目进行打包。
编写测试阶段Test Stage
-
编写
test.sh
脚本# test echo "已执行测试脚本"
在这里可以对代码做一些测试,Jenkins 也支持对测试的结果进行展示,具体可以查看官方文档
-
编辑
Jenkinsfile
文件// 测试阶段 stage('Test') { steps { sh 'sh ./jenkins/scripts/test.sh' } }
编写部署阶段Deploy Stage
-
新增部署阶段环境变量
stage('Deploy') { environment { // 镜像名称 IMAGE_NAME = 'spring_boot_template' // 镜像版本号 IMAGE_VERSION = '1.0.0' // SpringBoot项目所使用的端口,我这里使用的netty,所以占用了两个 MAIN_SERVER_PORT = '8899' EMS_SERVER_PORT = '9898' // 启动参数 // SERVER_OPTS ="-Xmx512m -Xms64m" // SERVER_PARAMS ="--spring.profiles.active=docker --server.port=8080" SERVER_OPTS = '' SERVER_PARAMS = '' // 应用名称 APP_NAME = 'SpringBootTemplate' // 应用版本 APP_VERSION = "1.0.0" } }
用于指定镜像名,镜像版本,服务运行的端口,应用名称,应用版本
-
定义
Deploy Stage
stage('Deploy') { steps { // 获取Build stage构件的Jar包 unstash 'jar' sh 'sh ./jenkins/scripts/deploy.sh' } post { failure { echo '部署失败' } } }
定义
steps
,首先从暂存中获取Build Stage
阶段构建的 Jar 包,然后运行deploy.sh
脚本。post
可以根据不同的运行结果进行不同的响应,这里如果部署失败的话,打印 部署失败,可以使用 email 进行告警,具体可以查看清理和通知。post { failure { mail to: 'team@example.com', subject: "Failed Pipeline: ${currentBuild.fullDisplayName}", body: "Something is wrong with ${env.BUILD_URL}" } }
-
定义Dockerfile
本项目的Dockerfile比较复杂,因为其中包含视频转码,需要使用ffmpeg。
## 基础镜像 FROM aurorxa/oracle-jdk:8 ## 维护人信息 MAINTAINER junpzx<junpzx@163.com> ## 创建文件夹 RUN mkdir -p /mnt/workspace/app ## 设置工作目录 WORKDIR /mnt/workspace/app ## 往镜像中复制 ARG JAR_NAME=${JAR_NAME} ARG MAIN_SERVER_PORT=${MAIN_SERVER_PORT} ARG EMS_SERVER_PORT=${EMS_SERVER_PORT} COPY ${JAR_NAME}.jar app.jar ## 安装EasyMedia需要的资源 RUN yum -y install libxcb libx11-xcb1 libxss1 libasound2 libxkbfile1 alsa-lib-devel \ && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' >/etc/timezone ## 暴露端口 EXPOSE ${MAIN_SERVER_PORT} EXPOSE ${EMS_SERVER_PORT} # 环境变量 # docker run -e JAVA_OPTS="-Xmx512m -Xms64m" -e PARAMS="--spring.profiles.active=docker --server.port=8080" xxx ENV TZ=Asia/Shanghai ENV JAVA_OPTS="" ENV PARAMS="" ## 执行命令 ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar app.jar $PARAMS" ]
一般来说,以下Dockerfile可以满足大部分需求。
## 基础镜像 FROM moxm/java:1.8-full ## 维护人信息 MAINTAINER junpzx<junpzx@163.com> ## 创建文件夹 RUN mkdir -p /mnt/workspace/app ## 设置工作目录 WORKDIR /mnt/workspace/app ## 往镜像中复制 ARG JAR_NAME=${JAR_NAME} ARG MAIN_SERVER_PORT=${MAIN_SERVER_PORT} ARG EMS_SERVER_PORT=${EMS_SERVER_PORT} COPY ${JAR_NAME}.jar app.jar ## 暴露端口 EXPOSE ${MAIN_SERVER_PORT} EXPOSE ${EMS_SERVER_PORT} # 环境变量 # docker run -e JAVA_OPTS="-Xmx512m -Xms64m" -e PARAMS="--spring.profiles.active=docker --server.port=8080" xxx ENV TZ=Asia/Shanghai ENV JAVA_OPTS="" ENV PARAMS="" ## 执行命令 ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar app.jar $PARAMS" ]
在项目目录下,创建
docker/Dockerfile
文件,用于构建镜像。 -
编写
deploy.sh
脚本# 复制Jar到docker目录 cp "${JAR_URL}/${JAR_NAME}.jar" "docker/${JAR_NAME}.jar" # 构建镜像 docker build --build-arg JAR_NAME="${JAR_NAME}" --build-arg EMS_SERVER_PORT="${EMS_SERVER_PORT}" --build-arg MAIN_SERVER_PORT="${MAIN_SERVER_PORT}" -t "${IMAGE_NAME}:${IMAGE_VERSION}" ./docker # 删除旧容器 containerId=$(docker ps -f name="${APP_NAME}-${APP_VERSION}" -aq) if [ "${containerId}" != "" ]; then docker rm -f "${containerId}" fi # 运行新容器 docker run --restart=always -dp "${MAIN_SERVER_PORT}:${MAIN_SERVER_PORT}" -p "${EMS_SERVER_PORT}:${EMS_SERVER_PORT}" -e JAVA_OPTS="${SERVER_OPTS}" -e PARAMS="${SERVER_PARAMS}" --name "${APP_NAME}-${APP_VERSION}" "${IMAGE_NAME}:${IMAGE_VERSION}" # 判断容器运行情况,未运行则抛出异常 docker ps -f name="${APP_NAME}-${APP_VERSION}" containerId=$(docker ps -f name="${APP_NAME}-${APP_VERSION}" -q) if [ "${containerId}" = "" ]; then exit 42 fi
-
首先,复制从
Build Stage
构建的 Jar 包到docker
目录下 -
开始构建镜像,镜像名从环境变量中获取,同时传递构建参数
JAR_NAME
,EMS_SERVER_PORT
,MAIN_SERVER_PORT
,指定上下文为docker
目录 -
根据容器名称获取 Docker 中运行的旧容器id,删除旧容器
-
运行新容器,
${MAIN_SERVER_PORT}:${MAIN_SERVER_PORT}
,${EMS_SERVER_PORT}:${EMS_SERVER_PORT}
映射运行端口,"${APP_NAME}-${APP_VERSION}"
指定容器名称,JAVA_OPTS="${SERVER_OPTS}
传递java配置,PARAMS="${SERVER_PARAMS}
传递项目配置。 -
判断容器运行情况,未运行则抛出异常,终止流水线进行异常告警
Jenkinsfile 到这里就编写完了,接下来可以尝试运行流水线。
-
完整文件
Dockerfile
项目根目录 docker/Dockerfile
## 基础镜像
FROM aurorxa/oracle-jdk:8
## 维护人信息
MAINTAINER junpzx<junpzx@163.com>
## 创建文件夹
RUN mkdir -p /mnt/workspace/app
## 设置工作目录
WORKDIR /mnt/workspace/app
## 往镜像中复制
ARG JAR_NAME=${JAR_NAME}
ARG MAIN_SERVER_PORT=${MAIN_SERVER_PORT}
ARG EMS_SERVER_PORT=${EMS_SERVER_PORT}
COPY ${JAR_NAME}.jar app.jar
## 安装EasyMedia需要的资源
RUN yum -y install libxcb libx11-xcb1 libxss1 libasound2 libxkbfile1 alsa-lib-devel \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo 'Asia/Shanghai' >/etc/timezone
## 暴露端口
EXPOSE ${MAIN_SERVER_PORT}
EXPOSE ${EMS_SERVER_PORT}
# 环境变量
# docker run -e JAVA_OPTS="-Xmx512m -Xms64m" -e PARAMS="--spring.profiles.active=docker --server.port=8080" xxx
ENV TZ=Asia/Shanghai
ENV JAVA_OPTS=""
ENV PARAMS=""
## 执行命令
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar app.jar $PARAMS" ]
build.sh
项目根目录 docker/jenkins/scripts/build.sh
mvn -B -DskipTests clean package
deploy.sh
项目根目录 docker/jenkins/scripts/deploy.sh
# 复制Jar到docker目录
cp "${JAR_URL}/${JAR_NAME}.jar" "docker/${JAR_NAME}.jar"
# 构建镜像
docker build --build-arg JAR_NAME="${JAR_NAME}" --build-arg EMS_SERVER_PORT="${EMS_SERVER_PORT}" --build-arg MAIN_SERVER_PORT="${MAIN_SERVER_PORT}" -t "${IMAGE_NAME}:${IMAGE_VERSION}" ./docker
# 删除旧容器
containerId=$(docker ps -f name="${APP_NAME}-${APP_VERSION}" -aq)
if [ "${containerId}" != "" ]; then
docker rm -f "${containerId}"
fi
# 运行新容器
docker run --restart=always -dp "${MAIN_SERVER_PORT}:${MAIN_SERVER_PORT}" -p "${EMS_SERVER_PORT}:${EMS_SERVER_PORT}" -e JAVA_OPTS="${SERVER_OPTS}" -e PARAMS="${SERVER_PARAMS}" --name "${APP_NAME}-${APP_VERSION}" "${IMAGE_NAME}:${IMAGE_VERSION}"
# 判断容器运行情况,未运行则抛出异常
docker ps -f name="${APP_NAME}-${APP_VERSION}"
containerId=$(docker ps -f name="${APP_NAME}-${APP_VERSION}" -q)
if [ "${containerId}" = "" ]; then
exit 42
fi
test.sh
项目根目录 docker/jenkins/scripts/test.sh
echo '已执行测试脚本'