Spring Cloud DevOps之旅(二)Jenkins+Git+Docker搭建單機測試環(huán)境

本篇內(nèi)容

  • Spring Cloud 項目結(jié)構(gòu)概覽
  • Dockerfile文件準備
  • Jenkins參數(shù)設(shè)置
  • Jenkins中創(chuàng)建流水線腳本
  • 詳細Groovy Pipeline腳本
  • Docker Compose安裝
  • WebHook觸發(fā)構(gòu)建
  • Git多分支環(huán)境下構(gòu)建
  • Spring Cloud 項目部署Docker中的坑

Spring Cloud 項目結(jié)構(gòu)概覽

我以目前我們公司項目為例,我將Spring Cloud項目分成了如下幾塊

image.png
  1. eureka:服務(wù)發(fā)現(xiàn),端口8761
  2. config:配置中心,端口9118
  3. basic:基礎(chǔ)服務(wù),提供與業(yè)務(wù)沒有關(guān)系的通用service,端口9102
  4. pms:酒店P(guān)MS數(shù)據(jù)回調(diào)service,端口9103
  5. host:后臺基礎(chǔ)信息service,端口9100
  6. business:業(yè)務(wù)系統(tǒng)service,端口9101
  7. gateway:網(wǎng)關(guān),對外暴露端口為443和8088

maven項目模塊化
考慮到項目中的DTO、VO、通用類等在多個service中會使用到,且serivce之間互相調(diào)用使用了Fegin,所以,service中項目結(jié)構(gòu)會是這樣,以host-service為例:

image.png

host中pom.xml分了3個模塊


image.png

模塊之間互相引用需要如下幾步

  1. mvn -Dmaven.test.skip=true -U clean install 構(gòu)建jar包并安裝到本地
  2. mvn install:install-file 使用install-file命令將jar包上傳到本地倉庫
  3. 在pom中添加依賴包,會從本地倉庫獲取指定的包


    image.png

關(guān)于詳細的模塊化步驟,請看此文使用Maven構(gòu)建多模塊項目

Dockerfile文件準備

image.png

創(chuàng)建兩個文件夾,docker-test,docker-prod
docker-test為測試環(huán)境
docker-prod為正式環(huán)境

以測試環(huán)境為例,在docker-test中創(chuàng)建Dockerfile


image.png
FROM hub.c.163.com/library/java:8-alpine

MAINTAINER show

ADD /target/futurekey-host.jar app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

target下存放的是編譯后的項目jar包

Jenkins參數(shù)設(shè)置

為了WebHook能夠?qū)⒄埱髉ush到Jenkins,需要如下設(shè)置


image.png

取消防跨站點請求偽造和允許匿名訪問讀取


image.png
image.png

Jenkins中創(chuàng)建流水線腳本

創(chuàng)建流水線腳本


image.png

勾選觸發(fā)遠程構(gòu)建,身份令牌隨便填,觸發(fā)的url為JENKINS_URL/job/eureka/build?token=TOKEN

image.png

  • JENKINS_URL:jenkins的訪問地址
  • eureka:此處為腳本的名稱
  • TOKEN:輸入身份驗證令牌

Gitlab項目的Webhook輸入jenkins的地址,點擊Add webhook

image.png

測試推送


image.png

顯示201則為成功


image.png

Groovy流水線腳本格式


image.png

基本格式為

#!groovy
pipeline {
    agent any
    environment {
        ENV="test"
    }

    stages {
        stage('test') {
            steps {
                echo "${ENV}"
            }
        }
    }
}
  • environment: 環(huán)境變量,使用時為${xx}格式,xx為變量名
  • stages: 所有流水線的階段,stages中包含多個stage
  • steps: 腳本執(zhí)行步驟

流水線語法


image.png

提供了多個步驟的選擇


image.png

我現(xiàn)在需要生成獲取git clone的腳本

image.png
image.png

點擊生成


image.png

生成的腳本復制到文本,后面會使用到。

大致的腳本執(zhí)行步驟

我的項目前端網(wǎng)頁框架用的是VUE Element UI,所以分了兩個git項目,每次發(fā)布時,只需要將vue編譯后的文件夾copy到網(wǎng)關(guān)的static文件夾下,然后重寫static/index.html為首頁即可。

@Configuration
public class DefaultViewConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("forward:/index.html");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
    }
}
image.png

腳本步驟如下

