Devops系列六(CI篇之jenkinsfile)jenkins将gitlab helm yaml和argocd 串联,自动部署到K8S

news2024/11/15 23:34:55

一、为什么是jenkinsfile

上文我们说了pipeline,已为本文铺路不少,接下里就是将之串联起来。
先想说下,为什么是jenkinsfile, 因为jenkins job还支持pipeline方式。
在这里插入图片描述
这种方式,不建议实际使用,仅限于测试或调试groovy代码。

下面贴出来,我们的使用方式。好处是:采用分布式的思想,改动git上的jenkinsfile,就可以让所有的job更新。
在这里插入图片描述

二、jenkinsfile的参数

这也有两种方式,我反而又不建议你写在jenkinsfile里。

  • Job (建议方式,因为每个工程的参数不一样,需要持久化保存)
  • jenkinsfile

在这里插入图片描述
对于java语言来说, 我梳理了以下几个必须参数:

  • jarName
  • port
  • projectName(消息通知)
  • codeUrl
  • branch

为了一些额外需求,我们还定义了几个扩展参数:

  • packageType(打包方式,mvn或者gradle。默认为mvn)
  • robotKey(消息通知的机器人robot,默认为空)
  • childModule(子模块, 用于一个多module的项目打包,指定某模块以发布。默认为空)

三、groovy编码的几点建议

1、变量一定要使用trim()方法

举例,shell命令读取程序版本号,返回值如果没有使用trim(),会额外多一个换行符。-- 后期在使用该变量拼接的时候,很容易就把明明一行执行的,硬生生地被拆成了两行执行。

// 读取java应用的版本号
def getAppVersion(packagePath) {
    def appVersion = sh returnStdout: true,
            script: """
                    grep 'git.build.version' ${packagePath}/classes/git.properties | cut -d'=' -f2 | sed 's/\\r//'
                    """
    // trim()
    return appVersion.trim()
}

// 镜像版本号
def dockerImageVersion = appVersion + "-001"

docker build -f ${dockerfileName} -t ${repoProject}/${appName}:${dockerImageVersion} .
// 如果你没有使用trim()的话, 上面的这行代码,就会变成:
docker build -f ${dockerfileName} -t ${repoProject}/${appName}:${dockerImageVersion}
// 我还在纳闷,怎么最后的.被谁吃掉了呢。。。
// 当你期望的是
docker build -f /opt/Dockerfile -t xxx/devops-service:1.0.7-001 .

// 实际却是:
docker build -f /opt/Dockerfile -t xxx/devops-service:1.0.7

2、引入pipeline library

这里的名称,对应前文配置中的name

为了避免jenkinsfile的代码量过于复杂,我们往往会抽取出公共的方法。

@Library('jenkinslib') _

def tools = new com.xhtech.devops.tools()

tools.PrintMes("pull code!!!", "green")

3、读取参数

这里的参数分为两种,一是自定义,一是系统自带。

  • 字符串类型的变量,后面都紧跟着trim()方法。
// JOB_NAME是系统自带
String jobName = "${env.JOB_NAME}".trim()

// jarName是我们在job的参数化构建中配置
String jarName = "${env.jarName}".trim()

4、运行shell命令

// 正确的写法,必须由script括起来
script {
    sh "mvn clean package -Dmaven.test.skip=true -U"                
}

5、切换目录

dir("${env.WORKSPACE}") 

6、指定容器

默认容器是jnlp, 如果你的Pod配置了多个容器,docker image的相关操作就必须在docker容器里执行。

  • container(‘docker’),这里的docker就是对应PodTemplate中的容器名称。
container('docker') {
    dir("${env.WORKSPACE}") {
        docker.buildAndPushImage(jarName, port, dockerImageVersion, packagePath, dockerfileName)
    }
}

7、归档archiveArtifacts

把版本号写入到文件,并且归档

dir("${env.WORKSPACE}") {
    sh "echo ${appVersion} > version_${appVersion}.txt"

    archiveArtifacts artifacts: 'version_*.txt', followSymlinks: false
}

