使用 Jenkins 对 springboot 项目进行 docker 镜像一键部署,jenkins + docker + springboot

菠萝🍍的博客 有谁不喜欢吃菠萝呢 本文由博客端 https://www.runnable.run 主动推送

1. 需求背景

现在越来越多的公司进行部署 springboot 项目都是采用 Jenkins 的方式进行部署,可以自动拉取 git 上的项目,然后进行打包成 docker 镜像,部署本地,再推送到公司的镜像仓库。

这样不仅可以对打包的镜像做一个备份,当下一次生产上线的时候,出现严重问题可以及时进行回滚,而且自动化的部署,可以让开发和测试的沟通更加畅快。再也不用让测试催开发说,你这个 bug 怎么还没修复,开发总是会说:修复了,还在打包部署。而传统方式的打包总是会花 10 来分钟,甚至更长。

从传统流程:修复 bug-> 验证本地 bug 是否修复-> 打成 jar/war/image-> 上传服务器/仓库-> 替换启动/pull image

到现在的自动化:修复 bug-> 验证本地 bug 是否修复->Jenkins 自动构建

不得不说还是非常的方便。

这篇博客主要讲述,一个 springboot 项目如果使用 jenkins+docker 进行一键部署。

2. 开始前的准备

环境:

3. 通过 docker 安装 Jenkins

官方文档说明:

https://www.jenkins.io/doc/book/installing/docker/

这里将重点部分抽离出来,只需要较少的几步就可以完成 Jenkins 的镜像构建以及安装。

3.1 Jenkins 的自定义镜像构建

在你的工作空间下,新建文件名为 Dockerfile 的文件,并且输入以下字符用来构建 Jenkins 镜像

FROM jenkins/jenkins:2.289.3-lts-jdk11
USER root
RUN apt-get update && apt-get install -y apt-transport-https \
       ca-certificates curl gnupg2 \
       software-properties-common
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN apt-key fingerprint 0EBFCD88
RUN add-apt-repository \
       "deb [arch=amd64] https://download.docker.com/linux/debian \
       $(lsb_release -cs) stable"
RUN apt-get update && apt-get install -y docker-ce-cli
USER jenkins
RUN jenkins-plugin-cli --plugins "blueocean:1.24.7 docker-workflow:1.26"

在当前文件夹下打开命令行

docker build -t myjenkins-blueocean:1.1 .

不要忘了上面 👆 这个命令最后又个 “.”

这一个相当长时间的过程,可以先进行 SpringBoot 项目的对应操作

镜像构建完成之后,你可以通过 docker images 看到刚刚已经构建完成的镜像

3.2 创建 Jenkins 容器

sudo docker run -u root -d  -p 8080:8080  -p 50000:50000  -v /var/run/docker.sock:/var/run/docker.sock    myjenkins-blueocean:1.1

4. 阿里云配置个人容器镜像服务

为什么不使用 dockerhub 的仓库是因为个人版只有公开仓库,这样并不是一个好的选择。

登录阿里云之后,进行搜索 容器镜像服务 即可找到

image.png

4.1 设置密码

在个人版中,访问凭证中,设置 固定密码

image.png

4.2 仓库管理中,创建命名空间

image.png

4.3 仓库管理中,创建镜像仓库

image.png

下一步,选择本地仓库

image.png

返回到刚刚创建的列表,点击进入刚刚创建的仓库,当中的公网地址,就是我们之后需要推送到阿里云仓库的参数

image.png

5. SpringBoot 项目添加 Dockerfile,Jenkinsfile,以及 sh 脚本

官方文档操作:使用 Maven 构建 Java 应用程序

5.1 添加 Dockerfile 文件

为了完成这一步,我们需要编写 Dockerfile,如下所示:

FROM openjdk:8-alpine

