搭建整體流程圖
1640409529164.png
具體步驟
標題 | url |
---|---|
基于Jenkins構建微服務發布平臺流程(本文) | http://www.lxweimin.com/p/43e3f2e3eee1 |
部署一套完整的K8s高可用集群(二進制) | http://www.lxweimin.com/p/d574e1a9675d |
Gitlab | http://www.lxweimin.com/p/ea10df706808 |
企業級鏡像倉庫Harbor | http://www.lxweimin.com/p/ac4a66bb4709 |
Helm應用包管理 | http://www.lxweimin.com/p/25ca410b1efd |
k8s構建容器化微服務項目 | http://www.lxweimin.com/p/0f0b2c9ee415 |
k8s-Prometheus | http://www.lxweimin.com/p/8a88f42a4d94 |
k8s-elk | http://www.lxweimin.com/p/96ad780638fa |
基于Jenkins構建微服務發布平臺流程
1640310801682.png
配置PV持久化存儲
部署NFS共享服務器
#在所有節點安裝NFS軟件包
yum install nfs-utils -y
#master節點作為NFS共享存儲服務器,并授權網段
[root@k8s-m1 ~]# vi /etc/exports
/ifs/kubernetes 192.168.153.0/24(rw,no_root_squash)
#啟動nfs
[root@k8s-m1 ~]# systemctl start nfs
[root@k8s-m1 ~]# systemctl enable nfs
#找node掛載測試
[root@k8s-node1 ~]# mount -t nfs 192.168.153.25:/ifs/kubernetes /mnt
[root@k8s-node1 /]# umount /mnt
為Jenkins準備PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-jekins
spec:
capacity:
storage: 5Gi
accessModes: ["ReadWriteOnce"]
nfs:
path: /ifs/kubernetes/jenkins-data
server: 192.168.153.25
---------------------------------------------------------------------------------
[root@k8s-m1 jenkins]# kubectl apply -f pv-jekins.yaml
persistentvolume/pv-jekins created
[root@k8s-m1 jenkins]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS
pv-jekins 5Gi RWO Retain Available
Jenkins及其組件安裝
Jenkins安裝
[root@k8s-m1 jenkins]# kubectl apply -f jenkins.yml
Please use the following password to proceed to installation:
dc7d3ccd7b0749bbbd4e33134619140f
http://192.168.153.25:30006/
修改國內源
[root@k8s-m1 jenkins-data]# cd /ifs/kubernetes/jenkins-data/updates/
[root@k8s-m1 updates]# sed -i 's/https:\/\/updates.jenkins.io\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' default.json
[root@k8s-m1 updates]# sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json
#重啟jenkins
[root@k8s-m1 jenkins]# kubectl delete pod jenkins-578b57ddcb-jchrb
安裝插件
? Git:拉取代碼
? Git Parameter:Git參數化構建
? Pipeline:流水線
? kubernetes:連接Kubernetes動態創建Slave代理
? Config File Provider:存儲配置文件
? Extended Choice Parameter:擴展選擇框參數,支持多選
Jenkins在K8s中動態創建代理
Jenkins主從架構介紹
001 當觸發Jenkins任務時,Jenkins會調用Kubernetes API創建Slave Pod
002 Pod啟動后會連接Jenkins,接受任務并處理
1640325338695.png
Kubernetes插件配置
Configure Clouds
#進入配置
Manage Jenkins -> Manage Nodes and Clouds -> Configure Clouds
[root@k8s-m1 ~]# kubectl get svc -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP
https://kubernetes.default
http://jenkins.default
注意:一定是http://jenkins.default而不是https://jenkins.default
#測試連接
Connected to Kubernetes v1.20.4
自定義Jenkins Slave鏡像
? Dockerfile:構建鏡像
? jenkins-slave:shell腳本啟動slave.jar
? settings.xml:修改maven官方源為阿里云源
? slave.jar:agent程序,接受master下發的任務,下載http://jenkinsip:port/jnlpJars/slave.jar
? helm和kubectl客戶端工具
[root@k8s-m1 jenkins-slave]# ls
Dockerfile helm jenkins-slave kubectl settings.xml slave.jar
構建并推送到鏡像倉庫:
docker build -t 192.168.153.20/library/jenkins-slave-jdk:1.8 .
docker push 192.168.153.20/library/jenkins-slave-jdk:1.8
測試主從架構是否正常
pipeline {
agent {
kubernetes {
label "jenkins-slave"
yaml """
kind: Pod
metadata:
name: jenkins-slave
spec:
containers:
- name: jnlp
image: "192.168.153.20/library/jenkins-slave-jdk:1.8"
"""
}
}
stages {
stage('第一步測試'){
steps {
sh 'hostname'
}
}
}
}
-----------------------------------------------------------------------------------
#運行過程時會啟動slave的pod
[root@k8s-m1 jenkins-slave]# kubectl get pod
NAME READY STATUS RESTARTS AGE
jenkins-578b57ddcb-7nwhj 1/1 Running 0 40m
jenkins-slave-354vz-0rcjt 0/1 ContainerCreating 0 13s
#運行結束后,slave的pod消失
[root@k8s-m1 jenkins-slave]# kubectl get pod
NAME READY STATUS RESTARTS AGE
jenkins-578b57ddcb-7nwhj 1/1 Running 0 41m
流水線自動發布微服務項目
準備工作
Gitlab
git config --global user.name "lql"
git config --global user.email "lql_h@163.com"
cd simple-microservice-dev3
git init
git remote add origin http://192.168.153.18/root/microservice.git
git add .
git commit -m "Initial commit"
git push -u origin master
http://192.168.153.18/root/microservice.git
http://192.168.153.18/root/microservice.git
Mysql
#Gitlab配置數據庫配置文件application-fat.yml [product、order、stock]
mysql://192.168.153.27:3306/tb_product
mysql://192.168.153.27:3306/tb_stock
mysql://192.168.153.27:3306/tb_order
#啟動mysql
[root@k8s-node1 ~]# docker start mysql
host
192.168.153.27 eureka.ctnrs.com
192.168.153.27 gateway.ctnrs.com
#ingresscontorl的pod在192.168.153.27上需要有
全局憑據
#憑據配置
Configure credentials -> 全局憑據 (unrestricted)
d713a4b9-7938-4fc1-98f5-83da66273c91 root/****** (12345678) git-auth
83ac8c1d-a5e6-4902-9e72-68b7ec6be75f admin/****** (Harbor12345) harbor-auth
Managed files
#Managed files
Managed files -> Custom file
[root@k8s-m1 jenkins-slave]# cat /root/.kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority: /opt/kubernetes/ssl/ca.pem
server: https://192.168.153.25:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: admin
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: admin
user:
client-certificate: /opt/kubernetes/ssl/admin.pem
client-key: /opt/kubernetes/ssl/admin-key.pem
----------------------------------------------------------------------------
Custom file
MyCustom aa483569-be58-4fc0-b9a3-070c4c9eef74
#取出(certificate-authority、client-certificate、client-key)的結果進行Base64編碼(Encode),得到:
certificate-authority-data
client-certificate-data
client-key-data
#測試配置文件
helm list --kubeconfig helmconfig
https://base64.us/
Harbor
#創建項目
microservice
#推送helm
helm push ms-0.1.0.tgz --username admin --password Harbor12345 http://192.168.153.20/chartrepo/microservice
部署ingress
#host(客戶端)
192.168.153.27 eureka.ctnrs.com
192.168.153.27 gateway.ctnrs.com
[root@k8s-m1 k8s]# kubectl apply -f ingress-controller.yaml
部署eurka
[root@k8s-m1 k8s]# kubectl apply -f eureka.yaml
Pipeline腳本
#!/usr/bin/env groovy
// 所需插件: Git Parameter/Git/Pipeline/Config File Provider/kubernetes/Extended Choice Parameter
// 公共
def registry = "192.168.153.20"
// 項目
def project = "microservice"
def git_url = "http://192.168.153.18/root/microservice.git"
def gateway_domain_name = "gateway.ctnrs.com"
def portal_domain_name = "portal.ctnrs.com"
// 認證
def image_pull_secret = "registry-pull-secret"
def harbor_auth = "83ac8c1d-a5e6-4902-9e72-68b7ec6be75f"
def git_auth = "d713a4b9-7938-4fc1-98f5-83da66273c91"
// ConfigFileProvider ID
def k8s_auth = "aa483569-be58-4fc0-b9a3-070c4c9eef74"
pipeline {
agent {
kubernetes {
label "jenkins-slave"
yaml """
apiVersion: v1
kind: Pod
metadata:
name: jenkins-slave
spec:
containers:
- name: jnlp
image: "${registry}/library/jenkins-slave-jdk:1.8"
imagePullPolicy: Always
volumeMounts:
- name: docker-cmd
mountPath: /usr/bin/docker
- name: docker-sock
mountPath: /var/run/docker.sock
- name: maven-cache
mountPath: /root/.m2
volumes:
- name: docker-cmd
hostPath:
path: /usr/bin/docker
- name: docker-sock
hostPath:
path: /var/run/docker.sock
- name: maven-cache
hostPath:
path: /tmp/m2
"""
}
}
parameters {
gitParameter branch: '', branchFilter: '.*', defaultValue: 'origin/master', description: '選擇發布的分支', name: 'Branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH'
extendedChoice defaultValue: 'none', description: '選擇發布的微服務', \
multiSelectDelimiter: ',', name: 'Service', type: 'PT_CHECKBOX', \
value: 'gateway-service:9999,portal-service:8080,product-service:8010,order-service:8020,stock-service:8030'
choice (choices: ['ms', 'demo'], description: '部署模板', name: 'Template')
choice (choices: ['1', '3', '5', '7'], description: '副本數', name: 'ReplicaCount')
choice (choices: ['ms'], description: '命名空間', name: 'Namespace')
}
stages {
stage('拉取代碼'){
steps {
checkout([$class: 'GitSCM',
branches: [[name: "${params.Branch}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [], submoduleCfg: [],
userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]
])
}
}
stage('代碼編譯') {
// 編譯指定服務
steps {
sh """
mvn clean package -Dmaven.test.skip=true
"""
}
}
stage('構建鏡像') {
steps {
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
sh """
docker login -u ${username} -p '${password}' ${registry}
for service in \$(echo ${Service} |sed 's/,/ /g'); do
service_name=\${service%:*}
image_name=${registry}/${project}/\${service_name}:${BUILD_NUMBER}
cd \${service_name}
if ls |grep biz &>/dev/null; then
cd \${service_name}-biz
fi
docker build -t \${image_name} .
docker push \${image_name}
cd ${WORKSPACE}
done
"""
configFileProvider([configFile(fileId: "${k8s_auth}", targetLocation: "admin.kubeconfig")]){
sh """
# 添加鏡像拉取認證
kubectl create secret docker-registry ${image_pull_secret} --docker-username=${username} --docker-password=${password} --docker-server=${registry} -n ${Namespace} --kubeconfig admin.kubeconfig |true
# 添加私有chart倉庫
helm repo add --username ${username} --password ${password} myrepo http://${registry}/chartrepo/${project}
"""
}
}
}
}
stage('Helm部署到K8S') {
steps {
sh """
common_args="-n ${Namespace} --kubeconfig admin.kubeconfig"
for service in \$(echo ${Service} |sed 's/,/ /g'); do
service_name=\${service%:*}
service_port=\${service#*:}
image=${registry}/${project}/\${service_name}
tag=${BUILD_NUMBER}
helm_args="\${service_name} --set image.repository=\${image} --set image.tag=\${tag} --set replicaCount=${replicaCount} --set imagePullSecrets[0].name=${image_pull_secret} --set service.targetPort=\${service_port} --description=\${image}:\${tag} myrepo/${Template}"
# 判斷是否為新部署
if helm history \${service_name} \${common_args} &>/dev/null;then
action=upgrade
else
action=install
fi
# 針對服務啟用ingress
if [ \${service_name} == "gateway-service" ]; then
helm \${action} \${helm_args} \
--set ingress.enabled=true \
--set ingress.host=${gateway_domain_name} \
\${common_args}
elif [ \${service_name} == "portal-service" ]; then
helm \${action} \${helm_args} \
--set ingress.enabled=true \
--set ingress.host=${portal_domain_name} \
\${common_args}
else
helm \${action} \${helm_args} \${common_args}
fi
done
# 查看Pod狀態
sleep 10
kubectl get pods \${common_args}
"""
}
}
}
}
執行結果
[root@k8s-m1 ~]# helm list -n ms
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
gateway-service ms 2 2021-12-25 02:49:52.32458612 +0000 UTC deployed ms-0.1.0 0.1.0
order-service ms 1 2021-12-24 14:34:25.413383174 +0000 UTC deployed ms-0.1.0 0.1.0
portal-service ms 4 2021-12-25 04:54:16.041854433 +0000 UTC deployed ms-0.1.0 0.1.0
product-service ms 1 2021-12-24 14:27:54.785617184 +0000 UTC deployed ms-0.1.0 0.1.0
stock-service ms 1 2021-12-24 14:34:27.396239729 +0000 UTC deployed ms-0.1.0 0.1.0
執行日志
kubectl create secret docker-registry registry-pull-secret --docker-username=admin --docker-password=**** --docker-server=192.168.153.20 -n ms --kubeconfig admin.kubeconfig
helm repo add --username admin --password **** myrepo http://192.168.153.20/chartrepo/microservice
helm upgrade portal-service --set image.repository=192.168.153.20/microservice/portal-service --set image.tag=11 --set replicaCount=1 --set 'imagePullSecrets[0].name=registry-pull-secret' --set service.targetPort=8080 --description=192.168.153.20/microservice/portal-service:11 myrepo/ms --set ingress.enabled=true --set ingress.host=portal.ctnrs.com -n ms --kubeconfig admin.kubeconfig
1640357371000.png
自動化部署效果
#網關測試
http://gateway.ctnrs.com/product/queryAllProduct?page=1&limit=10
{"status":200,"msg":"success","result":[{"id":1,"productName":"測試商品1","price":99.99,"stock":98},{"id":2,"productName":"美女","price":999.0,"stock":87},{"id":3,"productName":"Q幣","price":100.0,"stock":77},{"id":4,"productName":"貂皮大衣很厚很厚的那種","price":9999.0,"stock":65}]}
#首頁展示
http://portal.ctnrs.com/
1640356856850.png
1640356877654.png
1640356894602.png
項目回滾
整體流程
1640409785132.png
根據服務獲取最近3次的版本號
[root@k8s-m1 ~]# curl -s -X GET -u admin:Harbor12345 http://192.168.153.20/v2/microservice/portal-service/tags/list
{"name":"microservice/portal-service","tags":["10"]}
----------------------------------------------------------------------------------
[root@k8s-m1 ~]# vi get_tag.sh
#!/bin/bash
Harbor_add=192.168.153.20
Username=admin
Password=Harbor12345
Project=$1
Service=$2
curl -s -X GET -u "${Username}:${Password}" "http://${Harbor_add}/v2/${Project}/${Service}/tags/list" |awk -F'[][]' '{split($2,a,",");for (v in a) print
a[v]}' |sed 's/"http://g' |sort -nr|head -n 3
------------------------------------------------------------------------------------
[root@k8s-m1 ~]# ./get_tag.sh microservice portal-service
10
9
8
#腳本復制到jenkins容器中
[root@k8s-m1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
jenkins-578b57ddcb-7nwhj 1/1 Running 2 21h
[root@k8s-m1 ~]# kubectl cp get_tag.sh jenkins-578b57ddcb-7nwhj:/var/jenkins_home
配置插件
General ->This project is parameterized ->Choice parameter
Name: Service
Choices:
portal-service
gateway-service
product-service
order-service
stock-service
Description:請選擇要回滾的服務
--------------------------------------------------------------------------
#安裝插件Active Choices
Active Choices Reactive Parameter
Name : Tag
Script: Groovy Script
Groovy Script:
cmd = "/bin/bash /var/jenkins_home/get_tag.sh microservice ${Service}"
tags_list = cmd.execute().text.tokenize()
return tags_list
Referenced parameters:Service
定位回滾版本號腳本
[root@k8s-m1 ~]# helm history portal-service -n ms
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
2 Sat Dec 25 02:43:58 2021 deployed ms-0.1.0 0.1.0 192.168.153.20/microservice/portal-service:11
3 Sat Dec 25 02:43:58 2021 deployed ms-0.1.0 0.1.0 192.168.153.20/microservice/portal-service:12
[root@k8s-m1 ~]# helm history portal-service -n ms|awk '$NF~/portal-service:12/{print $1}'
3
Pipeline腳本
def registry = "192.168.153.20"
def namespace = "ms"
def k8s_auth = "aa483569-be58-4fc0-b9a3-070c4c9eef74"
pipeline {
agent {
kubernetes {
label "jenkins-slave"
yaml """
apiVersion: v1
kind: Pod
metadata:
name: jenkins-slave
spec:
containers:
- name: jnlp
image: "${registry}/library/jenkins-slave-jdk:1.8"
imagePullPolicy: Always
volumeMounts:
- name: docker-cmd
mountPath: /usr/bin/docker
- name: docker-sock
mountPath: /var/run/docker.sock
- name: maven-cache
mountPath: /root/.m2
volumes:
- name: docker-cmd
hostPath:
path: /usr/bin/docker
- name: docker-sock
hostPath:
path: /var/run/docker.sock
- name: maven-cache
hostPath:
path: /tmp/m2
"""
}
}
stages {
stage('執行回滾的操作') {
steps {
configFileProvider([configFile(fileId: "${k8s_auth}", targetLocation: "admin.kubeconfig")]){
sh """
#根據選擇的服務名稱拼接鏡像地址
rollback_image=${Service}:${Tag}
revision=\$(helm history ${Service} --kubeconfig admin.kubeconfig -n ms|awk '\$NF~/${Service}:${Tag}/{print \$1}')
helm rollback ${Service} \$revision --kubeconfig admin.kubeconfig -n ${namespace}
"""
}
}
}
}
}
執行結果
[root@k8s-m1 ~]# helm list -n ms
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
gateway-service ms 2 2021-12-25 02:49:52.32458612 +0000 UTC deployed ms-0.1.0 0.1.0
......
執行日志
helm rollback portal-service 2 --kubeconfig admin.kubeconfig -n ms