基於Java+Spring Boot開源專案JeeSite的Jenkins持續互動介紹

塵世風發表於2021-02-03

一、實戰專案介紹- 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,完結,撒花~~

相關文章