#!groovy
pipeline {
    agent any
    
    environment {
        REPOSITORY="https://gitee.com/lanzhima-show/project.git"
        WEB_REPOSITORY="https://gitee.com/lanzhima-show/web.git"
        JENKINSDIR="/var/lib/jenkins/workspace/"
        PORJECTDIR="eureka/"
        ENV="test"
        FILEDIR="shell/"
    }

    stages {
        stage('獲取網(wǎng)頁代碼') {
            steps {

            }
        }

        stage('獲取代碼') {
            steps {

            }
        }

        stage('提升shell文件執(zhí)行權(quán)限') {
            steps {

            }
        }

        stage('生成maven jar包') {
            steps {

            }
        }

        stage('編譯項目') {
            steps {

            }
        }

        stage('構(gòu)建docker鏡像') {
            steps {

            }
        }

        stage('docker發(fā)布') {
            steps {

            }
        }
    }
}

詳細Groovy Pipeline腳本

獲取網(wǎng)頁代碼

stage('獲取網(wǎng)頁代碼') {
  echo "start fech code form git:${WEB_REPOSITORY}"
                deleteDir()
                git branch: 'develop', credentialsId: 'd964d8c1-366a-42d4-aff7-b429d43952ec', url: "${WEB_REPOSITORY}"
                sh '''
                    cd ${JENKINSDIR}
                    rm -rf pages
                    mkdir pages
                    cp -rf ${JENKINSDIR}${PORJECTDIR}dist/. ${JENKINSDIR}pages
                '''
}
  1. 刪除原來的文件夾
  2. 從Git克隆代碼
  3. 創(chuàng)建一個pages的文件夾
  4. 將VUE編譯好的dist文件夾下的所有內(nèi)容,復制到pages文件夾中

獲取代碼

stage('獲取代碼') {
          steps {
                echo "start fech code form git:${REPOSITORY}"
                deleteDir()
                git branch: 'develop', credentialsId: 'd964d8c1-366a-42d4-aff7-b429d43952ec', url: "${REPOSITORY}"
                sh '''
                    mv -f ${JENKINSDIR}pages/* ${JENKINSDIR}${PORJECTDIR}project-gateway/src/main/resources/static/
                '''
            }
        }
  1. 刪除原來的文件夾
  2. 從Git克隆代碼
  3. 將pages文件夾下內(nèi)容移動到gateway的static下

提升shell文件執(zhí)行權(quán)限

stage('提升shell文件執(zhí)行權(quán)限') {
            steps {
                sh '''
                    cd ${JENKINSDIR}${PORJECTDIR}${FILEDIR}
                    chmod 755 mvn-build.sh
                    chmod 755 packages-build.sh
                    cd ${JENKINSDIR}${PORJECTDIR}
                    chmod +x docker-compose.yml
                '''
            }
        }

為了能夠執(zhí)行shell腳本,需要賦予權(quán)限

maven編譯的shell腳本,主要作用是編譯可執(zhí)行jar包,將jar包復制到Dockerfile文件夾中,便于后續(xù)docker生成鏡像
mvn-build.sh

#!/bin/bash -ilex

PROJECTDIR=$1
MODULEDIR=$2
ENV=$3
JARNAME=$4
cd ${PROJECTDIR}${MODULEDIR}
mvn -Dmaven.test.skip=true -U clean install
mkdir ${PROJECTDIR}${MODULEDIR}docker-${ENV}/target 
cp -f ${PROJECTDIR}${MODULEDIR}target/${JARNAME} ${PROJECTDIR}${MODULEDIR}docker-${ENV}/target/${JARNAME}
  1. 生成jar包
  2. 創(chuàng)建target文件夾
  3. 復制jar包到target文件夾下

打包jar包前,需要對各項目中的模塊先進行maven打包,可根據(jù)自己的項目進行實際修改
package-build.sh

image.png

Docker Compose編排腳本
經(jīng)過我測試,如果將所有的容器服務(wù)全部放入一個docker-compose.yml中后,由于無法指定啟動的先后順序,并且?guī)讉€service必須依賴于eureka和config后才能啟動,會導致service找不到eureka和config。
所以,我的做法是將整個spring cloud的服務(wù)編排做成兩個編排腳本

第一個docker-compose.yml負責啟動eureka和config
docker-compose.yml