# PROJECT_NAME 填写你的项目名字
ENV PROJECT_NAME learn-1.4.1
# PROJECT_HOME 构建成镜像之后,存放的目录位置
ENV PROJECT_HOME /usr/local/${PROJECT_NAME}

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
RUN mkdir $PROJECT_HOME && mkdir $PROJECT_HOME/logs

ARG JAR_FILE
COPY ${JAR_FILE} $PROJECT_HOME

ENTRYPOINT /usr/bin/java -jar -Xms1536m -Xmx1536m $PROJECT_HOME/$PROJECT_NAME.jar

Dockerfile 的详细用法可以参考:Docker Dockerfile | 菜鸟教程

将这个文件放置于项目的根目录下

image.png

5.2 引入 maven docker 镜像编译插件

5.2.1 maven 的 pom.xml 文件的 properties

    <properties>
        <docker.registry.publish.url>registry.cn-hangzhou.aliyuncs.com</docker.registry.publish.url>
        <docker.namespace>namespace</docker.namespace>
        <docker.profile>dev</docker.profile>
    </properties>

其中

5.2.2 maven 的 pom.xml 文件的 plugin

  	<!-- spring-boot-maven-plugin使得springboot项目能打成jar包进行启动 -->
          <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>run.runnable.learn.LearnApplication</mainClass>
                    <layout>ZIP</layout>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <!--docker 镜像编译插件-->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>dockerfile-maven-plugin</artifactId>
                <version>1.4.10</version>
                <dependencies>
                    <dependency>
                        <groupId>javax.activation</groupId>
                        <artifactId>activation</artifactId>
                        <version>1.1.1</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>default</id>
                        <goals>
                            <goal>build</goal>
                            <goal>push</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <repository>${docker.registry.publish.url}/${docker.namespace}/${project.build.finalName}-${docker.profile}</repository>
                    <tag>${project.version}</tag>
                    <buildArgs>
                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
                    </buildArgs>
                </configuration>
            </plugin>

5.3 Jenkinsfile 文件编写

参考资料:

官方文档 | 使用 Jenkinsfile

官方文档 | 流水线语法

Jenkins 官方文档 |Maven 构建 Java 应用

在项目根目录下创建文件:Jenkinsfile

pipeline {
    agent none
    environment {
        deploy_tag = '1.4.1'
        profile='dev'
    }
    stages {
        stage('Build') {
            agent {
                docker {
                    image 'maven:3-alpine'
                    args '-v /root/.m2:/root/.m2'
                }
            }
            steps {
                sh 'mvn clean install -DskipTests=true -P${profile} \
                && mvn clean'
            }
        }
        stage('Deploy') {
            agent any
            steps {
                sh 'sh deploy_docker.sh restart ${deploy_tag} ${profile}'
            }
        }
        stage('Push') {
            agent any
            steps {
                sh 'sh deploy_docker.sh push ${deploy_tag} ${profile}'
            }
        }
    }
}

在这段脚本中,

定义了三个阶段:Build Deploy Push

这里的 image 参数(参考 agent 章节的 docker 参数) 是用来下载 maven:3-apline Docker 镜像 (如果你的机器还没下载过它)并将该镜像作为单独的容器运行。这意味着:* 你将在 Docker 中本地运行相互独立的 Jenkins 和 Maven 容器。

Maven 容器成为了 Jenkins 用来运行你的流水线项目的 agent。 这个容器寿命很短——它的寿命只是你的流水线的执行时间。

这里的 args 参数在暂时部署的 Maven Docker 容器的 /root/.m2 (即 Maven 仓库)目录 和 Docker 主机文件系统的对应目录之间创建了一个相互映射。这背后的实现细节超出了本教程的范围,在此不做解释。 但是,这样做的主要原因是,在 Maven 容器的生命周期结束后,构建 Java 应用程序所需的工件 (Maven 在流水线执行时进行下载)能够保留在 Maven 存储库中。这避免了在后续的流水线执行过程中, Maven 反复下载相同的工件。请注意,不同于你为 jenkins-data 创建的 Docker 数据卷,Docker 主机的文件系统在每次重启 Docker 时都会被清除。 这意味着每次 Docker 重新启动时,都会丢失下载的 Maven 仓库工件。

  执行 deploy_docker.sh 中的 restart 方法

