当前位置:首页 » 《随便一记》 » 正文

基于K8s的DevOps平台实践(三)

10 人参与  2023年01月12日 10:07  分类 : 《随便一记》  评论

点击全文阅读


文章目录

前言1. Jenkins与k8s集成? 插件安装及配置? 演示动态slave pod? Pod-Template中容器镜像的制作? 实践通过Jenkinsfile实现demo项目自动发布到kubenetes环境 2. Jenkins集成Sonarqube? sonarqube架构简介? sonarqube on kubernetes环境搭建? 插件安装及配置? Jenkinsfile集成sonarqube演示 3. Jenkins集成robotFramework? robot用例简介? 与 tools 工具镜像集成? 插件安装及配置? 实践通过Jenkinsfile实现demo项目的验收测试 4. 总结与反思


前言

今天是「基于K8s的DevOps平台实践」的最后一篇,前两篇地址如下:

基于K8s的DevOps平台实践(一)基于K8s的DevOps平台实践(二)

上面两篇均排在云原生领域榜第一

1. Jenkins与k8s集成

Jenkins 如何对接 kubernetes 集群使用 kubernetes 的 Pod-Template 来作为动态的 agent 执行 Jenkins 任务如何制作 agent 容器实现不同类型的业务的集成集成代码扫描、docker 镜像自动构建、k8s 服务部署、自动化测试

? 插件安装及配置

插件官方文档

[系统管理] -> [插件管理] -> [搜索kubernetes] -> 直接安装

若安装失败,请先更新 bouncycastle API Plugin 并重新启动Jenkins

[系统管理] -> [系统配置] -> [Add a new cloud]

配置地址信息

Kubernetes 地址:https://kubernetes.defaultKubernetes 命名空间:jenkins服务证书不用写(我们在安装 Jenkins 的时候已经指定过 serviceAccount),均使用默认连接测试,成功会提示:Connection test successfulJenkins地址:http://jenkins:8080Jenkins 通道 :jenkins:50000

配置 Pod Template

名称:jnlp-slave

命名空间:jenkins

标签列表:jnlp-slave,作为 agent 的 label 选择用

连接 Jenkins 的超时时间(秒):300,设置连接 jenkins 超时时间

工作空间卷:选择 hostpath,设置 /opt/jenkins,注意需要设置目录权限,否则 Pod 没有权限

在这里插入图片描述

$ chown -R 1000:1000 /opt/jenkins$ chmod 700 /opt/jenkins

? 演示动态slave pod

# 为准备运行jnlp-slave-agent的pod的节点打上label$ kubectl label node k8s-slave1 agent=true### 回放一次多分支流水线develop分支agent { label 'jnlp-slave'}

执行任务,会下载默认的 jnlp-slave 镜像,地址为 jenkins/inbound-agent:4.3-4,我们可以先在 k8s-master 节点拉取下来该镜像:

$ docker pull jenkins/inbound-agent:4.3-4

保存 jenkinsfile 提交后,会出现报错,因为我们的 agent 已经不再是宿主机,而是 Pod 中的容器内,报错如下:

在这里插入图片描述

因此我们需要将用到的命令行工具集成到 Pod 的容器内,但是思考如下问题:

目前是用的 jnlp 的容器,是 java 的环境,我们在此基础上需要集成很多工具,能不能创建一个新的容器,让新容器来做具体的任务,jnlp-slave 容器只用来负责连接 jenkins-master针对不同的构建环境(java、python、go、nodejs),可以制作不同的容器,来执行对应的任务

? Pod-Template中容器镜像的制作

为解决上述问题,我们制作一个 tools 镜像,集成常用的工具,来完成常见的构建任务,需要注意的几点:

使用 alpine 基础镜像,自身体积比较小替换国内安装源为了使用 docker,安装了 docker为了克隆代码,安装 git为了后续做 python 的测试等任务,安装 python 环境为了在容器中调用 kubectl 的命令,拷贝了 kubectl 的二进制文件为了认证 kubectl,需要在容器内部生成 .kube 目录及 config 文件
$ mkdir tools;$ cd tools;$ cp `which kubectl` .$ cp ~/.kube/config .

Dockerfile

jenkins/custom-images/tools/Dockerfile

FROM alpine:3.13.4LABEL maintainer="inspur_lyx@hotmail.com"USER rootRUN 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 && \    mkdir -p /root/.kube && \    usermod -a -G docker rootCOPY 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 172.21.51.143:5000/devops/tools:v1$ docker push 172.21.51.143:5000/devops/tools:v1

我们可以直接使用该镜像做测试:

## 启动临时镜像做测试$ docker run --rm -ti 172.21.51.143:5000/devops/tools:v1 bash# / git clone http://xxxxxx.git# / kubectl get no# / python3#/ docker## 重新挂载docker的sock文件docker run -v /var/run/docker.sock:/var/run/docker.sock --rm -ti 172.21.51.143:5000/devops/tools:v1 bash

? 实践通过Jenkinsfile实现demo项目自动发布到kubenetes环境

实践通过 Jenkinsfile 实现 demo 项目自动发布到 kubenetes 环境

在这里插入图片描述

在卷栏目,添加卷,Host Path Volume,不然在容器中使用 docker 会提示 docker 服务未启动

在这里插入图片描述

tools 容器做好后,我们需要对 Jenkinsfile 做如下调整:

jenkins/pipelines/p8.yaml

pipeline {    agent { label 'jnlp-slave'}        options {buildDiscarder(logRotator(numToKeepStr: '10'))disableConcurrentBuilds()timeout(time: 20, unit: 'MINUTES')gitLabConnection('gitlab')}    environment {        IMAGE_REPO = "172.21.51.143:5000/myblog"        DINGTALK_CREDS = credentials('dingTalk')        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 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' manifests/*"                    timeout(time: 1, unit: 'MINUTES') {                        sh "kubectl apply -f manifests/"                    }                }                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**项目名称**:luffy  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${BRANCH_NAME}   \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**项目名称**:luffy  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${BRANCH_NAME}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"                        }                    }'            """        }        always {             echo 'I will always say Hello again!'        }    }}

2. Jenkins集成Sonarqube

集成 sonarQube 实现代码扫描

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

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

? sonarqube架构简介

如图所示:

在这里插入图片描述

CS 架构 sonarqube scannersonarqube server SonarQube Scanner 扫描仪在本地执行代码扫描任务执行完后,将分析报告被发送到 SonarQube 服务器进行处理SonarQube 服务器处理和存储分析报告导致 SonarQube 数据库,并显示结果在 UI 中

? sonarqube on kubernetes环境搭建

资源文件准备

sonar/sonar.yaml

和 gitlab 共享 postgres 数据库使用 ingress 地址 sonar.luffy.com 进行访问使用 initContainers 进行系统参数调整
apiVersion: v1kind: Servicemetadata:  name: sonarqube  namespace: jenkins  labels:    app: sonarqubespec:  ports:  - name: sonarqube    port: 9000    targetPort: 9000    protocol: TCP  selector:    app: sonarqube---apiVersion: apps/v1kind: Deploymentmetadata:  namespace: jenkins  name: sonarqube  labels:    app: sonarqubespec:  replicas: 1  selector:    matchLabels:      app: sonarqube  template:    metadata:      labels:        app: sonarqube    spec:      initContainers:      - command:        - /sbin/sysctl        - -w        - vm.max_map_count=262144        image: alpine:3.6        imagePullPolicy: IfNotPresent        name: elasticsearch-logging-init        resources: {}        securityContext:          privileged: true      containers:      - name: sonarqube        image: sonarqube:7.9-community        ports:        - containerPort: 9000        env:        - name: SONARQUBE_JDBC_USERNAME          valueFrom:            secretKeyRef:              name: gitlab-secret              key: postgres.user.root        - name: SONARQUBE_JDBC_PASSWORD          valueFrom:            secretKeyRef:              name: gitlab-secret              key: postgres.pwd.root        - name: SONARQUBE_JDBC_URL          value: "jdbc:postgresql://postgres:5432/sonar"        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: 300m            memory: 512Mi---apiVersion: networking.k8s.io/v1kind: Ingressmetadata:  name: sonarqube  namespace: jenkinsspec:  rules:  - host: sonar.luffy.com    http:      paths:      - path: /        pathType: Prefix        backend:          service:             name: sonarqube            port:              number: 9000
sonarqube 服务端安装
# 创建sonar数据库$ kubectl -n jenkins exec -ti postgres-5859dc6f58-mgqz9 bash#/ psql # create database sonar;## 创建sonarqube服务器$ kubectl create -f sonar.yaml## 配置本地hosts解析172.21.51.143 sonar.luffy.com## 访问sonarqube,初始用户名密码为 admin/admin$ curl http://sonar.luffy.com

sonar-scanner的安装

下载地址: sonar

演示sonar代码扫描功能

在项目根目录中准备配置文件 sonar-project.properties
sonar.projectKey=myblogsonar.projectName=myblog# if you want disabled the DTD verification for a proxy problem for example, true by defaultsonar.coverage.dtdVerification=false# JUnit like test report, default value is test.xmlsonar.sources=blog,myblog

配置 sonarqube 服务器地址

由于sonar-scanner 需要将扫描结果上报给 sonarqube 服务器做质量分析,因此我们需要在 sonar-scanner 中配置 sonarqube 的服务器地址:

在集群宿主机中测试,先配置一下 hosts 文件,然后配置 sonar 的地址:

$ cat /etc/hosts172.21.51.143  sonar.luffy.com$ cat sonar-scanner/conf/sonar-scanner.properties#----- Default SonarQube server#sonar.host.url=http://localhost:9000sonar.host.url=http://sonar.luffy.com#----- Default source code encoding#sonar.sourceEncoding=UTF-8
为了使所有的 pod 都可以通过sonar.luffy.com访问,可以配置 coredns 的静态解析
# 静态解析$ kubectl -n kube-system edit cm coredns ...             hosts {                 172.21.51.143 jenkins.luffy.com gitlab.luffy.com sonar.luffy.com                 fallthrough          }
执行扫描
## 在项目的根目录下执行$ /opt/sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner  -X 

sonarqube 界面查看结果

登录 sonarqube 界面查看结果,Quality Gates 说明

java 项目的配置文件通常格式为:

sonar.projectKey=eureka-clustersonar.projectName=eureka-cluster# if you want disabled the DTD verification for a proxy problem for example, true by default# JUnit like test report, default value is test.xmlsonar.sources=src/main/javasonar.language=javasonar.tests=src/test/javasonar.java.binaries=target/classes

? 插件安装及配置

集成到 tools 容器中

由于我们的代码拉取、构建任务均是在 tools 容器中进行,因此我们需要把 scanner 集成到我们的 tools 容器中,又因为 scanner 是一个 cli 客户端,因此我们直接把包解压好,拷贝到 tools 容器内部,配置一下 PATH 路径即可,注意两点:

直接在 tools 镜像中配置 http://sonar.luffy.com

由于 tools 已经集成了 java 环境,因此可以直接剔除 scanner 自带的 jre

删掉 sonar-scanner/jre 目录

修改 sonar-scanner/bin/sonar-scanner

use_embedded_jre=false

$ cd tools$ cp -r /opt/sonar-scanner-4.2.0.1873-linux/ sonar-scanner## sonar配置,由于我们是在Pod中使用,也可以直接配置:sonar.host.url=http://sonarqube:9000$ cat sonar-scanner/conf/sonar-scanner.properties#----- Default SonarQube serversonar.host.url=http://sonar.luffy.com#----- Default source code encoding#sonar.sourceEncoding=UTF-8$ rm -rf sonar-scanner/jre$ vi sonar-scanner/bin/sonar-scanner...use_embedded_jre=false...

Dockerfile

jenkins/custom-images/tools/Dockerfile2

FROM alpine:3.13.4LABEL maintainer="inspur_lyx@hotmail.com"USER rootRUN 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 && \    mkdir -p /root/.kube && \    usermod -a -G docker rootCOPY 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-scannerRUN ln -s /usr/lib/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner && chmod +x /usr/local/bin/sonar-scannerENV SONAR_RUNNER_HOME=/usr/lib/sonar-scanner# ------------------------------------------------#

重新构建镜像,并推送到仓库:

$ docker build . -t 172.21.51.143:5000/devops/tools:v2$ docker push 172.21.51.143:5000/devops/tools:v2  

修改 Jenkins PodTemplate

为了在新的构建任务中可以拉取 v2 版本的 tools 镜像,需要更新 PodTemplate

安装并配置 sonar 插件

由于 sonarqube 的扫描的结果需要进行 Quality Gates 的检测,那么我们在容器中执行完代码扫描任务后,如何知道本次扫描是否通过了 Quality Gates,那么就需要借助于 sonarqube 实现的 jenkins 的插件。

安装插件

插件中心搜索 sonarqube,直接安装

配置插件

系统管理 -> 系统配置 -> SonarQube servers -> Add SonarQube

Name:sonarqube

Server URL:http://sonar.luffy.com

Server authentication token

① 登录 sonarqube -> My Account -> Security -> Generate Token

② 登录 Jenkins,添加全局凭据,类型为 Secret text

如何在 jenkinsfile 中使用

我们在 官方介绍 中可以看到:

? Jenkinsfile集成sonarqube演示

jenkins/pipelines/p9.yaml

pipeline {    agent { label 'jnlp-slave'}        options {buildDiscarder(logRotator(numToKeepStr: '10'))disableConcurrentBuilds()timeout(time: 20, unit: 'MINUTES')gitLabConnection('gitlab')}    environment {        IMAGE_REPO = "172.21.51.143:5000/myblog"        DINGTALK_CREDS = credentials('dingTalk')        TAB_STR = "\n                    \n                    "    }    stages {        stage('git-log') {            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 3                            }                            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 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' manifests/*"                    timeout(time: 1, unit: 'MINUTES') {                        sh "kubectl apply -f manifests/"                    }                }                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**项目名称**:luffy  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${BRANCH_NAME}   \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**项目名称**:luffy  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${BRANCH_NAME}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"                        }                    }'            """        }        always {             echo 'I will always say Hello again!'        }    }}

