jenkins pipeline改造之實現(xiàn)部署完成通知

在開始之前,我認(rèn)為你已經(jīng)完成了,jenkins的搭建已經(jīng)jenkins pipeline的編寫,并能正常的發(fā)布項目。

實現(xiàn)的成果

image.png

原理介紹

首先企業(yè)中使用的開發(fā)語言大部分都是java,既然已經(jīng)使用上k8s了,相信業(yè)務(wù)架構(gòu)是微服務(wù)類型的。這種微服務(wù)我們部署在k8s中時,最常用的一種資源類型時deployment.

我們來看一下deployment資源
我創(chuàng)建了一個deployment

$ kubectl create deploy web --image=nginx -n gebangfeng-test
deployment.apps/web created

而deployment會使用rs去管理我們的pod

$ kubectl get rs  -n gebangfeng-test
NAME                             DESIRED   CURRENT   READY   AGE
web-96d5df5c8                    1         1         1       4m25s

接著我們查一下啟動后的pod

$ kubectl get  pod -n gebangfeng-test
NAME                  READY   STATUS    RESTARTS   AGE
web-96d5df5c8-fmk7r   1/1     Running   0          118s

可以看到pod的名字是由deployment名字+rs名字+隨機字符組成的。

我們的目的是想實現(xiàn)服務(wù)的部署通知,首先我們要知道怎么才能判斷服務(wù)有沒有部署完成。
首先還是那個deployment


image.png

可以看到在deployment一些信息中有Available 即可用的副本數(shù)。本來想著這個值可以利用一下,但是deployment的更新策略是滾動更新,deployment不太好判斷服務(wù)有沒有更新完成。deployment的更新操作都是rs來實現(xiàn)的。
deployment的每次更新操作都會創(chuàng)建一個rs完成更新,比如一個應(yīng)用的需要的副本數(shù)是1 準(zhǔn)備好了的副本數(shù)為1 這樣我們就可以認(rèn)為更新成功了。

方案如下

給rs增加版本標(biāo)簽,每次更新這個標(biāo)簽都會變更,然后我們通過標(biāo)簽去篩選出這次更新的rs,并取出rs需要副本數(shù),和準(zhǔn)備好了的副本數(shù)。然后進行循環(huán)的判斷就可以知道服務(wù)有沒有更新完成了。
準(zhǔn)備服務(wù)更新文件

cat nginx-deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  generation: 1
  labels:
    app: web
  name: web
  namespace: gebangfeng-test
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: web
    spec:
      containers:
      - image: nginx:1.22.1
        imagePullPolicy: Always
        name: nginx
      restartPolicy: Always

創(chuàng)建資源

kubectl apply -f   nginx-deployment.yaml 

模擬更新

添加標(biāo)簽

增加標(biāo)簽 version:1


image.png

獲取需要的副本數(shù)和準(zhǔn)備好了的副本數(shù)

kubectl get rs -l appversion=1 -n gebangfeng-test


image.png

-l指定新增加的標(biāo)簽,后面通過這個標(biāo)簽去篩選rs,就能準(zhǔn)確獲取到這次更新的rs了。

  • 獲取需要的副本數(shù):
 kubectl get rs -l appversion=1 -n gebangfeng-test -o jsonpath='{.items[0].status.replicas}'
  • 獲取ready狀態(tài)的副本數(shù)
 kubectl get rs -l appversion=1 -n gebangfeng-test -o jsonpath='{.items[0].status.readyReplicas}'
image.png

我們獲取到了這兩個值,然后寫個腳本定期去查詢,進行判斷,就能知道服務(wù)是否更新完成了。

腳本檢查

我這里寫好了一個腳本

  • 首先是發(fā)送到微信的腳本
cat /usr/local/scripts/jenkins-weixin.sh
#!/bin/bash
message=$1
date=$(date +%Y-%m-%d)
time=$(date "+%H:%M:%S")
if [ _"${message}" = _"" ]; then
   message='this is test message...'
fi