在这里插入图片描述

8、设置操作的超时时间

timeout(time: 1, unit: "MINUTES") {
  // 具体操作,预计在1分钟内完成
}

9、指定工作空间

我们把工作空间持久化到某个目录,而不是放在Pod里。这样的好处是:起到了缓存的作用,不会随着Pod的销毁而需重建。

// 我们会对/opt/.m2进行持久化操作
String sharefile = "/opt/.m2"
String jobName = "${env.JOB_NAME}".trim()

String PROJECT_WORKSPACE = "$sharefile/java-workspaces/$jobName"

agent {
    kubernetes {
        inheritFrom 'jnlp-maven'
        customWorkspace "${PROJECT_WORKSPACE}"
    }
}

10、清理空间

安装jenkins plugin: Workspace Cleanup

// 文档地址: https://plugins.jenkins.io/ws-cleanup/

stage('Clean Workspace') {
    steps {
        cleanWs()
    }
}

四、完整的java-k8s.jenkinsfile


#!groovy

@Library('jenkinslib') _

String sharefile = "/opt/.m2"
String dockerfileName = sharefile + "/" + "Dockerfile"

// java代码仓库gitlab的ssh密钥
String gitlabCredentialsId = "ffcbbd41-1e6a-49ec-8277-87a1299606dd"

def tools = new com.xxtech.devops.tools()
def http = new com.xxtech.devops.http()
def docker = new com.xxtech.devops.docker()
def helm = new com.xxtech.devops.helm()

// OA:项目管理--研发项目--产品列表中的英文名称
String projectName = "${env.projectName}".trim()

// jenkins job
String jobName = "${env.JOB_NAME}".trim()

// jar包的名称
String jarName = "${env.jarName}".trim()
// java应用的端口
int port = "${env.port}"
// java代码的git仓库地址
String codeUrl = "${env.codeUrl}".trim()
// java代码的git分支
String branch = "${env.branch}".trim()

// 消息通知的机器人robot
String robotKey = "${env.robotKey}".trim()

// 打包方式,mvn或者gradle
String packageType = "${env.packageType}".trim()

// 构建者
String buildUser = ""

// 版本号, 由jenkins去读取
String appVersion = ""

//docker image version
String dockerImageVersion = ""

// 子模块, 用于一个多module的项目打包,指定某模块以发布
String childModule = "${env.childModule}".trim()
String packagePath = "target"
if (childModule && childModule != "null" && childModule.trim() != "") {
    tools.PrintMes("build package for childModule: " + childModule, "green")
    packagePath = childModule + "/target"
}

// 判断是否为gradle打包
boolean gradlePackage = packageType.equalsIgnoreCase("gradle")
if (gradlePackage) {
    packagePath = "build/libs"
}
String gradleUserHome = "$sharefile/java-gradle-homes"
String gradleProjectCacheDir = "$gradleUserHome/$jobName"

// workspace
String PROJECT_WORKSPACE = "$sharefile/java-workspaces/$jobName"

// 校验不能为空
if (!projectName) {
    tools.PrintMes("projectName is null", "red")
    return
}

if (!jarName) {
    tools.PrintMes("jarName is null", "red")
    return
}

if (!codeUrl) {
    tools.PrintMes("codeUrl is null", "red")
    return
}

if (!branch) {
    tools.PrintMes("branch is null", "red")
    return
}

//根据jobName读取环境区分
String[] jobInfoArray = jobName.split("_")
// 环境
String buildEnv = jobInfoArray[0]