version: "3"
services:
  eureka:
    container_name: eureka
    image: demo/eureka:latest
    ports:
      - "8761:8761"
  
  config:
    image: demo/config:latest
    ports:
      - "9118:9118"
    links:
      - "eureka"
    environment:
      - eureka.client.service-url.defaultZone=http://eureka:8761/eureka/
      - spring.profiles.active=test

指定eureka的container_name為eureka后,就可以通過容器名稱找到eureka服務(wù),所以需要修改環(huán)境變量eureka.client.service-url.defaultZone=http://eureka:8761/eureka/

第二個docker-compose.yml負責把service和gateway拉起
docker-compose-service-test.yml

version: "3"
services:
  basic:
    image: demo/basic:latest
    ports:
      - "9102:9102"
    environment:
      - eureka.client.service-url.defaultZone=http://eureka:8761/eureka/
      - spring.cloud.config.profile=test
  
  pms:
    image: demo/pms:latest
    ports:
      - "9103:9103"
    environment:
      - eureka.client.service-url.defaultZone=http://eureka:8761/eureka/
      - spring.cloud.config.profile=test

  host:
    image: demo/host:latest
    ports:
      - "9100:9100"
    environment:
      - eureka.client.service-url.defaultZone=http://eureka:8761/eureka/
      - spring.cloud.config.profile=test

  business:
    image: demo/business:latest
    ports:
      - "9101:9101"
    environment:
      - eureka.client.service-url.defaultZone=http://eureka:8761/eureka/
      - spring.cloud.config.profile=test

  gateway:
    image: demo/gateway:latest
    ports:
      - "443:443"
      - "8088:8088"
    environment:
      - eureka.client.service-url.defaultZone=http://eureka:8761/eureka/
      - spring.cloud.config.profile=test

  

生成maven jar包

stage('生成maven jar包') {
            steps {
                echo "start build maven package"
                sh "${JENKINSDIR}${PORJECTDIR}${FILEDIR}packages-build.sh ${JENKINSDIR}${PORJECTDIR}"
            }
        }

編譯項目

steps {
                echo "start compile eureka"
                sh "${JENKINSDIR}${PORJECTDIR}${FILEDIR}mvn-build.sh ${JENKINSDIR}${PORJECTDIR} demo-eureka/ ${ENV} demo-eureka.jar"
                echo "start compile config"
                sh "${JENKINSDIR}${PORJECTDIR}${FILEDIR}mvn-build.sh ${JENKINSDIR}${PORJECTDIR} demo-config/ ${ENV} demo-config.jar"
                echo "start compile basic"
                sh "${JENKINSDIR}${PORJECTDIR}${FILEDIR}mvn-build.sh ${JENKINSDIR}${PORJECTDIR} demo-basic/web/ ${ENV} demo-basic.jar"
                echo "start compile pms"
                sh "${JENKINSDIR}${PORJECTDIR}${FILEDIR}mvn-build.sh ${JENKINSDIR}${PORJECTDIR} demo-pms/web/ ${ENV} demo-pms.jar"
                echo "start compile host"
                sh "${JENKINSDIR}${PORJECTDIR}${FILEDIR}mvn-build.sh ${JENKINSDIR}${PORJECTDIR} demo-host/web/ ${ENV} demo-host.jar"
                echo "start compile business"
                sh "${JENKINSDIR}${PORJECTDIR}${FILEDIR}mvn-build.sh ${JENKINSDIR}${PORJECTDIR} demo-business/web/ ${ENV} demo-business.jar"
                echo "start compile gateway"
                sh "${JENKINSDIR}${PORJECTDIR}${FILEDIR}mvn-build.sh ${JENKINSDIR}${PORJECTDIR} demo-gateway/ ${ENV} demo-gateway.jar"
            }

構(gòu)建docker鏡像