若 Jenkins 执行任务过程中 sonarqube 端报类似下图的错:

在这里插入图片描述

则需要在 sonarqube 服务端进行如下配置,添加一个 webhook:

在这里插入图片描述

3. Jenkins集成robotFramework

集成 RobotFramework 实现验收测试

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

? robot用例简介

robot/robot.txt

*** Settings ***Library           RequestsLibraryLibrary           SeleniumLibrary*** Variables ***${demo_url}       http://myblog.luffy/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} == 200ui    [Tags]  critical    ${chrome_options} =     Evaluate    sys.modules['selenium.webdriver'].ChromeOptions()    sys, selenium.webdriver    Call Method    ${chrome_options}   add_argument    headless    Call Method    ${chrome_options}   add_argument    no-sandbox    ${options}=     Call Method     ${chrome_options}    to_capabilities    Open Browser    ${demo_url}/    browser=chrome       desired_capabilities=${options}    sleep    2s    Capture Page Screenshot    Page Should Contain    Django    close browser
# 使用tools镜像启动容器,来验证手动使用robotframework来做验收测试$ docker run --rm -ti 172.21.51.143:5000/devops/tools:v2 bashbash-5.0# apk add chromium chromium-chromedriver$ cat requirements.txtrobotframeworkrobotframework-seleniumlibraryrobotframework-databaselibraryrobotframework-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