pipeline {
    agent {
        kubernetes {
            inheritFrom 'jnlp-maven'
            customWorkspace "${PROJECT_WORKSPACE}"
        }
    }

    options {
        timestamps()  //日志会有时间
        skipDefaultCheckout()  //删除隐式checkout scm语句
        disableConcurrentBuilds() //禁止并行
        timeout(time: 1, unit: 'HOURS')  //流水线超时设置1h
    }

    stages {
        stage('Get UserInfo') {
            steps {
                wrap([$class: 'BuildUser']) {
                    script {
                        def BUILD_USER = "${env.BUILD_USER}"
                        def BUILD_USER_ID = "${env.BUILD_USER_ID}"

                        buildUser = BUILD_USER + "(" + BUILD_USER_ID + ")"
                    }
                }
            }
        }

        stage('Pull Code') {
            steps {
                script {
                    tools.PrintMes("pull code!!!", "green")
                    // 拉取代码:git协议
                    git branch: "$branch", credentialsId: "$gitlabCredentialsId", url: "$codeUrl"
                }
            }
            post {
                failure {
                    //当此Pipeline失败时打印消息
                    script {
                        tools.PrintMes("pull code failure!!!", "red")
                        http.imNotify(projectName, "FAIL", buildEnv, "pull code failure", branch, buildUser, robotKey)
                    }
                }
            }
        }

        stage('Compile') {
            steps {
                script {
                    tools.PrintMes("compile!!!", "green")
                    container('maven') {
                        dir("${env.WORKSPACE}") {
                            if (gradlePackage) {
                                sh "$sharefile/gradle-7.6/bin/gradle build -x test --build-cache -g $gradleUserHome --no-daemon --project-cache-dir $gradleProjectCacheDir"
                            } else {
                                sh "mvn clean package -Dmaven.test.skip=true -U  -s  $sharefile/settings.xml"
                            }
                        }
                    }
                }
            }
            post {
                failure {
                    //当此Pipeline失败时打印消息
                    script {
                        tools.PrintMes("compile failure!!!", "red")
                        http.imNotify(projectName, "FAIL", buildEnv, "compile failure", branch, buildUser, robotKey)
                    }
                }
            }
        }

        stage('Get Version') {
            steps {
                script {
                    tools.PrintMes("Get Version!!!", "green")
                    appVersion = tools.getAppVersion(packagePath)
                    // 输出程序的版本号
                    tools.PrintMes("get version: ${appVersion}", "green")
                }
            }
            post {
                failure {
                    //当此Pipeline失败时打印消息
                    script {
                        tools.PrintMes("Get Version failure!!!", "red")
                        http.imNotify(projectName, "FAIL", buildEnv, "Get Version failure", branch, buildUser, robotKey)
                    }
                }
            }
        }

        stage('Push Docker Image') {
            steps {
                script {
                    tools.PrintMes("Push Docker Image!!!", "green")
                    def currentTime = sh returnStdout: true,
                            script: """
                                    date +%m%d%H%M%S
                                    """
                    // 注意:这里的分隔符不能使用冒号, trim()
                    dockerImageVersion = appVersion + "-" + currentTime.trim()
                    tools.PrintMes("Docker Image Version: ${dockerImageVersion}", "green")

                    container('docker') {
                        dir("${env.WORKSPACE}") {
                            docker.buildAndPushImage(jarName, port, dockerImageVersion, packagePath, dockerfileName)
                        }
                    }
                }
            }
            post {
                failure {
                    //当此Pipeline失败时打印消息
                    script {
                        tools.PrintMes("Push Docker Image failure!!!", "red")
                        http.imNotify(projectName, "FAIL", buildEnv, "Push Docker Image failure", branch, buildUser, robotKey)
                    }
                }
            }
        }

        stage('Clean Workspace') {
            steps {
                script {
                    tools.PrintMes("Clean Workspace!!!", "green")
                    cleanWs()
                }
            }
            post {
                failure {
                    script {
                        //当此Pipeline失败时打印消息
                        tools.PrintMes("Clean Workspace failure!!!", "red")
                        http.imNotify(projectName, "FAIL", buildEnv, "Clean Workspace failure", branch, buildUser, robotKey)
                    }
                }
            }
        }

        stage('Update Helm Yaml') {
            steps {
                script {
                    tools.PrintMes("Update Helm Yaml!!!", "green")

                    helm.updateYaml(jarName, dockerImageVersion)
                }
            }
            post {
                failure {
                    script {
                        //当此Pipeline失败时打印消息
                        tools.PrintMes("Update Helm Yaml failure!!!", "red")
                        http.imNotify(projectName, "FAIL", buildEnv, "Update Helm Yaml failure", branch, buildUser, robotKey)
                    }
                }
            }
        }
    }

    post {
        success {
            //当此Pipeline成功时打印消息
            timeout(time: 1, unit: "MINUTES") {
                script {
                    tools.PrintMes("archive jar, send the message of build successfully to user", "green")
                    container('maven') {
                        dir("${env.WORKSPACE}") {
                            sh "echo ${appVersion} > version_${appVersion}.txt"

                            archiveArtifacts artifacts: 'version_*.txt', followSymlinks: false
                        }

                        http.imNotify(projectName, "SUCCESS", buildEnv, "OK", branch, buildUser, robotKey)
                    }
                }
            }
        }
    }
}

