jenkins初始wed配置和ci cd部署

学习笔记,仅供参考!

Jenkins Plugins

Kubernetes 安装


jenkins初始wed配置和ci cd部署_第1张图片

jenkins初始wed配置和ci cd部署_第2张图片

修改password密码点击保存!

安装插件

jenkins初始wed配置和ci cd部署_第3张图片

安装插件:blue Ocean

点击Manage Jenkins  --》点击插件管理  --》Available plugins --》搜索blue Ocean  ----》点击安装

Updates:更新插件

Available plugins  下载并安装插件

Installed plugins  已安装的插件

Advanced settings  插件高级设置

blue Ocean:pipaline可视化管理工具

对接kubernete

jenkins初始wed配置和ci cd部署_第4张图片

jenkins初始wed配置和ci cd部署_第5张图片

jenkins初始wed配置和ci cd部署_第6张图片

重启完成后进行k8s对接操作

jenkins初始wed配置和ci cd部署_第7张图片

jenkins初始wed配置和ci cd部署_第8张图片

jenkins初始wed配置和ci cd部署_第9张图片

如果jenkins是部署到k8s之上的那麽对接就不需要密钥对接和证书key(需要在jenkins设置里面配置特殊凭据(Certificate))把k8s的证书导出来进行制作,让后在导入到jenkins中

但是如果是部署到k8s之外的那麽对接就需要密钥对接

jenkins初始wed配置和ci cd部署_第10张图片

点击save保存

jenkins对接github

因为我们在初始安装的时候已经安装好了github的插件

jenkins初始wed配置和ci cd部署_第11张图片

点击构建新的流水线

jenkins初始wed配置和ci cd部署_第12张图片

jenkins初始wed配置和ci cd部署_第13张图片

jenkins初始wed配置和ci cd部署_第14张图片

对接gitee

需要下载对应的插件gitee

jenkins初始wed配置和ci cd部署_第15张图片


根据提示的gitee对接所需要的url 以及域名 和需要的凭证信息(在gitee设置里面配置,当然导入到jenkins中)可以实现对接

这里不再做操作

gitlab私有代码仓库部署操作(基础版)

ps:如果想要部署完整版的可以去网上自行查询

ps因为我们用的是云服务器所以因为没有域名的缘故直接写的ip地址,就不需要写host

常用的代码仓库只有gitlab提供私有代码托管仓库的搭建 免费开源的

提示:如果后期需要配置钉钉消息,那麽应该在pod应用容器中将本地时间进行挂载在容器中,因为钉钉消息需要时间一致

- name: time-localtime

      readOnly: true

      mountPath: /etc/localtime

gitlab私有代码仓库的搭建:需要用到alertmanager nginx redis node-exporter 等等的一些组件搭建依赖postgres(部署harbor和sonarqube代码扫描 时有用到 ) redis

Docker

一部署postgres数据库

实现:部署postgres完成后数据库里自动的创建名为gitlab数据库

ps:(可以bash脚本的方式书写成config进行挂载到里面)

配置数据库用户和密码 以secret资源的方式创建  ps:加密

cat gitlab-secret.txt

postgres.user.root=root

postgres.pwd.root=redhat123

kubectl create -n jenkins secret generic gitlab-secret --from-env-file=gitlab-secret.txt

generic:手动

部署postgres完成后数据库里自动的创建名为gitlab数据库

实现1:
spec:

      initContainers:

      - name: install

        image: busybox

        command:

        - sh

        - "-c"

        - |

          set -e

          cat > /docker-entrypoint-initdb.d/init-user-db.sh  << EOF

          POSTGRES_USER=root

          psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSSQL

                  CREATE DATABASE gitlab;

          EOSSQL

          EOF

        volumeMounts:

        - name: install

          mountPath: /docker-entrypoint-initdb.d

在容器下面

volumeMounts:

        - name: install

          mountPath: /docker-entrypoint-initdb.d

volumes:

      - name: install

        emptyDir: {}

\ psql   \l

实现2:

cat init-user-db.sh

#!/bin/bash

set -e

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL

CREATE DATABASE gitlab;

EOSQL

kubectl create -n jenkins configmap init-db --from-file init-user-db.sh

cat postgres-statefulset.yaml

apiVersion: v1

kind: Service

metadata:

  name: postgres-headless

  namespace: jenkins

  labels:

    app: postges

spec:

  type: ClusterIP

  clusterIP: None

  ports:

  - port: 5432

    name: postgres

    targetPort: 5432

  selector:

    app: postgres

---

apiVersion: v1

kind: Service

metadata:

  name: postgres-svc

  labels:

    app: postgres

  namespace: jenkins

spec:

  type: ClusterIP

  ports:

  - name: server

    port: 5432

    targetPort: 5432

    #protocol: TCP

  # nodePort: 32088

  clusterIP: 10.97.235.67

  selector:

    app: postgres

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

  namespace: jenkins

  name: postgres-statefulset

  labels:

    app: postgres

spec:

  replicas: 1

  selector:

    matchLabels:

      app: postgres

  serviceName: postgres-headless

  template:

    metadata:

      labels:

        app: postgres

    spec:

      containers:

      - name: postgres

        image: postgres:11.4 #若本地没有启动该仓库,换成postgres:11.4

        imagePullPolicy: "IfNotPresent"

        ports:

        - containerPort: 5432

        env:

        - name: POSTGRES_USER          #PostgreSQL 用户名

          valueFrom:

            secretKeyRef:

              name: gitlab-secret

              key: postgres.user.root

        - name: POSTGRES_PASSWORD      #PostgreSQL 密码

          valueFrom:

            secretKeyRef:

              name: gitlab-secret

              key: postgres.user.root

        resources:

          limits:

            cpu: 1000m

            memory: 2048Mi

          requests:

            cpu: 50m

            memory: 100Mi

        volumeMounts:

        - name: init-db

          mountPath: /docker-entrypoint-initdb.d/init-user-db.sh

          subPath: init-user-db.sh

        - mountPath: /var/lib/postgresql/data

          name: postgres-nfs

        - name: time-localtime

          readOnly: true

          mountPath: /etc/localtime

      volumes:

      - name: init-db

        configMap:

          name: init-db

    # - name: postgre-nfs

      #  mountPath: /var/lib/postgresql/data

      - name: time-localtime

        hostPath:

          path: /etc/localtime

  volumeClaimTemplates:

  - metadata:

      name: postgres-nfs

    spec:

      accessModes: [ "ReadWriteMany" ]

      storageClassName: "nfs-storageclass"

      resources:

        requests:

          storage: 3Gi


二.部署redis

cat redis-statefults.yaml

apiVersion: v1

kind: Service

metadata:

  name: redis-headless

  namespace: jenkins

  labels:

    app: redis

spec:

  type: ClusterIP

  clusterIP: None  # 创建无头服务,如果需要对外暴露端口可自行创建service

  ports:

  - name: redis

    port: 6379

    targetPort: redis

  selector:

    app: redis

---

apiVersion: v1

kind: Service

metadata:

  name: redis-svc

  namespace: jenkins

  labels:

    app: redis

spec:

  type: ClusterIP

  ports:

  - port: 6379

    targetPort: 6379

  clusterIP: 10.110.1.132

  selector:

    app: redis

---

apiVersion: v1

kind: ConfigMap

metadata:

  name: redis-config

  namespace: jenkins

  labels:

    app: redis

data:

  redis.conf: |

    bind 0.0.0.0

    protected-mode yes

    port 6379

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

  name: redis

  namespace: jenkins

spec:

  selector:

    matchLabels:

      app: redis

  serviceName: redis-headless

  replicas: 2

  updateStrategy:

    type: RollingUpdate

  template:

    metadata:

      name: redis

      labels:

        app: redis

    spec:

      affinity:

        podAntiAffinity: #pod的反亲和性

          requiredDuringSchedulingIgnoredDuringExecution: #硬亲和性(硬策略)

          - labelSelector:

              matchExpressions:

              - key: app

                operator: In

                values:

                - redis

            topologyKey: "kubernetes.io/hostname"   

      containers:

      - name: redis

        image: redis:5.0.13

        imagePullPolicy: "IfNotPresent"

        ports:

        - name: redis

          containerPort: 6379

        volumeMounts:

        - name: data

          mountPath: /etc/redis/

        - name: "redis-data"

          mountPath: "/data"

      volumes:

      - name: data

        configMap:

        name: redis-config         

  volumeClaimTemplates:

  - metadata:

      name: redis-data

    spec:

      accessModes: [ "ReadWriteMany" ]

      storageClassName: "nfs-storageclass"

      resources:

        requests:

          storage: 200M   

gitlab部署 (启动时间较长)
可以选择有状态或无状态部署策略

cat gitlab-statefulset.yaml

---

apiVersion: v1

kind: Service

metadata:

  name: gitlab-headless

  namespace: jenkins

  labels:

    app: gitlab

spec:

  clusterIP: None

  ports:

  - port: 80

    name: postgres

    targetPort: 80

  selector:

    app: gitlab

---

apiVersion: v1

kind: Service

metadata:

  name: gitlab-svc

  labels:

    app: gitlab

  namespace: jenkins

spec:

  type: NodePort

  ports:

  - name: server

    port: 80

    targetPort: 80

    protocol: TCP

    nodePort: 32085

  clusterIP: 10.99.69.119

  selector:

    app: gitlab

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

  namespace: jenkins

  name: gitlab

  labels:

    app: gitlab

spec:

  replicas: 1

  selector:

    matchLabels:

      app: gitlab

  serviceName: gitlab-headless

  template:

    metadata:

      labels:

        app: gitlab

    spec:

      containers:

      - name: gitlab

        image:  sameersbn/gitlab:13.2.2

        imagePullPolicy: "IfNotPresent"

        env:

        - name: GITLAB_HOST

          value: "gitlab-svc"

        - name: GITLAB_PORT

          value: "80"

        - name: GITLAB_SECRETS_DB_KEY_BASE

          value: "long-and-random-alpha-numeric-string"

        - name: GITLAB_SECRETS_DB_KEY_BASE

          value: "long-and-random-alpha-numeric-string"

        - name: GITLAB_SECRETS_SECRET_KEY_BASE

          value: "long-and-random-alpha-numeric-string"

        - name: GITLAB_SECRETS_OTP_KEY_BASE

          value: "long-and-random-alpha-numeric-string"

        - name: DB_HOST

          value: "postgres-svc" #

        - name: DB_NAME

          value: "gitlab"

        - name: DB_USER

          valueFrom:

            secretKeyRef:

              name: gitlab-secret2

              key: username

        - name: DB_PASS

          valueFrom:

            secretKeyRef:

              name: gitlab-secret2

              key: password

        - name: REDIS_HOST

          value: "redis-svc"

        - name: REDIS_PORT

          value: "6379"

        ports:

        - containerPort: 80

        resources:

          limits:

            cpu: 2000m

            memory: 5048Mi

          requests:

            cpu: 100m

            memory: 500Mi

        volumeMounts:

        - mountPath: /home/git/data

          name: gitlab-nfs

        - name: time-localtime

          readOnly: true

          mountPath: /etc/localtime

      volumes:

      - name: time-localtime

        hostPath:

          path: /etc/localtime

  volumeClaimTemplates:

  - metadata:

      name: gitlab-nfs

    spec:

      accessModes: [ "ReadWriteMany" ]

      storageClassName: "nfs-storageclass"

      resources:

        requests:

          storage: 3Gi