? 与 tools 工具镜像集成

FROM alpine:3.13.4LABEL maintainer="inspur_lyx@hotmail.com"USER rootRUN 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 rootCOPY 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-scannerRUN ln -s /usr/lib/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner && chmod +x /usr/local/bin/sonar-scannerENV SONAR_RUNNER_HOME=/usr/lib/sonar-scanner# ------------------------------------------------#
$ docker build . -t 172.21.51.143:5000/devops/tools:v3$ docker push 172.21.51.143:5000/devops/tools:v3

更新 Jenkins 中 kubernetes 中的 containers template

? 插件安装及配置

为什么要安装 robot 插件?

安装 robotFramework

插件中心搜索 robotframework,直接安装tools 集成 robot命令(之前已经安装)

与 jenkinsfile 的集成

    container('tools') {        sh 'robot -i critical  -d artifacts/ robot.txt'        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    }

? 实践通过Jenkinsfile实现demo项目的验收测试

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 = "172.21.51.143:5000/myblog"        DINGTALK_CREDS = credentials('dingTalk')        TAB_STR = "\n                    \n                    "    }    stages {        stage('git-log') {            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 3                            }                            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 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' manifests/*"                    timeout(time: 1, unit: 'MINUTES') {                        sh "kubectl apply -f manifests/;sleep 20;"                    }                }                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**项目名称**:luffy  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${BRANCH_NAME}   \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**项目名称**:luffy  \n**Git log**: ${GIT_LOG}   \n**构建分支**: ${BRANCH_NAME}  \n**构建地址**:${RUN_DISPLAY_URL}  \n**构建任务**:${BUILD_TASKS}"                        }                    }'            """        }        always {             echo 'I will always say Hello again!'        }    }}

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

4. 总结与反思

通过这三篇文章,主要学习了以下几个方面:

讲解最基础的 Jenkins 的使用Pipeline 流水线的使用Jenkinsfile 的使用多分支流水线的使用与 Kubernetes 集成,动态 jnlp slave pod 的使用与 sonarqube 集成,实现代码扫描与 Robotframework 集成,实现验收测试

但是,也存在一些问题:

Jenkinsfile 过于冗长多个项目配置 Jenkinsfile,存在很多重复内容没有实现根据不同分支来部署到不同的环境Java 项目的构建k8s部署后,采用等待的方式执行后续步骤,不合理

后面会通过 sharedLibrary 对进行 CI/CD 流程进行一个优化


点击全文阅读


本文链接:http://zhangshiyu.com/post/51297.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1