五、消息通知

  • TODO: 我们对于构建成功的消息内容,增加程序版本号。

在这里插入图片描述
具体消息通知的实现,就不在本文的范畴。

写在末尾的话

后期,我们有空,就Jenkins集群、插件(包括二次开发插件)等话题,聊一聊我们的实际使用情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/731487.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2 第一个springboot程序

2.1 创建springboot项目 有两种方法:一种是从官网上下载jar包,然后使用idea的import导入。另一种是直接在idea创建springboot项目。 2.1.1 方法一 进入spring官网(Spring | Home)。 点击projects的spring boot。 点击overview。 …

【java】空字符串“”和null区别,以及判空的方案

空字符串“” 1、类型:“”是一个空字符串(String),长度为0,占内存,在内存中分配一个空间,可以使用Object对象中的方法。(例如:“”.toString()等) 2、内存分…

【Distributed】分布式监控系统zabbix应用(一)

文章目录 前言一、Zabbix基本概述1. Zabbix 的概念2. Zabbix 的工作原理3. Zabbix 6.0 新特性3.1 Zabbix 可以实现高可用3.2 Zabbix 新增监控系统 4. Zabbix 的构成5. Zabbix 的功能组件5.1 数据库5.2 Web 界面5.3 Zabbix Agent5.4 Zabbix Proxy5.5 Java Gateway 6. Zabbix 和 …

【JavaWeb】了解JavaScript DOM API

目录 1、什么是DOM 1.1、DOM树 1.2、 了解事件 2、常用的DOM API 2.1、选中页面元素 2.2、获取/修改元素内容 2.2.1、innerHTML和innerText 2.2、获取/修改元素属性 2.3、获取/修改表单元素属性 3、JQuery框架的简单了解和使用 4、代码案例:实现聚合索引功…

python 常用数据结构-元祖

Tuple 元组 元组定义与使用元组常用方法元组与列表 元组定义 元组是有序的不可变对象集合元组使用小括号包围,各个对象之间使用逗号分隔定义元祖可以不用括号,直接使用,隔开元组是异构的,可以包含多种数据类型 元组使用:创建 …

解决Unity的PostProcess奇怪报错

大家好,我是阿赵。   最近在使用Unity的PostProcess后处理效果的时候,发现了一个问题,下面记录一下这个问题的出现原因和解决办法。 一、出现问题 问题是这样出现的:   在场景里面添加某一个后处理效果后,当这个后…

JUC之ThreadLocal

文章目录 1 基础知识1.1 强软弱虚四种引用 2 ThreadLocal出现的好处3 ThreadLocal源码分析3.1 ThreadLocal内存泄露问题3.2 ThreadLocal为什么使用的是弱引用3.3 清扫过期的Entry 4 ThreadLocal使用建议 1 基础知识 1.1 强软弱虚四种引用 【整体结构】 【强引用】 【软引用…

(文件[夹]批量分类整理_多级匹配_交叉匹配_路径结构交叉调整)文件[夹]批量复制

​ 首先,需要用到的这个工具: 百度 密码:qwu2 蓝奏云 密码:2r1z ​如果没有使用过此工具,建议阅读此教程前,先看下这篇文章:https://mp.csdn.net/mp_blog/creation/editor/113605549 原理&…

