本篇內(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項目分成了如下幾塊
- eureka:服務(wù)發(fā)現(xiàn),端口8761
- config:配置中心乃正,端口9118
- basic:基礎(chǔ)服務(wù)隔崎,提供與業(yè)務(wù)沒有關(guān)系的通用service组去,端口9102
- pms:酒店P(guān)MS數(shù)據(jù)回調(diào)service巨双,端口9103
- host:后臺基礎(chǔ)信息service,端口9100
- business:業(yè)務(wù)系統(tǒng)service噩茄,端口9101
- gateway:網(wǎng)關(guān),對外暴露端口為443和8088
maven項目模塊化
考慮到項目中的DTO复颈、VO、通用類等在多個service中會使用到沥割,且serivce之間互相調(diào)用使用了Fegin
,所以耗啦,service中項目結(jié)構(gòu)會是這樣,以host-service為例:
host中pom.xml分了3個模塊
模塊之間互相引用需要如下幾步
- mvn -Dmaven.test.skip=true -U clean install 構(gòu)建jar包并安裝到本地
- mvn install:install-file 使用install-file命令將jar包上傳到本地倉庫
-
在pom中添加依賴包机杜,會從本地倉庫獲取指定的包
image.png
關(guān)于詳細(xì)的模塊化步驟帜讲,請看此文使用Maven構(gòu)建多模塊項目
Dockerfile文件準(zhǔn)備
創(chuàng)建兩個文件夾,docker-test椒拗,docker-prod
docker-test為測試環(huán)境
docker-prod為正式環(huán)境
以測試環(huán)境為例似将,在docker-test中創(chuàng)建Dockerfile
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è)置
取消防跨站點請求偽造和允許匿名訪問讀取
Jenkins中創(chuàng)建流水線腳本
創(chuàng)建流水線腳本
勾選觸發(fā)遠(yuǎn)程構(gòu)建获黔,身份令牌隨便填,觸發(fā)的url為JENKINS_URL
/job/eureka
/build?token=TOKEN
-
JENKINS_URL
:jenkins的訪問地址 -
eureka
:此處為腳本的名稱 -
TOKEN
:輸入身份驗證令牌
Gitlab項目的Webhook輸入jenkins的地址在验,點擊Add webhook
測試推送
顯示201則為成功
Groovy流水線腳本格式
基本格式為
#!groovy
pipeline {
agent any
environment {
ENV="test"
}
stages {
stage('test') {
steps {
echo "${ENV}"
}
}
}
}
- environment: 環(huán)境變量玷氏,使用時為
${xx}
格式,xx
為變量名 - stages: 所有流水線的階段腋舌,stages中包含多個stage
- steps: 腳本執(zhí)行步驟
流水線語法
提供了多個步驟的選擇
我現(xiàn)在需要生成獲取git clone的腳本
點擊生成
生成的腳本復(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);
}
}
腳本步驟如下
#!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
'''
}
- 刪除原來的文件夾
- 從Git克隆代碼
- 創(chuàng)建一個pages的文件夾
- 將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/
'''
}
}
- 刪除原來的文件夾
- 從Git克隆代碼
- 將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}
- 生成jar包
- 創(chuàng)建target文件夾
- 復(fù)制jar包到target文件夾下
打包jar包前,需要對各項目中的模塊先進行maven打包,可根據(jù)自己的項目進行實際修改
package-build.sh
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è)置
當(dāng)每次提交之后,都會觸發(fā)構(gòu)建驹碍。
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插件
安裝成功后,在編輯腳本中漫试,可以看到新增的Generic Webhook Trigger
定義兩個參數(shù)六敬,ref為提交的分支名稱,project.git_ssh_url為項目的ssh地址
添加驗證表達式
webhook地址
webhook地址為http://JENKINS_URL/generic-webhook-trigger/invoke
添加token參數(shù)作為驗證
設(shè)置新的webhook地址
構(gòu)建成功后驾荣,能看到各步驟花費了多少時間
Spring Cloud 項目部署Docker中的坑
- 內(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"]