执行 deploy_docker.sh 中的 push 方法

5.4 deploy_docker.sh 脚本编写

这个脚本是用来控制打包好的 docker image 一系列操作,包括 push remove stop start 等等

#!/bin/bash

#项目占用端口
APP_PORT=8081
# 项目名字
APP_NAME=learn-1.4.1
APP_HOME=/home/admin/${APP_NAME}
APP_OUT=${APP_HOME}/logs

PROG_NAME=$0
ACTION=$1
TAGNAME=$2
PROFILE=$3

# 阿里云仓库命名空间
APP_NAMESPACE=workspace
# 推送至阿里云仓库的地址
APP_REGISURL=registry.cn-hangzhou.aliyuncs.com
APP_RESP=${APP_REGISURL}/${APP_NAMESPACE}/${APP_NAME}-${PROFILE}
# 阿里云账号
APP_USERNAME=
# 阿里云配置个人容器镜像服务时,设置的密码
APP_PASSWORD=

mkdir -p ${APP_HOME}
mkdir -p ${APP_HOME}/logs

usage() {
    echo "Usage: $PROG_NAME {start|stop|restart|push}"
    exit 2
}

start_application() {
    echo "starting java process"
    docker run --log-opt max-size=1024m --log-opt max-file=3 --security-opt seccomp:unconfined -dit --name ${APP_NAME} -p ${APP_PORT}:${APP_PORT} --pid=host -v ${APP_OUT}:/usr/local/${APP_NAME}/logs ${APP_RESP}:${TAGNAME}
    echo "started java process"
}

stop_application() {
    echo "ending java process"
    docker stop ${APP_NAME} && docker rm ${APP_NAME}
    echo "ended java process"
}

push_application() {
    echo "starting image push"
    docker login --username=${APP_USERNAME} --password=${APP_PASSWORD} ${APP_REGISURL}
    echo "docker tag ${APP_RESP}:${TAGNAME} ${APP_RESP}:${TAGNAME}"
    echo "docker push ${APP_RESP}:${TAGNAME}"

    #docker tag 用于给镜像打标签
    docker tag ${APP_RESP}:${TAGNAME} ${APP_RESP}:${TAGNAME}
    # 推送到个人的阿里云仓库
    docker push ${APP_RESP}:${TAGNAME}
    docker images|grep ${APP_RESP}|grep none|awk '{print $3 }'|xargs docker rmi
    echo "ended image push"
}

rmi_application() {
    docker rmi ${APP_RESP}:${TAGNAME}
}

start() {
    start_application
}
stop() {
    stop_application
}
push() {
    push_application
}
rmi() {
    rmi_application
}

case "$ACTION" in
    start)
        start
    ;;
    stop)
        stop
    ;;
    restart)
        stop
        start
    ;;
    slaveRestart)
        stop
        rmi
        start
    ;;
    push)
        push
    ;;
    *)
        usage
    ;;
esac

6. Jenkins 配置

在安装完 Jenkins 之后,这里我们使用 Jenkins 的默认推荐安装,没有安装自定义的功能

我们要对 Jenkins 创建流水线,对以上的操作进行配置

参考资料:Jenkins 官方文档 |Maven 构建 Java 应用

image.png

6.1 general

image.png

6.2 构建触发器

image.png

6.3 流水线

image.png

这里需要提一下,git 的话,在截图中的 Credentials 中配置时需要注意

image.png

6.4 立即构建

image.png

成功后,可以看到 linux 上已经启动该服务,并且阿里云上镜像仓库已经存在你推送的镜像

image.png

  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    420 引用 • 874 回帖 • 227 关注
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    49 引用 • 37 回帖
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    842 引用 • 907 回帖 • 237 关注

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...