jenkins初始wed配置和ci cd部署_第16张图片


部署和注册gitlab完成后点击Create a project创建一个仓库

jenkins初始wed配置和ci cd部署_第17张图片


点击Create Project创建

将gitee上面的代码导入到部署的gitlab私有仓库中


(因为往我们部署的私有仓库里面进行导入代码需要git命令 所有需要在控制或工作节点都下载上git命令 yum install git -y)

去要下载的平台上 在要下载的代码列表里面点击克隆或者下载按钮,复制下载连接地址 

jenkins初始wed配置和ci cd部署_第18张图片

jenkins初始wed配置和ci cd部署_第19张图片

直接在master上下载 让后解压 : unzip +压缩包 (yum search unzip)

把解压出来的代码上传到私密仓库中

在解压出来的代码目录中执行我们部署的gitlab提供的git命令(一定要在本目录执行!!!

jenkins初始wed配置和ci cd部署_第20张图片

jenkins初始wed配置和ci cd部署_第21张图片

Create a new repository创建新的存储库

Push an existing folder推送现有文件夹

Push an existing Git repository推送现有的Git存储库

根据我们的部署我们是要推送gitee下载的代码进行推送,所以使用第二个哐框里面的命令
git init 代码初始化

git remote add origin http://gitlab-svc/root/myblog.git 生成一个git地址 

(在执行这个命令时如果出现fatal: unable to access 'http://gitlab-svc/root/myblog.git/': Could not resolve host: gitlab-svc; Unknown error

需要将此命令的http://gitlab-svc/root/myblog.git修改为内部ip来使用)

git remote -v 查看git地址

git add .

git commit -m "Initial commit" 初始化  git commit -m "updata Dockerfile(指定上传的包)"

git push -u origin master    #出现以下error  在host里面写指定

jenkins初始wed配置和ci cd部署_第22张图片

jenkins和gitlab对接

在jenkins下载gitlab插件(不需要重启)

jenkins初始wed配置和ci cd部署_第23张图片

安装完成后点击系统管理---》系统配置

jenkins初始wed配置和ci cd部署_第24张图片

这里的URL:我写的是gitlab-svc的地址,如果配置了ingress的话写域名

jenkins初始wed配置和ci cd部署_第25张图片

在gitlab创建token

jenkins初始wed配置和ci cd部署_第26张图片

jenkins初始wed配置和ci cd部署_第27张图片

jenkins初始wed配置和ci cd部署_第28张图片

jenkins初始wed配置和ci cd部署_第29张图片

复制创建好的token到jenkins上面

jenkins初始wed配置和ci cd部署_第30张图片

点击Test Connection进行连接测试

ps:如果失败就可能是需要在host主机列表里面书写映射ip

或者kubectl edit -n kube-system cm coredns #里面添加静态映射
hosts {
            192.168.10.30 jenkins.smile.com  gitlab.smile.com
            fallthrough
        }
 想要快速生效就删除
kubectl delete pods -n kube-system coredns-7f89b7bc75-

至此jenkins和gitlab成功打通


在jenkins配置当gitlab代码仓库发生更新后直接下载到本地并推送消息给钉钉

点击新建任务

jenkins初始wed配置和ci cd部署_第31张图片

jenkins初始wed配置和ci cd部署_第32张图片

源码管理选择git

添加凭证

jenkins初始wed配置和ci cd部署_第33张图片

jenkins初始wed配置和ci cd部署_第34张图片


这里的URL:有ingress就写域名 没有就写无头或者svc地址 gitlab的

选择构建触发器

点击  Build when a change is pushed to GitLab. GitLab webhook URL: http://123.60.162.77:32080/project/freeeee?  #当代码发生变化的时候向gitlab进行推送  #因为地址的原因不能直接进行推送,需要配置wedhooks

jenkins初始wed配置和ci cd部署_第35张图片

jenkins初始wed配置和ci cd部署_第36张图片

在wedhooks里面配置上个图片中的url地址进行创建

jenkins初始wed配置和ci cd部署_第37张图片

jenkins初始wed配置和ci cd部署_第38张图片

jenkins初始wed配置和ci cd部署_第39张图片

ps:Url is blocked:Requests to the local network are not allowed

如果出现这个错误:是因为本地网络连接不允许,他是从一个安全方面的进行一个考量

解决方法: 点击此页面左上角的小扳手 点击左边的状态栏 Settings设置 点击里面的network

jenkins初始wed配置和ci cd部署_第40张图片

jenkins初始wed配置和ci cd部署_第41张图片

jenkins点击save

测试:看到200 成功

jenkins初始wed配置和ci cd部署_第42张图片

jenkins初始wed配置和ci cd部署_第43张图片

jenkins初始wed配置和ci cd部署_第44张图片


ps:构建过后的代码存放在jenkins动态存储类中/workspace/freeeee目录中

至此这边只是实现了简单的构建任务,要实现向钉钉推送还需要重新配置一个

jenkins初始wed配置和ci cd部署_第45张图片

jenkins初始wed配置和ci cd部署_第46张图片

钉钉创建自定义机器人!!!

jenkins初始wed配置和ci cd部署_第47张图片

 win 查询公网ip命令:curl ifconfig.me

自定义机器人接入 - 钉钉开放平台 (dingtalk.com)

curl 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx' \
 -H 'Content-Type: application/json' \
 -d '{"msgtype": "text","text": {"content":"我就是我, 是不一样的烟火"}}'

ps⛵️创建钉钉机器人的ip段要尤其注意 !!!!!!正常情况下在本地搭建的需要写的win公网ip 

jenkins初始wed配置和ci cd部署_第48张图片

jenkins初始wed配置和ci cd部署_第49张图片

jenkins添加备节点

 因为jenkins目前只有一个master节点 ,所以的工作都来自于master节点,master处于单点状态!!很危险

jenkins初始wed配置和ci cd部署_第50张图片

创建一个名为k8s-slave1的节点 并勾选固定节点  

jenkins初始wed配置和ci cd部署_第51张图片

jenkins执行任务时候严重依赖标签

Jenkins 可以在此节点上执行并发构建的最大数目 #同时可以跑多个任务

点击save保存  jenkins初始wed配置和ci cd部署_第52张图片jenkins初始wed配置和ci cd部署_第53张图片

点击创建好的节点上面进行配置   

jenkins初始wed配置和ci cd部署_第54张图片

在选订好的节点上进行配置,将其配置成 从节点

Or run from agent command line, with the secret stored in a file:

echo a763cb84fc791b919eb64532910af0e09515c395684ec66b1a231dbed5824091 > secret-file
curl -sO http://123.60.162.77:32080/jnlpJars/agent.jar    
 #因为我部署在云服务器 所以123.60.162.77:32080尽量用内网,如果是本地部署的需要在host里面写ingres地址做映射

java -jar agent.jar -jnlpUrl http://123.60.162.77:32080/manage/computer/k8s%2Dslave1/jenkins-agent.jnlp -secret @secret-file -workDir "/opt/jenkins_jobs"  
在执行此命令同时需要下载java环境yum install java-11-openjdk -y  java --version

jenkins初始wed配置和ci cd部署_第55张图片

java -jar agent.jar -jnlpUrl http://123.60.162.77:32080/manage/computer/k8s%2Dslave1/jenkins-agent.jnlp -secret @secret-file -workDir "/opt/jenkins_jobs"  
当在本地或者云服务器部署执行此命令的时候会出现连接不上(50000端口)

解决办法:

jenkins初始wed配置和ci cd部署_第56张图片

jenkins初始wed配置和ci cd部署_第57张图片

(记得写完成加端口号50000 10.99.93.125:50000)点击保存就好  

jenkins初始wed配置和ci cd部署_第58张图片

jenkins初始wed配置和ci cd部署_第59张图片

成功(可以写多个从节点)

但是因为java -jar agent.jar -jnlpUrl http://123.60.162.77:32080/manage/computer/k8s%2Dslave1/jenkins-agent.jnlp -secret @secret-file -workDir "/opt/jenkins_jobs"  
给定的命令是前台运行不符合工作使用,很不方便 所以要将其修改为后台运行!
nohup java -jar agent.jar -jnlpUrl http://123.60.162.77:32080/manage/computer/k8s%2Dslave1/jenkins-agent.jnlp -secret @secret-file -workDir "/opt/jenkins_jobs" &

jenkins初始wed配置和ci cd部署_第60张图片

 让后修改我们直接构建的任务 让他能够使用从节点

jenkins初始wed配置和ci cd部署_第61张图片

jenkins初始wed配置和ci cd部署_第62张图片

点击保存

在开始构建之前要在从节点上安装git命令 因为我们需要从仓库克隆下面,需要用到git

yum install git -y  

jenkins初始wed配置和ci cd部署_第63张图片

jenkins初始wed配置和ci cd部署_第64张图片

以上都是我们手动触发的任务,我们要的是实现自动触发

测试:往gitlab仓库里面上传代码

将准备好的代码或者dockerfile上传到master节点上面的仓库目录里面  

git status #查看是否有新的文件需要上传
git add .
git status
git commit -am 'add Dockerfile'
git push #输入gitlab密码和账户进行上传

jenkins初始wed配置和ci cd部署_第65张图片

jenkins初始wed配置和ci cd部署_第66张图片

jenkins初始wed配置和ci cd部署_第67张图片

 呈上启下 :以上的操作仅仅只是将代码从仓库里面下载到了本地 没有其他的操作

但是我们希望把代码下载到本地后的通过制作镜像,上传到镜像仓库 ,并部署到k8s上面!

流水线:

Scripted Pipeline 脚本式流水线 最初的形态

Declarative Pipeline: 声明式流水线 (类似于Dockerfile)

pipeline脚本:的实现方式是通过Groovy DSL (领域专用语言)写的,所有的发布流程都可以有一段Groovy脚本,通过wed.ui的方式表述出来

构建并部署单分支流水线(pipeline)

ps:一个程序员在自己的node boot上面对代码进行了更新,把代码推送到gitlab上面,由我们部署的jenkins自动的获取到我们更新的代码, gitlab和jenkins要产生关联/一个认证的关系

jenkins所有的功能基本上都是以插件的形势来实现的

触发器(jenkins):当gitlab仓库代码发生更新的时候会自动的触发jenkins进行代码拉取,jenkins之所以能够拉取gitlab仓库实时更新的代码是因为构建了一个任务类型的触发器

gitlab_wed_hooks: jenkins触发器是通过gitlab的wed-hooks来进行勾连,或对接

只要代码一发生改变就会触发流水线

jenkins ---》往gitlab拉取更新的代码 ---》让后制作成镜像 ---》将镜像上传到镜像仓库中(私有公有)---》 部署到k8s 实现对外的访问

pipeline { 
    agent {label 'k8s-slave1'}       #any 可以写多个从节点,在任意多个从节点上运行
    environment { 
        PROJECT = 'myblog'
    }
    stages { 
        stage('Checkout') {                       #第一步  获取代码
            steps { 
                checkout scm     #检测代码  scm是一个特殊代码 由pipeline特殊执行     
            }
        }
        stage('Build') {                          #第二步   通过代码构建镜像
            steps { 
                sh 'make' 
            }
        }
        stage('Test'){                          #第三步 上传镜像至镜像仓库(harbor)
            steps {
                sh 'make check'
                junit 'reports/**/*.xml' 
            }
        }                               
        stage('Deploy') {                      #第四步   把镜像部署在K8S集群中
            steps {
                sh 'make publish'
            }
        }
    }
	post {                                     #声明  定义一个steps或多个steps 根据流水线的阶段来进行判断 成功发什么,失败了又发什么
        success { 
            echo 'Congratulations!'
        }
		failure { 
            echo 'Oh no!'
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

声明式流水线范例:
pipeline {
   agent {label 'k8s-slave1'} #agent标签代理,声明是由那个agent(从节点)来执行,label标签跟从节点配置的标签一致  #通过标签来选择   属于全局定义   可以在每个stages阶段下面写agent来单独声明由那个节点执行  ! 
   environment {              #环境变量
      PROJECT = 'myblog'
   }
   stages {                   #阶段 分段执行   一个stages里面可以包含多个stage
      stage('printenv') {      #将本地的所有环境变量都输出出来
         steps {
            echo 'Hello World'
            sh 'printenv'
         }
      }
      stage('check') {         #当gitlab代码仓库内容发生改变的话,jenkins自动获取拉取代码
         steps {
            checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.smile.com/root/myblog.git']])   #可以通过jenkins自动生成
         }
      }
      stage('build-image') {    
         steps {
            sh 'docker build . -t myblog:latest -f Dockerfile'
         }
      }
      stage('send-msg') {
         steps {
            sh """
            curl 'https://oapi.dingtalk.com/robot/send?access_token=' \
   -H 'Content-Type: application/json' \
   -d '{"msgtype": "text", 
        "text": {
             "content": "您有新的代码更新"
        }
      }'
      """
         }
      }
   }
}

options :允许从流水线内部配置特定于流水线的选项  {timeout: 设置流水线的运行超时时间,工作阶段一般会在30分钟 }

书写格式: options { timent(time: 1, unit: 'HOURS')} 设置允许超时时间为一个小时

restry: 可以失败的次数 书写方式: options { retry(3) }s

在构建单分支流水线之前,选择禁用之前创建的任务

jenkins初始wed配置和ci cd部署_第68张图片

jenkins初始wed配置和ci cd部署_第69张图片

jenkins初始wed配置和ci cd部署_第70张图片

jenkins初始wed配置和ci cd部署_第71张图片

jenkins初始wed配置和ci cd部署_第72张图片

创建流水线的方式有两种:

1.在wed UI上面直接书写脚本Pipeline script  

2.不需要在jenkins wed-ui里面书写脚本,通过设置Pipeline script from SCM在节点上面书写脚本上传到gitlab仓库

使用wed ui的方式实现Pipeline流水线:

 checkout scmGit:
 {checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.smile.com/root/myblog.git']])} #此代码可以使用jenkins自行生成

点击Pipeline流水线下面的流水线语法----》 找到并点击checkout  

jenkins初始wed配置和ci cd部署_第73张图片

jenkins初始wed配置和ci cd部署_第74张图片

jenkins初始wed配置和ci cd部署_第75张图片

  

jenkins初始wed配置和ci cd部署_第76张图片

ps⛵️:请自行将Dockerfile文件上传致要执行的节点上面并上传到gitlab仓库中

master(源码存放节点):git commit -am 'updata Dockerfile'

cat Dockerfile 
# Base images 基础镜像
FROM centos:centos7.5.1804

#MAINTAINER 维护者信息
LABEL maintainer="[email protected]"

#ENV 设置环境变量
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8

#RUN 执行以下命令
RUN curl -so /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo && rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
RUN yum install -y  python36 python3-devel gcc pcre-devel zlib-devel make net-tools nginx

#工作目录
WORKDIR /opt/myblog

#拷贝文件至工作目录
COPY . .   #把当前node1里面的内容直接copy到/opt/jen_jobs

# 拷贝nginx配置文件
COPY myblog.conf /etc/nginx

#安装依赖的插件
RUN pip3 install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt

RUN chmod +x run.sh && rm -rf ~/.cache/pip

#EXPOSE 映射端口
EXPOSE 8002

#容器启动时执行命令
CMD ["./run.sh"]

 点击save保存 并点击立即构建

jenkins初始wed配置和ci cd部署_第77张图片

jenkins初始wed配置和ci cd部署_第78张图片

以上操作完成任务的描述: 从代码仓库下载代码到本地 然后通过dockerfile构建镜像

流水线的图形界面形势来查看整体

ps:还可以通过Pipeline流水线的图形界面形势来查看整体的结果

#缺点:对于阶段执行的描述不是特别完整

jenkins初始wed配置和ci cd部署_第79张图片

jenkins初始wed配置和ci cd部署_第80张图片

 在Pipeline流水线的图形界面时,当个别阶段执行出错/失败的时候可以单独的重启此阶段让他重新跑完

2.通过设置Pipeline script from SCM在节点上面书写Pipeline脚本上传到gitlab仓库

Pipeline script from SCM:  

jenkins初始wed配置和ci cd部署_第81张图片

将jenkinsfile 上传到源码节点的存放目录里面 ,并上传到gitlab里面
cat Jenkinsfile 
pipeline {
   agent {label 'k8s-slave1'}
   environment { 
      PROJECT = 'myblog'
   }
   stages {
      stage('printenv') {
         steps {
            echo 'Hello World'
            sh 'printenv'
         }
      }
      stage('check') {
         steps {
            checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.smile.com/root/myblog.git']])
         }
      }
      stage('build-image') {
         steps {
            sh 'docker build . -t myblog:latest -f Dockerfile'
         }
      }
      stage('send-msg') {
         steps {
            sh """
            curl 'https://oapi.dingtalk.com/robot/send?access_token=' \
   -H 'Content-Type: application/json' \
   -d '{"msgtype": "text", 
        "text": {
             "content": "您有新的代码更新"
        }
      }'
      """
         }
      }
   }
}
git add .
git remote -v
git status 
git push

jenkins初始wed配置和ci cd部署_第82张图片

优化:Pipeline流水线优化

 从jenkinsfile: 
 问题1.:sh 'docker build . -t myblog:latest -f Dockerfile'    #tag latest
 以上的build构建的镜像因为每次构建的tag都是latest容易被覆盖  造成悬空镜像 永远只有一个镜像,造成版本无法后滚操作,当新版本出现问题之后无法回退
 解决方法:使用环境变量myblog:${GIT_COMMIT}   是gitlab自带的
 我们可以以{commit}id来作为镜像的版本号来使用 #tag
  sh 'docker build . -t myblog:${GIT_COMMIT}  -f Dockerfile' 

jenkins初始wed配置和ci cd部署_第83张图片

jenkins初始wed配置和ci cd部署_第84张图片

在源码存放节点 jenkinsfile文件中进行修改
将jenkinsfile 上传到源码节点的存放目录里面 ,并上传到gitlab里面
cat Jenkinsfile 
pipeline {
   agent {label 'k8s-slave1'}
   environment { 
      PROJECT = 'myblog'
   }
   stages {
      stage('printenv') {
         steps {
            echo 'Hello World'
            sh 'printenv'
         }
      }
      stage('check') {
         steps {
           checkout scm
         }
      }
      stage('build-image') {
         steps {
            sh 'docker build . -t myblog:${GIT_COMMIT} -f Dockerfile'
         }
      }
      stage('send-msg') {  
         steps {
            sh """
            curl 'https://oapi.dingtalk.com/robot/send?access_token=' \
   -H 'Content-Type: application/json' \
   -d '{"msgtype": "text", 
        "text": {
             "content": "您有新的代码更新"
        }
      }'
      """
         }
      }
   }
}
git add .
git remote -v
git commit -am 'updata Jenkinsfile'
git status 
git push

问题2.stage('send-msg') {  #美化   
向钉钉发送消息,使始终是以stage的形势来发送  ,如果错了就不会继续执行下面的消息 但是我们希望正确的发送就发送正确,错误就发送失败了,然后不会影响下面的执行  
这个时候就用到了post:(根据流水线或者阶段的完成情况而运行而运行post)
cat Jenkinsfile  #在源代码存放节点
pipeline {
    agent { label 'k8s-slave1'}

    stages {
        stage('printenv') {
            steps {
            echo 'Hello World'
            sh 'printenv'
            }
        }
        stage('check') {
            steps {
                checkout scm
            }
        }
        stage('build-image') {
            steps {
            	retry(2) { sh 'docker build . -t myblog:${GIT_COMMIT}'}  #可以失败两次
            }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=' \
                    -H 'Content-Type: application/json' \
                    -d '{"msgtype": "text", 
                            "text": {
                                "content": "构建成功\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"
                        }
                }'
            """
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=' \
                    -H 'Content-Type: application/json' \
                    -d '{"msgtype": "text", 
                            "text": {
                                "content": "❌构建失败❌\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"
                        }
                }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}
git commit -am 'updata Jenkinsfile'
git status 
git push

jenkins初始wed配置和ci cd部署_第85张图片

将构建的镜像上传到镜像仓库中(私有/公有)

公有镜像仓库 dockerhub

想要上传镜像首先要给镜像修改名字

docker tag myblog:4ba71b845bc07a86e7ad9101bf1a2227be43c896 1008611zmx/myblog:v1.0

docker login -u 1008611zmx -p

docker push 1008611zmx/myblog:v1.0
cat Jenkinsfile 
pipeline {
    agent { label 'k8s-slave1'}

    stages {
        stage('printenv') {
            steps {
            echo 'Hello World'
            sh 'printenv'
            }
        }
        stage('check') {
            steps {
                checkout scm
            }
        }
        stage('build-image') {
            steps {
            	retry(2) { sh 'docker build . -t 1008611zmx/myblog:${GIT_COMMIT}'}
            }
        }
        stage('builid-image') {
            steps {
                retry(2) { sh 'docker login -u 1008611zmx -p '}
                retry(2) { sh 'docker push 1008611zmx/myblog:${GIT_COMMIT}'}
            }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=' \
                    -H 'Content-Type: application/json' \
                    -d '{"msgtype": "text", 
                            "text": {
                                "content": "构建成功\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"
                        }
                }'
            """
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=' \
                    -H 'Content-Type: application/json' \
                    -d '{"msgtype": "text", 
                            "text": {
                                "content": "❌构建失败❌\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"
                        }
                }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}
git commit -am 'updata Jenkinsfile'
git status 
git push

jenkins初始wed配置和ci cd部署_第86张图片

持续优化: 账户和密码 'docker login -u  -p ' 和钉钉发送消息的token值 暴漏,存在安全隐患

创建两个包含docker账号密码和钉钉的token值的凭证 #通过环境变量的方式进行调用

jenkins初始wed配置和ci cd部署_第87张图片

jenkins初始wed配置和ci cd部署_第88张图片

jenkins初始wed配置和ci cd部署_第89张图片

jenkins初始wed配置和ci cd部署_第90张图片

jenkins初始wed配置和ci cd部署_第91张图片

environment {
    BITBUCKET_COMMON_CREDS = credentials('jenkins-bitbucket-common-creds')
}
实际设置中包含了三个变量:
 BITBUCKET_COMMON_CREDS  -包含一个以冒号分隔的用户名和密码,格式为username:password
 BITBUCKET_COMMON_CREDS_USR -附加的一个仅包含用户名部分的变量
 BITBUCKET_COMMON_CREDS_PSW -附加的一个仅包含密码部分的变量
cat Jenkinsfile 
pipeline {
    agent { label 'k8s-slave1'}
    environment {
       DOCKERHUB_CREDS = credentials('dockerhub')
       DINGTALK_CREDS = credentials('dingtalk')
    }
    stages {
        stage('printenv') {
            steps {
            echo 'Hello World'
            sh 'printenv'
            }
        }
        stage('check') {
            steps {
                checkout scm
            }
        }
        stage('build-image') {
            steps {
            	retry(2) { sh 'docker build . -t 1008611zmx/myblog:${GIT_COMMIT}'}
            }
        }
        stage('push-image') {
            steps {
                retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}
                retry(2) { sh 'docker push 1008611zmx/myblog:${GIT_COMMIT}'}
            }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{"msgtype": "text", 
                            "text": {
                                "content": "????构建成功????\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"
                        }
                }'
            """
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{"msgtype": "text", 
                            "text": {
                                "content": "???构建失败???\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"
                        }
                }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

jenkins初始wed配置和ci cd部署_第92张图片

私有镜像仓库 harbor

curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
yum install docker-compose -y
tar -zxvf harbor-offline-installer-v2.5.3.tgz 
配置 :harbor.yml
安装./install.sh   

通过流水线构建的镜像上传到镜像仓库中部署到k8s 实现对外的访问

导入准备好的yaml文件 ;deploy-myblog.yaml deploy-mysql.yaml

先执行deploy-mysql.yaml

        stage('deploy') {
            steps {
                sh "sed -i 's#{{IMAGE}}#1008611zmx/myblog:${GIT_COMMIT}#g' deploy/*"   #deploy/*是指在deploy目录下面存放的yaml文件都修改并执行   deploy/*存放yaml的目录  /*匹配所有
cat Jenkinsfile 
pipeline {
    agent { label 'k8s-slave1'}   #######
    environment {
       DOCKERHUB_CREDS = credentials('dockerhub')
       DINGTALK_CREDS = credentials('dingtalk')
    }
    stages {
        stage('printenv') {
            steps {
            echo 'Hello World'
            sh 'printenv'
            }
        }
        stage('check') {
            steps {
                checkout scm
            }
        }
        stage('build-image') {
            steps {
            	retry(2) { sh 'docker build . -t 1008611zmx/myblog:${GIT_COMMIT}'}
            }
        }
        stage('push-image') {
            steps {
                retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}
                retry(2) { sh 'docker push 1008611zmx/myblog:${GIT_COMMIT}'}
            }
        }
        stage('deploy') {
            steps {
                sh "sed -i 's#{{IMAGE_URL}}#1008611zmx/myblog:${GIT_COMMIT}#g' deploy/deploy-myblog.yaml"
                timeout(time: 1, unit: 'MINUTES') {
                    sh "kubectl apply -f deploy/deploy-myblog.yaml"
                }
            }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{"msgtype": "text", 
                            "text": {
                                "content": "构建成功\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"
                        }
                }'
            """
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{"msgtype": "text", 
                            "text": {
                                "content": "❌构建失败❌\n 关键字:smile\n 项目名称: ${JOB_BASE_NAME}\n Commit Id: ${GIT_COMMIT}\n 构建地址:${RUN_DISPLAY_URL}"
                        }
                }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}
git add .   #因为加入的有新的目录
git status
git commit -am 'add depoly'
git push

 ps️:因为我们是写的流水线脚本是从节点上执行的,但是从节点上面没有kubectl命令,所以会报错

解决办法:  在从节点上面创建/root/.kube 把master节点上面/root/.kube目录里面的config文件copy到从节点上面 从节点就可以使用k8s命令有了k8s权限  #但是极其不推荐,相当于从节点有了master权限
cat deploy-myblog.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: develop
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myblog
  namespace: develop
spec:
  replicas: 1   #指定Pod副本数
  selector:             #指定Pod的选择器
    matchLabels:
      app: myblog
  template:
    metadata:
      labels:   #给Pod打label
        app: myblog
    spec:
      containers:
      - name: myblog
        image: {{IMAGE_URL}} ## 修改
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_HOST
          value: "10.104.238.30"
        - name: MYSQL_PORT
          value: "3306"
        #- name: MYSQL_USER
          #value: "root"
        - name: MYSQL_PASSWD
          value: "123456"
        ports:
        - containerPort: 8002
        resources:
          requests:
            memory: 100Mi
            cpu: 50m
          limits:
            memory: 500Mi
            cpu: 100m
        livenessProbe:
          httpGet:
            path: /blog/index/
            port: 8002
            scheme: HTTP
          initialDelaySeconds: 10  # 容器启动后第一次执行探测是需要等待多少秒
          periodSeconds: 15     # 执行探测的频率
          timeoutSeconds: 2             # 探测超时时间
        readinessProbe: 
          httpGet: 
            path: /blog/index/
            port: 8002
            scheme: HTTP
          initialDelaySeconds: 10 
          timeoutSeconds: 2
          periodSeconds: 15
git commit -am 'updata Jenkinsfile'
git push

jenkins初始wed配置和ci cd部署_第93张图片

至此完成!

多分支流水线的构建

测试环境                生产环境  

测试/生产环境通过命名空间namespace进行区分

ps:当部署的gpmall的项目出现了问题,要在测试环境进行调整/测试,完成后与生产环境的代码相互集成校验

代码提交的之后代码应该先往测试环境部署进行测试 (代码严格禁止在源代码上进行修改,修改源代码的复制品),如果测试环境的代码没有问题的话,在将其通过生产流水线,部署到生产环境中去,如果生产环境下的代码出现问题后,要修改测试环境的源代码的复制品进行更新,进行循环测试,当更新的代码没有问题后,把源代码复制器的更新,真正的源代码进行更新,进行合并,推送给生产环境的流水线进行部署!

根据以上定义:我们至少需要两条流水线

我们可以构建两条流水线,但是这样的方式很不好,不利于我们后面的持续集成,从而引出多分支!  

jenkins初始wed配置和ci cd部署_第94张图片

先将之前构建的单分支流水线进行禁用操作~  

jenkins初始wed配置和ci cd部署_第95张图片

然后在源代码存放的节点上面创建代码仓库分支
git log 查看操作步骤
git branch                  查看分支   * 指到/处于那个分支就表示正在那个分支上面

git checkout -b develop     创建名为develop的分支
:Switched to a new branch 'develop'  #产生了一个新的分支

git checkout master         切换分支  到master上面
:Switched to branch 'master'

git branch 
  develop
* master

git push --set-upstream origin develop   上传/更新分支到代码仓库上面
在develop分支上面修改的内容不会影响到master分支的代码仓库

git merge 代码合并

jenkins初始wed配置和ci cd部署_第96张图片

jenkins初始wed配置和ci cd部署_第97张图片

配置多分支流水线

发现分支 add 选择正则表达式 因为只有几个分支所有就写.* 匹配所有 当分支多的时候可以写成 master|develop这种格式

jenkins初始wed配置和ci cd部署_第98张图片

jenkins初始wed配置和ci cd部署_第99张图片

点击保存会自动扫描  

jenkins初始wed配置和ci cd部署_第100张图片

效果实现: 我们在源代码节点上面修改master分支的代码 会触发jenkins 多分支流水线中master任务

                   我们在源代码节点上面修改develog分支的代码 会触发jenkins 多分支流水线中develog任务

 在原来单分支流水线上面的的流水线脚本进行美化

将Jenkinsfile写好的脚本上传到develop分支上面  #把原来的Jenkinsfile转移到其他的目录里面保证当我们上传的新的流水线脚本发现问题会后能够回滚
pipeline {
    agent { label 'k8s-slave1'}

    environment {
        IMAGE_REPO = "1008611zmx/myblog"
        DINGTALK_CREDS = credentials('dingtalk')
        DOCKERHUB_CREDS = credentials('dockerhub')
        TAB_STR = "\n                    \n                    "  #换行
    }

    stages {
        stage('printenv') {
            steps {
                script{
                    sh "git log --oneline -n 1 > gitlog.file"  #显示第一行到gitlog里面
                    env.GIT_LOG = readFile("gitlog.file").trim() #env.GIT_LOG 读取gitlog.file  trim的意思是修剪 美化 
                }
                sh 'printenv'
            }
        }
        stage('checkout') {
            steps {
                checkout scm
                script{
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR 
                }
            }
        }
        stage('build-image') {
            steps {
                retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR #(a=沙
a += 淼   +=是在原来的基础上对变量的新一轮赋值
a=沙淼)
                }
            }
        }
        stage('push-image') {
            steps {
                retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}
                retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('deploy') {
            steps {
                sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"
                timeout(time: 1, unit: 'MINUTES') {
                    sh "kubectl apply -f deploy/"
                }
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": " 构建成功   \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}   \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """ 
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": "❌ 构建失败 ❌  \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}
git log 将源代码存放目录里面的log目录上传到代码仓库中 
         git commit -am 'add log'  git push

例子:(可copy)
cat Jenkinsfile 
pipeline {
    agent { label 'k8s-slave1'}

    environment {
        IMAGE_REPO = "1008611zmx/myblog"
        DINGTALK_CREDS = credentials('dingtalk')
        DOCKERHUB_CREDS = credentials('dockerhub')
        TAB_STR = "\n                    \n                    "
    }

    stages {
        stage('printenv') {
            steps {
                script{
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                }
                sh 'printenv'
            }
        }
        stage('checkout') {
            steps {
                checkout scm
                script{
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('build-image') {
            steps {
                retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('push-image') {
            steps {
                retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}
                retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('deploy') {
            steps {
                sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"
                timeout(time: 1, unit: 'MINUTES') {
                    sh "kubectl apply -f deploy/"
                }
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": " 构建成功   \n**项目名称**:smile 33 \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}   \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """ 
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": "❌ 构建失败 ❌  \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

jenkins初始wed配置和ci cd部署_第101张图片

jenkins初始wed配置和ci cd部署_第102张图片

gitlab做代码合并 通知gitlab构建状态 Merge 代码合并  

jenkins初始wed配置和ci cd部署_第103张图片

将下面的代码部分替换到上面写好的Jenkinsfile里面  57dd行
cat Jenkinsfile 
pipeline {
    agent { label 'k8s-slave1'}
    
    options {
		buildDiscarder(logRotator(numToKeepStr: '10'))
		disableConcurrentBuilds()
		timeout(time: 20, unit: 'MINUTES')
		gitLabConnection('10.99.69.119')  #定义三个参数   多分支为代码合并做准备  连接gitlab让我们在gitlab中看到jenkins流水线执行的状态
	}

    environment {
        IMAGE_REPO = "1008611zmx/myblog"
        DINGTALK_CREDS = credentials('dingtalk')
        DOCKERHUB_CREDS = credentials('dockerhub')
        TAB_STR = "\n                    \n                    "
    }

    stages {
        stage('printenv') {
            steps {
                script{
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                }
                sh 'printenv'
            }
        }
        stage('checkout') {
            steps {
                checkout scm
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')  #向gitlab发送流水线执行状态
                script{
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR       #钉钉的消息通知 如果checkout执行成功就写一个这个,向钉钉发送消息
                }
            }
        }
        stage('build-image') {
            steps {
                retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('push-image') {
            steps {
                retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}
                retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('deploy') {
            steps {
                sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"
                timeout(time: 1, unit: 'MINUTES') {
                    sh "kubectl apply -f deploy/"
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
    }

    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": " 构建成功   \n**项目名称**:smile 33 \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}   \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """ 
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": "❌ 构建失败 ❌  \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

由于我们配置了gitlab的互联 通知gitlab的构建状态 可以在gitlab commit里面看到完整的gitlab构建是否成功 每个构建步骤都可以从这里跳转到jenkins的图形界面 确定运行中没有问题 ,但是这个仅仅至少依据,不能确保代码是否真的有问题 可以作为代码合并的依据  

jenkins初始wed配置和ci cd部署_第104张图片

jenkins初始wed配置和ci cd部署_第105张图片

把deploy yaml文件添加命名空间 把所有的服务和代码在命名空间下运行 让后隔离测试 ,让后把代码合并到master就可以更新了

多分支流水线的进阶

现在基与整个jenkins不管是单/多流水线,还是自由构建的项目,首先要依赖于agent (标签代理)

我们在jenkins上配置了从节点, 将任务都尽量的在从节点上部署,那是因为master节点只要一出问题那麽就会导致整个jenkins服务的崩坏,但是要想在从节点上部署工作上的服务进行测试或者生产 ,就要给从节点master权限,但是在这样在k8s集群中是一种高度风险的操作(高危),就算我们可以单独在集群中拿出两道三台的从节点来运行任务 但是还是会带来一个新的问题,流水线不是24小时都会有流水的,只有当代码发生了更新之后才会有触发任务,当没有流水的时候机器也不可能一直的开着会造成资源的浪费 (造成平时没有代码更新的时候没有任何的流水线任务,但是一旦发送了代码更新之后就会连带这一大批的代码任务,就会可能造成节点不够用)

 实现的目的:当我们需要流水线的时候我们就基与流水线的pod就创建出来,当不需要的时候就把pod资源删除
或者说:有代码更新触发流水线的时候,创建基与流水线的jenkins的pods,这个pod负责真个流水线的运行,当整个流水线任务完成后把jenkins的pod自动的删除   (前提条件是必须要和k8s集群对接)
好处:1.资源不会被浪费  2.就算很多时候一瞬间需要构建的任务或者一个节点承受不住的时候,也可以自动的创建出多个pods来抵挡 ,jenkins和k8s结合的很紧密

jenkins对接k8s  

jenkins初始wed配置和ci cd部署_第106张图片

jenkins初始wed配置和ci cd部署_第107张图片

创建对接k8s下的pod模板  

jenkins初始wed配置和ci cd部署_第108张图片

jenkins初始wed配置和ci cd部署_第109张图片

jenkins初始wed配置和ci cd部署_第110张图片

ps:如果工作空间卷选择的是host Path 本地存储的时候 就需要让pod设置成只运行在host path本地存储的节点上面才能读取数据

我们使用的是本地存储 在工作空间卷上面配置节点选择器 通过给所选的从节点上面打标签来实现

在master节点上面执行:kubectl label nodes master-node1 agent=true
                   同时在jenkins wed界面上面设置主机路径为绝对路径 
                   并修改从节点的主机路径目录的权限chown -R 1000:1000 /opt/jenkins_jobs/  
#是因为我们在创建jenkins pod的时候yaml将权限修改为了1000,如果不修改权限的话进来的时候直接就会挂root,那和我们的pod和jenkins的pod对连的时候直接就会报错!!

jenkins初始wed配置和ci cd部署_第111张图片

 如果工作空间卷选择pvc的话,就需要创建一个pvc将pvc的值写在声明值里面

点击save #注意保存完成后将jenkinsfile脚本中的agent 修改为jnlp-slave 才能识别

修改完agent后 在源代码节点的目录中 
git commit -am 'change slave to k8s pod'
git push
https://hub.docker.com/r/jenkins/inbound-agent
# git merge 代码合并

jenkins初始wed配置和ci cd部署_第112张图片

 但是这样下载的jenkins-inbound-agent的镜像中没有docker命令,会报错!

解决方法: 1.直接在jenkins-inbound-agent镜像中安装docker命令

                   2.直接做两个pod

(在一个pod中搞两个容器 jenkins-inbound 作用:和jenkins来进行对接

                                        tools 工具容器 作用:在这个容器中安装各种各样的工具

比如:tools-pyhon的容器环境 java-tools的容器环境等等 我们也可以直接把环境和docker命令等等的东西直接安装在jenkins-inbound但是这样的话就不够灵活 )

这里选择第二种:制作tools工具容器

在master节点 创建tools
mkdir tools
制作的tools工作容器中要有git命令 .kube/config 加入master权限 docker命令  和kubectl
 which kubectl 
/usr/bin/kubectl
[root@master-jenkins tools]# cp `which kubectl` .
[root@master-jenkins tools]# ls
kubectl
[root@master-jenkins tools]# cp /root/.kube/config .
[root@master-jenkins tools]# ls
config  kubectl
[root@master-jenkins tools]# touch Dockerfile
# FROM alpine alpine的特点是小  run=apk

cat Dockerfile 
FROM alpine
LABEL maintainer="1234567890.com"
USER root

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
    apk update && \
    apk add  --no-cache openrc docker git curl tar gcc g++ make \
    bash shadow openjdk8 python2 python2-dev py-pip python3-dev openssl-dev libffi-dev \
    libstdc++ harfbuzz nss freetype ttf-freefont chromium chromium-chromedriver && \
    mkdir -p /root/.kube && \
    usermod -a -G docker root

COPY config /root/.kube/

RUN rm -rf /var/cache/apk/*

#-----------------安装 kubectl--------------------#
COPY kubectl /usr/local/bin/
RUN chmod +x /usr/local/bin/kubectl
# ------------------------------------------------#
docker build -t 1008611zmx/tools:v1.0 .  #docker push 1008611zmx/tools:v1.0 可以直接上传到工有镜像仓库中

在pod-template中添加 容器 Container Template

jenkins初始wed配置和ci cd部署_第113张图片

jenkins初始wed配置和ci cd部署_第114张图片 jenkins初始wed配置和ci cd部署_第115张图片

 docker inspect jenkins/inbound-agent:3107.v665000b_51092-4 查看容器的详细信息

 jenkins初始wed配置和ci cd部署_第116张图片

添加卷: 是因为tools这个容器想要运行必须调用docker.sock 安全套接字ll /var/run/docker.sock,要在添加卷的主机路径和挂载路径中挂载他  

jenkins初始wed配置和ci cd部署_第117张图片

 点击save

然后修改源代码仓库中的jenkinsfile,进行调用tools

cat Jenkinsfile 
pipeline {
    agent { label 'jnlp-slave'}
    
    options {
		buildDiscarder(logRotator(numToKeepStr: '10'))
		disableConcurrentBuilds()
		timeout(time: 20, unit: 'MINUTES')
		gitLabConnection('10.99.69.119')
	}

    environment {
        IMAGE_REPO = "1008611zmx/myblog"
        DINGTALK_CREDS = credentials('dingtalk')
        DOCKERHUB_CREDS = credentials('dockerhub')
        TAB_STR = "\n                    \n                    "
    }

    stages {
        stage('printenv') {
            steps {
                script{
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                }
                sh 'printenv'
            }
        }
        stage('checkout') {
            steps {
                container('tools') {   #调用这个容器
                    checkout scm
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('build-image') {
            steps {
                container('tools') {
                    retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('push-image') {
            steps {
                container('tools') {
                    retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}
                    retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('deploy') {
            steps {
                container('tools') {
                    sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"
                    timeout(time: 1, unit: 'MINUTES') {
                        sh "kubectl apply -f deploy/"
                    }
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": " 构建成功   \n**项目名称**:smile 33 \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}   \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """ 
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": "❌ 构建失败 ❌  \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}
git commit -am 'change slave to k8s pod'

jenkins初始wed配置和ci cd部署_第118张图片

 至此具备小型工作的jenkins雏形已经具备

集成sonarQube实现 代码扫描

SonarQube 10.0 #merge 代码合并

jenkins: 在ci/cd中起到管理调度的作用

ps:为什么需要多分支流水线 :是因为在工作环境中不可能只有一个环境,而是至少有两个

测试环境和生产环境

代码提交之后的代码应该先往测试环境部署进行测试 (代码严格禁止在源代码上进行修改,修改源代码的复制品),如果测试环境的代码没有问题的话,在将其通过生产流水线,部署到生产环境中去,如果生产环境下的代码出现问题后,要修改测试环境的源代码的复制品进行更新,进行循环测试,当更新的代码没有问题后,把源代码复制器的更新,真正的源代码进行更新,进行合并,推送给生产环境的流水线进行部署!

ingress --金丝雀发布 --灰度发布 会对新版和旧版的流量进行一个分流 新版本抢先体验

Sonar可以从以下七个维度检测代码质量,而作为开发人员至少需要处理前5种代码质量问题。

1. 不遵循代码标准
   sonar可以通过PMD,CheckStyle,Findbugs等等代码规则检测工具规范代码编写。
2. 潜在的缺陷
   sonar可以通过PMD,CheckStyle,Findbugs等等代码规则检测工具检 测出潜在的缺陷。
3. 糟糕的复杂度分布
   文件、类、方法等,如果复杂度过高将难以改变,这会使得开发人员 难以理解它们, 且如果没有自动化的单元测试,对于程序中的任何组件的改变都将可能导致需要全面的回归测试。
4. 重复
   显然程序中包含大量复制粘贴的代码是质量低下的,sonar可以展示 源码中重复严重的地方。
5. 注释不足或者过多
   没有注释将使代码可读性变差,特别是当不可避免地出现人员变动 时,程序的可读性将大幅下降 而过多的注释又会使得开发人员将精力过多地花费在阅读注释上,亦违背初衷。
6. 缺乏单元测试
   sonar可以很方便地统计并展示单元测试覆盖率。
7. 糟糕的设计
   通过sonar可以找出循环,展示包与包、类与类之间的相互依赖关系,可以检测自定义的架构规则 通过sonar可以管理第三方的jar包,可以利用LCOM4检测单个任务规则的应用情况, 检测耦合。

sonarqube架构简介

jenkins初始wed配置和ci cd部署_第119张图片

  1. CS架构

    • sonarqube scanner

    • sonarqube server

  2. SonarQube Scanner 扫描仪在本地执行代码扫描任务

  3. 执行完后,将分析报告被发送到SonarQube服务器进行处理

  4. SonarQube服务器处理和存储分析报告导致SonarQube数据库,并显示结果在UI中

  5. 能够检测很多的代码 java python等等的

sonarqube on kubernetes环境搭建

  1. 资源文件准备

sonar/sonar.yaml

  • 和gitlab共享postgres数据库

  • 使用ingress地址 sonar.smile.com 进行访问

  • 使用initContainers进行系统参数调整

安装sonarqube服务端

apiVersion: v1
kind: Service
metadata:
  name: sonarqube-svc
  namespace: jenkins
  labels:
    app: sonarqube
spec:
  type: NodePort
  ports:
  - name: sonarqube
    port: 9000
    targetPort: 9000
    protocol: TCP
    nodePort: 32086
  clusterIP: 10.107.89.235
  selector:
    app: sonarqube
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: jenkins
  name: sonarqube
  labels:
    app: sonarqube
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sonarqube
  template:
    metadata:
      labels:
        app: sonarqube
    spec:
      #nodeSelector:
      #  sonar: "true"
      initContainers:
      - command:
        - /sbin/sysctl
        - -w
        - vm.max_map_count=262144  # 调整虚拟内存
        image: alpine:3.6
        imagePullPolicy: IfNotPresent
        name: elasticsearch-logging-init
        resources: {}
        securityContext:    #开启特权级  root权限 调整虚拟内存需要开启root模式
          privileged: true
      containers:
      - name: sonarqube
        image: sonarqube:7.9-community
        ports:
        - containerPort: 9000
        env:
        - name: SONARQUBE_JDBC_USERNAME
          valueFrom:
            secretKeyRef:
              name: gitlab-secret2
              key: username
        - name: SONARQUBE_JDBC_PASSWORD
          valueFrom:
            secretKeyRef:
              name: gitlab-secret2
              key: password
        - name: SONARQUBE_JDBC_URL
          value: "jdbc:postgresql://10.97.235.67:5432/sonar"   #5432 写的的postgresal的svc 的地址 
        livenessProbe:
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60
          periodSeconds: 30
          failureThreshold: 6
        resources:
          limits:
            cpu: 2000m
            memory: 4096Mi
          requests:
            cpu: 100m
            memory: 512Mi
        volumeMounts:
        - name: time-localtime
          readOnly: true
          mountPath: /etc/localtime
      volumes:
      - name: time-localtime
        hostPath:
          path: /etc/localtime

把代码质量检测写在Jenkins 流水线脚本里面的build-image前面,因为前面的checkout代码检测之后才能触发流水线的工作,让后才能扫描我们的代码,构建镜像,上传镜像 ,但是代码扫描解决不了所有问题只能解决一些基础问题比如:性能过多,注释不足,更多的是检测基础有没有问题,如果基础没有问题就上线进行测试,测试性能

️:因为用的云服务器所以如果服务连接不上或者报错的时候就检查以下host主机文件和k8s集群coreDns里面配置的静态路由!

在k8s集群中部署sonarqube服务端在jenkins命名空间下

因为我使用的是云服务器所以要修改yaml文件在使用

在执行此yaml文件的时候需要在postgres数据库中创建一个sonar数据库 !!!

kubectl exec -it -n jenkins postgres-statefulset-0 -- bash

create database sonar;

kubectl apply -f sonar.yaml

jenkins初始wed配置和ci cd部署_第120张图片

登录密码: 默认都是admin !

开发 专业 数据中心的插件需要付费 插件不是由sonar官方提供的 都是由社区或者国外提供的

 jenkins初始wed配置和ci cd部署_第121张图片

jenkins初始wed配置和ci cd部署_第122张图片 我们安装部署了sonarqube-server服务端 同样还需要安装客户端 才能使用

通过客户端将scm检测之后触发jenkins触发器通过gitlab的wedhooks收取到更新的代码后通过sonarqube-client进行代码检测 ,之后将扫描的结果在发送给服务端 ,让后进行镜像构建 上传镜像等等

安装sonarqube-client客户端 (客户端和服务端都安装在master节点)

wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.2.0.1873-linux.zip
unzip sonar-scanner-cli-4.2.0.1873-linux.zip

修改配置文件 !
[root@master-jenkins sonar-scanner-4.2.0.1873-linux]# ls
 bin#执行命令  conf#配置文件  jre#java包内置了java环境 支撑扫描脚本的java运行  lib#二进制文件
vim conf
#Configure here general information about the environment, such as SonarQube server connection details for example
#No information about specific project should appear here

#----- Default SonarQube server
#sonar.host.url=http://localhost:9000
sonar.host.url=http://10.107.89.235:9000  # 书写 svc的 地址  #如果是本地部署的话是应该写ingres域名  #注意如果本地部署就需要写host 和coredns里面配置静态路由

#----- Default source code encoding
#sonar.sourceEncoding=UTF-8

sonar bin下的执行脚本
[root@master-jenkins conf]# cd ../bin/
[root@master-jenkins bin]# ls
sonar-scanner  sonar-scanner-debug #执行脚本  扫描代码!

如果想要执行代码扫描那麽应该在jenkins源代码的仓库目录里面执行脚本
../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X
执行../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X
报错:  Caused by: You must define the following mandatory properties for 'Unknown': sonar.projectKey
原因: 缺乏sonar.projectKey  可以在官网中找到 https://docs.sonarqube.org/latest/analyzing-source-code/scanners/sonarscanner/

解决方法: 将**vim sonar-project.properties**  写到源代码仓库目录中  来定义扫描的内容
sonar.projectKey=myblog
sonar.projectName=myblog
# if you want disabled the DTD verification for a proxy problem for example, true by default
sonar.coverage.dtdVerification=false
# JUnit like test report, default value is test.xml
sonar.sources=blog,myblog  #扫描的内容        blog,myblog代码在源代码放开目录中
让后在源代码仓库执行../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X

jenkins初始wed配置和ci cd部署_第123张图片

 源代码仓库 ../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X

jenkins初始wed配置和ci cd部署_第124张图片

 Total time: 5.740s #耗时 15:14:35.074 INFO: Final Memory: 12M/47M #使用了多少内存

jenkins初始wed配置和ci cd部署_第125张图片

 到这,客户端扫描了之后就会把结果上传到服务端

jenkins初始wed配置和ci cd部署_第126张图片

 jenkins初始wed配置和ci cd部署_第127张图片

 ​:因为CI/CD的缘故我们希望将代码扫描检测sonar 集成到jenkins 代码仓库中!

:实现方法: 因为tools的镜像涵盖了很多的工具,如果我们想做一个具有代码检测功能的流水线,客户我们就需要把sonar-scanner-4.2.0.1873-linux端集成到我们jnlppod中,在我们的jnlp中pod中有两个容器,一个是jnlp容器 和jenkins来进行对接,一个是tools 工具容器 作用,根据两个容器的作用我们应该把sonar-scanner-4.2.0.1873-linux客户端集成放在tools容器中

因为我们sonar-scanner-4.2.0.1873-linux客户端中在jre中集成内置了java环境 而我们要把客户端集成到tools容器中而tools容器中已经有了java环境 为了减少我们的环境在集成过程中就不需要jre目录了 还有一点是因为在sonar-scanner-4.2.0.1873-linux客户端jre目录特别占存储
[root@master-jenkins tools]# cp -r ../sonar-scanner-4.2.0.1873-linux/ sonar-scanner

[root@master-jenkins tools]# ls
config  Dockerfile  kubectl  sonar-scanner

[root@master-jenkins tools]# cd sonar-scanner/
[root@master-jenkins sonar-scanner]# ls
bin  conf  jre  lib

[root@master-jenkins sonar-scanner]# rm -rf jre/

[root@master-jenkins sonar-scanner]# cd ..

[root@master-jenkins tools]# cd sonar-scanner/

[root@master-jenkins sonar-scanner]# cd bin/

[root@master-jenkins bin]# vim sonar-scanner
sonar-scanner        sonar-scanner-debug  

#use_embedded_jre=true 改为 use_embedded_jre=false   embedded内置 因为我们我们tools应该有java环境我们不希望他使用自己内置的java环境所以要修改为false

然后将tools的Dockerfile重新书写
cat Dockerfile 
FROM alpine
LABEL maintainer="[email protected]"
USER root

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
    apk update && \
    apk add  --no-cache openrc docker git curl tar gcc g++ make \
    bash shadow openjdk8 python2 python2-dev py-pip python3-dev openssl-dev libffi-dev \
    libstdc++ harfbuzz nss freetype ttf-freefont chromium chromium-chromedriver && \
    mkdir -p /root/.kube && \
    usermod -a -G docker root

COPY config /root/.kube/

RUN rm -rf /var/cache/apk/*

#-----------------安装 kubectl--------------------#
COPY kubectl /usr/local/bin/
RUN chmod +x /usr/local/bin/kubectl
# ------------------------------------------------#

#---------------安装 sonar-scanner-----------------#
COPY sonar-scanner /usr/lib/sonar-scanner
RUN ln -s /usr/lib/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner && chmod +x /usr/local/bin/sonar-scanner
ENV SONAR_RUNNER_HOME=/usr/lib/sonar-scanner
# ------------------------------------------------#
docker build -t 1008611zmx/tools:v2 .
让后docker login 登录公有镜像仓库
docker push 1008611zmx/tools:v2

让后把jenkins系统管理--节点管理---配置集群中的pod template的jnlp容器中的tools容器进行修改!修改为我们最新制作的tools容器的名称

 然后将jenkins 连接sonar

在jenkins wed页面下载sonar的插件

jenkins初始wed配置和ci cd部署_第128张图片

 当gitlab发生了代码更新之后 代码拉到我们jenkins-slave中jenkins-slave-pod(inbound tools两个容器)我们tools容器中集成了sonar-client 将代码的扫描结果上传给了sonar-server服务端,当扫描结果通过了之后,让后用代码制作镜像,把镜像推送给镜像仓库,因为我们jenkins对接了k8s,k8s向我们的镜像仓库进行访问拉取镜像 部署服务 其中jenkins扮演了一个指挥官 调度官的角色

jenkins初始wed配置和ci cd部署_第129张图片

 下载安装完sonar插件之后我们需要创建一个属于自己的对接的工具,这个工具需要有一个自己的token凭证

#在jenkins wed页面 系统管理--创建凭证

而jenkins凭证里面需要的token值需要到sonar中创建

jenkins初始wed配置和ci cd部署_第130张图片

 jenkins初始wed配置和ci cd部署_第131张图片

jenkins初始wed配置和ci cd部署_第132张图片 让后在系统管理中 SonarQube servers配置 进行对接

 jenkins初始wed配置和ci cd部署_第133张图片

 Server URL:如果是本地部署的那麽我们应该写ingress 因为ingress的域名是集群的内部

需要在kubectl edit -n kube-system cm coredns中配置静态路由 # 着急的话可以强制更新

jenkins初始wed配置和ci cd部署_第134张图片 sonar对接完成

sonar距拣门 (什么样的标准 代码才会允许通过) #相当于操作系统的防火墙!!!

 jenkins初始wed配置和ci cd部署_第135张图片

什么样的标准 代码运行通过 (如果开发写的代码符合我们的标准就通过,如果没符合就不通过)

Metric                     Operator	    Error

Coverage on New Code	is less than	80.0%  #代码 修改/改变超过代码80%才能通过  小于百分之80报错

Duplicated Lines on New Code	is greater than	3.0% #新代码的重复行大于百分之三 才通过

Maintainability Rating on New Code	is worse than	A

Reliability Rating on New Code	is worse than	A

Security Rating on New Code	is worse than	A

 在sonar中创建规则 (因为默认的规则可能会不符合需要创建)

jenkins初始wed配置和ci cd部署_第136张图片

创建出新的规则后如何将项目调进来:通过以下方式就可以

jenkins初始wed配置和ci cd部署_第137张图片

 让后在规则中创建策略来对All选中的项目进行使用

jenkins初始wed配置和ci cd部署_第138张图片

 让后将实例apply上:#将测试yaml文件先执行上 让后通过流水线进行更新 更新的时候只需要更新myblog!!!

cat deploy-mysql.yaml 
apiVersion: v1
kind: Service
metadata:
  name: mysql-svc
  namespace: develop
spec:
  ports:
  - port: 3306
    protocol: TCP
    targetPort: 3306
  selector:
    app: mysql
  #type: ClusterIP
  clusterIP: 10.104.238.30
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: develop
spec:
  replicas: 1   #指定Pod副本数
  selector:             #指定Pod的选择器
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:   #给Pod打label
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:5.7
        ports:
        - containerPort: 3306
        env:
        #- name: MYSQL_USER
        # #value: root
        - name: MYSQL_ROOT_PASSWORD
          value: "123456"
        - name: MYSQL_DATABASE
          value: "myblog"
        resources:
          requests:
            memory: 100Mi
            cpu: 50m
          limits:
            memory: 500Mi
            cpu: 100m

 cat /myblog/deploy-myblog.yaml 
apiVersion: v1
kind: Service
metadata:
  name: myblog-svc
  labels:
    app: myblog
  namespace: develop
spec:
  type: NodePort
  ports:
  - name: myblog
    port: 8002
    targetPort: 8002
    protocol: TCP
    nodePort: 32087
  clusterIP: 10.104.222.190
  selector:
    app: myblog      
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myblog
  namespace: develop
spec:
  replicas: 1   #指定Pod副本数
  selector:             #指定Pod的选择器
    matchLabels:
      app: myblog
  template:
    metadata:
      labels:   #给Pod打label
        app: myblog
    spec:
      containers:
      - name: myblog
        image: smsmcopy/myblog:627c60d6727d617f78b4ca7cc1d1191f91def301
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_HOST
          value: "10.104.238.30"
        - name: MYSQL_PORT
          value: "3306"
        - name: MYSQL_USER
          value: "root"
        - name: MYSQL_PASSWD
          value: "123456"
        ports:
        - containerPort: 8002
        resources:
          requests:
            memory: 100Mi
            cpu: 50m
          limits:
            memory: 500Mi
            cpu: 100m

jenkins初始wed配置和ci cd部署_第139张图片 kubectl exec -it -n develop myblog-67976548d7-k58ln -- bash

myblog有一个典型的python文件需要执行 manage.py

python3 manage.py makemigrations   #如果失败请先检测pod的与svc直接是否正常连接  lable
python3 manage.py migrate
python3 manage.py createsuperuser   #生成一个管理员密码  也是myblog的登录密码  

jenkins初始wed配置和ci cd部署_第140张图片jenkins初始wed配置和ci cd部署_第141张图片 jenkins初始wed配置和ci cd部署_第142张图片

 jenkins初始wed配置和ci cd部署_第143张图片

 至此一个简略的博客完成

 http://123.60.162.77:32087/blog/article/edit/0

 jenkins初始wed配置和ci cd部署_第144张图片

将源代码仓库中的deploy目录里面的myblog的yaml文件进行修改 也可以直接替换 
cp /myblog/deploy-myblog.yaml .

 部署完sonar代码扫描之后和myblog的博客之后对源代码目录中的Jenkins流水线脚本进行修改

因为在源代码仓库执行../sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -X 所以要在Jenkins中进行调用

在jenkins中介绍了如何在流水线脚本中写pipeline SonarQube Scanner for Jenkins

把sonar的脚本写并放在Jenkinsfile流水线脚本scm后面

jenkins初始wed配置和ci cd部署_第145张图片

cat Jenkinsfile 
pipeline {
    agent { label 'jnlp-slave'}
    
    options {
		buildDiscarder(logRotator(numToKeepStr: '10'))
		disableConcurrentBuilds()
		timeout(time: 20, unit: 'MINUTES')
		gitLabConnection('10.99.69.119')
	}

    environment {
        IMAGE_REPO = "1008611zmx/myblog"
        DINGTALK_CREDS = credentials('dingtalk')
        DOCKERHUB_CREDS = credentials('dockerhub')
        TAB_STR = "\n                    \n                    "
    }

    stages {
        stage('printenv') {
            steps {
                script{
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                }
                sh 'printenv'
            }
        }
        stage('checkout') {
            steps {
                container('tools') {
                    checkout scm
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('CI'){
            failFast true #只要失败立刻终止
            parallel {        #并行参数 (因为我们的steps是一步一步的进行的 但是加了paraller参数之后 stage可以同时运行)
                stage('Unit Test(单元测试)') {
                    steps {
                        echo "Unit Test Stage Skip..."
                    }
                }
                stage('Code Scan') { #代码扫描
                    steps {
                        container('tools(调用tools容器)') {
                            withSonarQubeEnv('sonarqube(是我们的sonarqube的svc地址#如果写svc地址错误之后 替换成sonarqube的全局唯一凭证)') {
                                sh 'sonar-scanner -X' #调用我们代码扫描的程序在源代码目录里面进行扫描 (之前客户端的执行脚本命令)
                                sleep 10
                            }
                            script {
                                timeout(1) {
                                    def qg = waitForQualityGate('sonarqube(是我们的sonarqube的svc地址#如果写svc地址错误之后 替换成sonarqube的全局唯一凭证)') #def 函数 调用 sonarqube变量  如果qg.status不ok的话就报一个错 配合failFast true 立刻停止
                                    if (qg.status != 'OK') {
                                        error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        stage('build-image') {
            steps {
                container('tools') {
                    retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('push-image') {
            steps {
                container('tools') {
                    retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}
                    retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('deploy') {
            steps {
                container('tools') {
                    sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"
                    timeout(time: 1, unit: 'MINUTES') {
                        sh "kubectl apply -f deploy/"
                    }
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": " 构建成功   \n**项目名称**:smile 33 \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}   \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """ 
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": "❌ 构建失败 ❌  \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

[root@master-jenkins python-demo-master]# git branch 
* develop
  master
因为内我们deploy改变了  sonar-project.properties 又新增了一个目录(是我们执行sonar客户端执行出来的)
[root@master-jenkins python-demo-master]# git add .
[root@master-jenkins python-demo-master]# git commit -am 'add sonarqube'
[root@master-jenkins python-demo-master]# git push

git add --all
git status
git comtios -am "updata Jenkins"
git push

jenkins初始wed配置和ci cd部署_第146张图片

 如果报错就将我们之前手动执行的代码检测生成的.sonarqube删除就可以了 

 如果报错的话,将当时我们用命令执行出来的.scannerwork 进行删除 !!!

那麽做到这里请严格注意凭证的全局唯一id名字!

jenkins初始wed配置和ci cd部署_第147张图片

 集成RobotFramework实现验收测试

一个基于Python语言,用于验收测试和验收测试驱动开发(ATDD)的通用测试自动化框架,提供了一套特定的语法,并且有非常丰富的测试库 。

robot用例简介 #在下面启动tools容器做验收测试的时候写入进去 vi的方式

robot/robot.txt

*** Settings ***
Library           RequestsLibrary
Library           SeleniumLibrary

*** Variables ***
${demo_url}       http://myblog.smile.com/admin

*** Test Cases ***
api
    [Tags]  critical
    Create Session    api    ${demo_url}
    ${alarm_system_info}    RequestsLibrary.Get Request    api    /
    log    ${alarm_system_info.status_code}
    log    ${alarm_system_info.content}
    should be true    ${alarm_system_info.status_code} == 200


# 使用tools镜像启动容器,来验证手动使用robotframework来做验收测试  #在v2容器中
$ docker run --rm -ti smsmcopy/tools:v2 bash
bash-5.0# apk add chromium chromium-chromedriver
$ vi requirements.txt
robotframework
robotframework-seleniumlibrary
robotframework-databaselibrary
robotframework-requests

#pip安装必要的软件包
$ pip install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt 

#使用robot命令做测试
$ robot -d artifacts/ robot.txt

jenkins初始wed配置和ci cd部署_第148张图片

jenkins初始wed配置和ci cd部署_第149张图片 与tools工具镜像集成

FROM alpine
LABEL maintainer="[email protected]"
USER root

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
    apk update && \
    apk add  --no-cache openrc docker git curl tar gcc g++ make \
    bash shadow openjdk8 python2 python2-dev py-pip python3-dev openssl-dev libffi-dev \
    libstdc++ harfbuzz nss freetype ttf-freefont chromium chromium-chromedriver && \
    mkdir -p /root/.kube && \
    usermod -a -G docker root


COPY config /root/.kube/

COPY requirements.txt /

RUN pip install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt 


RUN rm -rf /var/cache/apk/* && \
    rm -rf ~/.cache/pip

#-----------------安装 kubectl--------------------#
COPY kubectl /usr/local/bin/
RUN chmod +x /usr/local/bin/kubectl
# ------------------------------------------------#

#---------------安装 sonar-scanner-----------------#
COPY sonar-scanner /usr/lib/sonar-scanner
RUN ln -s /usr/lib/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner && chmod +x /usr/local/bin/sonar-scanner
ENV SONAR_RUNNER_HOME=/usr/lib/sonar-scanner
# ------------------------------------------------#

在构建镜像之前将requirements.txt 写入到tools目录中
vi requirements.txt
robotframework
robotframework-seleniumlibrary
robotframework-databaselibrary
robotframework-requests

docker build -t 1008611zmx/tools:v3 .

docker push 1008611zmx/tools:v3

jenkins初始wed配置和ci cd部署_第150张图片

 让后 更新Jenkins wed界面中kubernetes中的containers template tools版本

jenkins初始wed配置和ci cd部署_第151张图片

 安装插件:

安装robotFramework

  • 插件中心搜索robotframework,直接安装

jenkins初始wed配置和ci cd部署_第152张图片

 在源代码仓库添加 robbot.txt

cat robbot.txt 
*** Settings ***
Library           RequestsLibrary
Library           SeleniumLibrary

*** Variables ***
${demo_url}       http://10.98.118.196/admin #修改为myblog的svc

*** Test Cases ***
api
    [Tags]  critical
    Create Session    api    ${demo_url}
    ${alarm_system_info}    RequestsLibrary.Get Request    api    /
    log    ${alarm_system_info.status_code}
    log    ${alarm_system_info.content}
    should be true    ${alarm_system_info.status_code} == 200

python-demo项目添加robot.txt文件:

jenkins/pipelines/p10.yaml

pipeline {
    agent { label 'jnlp-slave'}
    
    options {
		buildDiscarder(logRotator(numToKeepStr: '10'))
		disableConcurrentBuilds()
		timeout(time: 20, unit: 'MINUTES')
		gitLabConnection('gitlab')
	}

    environment {
        IMAGE_REPO = "smsmcopay/myblog"
        DINGTALK_CREDS = credentials('dingtalk')
        DOCKERHUB_CREDS = credentials('dockerhub')
        TAB_STR = "\n                    \n                    "
    }

    stages {
        stage('printenv') {
            steps {
                script{
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                }
                sh 'printenv'
            }
        }
        stage('checkout') {
            steps {
                container('tools') {
                    checkout scm
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('build-image') {
            steps {
                container('tools') {
                    retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('push-image') {
            steps {
                container('tools') {
                    retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}
                    retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('deploy') {
            steps {
                container('tools') {
                    sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"
                    timeout(time: 1, unit: 'MINUTES') {
                        sh "kubectl apply -f deploy/"
                    }
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('Accept Test') {
            steps {
                    container('tools') {
                        sh 'robot -i critical  -d artifacts/ robot.txt|| echo ok'
                        echo "R ${currentBuild.result}"
                        step([
                            $class : 'RobotPublisher',
                            outputPath: 'artifacts/',
                            outputFileName : "output.xml",
                            disableArchiveOutput : false,
                            passThreshold : 80,
                            unstableThreshold: 20.0,
                            onlyCritical : true,
                            otherFiles : "*.png"
                        ])
                        echo "R ${currentBuild.result}"
                        archiveArtifacts artifacts: 'artifacts/*', fingerprint: true
                    }
             }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": " 构建成功   \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}   \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """ 
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": "❌ 构建失败 ❌  \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

在Jenkins中查看robot的构建结果。

cat Jenkinsfile 
pipeline {
    agent { label 'jnlp-slave'}
    
    options {
		buildDiscarder(logRotator(numToKeepStr: '10'))
		disableConcurrentBuilds()
		timeout(time: 20, unit: 'MINUTES')
		gitLabConnection('10.99.69.119')
	}

    environment {
        IMAGE_REPO = "1008611zmx/myblog"
        DINGTALK_CREDS = credentials('dingtalk')
        DOCKERHUB_CREDS = credentials('dockerhub')
        TAB_STR = "\n                    \n                    "
    }

    stages {
        stage('printenv') {
            steps {
                script{
                    sh "git log --oneline -n 1 > gitlog.file"
                    env.GIT_LOG = readFile("gitlog.file").trim()
                }
                sh 'printenv'
            }
        }
        stage('checkout') {
            steps {
                container('tools') {
                    checkout scm
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS = env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
         stage('CI'){
            failFast true
            parallel {
                stage('Unit Test') {
                    steps {
                        echo "Unit Test Stage Skip..."
                    }
                }
                stage('Code Scan') {
                    steps {
                        container('tools') {
                            withSonarQubeEnv('sonarqube') {
                                sh 'sonar-scanner -X'
                                sleep 10
                            }
                            script {
                                timeout(1) {
                                    def qg = waitForQualityGate('sonarqube')
                                    if (qg.status != 'OK') {
                                        error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        stage('build-image') {
            steps {
                container('tools') {
                    retry(2) { sh 'docker build . -t ${IMAGE_REPO}:${GIT_COMMIT}'}
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('push-image') {
            steps {
                container('tools') {
                    retry(2) { sh 'docker login -u ${DOCKERHUB_CREDS_USR} -p ${DOCKERHUB_CREDS_PSW}'}
                    retry(2) { sh 'docker push ${IMAGE_REPO}:${GIT_COMMIT}'}
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('deploy') {
            steps {
                container('tools') {
                    sh "sed -i 's#{{IMAGE_URL}}#${IMAGE_REPO}:${GIT_COMMIT}#g' deploy/*"
                    timeout(time: 1, unit: 'MINUTES') {
                        sh "kubectl apply -f deploy/"
                    }
                }
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script{
                    env.BUILD_TASKS += env.STAGE_NAME + "√..." + env.TAB_STR
                }
            }
        }
        stage('Accept Test') {
            steps {
                    container('tools') {
                        sh 'robot -i critical  -d artifacts/ robot.txt|| echo ok'
                        echo "R ${currentBuild.result}"
                        step([
                            $class : 'RobotPublisher',
                            outputPath: 'artifacts/',
                            outputFileName : "output.xml",
                            disableArchiveOutput : false,
                            passThreshold : 80,
                            unstableThreshold: 20.0,
                            onlyCritical : true,
                            otherFiles : "*.png"
                        ])
                        echo "R ${currentBuild.result}"
                        archiveArtifacts artifacts: 'artifacts/*', fingerprint: true
                    }
             }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": " 构建成功   \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}   \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """ 
        }
        failure {
            echo 'Oh no!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"myblog",
                            "text": "❌ 构建失败 ❌  \n**项目名称**:smile  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${GIT_BRANCH}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"
                        }
                    }'
            """
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

小结

思路:

  1. 讲解最基础的Jenkins的使用

  2. Pipeline流水线的使用

  3. Jenkinsfile的使用

  4. 多分支流水线的使用

  5. 与Kubernetes集成,动态jnlp slave pod的使用

  6. 与sonarqube集成,实现代码扫描

  7. 与Robotframework集成,实现验收测试

问题:

  1. Jenkinsfile过于冗长

  2. 多个项目配置Jenkinsfile,存在很多重复内容

  3. 没有实现根据不同分支来部署到不同的环境

  4. Java项目的构建

  5. k8s部署后,采用等待的方式执行后续步骤,不合理

你可能感兴趣的:(jenkins,运维,程序人生,linux,centos)