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

本篇內(nèi)容

  • Spring Cloud 項目結(jié)構(gòu)概覽
  • Dockerfile文件準(zhǔn)備
  • Jenkins參數(shù)設(shè)置
  • Jenkins中創(chuàng)建流水線腳本
  • 詳細(xì)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)于詳細(xì)的模塊化步驟帜讲,請看此文使用Maven構(gòu)建多模塊項目

Dockerfile文件準(zhǔn)備

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ā)遠(yuǎn)程構(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

生成的腳本復(fù)制到文本盏触,后面會使用到。

大致的腳本執(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 {

            }
        }
    }
}

詳細(xì)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)容辨嗽,復(fù)制到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包復(fù)制到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. 復(fù)制jar包到target文件夾下

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

image.png

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

第一個docker-compose.yml負(fù)責(zé)啟動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負(fù)責(zé)把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ò)回怜,導(dǎo)致后續(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

當(dāng)每次提交之后,都會觸發(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默認(rèn)給一個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中的默認(rèn)時區(qū)是GMT件炉,與當(dāng)前時間會相差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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市斟冕,隨后出現(xiàn)的幾起案子口糕,更是在濱河造成了極大的恐慌,老刑警劉巖磕蛇,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件景描,死亡現(xiàn)場離奇詭異,居然都是意外死亡秀撇,警方通過查閱死者的電腦和手機超棺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捌袜,“玉大人,你說我怎么就攤上這事炸枣÷驳龋” “怎么了弄唧?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長霍衫。 經(jīng)常有香客問我候引,道長,這世上最難降的妖魔是什么敦跌? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任澄干,我火速辦了婚禮,結(jié)果婚禮上柠傍,老公的妹妹穿的比我還像新娘麸俘。我一直安慰自己,他們只是感情好惧笛,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布从媚。 她就那樣靜靜地躺著,像睡著了一般患整。 火紅的嫁衣襯著肌膚如雪拜效。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天各谚,我揣著相機與錄音紧憾,去河邊找鬼。 笑死昌渤,一個胖子當(dāng)著我的面吹牛赴穗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播愈涩,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼望抽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了履婉?” 一聲冷哼從身側(cè)響起煤篙,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎毁腿,沒想到半個月后辑奈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡已烤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年鸠窗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胯究。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡稍计,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出裕循,到底是詐尸還是另有隱情臣嚣,我是刑警寧澤净刮,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站硅则,受9級特大地震影響淹父,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜怎虫,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一暑认、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧大审,春花似錦蘸际、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至酷愧,卻和暖如春驾诈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背溶浴。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工乍迄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人士败。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓闯两,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谅将。 傳聞我的和親對象是個殘疾皇子漾狼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容