steps {
                echo "start build docker images"
                echo "start build eureka image"
                sh '''
                    cd ${JENKINSDIR}${PORJECTDIR}futurekey-eureka/docker-${ENV}
                    docker build -t demo/eureka --rm .
                '''
                echo "start build config image"
                sh '''
                    cd ${JENKINSDIR}${PORJECTDIR}futurekey-config/docker-${ENV}
                    docker build -t demo/config --rm .
                '''
                echo "start build basic image"
                sh '''
                    cd ${JENKINSDIR}${PORJECTDIR}futurekey-basic/web/docker-${ENV}
                    docker build -t demo/basic --rm .
                '''
                echo "start build pms image"
                sh '''
                    cd ${JENKINSDIR}${PORJECTDIR}futurekey-pms/web/docker-${ENV}
                    docker build -t demo/pms --rm .
                '''
                echo "start build host image"
                sh '''
                    cd ${JENKINSDIR}${PORJECTDIR}futurekey-host/web/docker-${ENV}
                    docker build -t demo/host --rm .
                '''
                echo "start build business image"
                sh '''
                    cd ${JENKINSDIR}${PORJECTDIR}futurekey-business/web/docker-${ENV}
                    docker build -t demo/business --rm .
                '''
                echo "start build gateway image"
                sh '''
                    cd ${JENKINSDIR}${PORJECTDIR}futurekey-gateway/docker-${ENV}
                    docker build -t demo/gateway --rm .
                '''
            }

docker發(fā)布

steps {
                echo "start deploy"
                sh '''
                    cd ${JENKINSDIR}${PORJECTDIR}
                    docker-compose down --remove-orphans
                    docker-compose -f docker-compose-service-test.yml down
                    docker-compose up -d
                    sleep 45s
                    docker-compose -f docker-compose-service-test.yml up -d
                    docker images|grep none|awk '{print $3}'|xargs docker rmi
                '''
            }

坑1:目前我沒有找到讓多個docker-compose.yml順序執(zhí)行的方法,所以我設(shè)置了sleep時間等待,45秒足夠讓eureka和config啟動了。

坑2:在docker-compose down --remove-orphans中,如果不加--remove-orphans,則無法刪除依賴的網(wǎng)絡(luò),導致后續(xù)命令執(zhí)行時報錯,從而發(fā)布會中斷。

坑3:我使用docker-compose down后,會產(chǎn)品none的鏡像,所以我最后會刪除none鏡像,docker images|grep none|awk '{print $3}'|xargs docker rmi,不知道有沒有大佬能夠指出我的問題。

Docker Compose安裝

安裝命令

pip install docker-compose

查看版本

docker-compose -version

重啟jenkins

systemctl restart jenkins


WebHook觸發(fā)構(gòu)建

我使用的是免費版的gitee,gitee也支持webhook,在gitee中設(shè)置


image.png

當每次提交之后,都會觸發(fā)構(gòu)建。


image.png

Git多分支環(huán)境下構(gòu)建

現(xiàn)在我們每次push代碼都會觸發(fā)構(gòu)建,但是一般我們會使用GitFlow管理各分支,這時,自動構(gòu)建就需要區(qū)分是哪個分支的了,一般公司都會按照分支構(gòu)建開發(fā)環(huán)境、測試環(huán)境、生成環(huán)境。
我們可以按照GitFlow工作流,將develop指定為測試環(huán)境,master指定為正式環(huán)境。

安裝Generic Webhook Trigger插件

在Jenkins的插件中心搜索webhook,安裝Generic Webhook Trigger插件

image.png

安裝成功后,在編輯腳本中,可以看到新增的Generic Webhook Trigger

image.png

定義兩個參數(shù),ref為提交的分支名稱,project.git_ssh_url為項目的ssh地址

image.png

添加驗證表達式

image.png

webhook地址

webhook地址為http://JENKINS_URL/generic-webhook-trigger/invoke

image.png

添加token參數(shù)作為驗證

image.png

設(shè)置新的webhook地址

image.png

構(gòu)建成功后,能看到各步驟花費了多少時間


image.png

Spring Cloud 項目部署Docker中的坑

  1. 內(nèi)存問題:centos中JVM默認給一個java進程分配的最大內(nèi)存空間是在JAVA_OPTS中設(shè)置的,如果不手動調(diào)整,內(nèi)存小一點的主機會馬上內(nèi)存吃緊。而spring cloud中明顯有多個java項目要執(zhí)行,所以必須手動調(diào)整。
    解決方法:在Dockerfile中添加-Xmx128m的參數(shù),設(shè)置最大內(nèi)存為128mb,你可以根據(jù)自己實際的配置進行調(diào)整。

2.時區(qū)問題:docker中的默認時區(qū)是GMT,與當前時間會相差8小時。
解決方法:在Dockerfile中添加-Duser.timezone=GMT+8

FROM hub.c.163.com/library/java:8-alpine

MAINTAINER show 

ADD /target/host.jar app.jar

ENTRYPOINT ["java","-Xmx128m","-Duser.timezone=GMT+8","-jar","/app.jar"]
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容