【Python爬虫与数据分析】文件读写

目录 一、概述 二、txt文件读写 三、csv文件读写 四、Json文件读写 一、概述 读写模式: r:读数据w:覆盖写入a:追加写入rb:以二进制形式读出wb:以二进制形式写入ab:以二进制形式追加r、w、…

Java log 日志

文章目录 log4j.properties配置 log4j.properties 配置 log4j.rootLoggerINFO, stdoutlog4j.appender.stdoutorg.apache.log4j.ConsoleAppender log4j.appender.stdout.layoutorg.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern%d{yyyy-MM-dd H…

MySQL单表查询练习题

目录 第一题 第二题 第三题 第一题 1.创建数据表pet,并对表进行插入、更新与删除操作,pet表结构如表8.3所示。 (1)首先创建数据表pet,使用不同的方法将表8.4中的记录插入到pet表中。 mysql> create table pet( name varchar(…

网络安全(黑客)内网渗透基础知识

0x01 内网概述 内网也指局域网(Local Area Network,LAN)是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的历程安排、电子邮件和传真通信服务等功能。 内…

交叉导轨的结构与特长

在交叉导轨中,精密滚柱互相直交地组合在一起的滚柱保持架与设置在专用轨道上的90V形沟槽滚动面组合起来使用。通过将2列滚子导轨平行地装配,使导轨系统能承受4个方向的负荷。而且,因能向交叉滚子导轨施加预压,从而能获得无间隙且高…

zookeeper单机安装

1 检查环境jdk 参考:https://blog.csdn.net/weixin_44098426/article/details/128446376 2 解压安装包 mkdir -p /opt/zookeeper mv /home/wh/software/zk/apache-zookeeper-3.5.7-bin.tar.gz /opt/zookeeper tar -xzvf apache-zookeeper-3.5.7-bin.tar.gz 3 配置…

在Windows环境下安装Elasticsearch 8.8.2

Elasticsearch是一种开源的分布式搜索和分析引擎,被广泛应用于构建实时搜索、日志分析、数据可视化等应用。本文将详细介绍如何在Windows环境下安装和配置Elasticsearch 8。 安装Elasticsearch 步骤1:准备工作 在开始安装之前,确保已满足以…

多元回归预测 | Matlab灰狼算法(GWO)优化极限梯度提升树XGBoost回归预测,GWO-XGBoost回归预测模型,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab灰狼算法(GWO)优化极限梯度提升树XGBoost回归预测,GWO-XGBoost回归预测模型,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源…

GPON MAC SFP ONU模块介绍与应用

伴随着网络通讯技术的发展,pon无源光网络正逐步走进人们的视野;在这之前你是否仅知道以太网接入?相比与以太网接入,pon作为一种点到多点网络,具有运维成本低、服务范围广、资源占用少等优势;我们最为熟知的…

CSS自学框架之漂浮提示(上方显示 底侧显示 右侧显示 左侧显示)

漂浮提示可以用较为隐藏的方式为用户提供帮助或引导信息,将鼠标移入文字内容后展示。 css代码: /* 浮漂提示框 */[myth-tag]{ position: relative }[myth-tag]:before, [myth-tag]:after{z-index: 1;opacity: 0;position: absolute;pointer-events: non…

LVS负载均衡集群之LVS-NAT集群

目录 一、什么是集群? 1.1、集群(cluster)的含义 1.2、问题 1.3解决方法 二、集群分类 2.1、负载均衡集群(Load Balance Cluster) 2.2、高可用集群(High Availability Cluster) 2.3、高性能运算集群 (High Performance Computer Clust…

企业服务器数据库被360后缀勒索病毒攻击后的正确处理方式

在数字化时代,企业服务器数据库的安全性至关重要。然而,即使采取了各种安全措施,仍然有可能遭受到360等后缀的勒索病毒网络攻击。近期,很多企业的云服务与物理服务器遭到了360后缀的勒索病毒攻击,导致企业内部数据库中…