webHookUrl="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=*************"
content='{"msgtype": "markdown","markdown": {"content": "'$message'"}}'
echo "content : $content"
curl --data-ascii "$content" $webHookUrl
echo "over!"

webHookUrl使用時需要替換成自己的企業(yè)微信群的地址

  • 檢查狀態(tài)的腳本
[root@base4 ~]# cat /usr/local/scripts/jenkins-check.sh
#!/bin/bash
#!/bin/bash
#項目名
#構(gòu)建版本號
usage(){
    echo "usage: $0 JOB_NAME ITEM_NAME K8S_NAMESPACE JOB_STATUS BUILD_NUM BUILD_USER BUILD_URL COMMENT"
}



JOB_NAME=$1
ITEM_NAME=$2
K8S_NAMESPACE=$3
JOB_STATUS=$4
BUILD_NUM=$5
BUILD_USER=$6
BUILD_URL=$7
COMMENT=$8
if [ $# -lt 8 ]; then
  usage
  exit 1
fi
declare -A userlist='([gebangfeng]='GeBangFeng')'
SendMessage='/usr/local/scripts/jenkins-weixin.sh'
count=1
APP_LIST=(`echo ${ITEM_NAME//,/\ }`)
AccessApp=()
EerorApp=()


Check() {
while true :
do
for app_id in ${!APP_LIST[@]}
do
app_name=${APP_LIST[$app_id]}
replicas=`kubectl get rs -n ${K8S_NAMESPACE}  -l jenkins-build-id=${BUILD_NUM},k8s-app=${app_name}  -o jsonpath='{.items[0].status.replicas}'`
readyReplicas=`kubectl get rs -n ${K8S_NAMESPACE}  -l jenkins-build-id=${BUILD_NUM},k8s-app=${app_name} -o jsonpath='{.items[0].status.readyReplicas}'`

if [ _"${readyReplicas}" = _"${replicas}" ]; then
  echo "服務(wù):${app_name} 發(fā)布成功 需要的副本數(shù)量:${replicas} 準(zhǔn)備好了的副本數(shù): ${readyReplicas}"
  AccessApp[${#AccessApp[*]}]=$app_name
  unset APP_LIST[$app_id]
  #sh /usr/local/scripts/jenkins-weixin.sh ${JOB_NAME} ${ITEM_NAME} "構(gòu)建成功 ?" ${BUILD_NUM} ${BUILD_USER} ${BUILD_URL}
  #exit 0
else
  if [ $count -gt 100 ];then
  echo "服務(wù):${app_name} 發(fā)布未成功, 需要的副本數(shù)量:${replicas} 準(zhǔn)備好了的副本數(shù): ${readyReplicas}"
  EerorApp[${#EerorApp[*]}]=$app_name
  #sh /usr/local/scripts/jenkins-weixin.sh ${JOB_NAME} ${ITEM_NAME} "構(gòu)建失敗 ?" ${BUILD_NUM} ${BUILD_USER} ${BUILD_URL}
 #exit 1
  else
  echo "服務(wù):${app_name} 第${count} 次檢查,發(fā)布未成功,繼續(xù)檢查 需要的副本數(shù)量:${replicas} 準(zhǔn)備好了的副本數(shù): ${readyReplicas}"
  fi
fi
done
if [ $count -gt 100 ];then
  echo "檢查周期結(jié)束退出循環(huán)"
  echo "發(fā)布成功的 ${AccessApp[@]} 發(fā)布失敗的 ${EerorApp[@]}"
  message="**jenkins構(gòu)建狀態(tài)通知**
> 任務(wù)名稱: **${JOB_NAME}**
> 應(yīng)用名稱: **${ITEM_NAME}**
> 構(gòu)建環(huán)境: **${K8S_NAMESPACE}**
> 構(gòu)建結(jié)果: **部分構(gòu)建失敗**
> 構(gòu)建成功: **${AccessApp[@]}** 
> 構(gòu)建失敗: **${EerorApp[@]}**
> 當(dāng)前版本: **${BUILD_NUM}**
> 構(gòu)建發(fā)起: **${BUILD_USER}**
> 構(gòu)建日志: ${BUILD_URL}console
<@${userlist["$BUILD_USER"]}><@gebangfeng>
"
echo $message
sh ${SendMessage} "${message}"
  exit 0
fi

if [ ${#APP_LIST[*]} -eq '0' ];then
  echo "所有服務(wù)發(fā)布完成"
  echo "發(fā)布成功的 ${AccessApp[@]} 發(fā)布失敗的 ${EerorApp[@]}"
  message="**jenkins構(gòu)建狀態(tài)通知**
> 任務(wù)名稱: **${JOB_NAME}**
> 應(yīng)用名稱: **${ITEM_NAME}**
> 構(gòu)建環(huán)境: **${K8S_NAMESPACE}**
> 構(gòu)建結(jié)果: **構(gòu)建成功 ?**
> 當(dāng)前版本: **${BUILD_NUM}**
> 更新內(nèi)容: **${COMMENT}**
> 構(gòu)建發(fā)起: **${BUILD_USER}**
<@${userlist["$BUILD_USER"]}>
"
echo $message
sh ${SendMessage} "${message}"
  exit 0
fi

sleep 5
let count++
done
}

#判斷狀態(tài)
if [ ${JOB_STATUS} -eq '0' ];then
   echo "開始更新"
   message="**jenkins構(gòu)建狀態(tài)通知**
> 任務(wù)名稱: **${JOB_NAME}**
> 應(yīng)用名稱: **${ITEM_NAME}**
> 構(gòu)建環(huán)境: **${K8S_NAMESPACE}**
> 構(gòu)建結(jié)果: **開始更新...**
> 當(dāng)前版本: **${BUILD_NUM}**
> 構(gòu)建發(fā)起: **${BUILD_USER}**
> 構(gòu)建日志: ${BUILD_URL}console
<@${userlist["$BUILD_USER"]}>
"
echo "${message}" 
#sh ${SendMessage} "${message}"
Check
elif [ ${JOB_STATUS} -eq '1' ];then
   echo "更新失敗"
   message="**jenkins構(gòu)建狀態(tài)通知**
> 任務(wù)名稱: **${JOB_NAME}**
> 應(yīng)用名稱: **${ITEM_NAME}**
> 構(gòu)建環(huán)境: **${K8S_NAMESPACE}**
> 構(gòu)建結(jié)果: **構(gòu)建失敗 ?**
> 當(dāng)前版本: **${BUILD_NUM}**
> 構(gòu)建發(fā)起: **${BUILD_USER}**
> 構(gòu)建日志: ${BUILD_URL}console
<@${userlist["$BUILD_USER"]}>
"
echo "${message}" 
sh ${SendMessage} "${message}"
exit 1
else 
    usage 
    exit 1
fi

需要注意的內(nèi)容SendMessage 替換 微信腳本的路徑
userlist時存放企業(yè)微信 userid的數(shù)組
執(zhí)行腳本需要傳幾個參數(shù)
JOB_NAME ITEM_NAME K8S_NAMESPACE JOB_STATUS BUILD_NUM BUILD_USER BUILD_URL COMMENT
JOB_NAME是jenkins項目的名字
ITEM_NAME是項目的名字比如我們例子web
K8S_NAMESPACE 是k8s的命名空間
JOB_STATUS 腳本檢查狀態(tài)0是直接發(fā)失敗消息,1是執(zhí)行檢查操作,
BUILD_NUM是對應(yīng)jenkns 的BUILD_NUM,用于識別每個更新任務(wù)的,這個id也會以標(biāo)簽的方式寫入到deployment部署文件中,保證這個標(biāo)簽每次更新服務(wù)都能對于的變更
BUILD_USER 更新者的名字 比如gebangfeng 用戶@更新的人和消息展示
BUILD_URL 主要用戶展示構(gòu)建通知,一般填寫jenkins任務(wù)的url,COMMENT填寫這次更新的內(nèi)容

接下來我們來模擬一次完整的過程。更新服務(wù)并給服務(wù)添加版本的標(biāo)簽,更新后調(diào)用腳本去檢查更新狀態(tài)。
刪除之前的web項目

kubeclt delete -f nginx-deployment.yaml 

編寫更新deployment文件

 cat nginx-deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  generation: 1
  labels:
    k8s-app: web
    jenkins-job: gebangfeng-test-web-demo
    jenkins-build-id: "1"
  name: web
  namespace: gebangfeng-test
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      k8s-app: web
  template:
    metadata:
      creationTimestamp: null
      labels:
        k8s-app: web
        jenkins-job: gebangfeng-test-web-demo
        jenkins-build-id: "1"
    spec:
      containers:
      - image: nginx:1.22.1
        imagePullPolicy: Always
        name: nginx
      restartPolicy: Always

jenkins-job 代表了jenkins job的名稱
jenkins-build-id 代表了jenkins的構(gòu)建id

image.png
kubectl apply -f nginx-deployment.yaml 

調(diào)用腳本去檢查

sh jenkins-check.sh test web gebangfeng-test 0 1 gebangfeng https://baidu.com '測試'

$ sh  jenkins-check.sh test web gebangfeng-test 0 1 gebangfeng https://baidu.com '測試'
開始更新
**jenkins構(gòu)建狀態(tài)通知**
> 任務(wù)名稱: **test**
> 應(yīng)用名稱: **web**
> 構(gòu)建環(huán)境: **gebangfeng-test**
> 構(gòu)建結(jié)果: **開始更新...**
> 當(dāng)前版本: **1**
> 構(gòu)建發(fā)起: **gebangfeng**
> 構(gòu)建日志: https://baidu.comconsole
<@GeBangFeng>

服務(wù):web 發(fā)布成功 需要的副本數(shù)量:1 準(zhǔn)備好了的副本數(shù): 1
所有服務(wù)發(fā)布完成
發(fā)布成功的 web 發(fā)布失敗的 
**jenkins構(gòu)建狀態(tài)通知** > 任務(wù)名稱: **test** > 應(yīng)用名稱: **web** > 構(gòu)建環(huán)境: **gebangfeng-test** > 構(gòu)建結(jié)果: **構(gòu)建成功 ?** > 當(dāng)前版本: **1** > 更新內(nèi)容: **測試** > 構(gòu)建發(fā)起: **gebangfeng** <@GeBangFeng>
content : {"msgtype": "markdown","markdown": {"content": "**jenkins構(gòu)建狀態(tài)通知**
> 任務(wù)名稱: **test**
> 應(yīng)用名稱: **web**
> 構(gòu)建環(huán)境: **gebangfeng-test**
> 構(gòu)建結(jié)果: **構(gòu)建成功 ?**
> 當(dāng)前版本: **1**
> 更新內(nèi)容: **測試**
> 構(gòu)建發(fā)起: **gebangfeng**
<@GeBangFeng>
"}}
{"errcode":0,"errmsg":"ok"}over!
image.png

和jenkins集成

       post{
        success{
            sh '''
            /usr/local/scripts/jenkins-check.sh ${JOB_NAME} ${ITEMS} ${K8S_NAMESPACE} '0' ${BUILD_NUMBER} ${BUILD_USER_ID} ${BUILD_URL} "${COMMENT}"
            '''
        }
        failure{
            sh '''
            /usr/local/scripts/jenkins-check.sh ${JOB_NAME} ${ITEMS} ${K8S_NAMESPACE} '1' ${BUILD_NUMBER} ${BUILD_USER_ID} ${BUILD_URL} "${COMMENT}"
            '''
        }
    }

失敗和成功傳遞的參數(shù)區(qū)別在于第四個參數(shù),如果說在發(fā)布流水線已經(jīng)報失敗異常了也就沒有必要再去調(diào)用腳本執(zhí)行檢查操作,傳遞1說明直接發(fā)送是被消息。

最后編輯于
?著作權(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)容