k8s基于Jenkins构建微服务发布平台流程

搭建整体流程图

1640409529164.png

具体步骤

标题 url
基于Jenkins构建微服务发布平台流程(本文) https://www.jianshu.com/p/43e3f2e3eee1
部署一套完整的K8s高可用集群(二进制) https://www.jianshu.com/p/d574e1a9675d
Gitlab https://www.jianshu.com/p/ea10df706808
企业级镜像仓库Harbor https://www.jianshu.com/p/ac4a66bb4709
Helm应用包管理 https://www.jianshu.com/p/25ca410b1efd
k8s构建容器化微服务项目 https://www.jianshu.com/p/0f0b2c9ee415
k8s-Prometheus https://www.jianshu.com/p/8a88f42a4d94
k8s-elk https://www.jianshu.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/"//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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342