Jenkins+gitlab+maven+jdk+docker部署springboot聚合項目
最終效果圖
目錄
安裝换途、運行jenkins
http://www.reibang.com/p/42e2771dcc94編寫Jenkinsfile,Dockerfile,start.sh
構(gòu)建jenkins多分支流水線工程、并觸發(fā)構(gòu)建
補(bǔ)充:
(1)安裝docker私有鏡像倉庫 Harbor囚衔;安裝方法鏈接:http://www.reibang.com/p/1e8cdb6e7a5a
(2)配置發(fā)送郵件 http://www.reibang.com/p/1913bf5c3322
(3)服務(wù)發(fā)布到Kubernetes集群(往后補(bǔ)充)
編寫Jenkinsfile,Dockerfile,start.sh
1. 什么是Jenkinsfile
流水線語法
Jenkinsfile 是 Jenkins 2.x 核心特性 Pipeline(流水線)的腳本霞扬,由Groovy語言實現(xiàn)。Jenkinsfile一般是放在項目根目錄,隨項目一起受源代碼管理軟件控制钠署,無需像創(chuàng)建“自由風(fēng)格"(Jenkins FreeStyle)項目一樣哗戈,每次可能需要拷貝很多設(shè)置到新項目,提供了一些直接的好處:
- Pipeline上的代碼審查/迭代
- Pipeline的審計跟蹤
- Pipeline的唯一真實來源玻佩,可以由項目的多個成員查看和編輯出嘹。
Pipeline支持:Declarative和Scripted Pipeline(聲明式和腳本式)兩種格式。兩者都支持建立Pipeline(流水線)咬崔,兩者都可以用于在Web UI中定義一個流水線Jenkinsfile税稼,將Jenkinsfile文件創(chuàng)建并檢查到源代碼控制庫中通常被認(rèn)為是最佳做法烦秩。
Declarative Pipeline (聲明式格式樣例)
pipeline {
agent any
stages {
stage('編譯構(gòu)建') {
steps {
//
}
}
stage('測試') {
steps {
//
}
}
stage('部署') {
steps {
//
}
}
}
}
Scripted Pipeline(腳本式格式樣例)
node {
stage('編譯構(gòu)建') {
//
}
stage('測試') {
//
}
stage('部署') {
//
}
}
2. 編寫聲明式Jenkinsfile ; 使用文本編輯器(最好支持 Groovy 語法高亮顯示)
工作原理:自動獲取源代碼 ==> 編譯構(gòu)建 ==> 構(gòu)建并推送鏡像到Harbor ==>根據(jù)環(huán)境部署服務(wù)到服務(wù)器
pipeline {
agent any
environment {
registryUrl= "192.168.1.110:5000" #搭建docker私有倉庫(Harbor)或者 用DockerHub 或者用云平臺的“容器鏡像服務(wù)”
registry_user= "xxx"
registry_pass= "xxx"
env=test //"test" or "prod" #選擇打包及發(fā)布的環(huán)境
}
options {
timestamps() //設(shè)置在項目打印日志時帶上對應(yīng)時間
disableConcurrentBuilds() //不允許同時執(zhí)行流水線,被用來防止同時訪問共享資源等
buildDiscarder(logRotator(numToKeepStr: '3')) // 表示保留n次構(gòu)建歷史
}
//gitlab webhook觸發(fā)器
//聚合項目郎仆,代碼發(fā)生以下動作后只祠,所有子項目將被觸發(fā)構(gòu)建,可選擇使用(前提需要gitlab配置 webhook)
//triggers{ //方法一,為All時
// gitlab( triggerOnPush: true, //代碼有push動作就會觸發(fā)job
// triggerOnMergeRequest: true, //代碼有merge動作就會觸發(fā)job
// branchFilterType: "All") //為All時(只有符合條件的分支才會觸發(fā)構(gòu)建 “All/NameBasedFilter/RegexBasedFilter”)
//}
//triggers{ //方法二扰肌,為branchFilterType時
// gitlab( triggerOnPush: true,
// triggerOnMergeRequest: true,
// branchFilterType: "branchFilterType", //為branchFilterType時
// includeBranchesSpec: "dev") //基于branchFilterType值抛寝,輸入期望包括的分支的規(guī)則
//}
stages{
stage('Print Message') { //自動獲取代碼+打印信息
steps {
echo "workspace: ${WORKSPACE} build_id: ${BUILD_ID} branch(gitlab分支名): ${env.BRANCH_NAME}"
echo "registryUrl: ${registryUrl}"
}
}
//此步驟在調(diào)試Jenkinsfile時可以注釋以便了解目錄結(jié)構(gòu)
stage('Delete Workspace') { //清理工作目錄
steps {
echo "清理工作目錄: ${WORKSPACE}"
deleteDir() //表示刪除當(dāng)前目錄(${WORKSPACE})下內(nèi)容,通常用在構(gòu)建完畢之后清空工作空間
}
}
stage('Packaging projetc') { //編譯構(gòu)建
steps {
echo 'mvn打包子項目'
script {
sh 'source /etc/profile && mvn clean package -pl ${JOB_NAME%%/*} -am -amd -P${env} -Dmaven.test.skip=true' //springboot maven 多模塊 jenkins 單獨打包子項目
}
}
}
stage('Build & Push Image to Harbor') { //構(gòu)建曙旭、推送鏡像
steps {
echo '構(gòu)建盗舰,推送鏡像到docker鏡像倉庫'
dir ('./') { //指定工作目錄(默認(rèn)為${WORKSPACE})
script {
sh 'mv ${JOB_NAME%%/*}/Dockerfile ./'
sh 'docker login --username=${registry_user} --password=${registry_pass} ${registryUrl}'
def app = docker.build('${registryUrl}/${JOB_NAME%%/*}:${env}')
app.push('${env}')
sh 'docker rmi ${registryUrl}/${JOB_NAME%%/*}:${env}' //刪除jenkins本地新建的鏡像
}
}
//推送鏡像后,刪除工作空間除Jenkinsfile & start.sh以外所有文件
sh '''rm -rf `ls | egrep -v '(Jenkinsfile|start.sh)'`'''
}
}
stage('Deploy to the Target server') { //部署
//when {
// beforeAgent true
// branch "${env.BRANCH_NAME}" //gitlab分支名稱
//}
steps {
timeout(time: 40, unit: 'SECONDS') { // 設(shè)置遠(yuǎn)程部署超過40秒桂躏,將終止該步驟
//部署到${env}環(huán)境
sh 'bash ./start.sh ${JOB_NAME%%/*} ${registryUrl}/${JOB_NAME%%/*}:${env} ${env}' //位置變量1:工程名字 钻趋; 位置變量2:鏡像名字 ; 位置變量3:指定環(huán)境
}
}
}
}
}
3. 編寫Dockerfile
FROM java:latest
LABEL maintainer="qiujt <qiujt123@163.com>"
WORKDIR /data/logs
ADD canfu-admin/target/canfu-admin-0.0.1-SNAPSHOT.jar canfu-admin.jar
EXPOSE 8091
ENTRYPOINT ["java","-jar","-Xms512m","-Xmx1024m","-XX:PermSize=512M","-XX:MaxPermSize=1024M","/canfu-admin.jar"]
- FROM 指定基礎(chǔ)鏡像剂习,并且必須是第一條指令爷绘。(可以選擇更小的鏡像openjdk:8-jdk-alpine,不過一些后臺項目驗證碼圖片會出不來进倍,慎用)
- LABEL MAINTAINER 指定維護(hù)者信息 語法:LABEL maintainer=" user_name <user_email>"
- WORKDIR:指定在創(chuàng)建容器后土至,終端默認(rèn)登陸進(jìn)來的工作目錄,一個落腳點
- ADD 將宿主機(jī)目錄下的文件拷貝進(jìn)鏡像且 ADD 命令會自動處理 URL 和解壓 tar 壓縮包
- EXPOSE 當(dāng)前容器對外暴露出的端口
- ENTRYPOINT:指定一個容器啟動時要運行的命令猾昆,ENTRYPOINT 的目的和 CMD 一樣陶因,都是在指定容器啟動程序及參數(shù)
4. 編寫部署腳本(start.sh)
#/bin/bash
#ENV
#docker私有倉庫(Harbor)
registryUrl=192.168.1.110:5000
registry_user="xxx"
registry_pass="xxx"
project_name=$1
image_name=$2
env=$3
node_user=root
if [ "${env}" == test ];then
#測試環(huán)境
node1=192.168.1.105
elif [ "${env}" == prod ];then
#生產(chǎn)環(huán)境
node1=192.168.1.106
else
echo '沒有${env}環(huán)境!4刮稀楷扬!'
fi
#Prepare
echo "project_name: $1 , image_name: $2 , env:$3"
#Deploying
#可以寫成一個ssh"",為了解析方便贴见,切分開(接下來為第一行烘苹,以此類推)
ssh $node_user@$node1 "docker login --username=${registry_user} --password=${registry_pass} ${registryUrl} “
ssh $node_user@$node1 "docker pull $image_name && docker rm -f $project_name || true "
ssh $node_user@$node1 "docker run -itd --name=$project_name --restart=always --net=host -e TZ="Asia/Shanghai" -v /data/logs/$project_name:/data/logs/$project_name $image_name"
ssh $node_user@$node1 "docker image prune -a -f --filter 'until=1h' "
ssh $node_user@$node1 "tailf /data/logs/$project_name/${project_name}.log || true"
(第一個ssh解析)登錄docker私有倉庫(Harbor)
服務(wù)器拉取對應(yīng)鏡像;判斷容器是否存在片部,如果存在就結(jié)束容器,否則跳過
基于鏡像創(chuàng)建容器镣衡。
--restart=always:當(dāng) Docker進(jìn)程重啟后,容器自動啟動档悠;
--net=host:指定容器網(wǎng)絡(luò)模式為host廊鸥,即容器暴露的端口,宿主機(jī)就是什么端口辖所;
-e TZ="Asia/Shanghai" :定義容器使用時區(qū)惰说;
-v:將子項目日志目錄從容器里映射到宿主機(jī)(視個人項目情況變更)。刪除1小時前拉取的缘回、并且未被使用的鏡像
打印服務(wù)輸出日志吆视,輸出到j(luò)enkins控制臺
補(bǔ)充:由于Pipeline(流水線)需要在目標(biāo)服務(wù)器(例如:192.168.1.105)上執(zhí)行操作典挑,因此需要通過ssh連接。
(1)首先需要在Jenkins容器里面生成ssh的公鑰密鑰啦吧;
docker exec -it jenkins /bin/bash -c 'ssh-keygen -C "root@jenkins"'
(2)然后自行復(fù)制jenkins容器的公鑰(/root/.ssh/id_rsa.pub)文件內(nèi)容到目標(biāo)服務(wù)器的/root/.ssh/authorized_keys文件中搔弄。
注意第一次連接目標(biāo)服務(wù)器會提示一個交互動作(提示輸入“yes”或者“no”繼續(xù)操作)
[root@tools-env-101 ~]# docker exec -it jenkins /bin/bash -c "ssh 192.168.1.105"
The authenticity of host '192.168.1.105 (192.168.1.105)' can't be established.
ECDSA key fingerprint is SHA256:/2CklRXsExQNpBUr08qN6jqbx6wBkYceC/IShzwAemk.
Are you sure you want to continue connecting (yes/no)? #提示輸入“yes”或者“no”繼續(xù)操作
要避開這個交互操作有兩種方式:
第一種將目標(biāo)服務(wù)器/etc/ssh/ssh_config里面的“StrictHostKeyChecking ask”改為“StrictHostKeyChecking no”,重啟ssh服務(wù)即可丰滑;
第二種直接執(zhí)行以下命令顾犹,交互輸入yes即可。
docker exec -it jenkins /bin/bash -c "sed -i '/192.168.1.105/d' /root/.ssh/known_hosts"
docker exec -it jenkins /bin/bash -c "ssh 192.168.1.105" #輸入yes
創(chuàng)建多分支流水線項目
SpringBoot聚合項目比起傳統(tǒng)復(fù)雜的單體工程褒墨,使用Maven的多模塊配置炫刷,可以幫助項目劃分模塊,鼓勵重用郁妈,防止POM變得過于龐大浑玛,方便某個模塊的構(gòu)建,而不用每次都構(gòu)建整個項目噩咪,并且使得針對某個模塊的特殊控制更為方便 顾彰。
Jenkinsfile ,Dockerfile , start.sh位置關(guān)系胃碾;及子項目下創(chuàng)建各個環(huán)境文件(application-test.yml 和 application-prod.yml)
Springboot聚合項目
指定環(huán)境文件
創(chuàng)建jenkins流水線項目
配置流水線項目
點擊 “立刻掃描 多分支流水線”涨享,Jenkins掃描源碼地址每個分支,如果該分支下有Jenkinsfile仆百,那么就會創(chuàng)建一個分支Job
(自定義release-test為測試分支厕隧,release-3.1-prod為生產(chǎn)分支)
(注意的是,維護(hù)源碼時俄周,需要構(gòu)建的分支才加上Jenkinsfile吁讨,不然Jenkins會將帶有Jenkinsfile的分支也創(chuàng)建一個分支job,會顯得凌亂峦朗。例如:下圖的release-prod & release_eat_away & release_eat_away_dev)
到此建丧,多分支流水線項目配置完畢,可以點擊觸發(fā)構(gòu)建項目了
實際開發(fā)中波势,SpringBoot聚合項目往往只會頻繁的構(gòu)建其中的幾個子項目翎朱,不必每次都構(gòu)建整個項目,因此我把Jenkinsfile里面的觸發(fā)構(gòu)建的代碼注釋了艰亮。需要構(gòu)建時闭翩,找到對應(yīng)項目,對應(yīng)分支點擊構(gòu)建即可迄埃,也是很方便的。當(dāng)然大家有什么高效的方法兑障,懇請私信發(fā)我侄非,抱拳=锻簟!逞怨!
點擊“打開 Blue Ocean”者疤,可以看到項目構(gòu)建的每個階段輸出日志