使用Jenkins和单个模板部署多个Kubernetes组件

前言

在持续集成和部署中,我们通常需要部署多个实例或组件到Kubernetes集群中。通过Jenkins的管道脚本,我们可以自动化这个过程。在本文中,我将演示如何使用Jenkins Pipeline及单个YAML模板文件(.tpl)来部署多个类似的Kubernetes组件,而不需要为每个组件提供单独的模板文件。

问题背景

参照:Jenkins Pipeline 脚本优化实践:从繁琐到简洁 批量生成 Kubernetes 部署模板:从 1 到20顺序模板

pipeline {
    agent none // Use none at the top level, each stage will define its own agent.

    environment {
        REGISTRY = "xxxx/master-metaspace"
        KUBE_CONFIG = "--namespace=master-metaspace --context=master"
        KUBE_YAML_PATH = "/home/jenkins/workspace/yaml/master-metaspace"
        // Assume that 'data' is defined elsewhere or injected as a parameter.
        BASE_WORKSPACE = "xxxxxxx" // 定义一个基础工作空间路径
    }

    stages {
        stage("GetCode") {
            agent { label "build01" }
            steps {
                script {
                    checkout scm: [
                        $class: 'GitSCM',
                        branches: [[name: env.branchName]],
                        extensions: [[$class: 'CloneOption', depth: 1, noTags: false, shallow: true]],
                        userRemoteConfigs: [[credentialsId: 'xxxx', url: env.gitHttpURL]]
                    ]
                }
            }
        }
        
        stage("Docker Builds") {
            parallel {
                stage('Build dataloader-game-ucenter') {
                    agent { label "build01" }
                    when { environment name: 'dataloader', value: 'true' }
                    steps {
                        buildAndPushDockerImage("dataloader-game-ucenter", env.data, env.BASE_WORKSPACE)
                    }
                }
                stage('Build datawriter-game-ucenter') {
                    agent { label "build01" }
                    when { environment name: 'datawriter', value: 'true' }
                    steps {
                        buildAndPushDockerImage("datawriter-game-ucenter", env.data, env.BASE_WORKSPACE)
                    }
                }
                stage('Build game-ucenter') {
                    agent { label "build01" }
                    when { environment name: 'game-ucenter', value: 'true' }
                    steps {
                        buildAndPushDockerImage("game-ucenter", env.data, env.BASE_WORKSPACE)
                    }
                }
            }
        }
        
        stage('Development Deployment') {
            parallel {
                stage("Deploy datawriter-game-ucenter") {
                    when { environment name: 'datawriter-game-ucenter', value: 'true' }
                    agent { label  "huaweiyun-xx" }
                    steps {
                        deployToKubernetes("datawriter-game-ucenter")
                    }
                }
                stage("Deploy dataloader-game-ucenter") {
                    when { environment name: 'dataloader', value: 'true' }
                    agent { label  "huaweiyun-xx" }
                    steps {
                        deployToKubernetes("dataloader-game-ucenter")
                    }
                }
                stage("Deploy game-ucenter") {
                    when { environment name: 'game-ucenter', value: 'true' }
                    agent { label  "huaweiyun-xx" }
                    steps {
                        deployToKubernetes("game-ucenter-1")
                        deployToKubernetes("game-ucenter-2")
                        deployToKubernetes("game-ucenter-3")
                        deployToKubernetes("game-ucenter-4")
                        ............................
                    }
                }
            }
        }
    }
}

// Define methods outside pipeline to avoid repetition

def buildAndPushDockerImage(String imageName, String tag, String workspacePath) {
    sh "cd ${workspacePath} && echo 'Current directory: \$(pwd)'" // 使用基础工作空间变量
    sh "cd ${workspacePath}/${imageName}&& docker build --build-arg NODE_ENV=$imageName -t $REGISTRY/$imageName:$tag ."
    withCredentials([usernamePassword(credentialsId: 'xxxxx', passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {
        sh "docker login -u $dockerUser -p $dockerPassword $REGISTRY"
        sh "docker push $REGISTRY/$imageName:$tag"
    }
}

def deployToKubernetes(String kubernetesComponent) {
    String templateFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.tpl"
    String outputFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.yaml"
    sh "sed -e 's/{data}/$data/g' $templateFile > $outputFile"
    sh "sudo kubectl apply -f $outputFile $KUBE_CONFIG"
}

默认jenkins pipeline如上,我们有多个相似的游戏用户中心服务game-ucenter-*运行在Kubernetes集群中,它们都使用非常相似的Kubernetes YAML配置文件,配置文件之间的差异主要是一些标识符的不同(例如,服务的序号)。在传统的做法中,维护一系列几乎一样的模板文件(如game-ucenter-1.tpl, game-ucenter-2.tpl 等)将非常低效且易出错。

为了精简流程和提高效率,我们需要一个方法来通过单一模板生成多个配置文件,并由此部署多个不同的服务实例。

解决方案

使用Jenkins Pipeline中的sed命令和循环结构,我们可以从单一模板生成多个Kubernetes配置文件,并相应地部署每个服务实例。参照generate_templates.sh脚本

#!/bin/bash

# Define the name of the template file.
TEMPLATE_FILE="game-ucenter.tpl"

# Check if the template file exists.
if [ ! -f "$TEMPLATE_FILE" ]; then
    echo "Template file $TEMPLATE_FILE does not exist."
    exit 1
fi

# Loop to create files from game-ucenter-2 to pvp-game-20 based on the template.
for i in $(seq 1 20); do
    # Define the name of the new file.
    NEW_FILE="game-ucenter-${i}.yaml"
    
    # Copy the template to the new file.
    cp $TEMPLATE_FILE $NEW_FILE
    
    # Use 'sed' to replace 'game-ucenter-1' with 'game-ucenter-N' and save inline (-i option).
    sed -i "s/game-ucenter/game-ucenter-${i}/g" $NEW_FILE


    echo "Created file: $NEW_FILE"
done

echo "All files created successfully."

步骤 1: 定义Jenkins Pipeline

在我们的Jenkins脚本中,我们首先定义了基础环境变量和两个函数:buildAndPushDockerImagedeployToKubernetes。这些函数将用于构建Docker镜像并部署到Kubernetes

def buildAndPushDockerImage(String imageName, String tag, String workspacePath) {
    sh "cd ${workspacePath} && echo 'Current directory: \$(pwd)'" // 使用基础工作空间变量
    sh "cd ${workspacePath}/${imageName}&& docker build --build-arg NODE_ENV=$imageName -t $REGISTRY/$imageName:$tag ."
    withCredentials([usernamePassword(credentialsId: 'xxx', passwordVariable: 'dockerPassword', usernameVariable: 'dockerUser')]) {
        sh "docker login -u $dockerUser -p $dockerPassword $REGISTRY"
        sh "docker push $REGISTRY/$imageName:$tag"
    }
}

def deployToKubernetes(String kubernetesComponent) {
    String templateFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.tpl"
    String outputFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.yaml"
    sh "sed -e 's/{data}/$data/g' $templateFile > $outputFile"
    sh "sudo kubectl apply -f $outputFile $KUBE_CONFIG"
}

步骤 2: 修改deployToKubernetes函数

接下来,我们需要修改deployToKubernetes函数,以便它能够接受组件名称,并使用单一模板文件创建具体的配置文件。

def deployToKubernetes(String kubernetesComponent, int instance=1, int totalInstances=1) {
    // 检查实例值
    if (instance < 1) {
        error("实例数必须大于0")
    }

    // 根据 instance 的值来定义资源的名称和文件名
    String nameSuffix = totalInstances > 1 ? "-${instance}" : "" // 总是添加后缀,除非只有一个实例
    String outputFileName = "${kubernetesComponent}${nameSuffix}.yaml"
    String templateFile = "${KUBE_YAML_PATH}/${kubernetesComponent}.tpl"
    String outputFile = "${KUBE_YAML_PATH}/${outputFileName}"
    String nameReplacement = "${kubernetesComponent}${nameSuffix}"

    sh """
        cat "${templateFile}" \
        | sed 's/{data}/${data}/g' \
        | sed 's/name: ${kubernetesComponent}/name: ${nameReplacement}/g' \
        | sed 's/app: ${kubernetesComponent}/app: ${nameReplacement}/g' \
        > "${outputFile}"
    """

    // 使用 KUBE_CONFIG 应用 Kubernetes 配置
    sh "kubectl apply -f ${outputFile} ${KUBE_CONFIG}"
}

对于单实例的业务,例如Deploy dataloader-game-ucenter,我们不需要传递实例编号。

stage("Deploy dataloader-game-ucenter") {
    when { environment name: 'dataloader', value: 'true' }
    agent { label  "huaweiyun-xx" }
    steps {
        deployToKubernetes("dataloader-game-ucenter")
    }
}

对于多实例。我这里生成 规则优点强迫症了。如果多实例我生成的规则要求符合game-ucenter-1,game-ucenter-2,game-ucenter-3…顺序,当单个实例的时候则保持原来的不加标签:

                stage("Deploy game-ucenter") {
                    when { environment name: 'game-ucenter', value: 'true' }
                    agent { label  "k8s-node-06" }
                    steps {
                        script {
                          int instances = 2 // 假设我们有2个实例
                          for (int i = 1; i <= instances; i++) {
                            def componentName = "game-ucenter"
                            deployToKubernetes("game-ucenter", i, instances)

                    }
                }
            }
        }

步骤 3: 准备模板文件

我们的模板文件game-ucenter.tpl将包含通用的Kubernetes服务或部署定义,使用占位符game-ucenter-1 game-ucenter-2来标识应该被替换的地方。

# game-ucenter-1.tpl (示例部分)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: game-ucenter
spec:
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: game-ucenter
  template:
    metadata:
      labels:
        app: game-ucenter
    spec:
      containers:
        - name: game-ucenter
          image: xxxx/xxx/game-ucenter:{data}
          envFrom:
          - configMapRef:
              name: deploy
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: "4096M"
              cpu: "2000m"
            limits:
              memory: "4096M"
              cpu: "2000m" 
          livenessProbe:
            httpGet:
              scheme: HTTP
              path: /test.html
              port: 80
            initialDelaySeconds: 20
            periodSeconds: 120
            successThreshold: 1
            failureThreshold: 3
          readinessProbe:
            httpGet:
              scheme: HTTP
              path: /test.html
              port: 80
            initialDelaySeconds: 20
            periodSeconds: 120
      imagePullSecrets:                                              
        - name: xxx
---

apiVersion: v1
kind: Service
metadata:
  name: game-ucenter
  labels:
    app: game-ucenter
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: game-ucenter

# ...

步骤 4: 执行Jenkins Pipeline

当Jenkins Pipeline运行到"Development Deployment"阶段时,它将循环创建和应用game-ucenter-1.yamlgame-ucenter-2.yaml的配置文件,从而部署2个game-ucenterdeployment服务实例。
image.png
并保证单个实例的原有命名规则:
image.png

通过这一方法,我们不再需要为每个服务实例维护一个单独的模板文件,而是可以通过一个模板文件和Jenkins Pipeline的自动化来简化服务部署工作。这样做不仅提升了效率,也降低了出错的风险。

注意:

以上代码和命令为示例性质,可能需要根据您具体的Jenkins环境和Kubernetes集群进行相应的调整。在生产环境中部署之前,请确保进行充分的测试。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL数据库——事务

1. 事务概述 事务是一个在数据库系统中执行的一系列操作的集合&#xff0c;这些操作被看作是一个不可分割的工作单位。事务的主要目的是确保数据的完整性和一致性。 在事务中&#xff0c;所有操作要么全部成功完成&#xff0c;要么全部不发生。也就是说&#xff0c;如果事务中…

javascript实现数据双向绑定

ES5中的双向绑定 ES5中的对象属性类型有两种&#xff1a;分别是数据属性和访问器属性 一&#xff0c;数据属性 数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性 1&#xff0c;configurable:表示能否通过delete删除属性而重新定义…

Spring基础IoC(控制反转)与DI(依赖注入)

1. Spring 基础 1.1 什么是Spring框架&#xff1f;它能带来那些好处&#xff1f; Spring 是一个开源的轻量级的 Java 开发框架&#xff0c;可以帮助开发人员更高效的进行开发&#xff0c;主要优势在于简化开发和框架整合。 Spring框架整合了很多模块&#xff0c;这些模块可以…

【ArkTS入门】ArkTS开发初探:语言特点和开发特点

什么是ArkTS&#xff1f; ArkTS是一个为鸿蒙组件而生的框架&#xff0c;语法亲人好用。基于TypeScript&#xff0c;ArkTS拓展了声明式UI、状态管理等的能力&#xff0c;从本质上来讲&#xff0c;是TypeScript的扩展&#xff0c;主要服务于前端。 ArkTS的开发可以满足“一次开…

【北亚服务器数据恢复】san环境下LUN Mapping出错导致文件系统一致性出错的数据恢复案例

服务器数据恢复环境&#xff1a; san环境下的存储上一组由6块硬盘组建的RAID6&#xff0c;划分为若干LUN&#xff0c;MAP到跑不同业务的服务器上&#xff0c;服务器上层是SOLARIS操作系统UFS文件系统。 服务器故障&#xff1a; 业务需求需要增加一台服务器跑新增的应用&#xf…

OR-NeRF论文笔记

OR-NeRF论文笔记 文章目录 OR-NeRF论文笔记论文概述Abstract1 Introduction2 Related Work3 Background4 Method4.1 Multiview Segmentation4.2 Scene Object Removal 5 ExperimentsDatasetsMetricsMultiview SegmentationScene Object Removal 6 Conclusion 论文概述 目的&am…

VMware Workstation Pro 16 安装 Ubuntu系统(带磁盘简单分区选择)

选择新建虚拟机选择典型&#xff08;推荐&#xff09;安装 选择稍后安装操作系统 选择Linux操作系统 版本选择Ubuntu 本地磁盘最少有10G空间&#xff0c;当然空间大一点更好。最好路径不带中文&#xff0c;新建文件夹&#xff0c;单独存放虚拟机&#xff0c;方便删除和更新。…

【Spark精讲】一文讲透SparkSQL执行过程

SparkSQL执行过程 逻辑计划 逻辑计划阶段会将用户所写的 SQL语句转换成树型数据结构(逻辑算子树)&#xff0c; SQL语句中蕴含的逻辑映射到逻辑算子树的不同节点。 顾名思义&#xff0c;逻辑计划阶段生成的逻辑算子树并不会直接提交执行&#xff0c;仅作为中间阶段 。 最终逻辑…

基于优化的规划方法 - 数值优化基础 Frenet和笛卡尔的转换 问题建模 实现基于QP的路径优化算法

本文讲解基于优化的规划算法&#xff0c;将从以下几个维度讲解&#xff1a;数值优化基础、Frenet与Cartesian的相互转换、问题建模OSQP 1 数值优化基础 1.1 优化的概念 一般优化问题公式&#xff1a; f ( x ) f(x) f(x)&#xff1a;目标/成本函数 x x x&#xff1a;决策变…

如何把握品牌新五感,打造小红书品牌

随着社会经济的发展&#xff0c;市场的进步&#xff0c;以及人们思维方式的改变。年轻人面对市场&#xff0c;面对营销&#xff0c;关注点也在发生着改变。那什么是小红书品牌新五感&#xff0c;如何把握品牌新五感&#xff0c;打造小红书品牌&#xff01; 一、品牌五感是什么 …

机器学习中的一些有趣点【Attack 和 Defence】

什么是机器学习中的攻击 “攻击机器学习模型” 指的是试图危害或操纵机器学习模型的概念。有各种类型的攻击机器学习模型的方法&#xff0c;它们可以广泛分为几类&#xff1a; 对抗攻击&#xff1a; 定义&#xff1a; 对抗攻击涉及对输入数据进行微小的、通常是难以察觉的更改…

k8s的资源管理

命令行: kubectl命令行工具优点: 90%以上的场景都可以满足 对资源的增&#xff0c;删&#xff0c;查比较方便&#xff0c;对改不是很友好缺点:命令比较冗长&#xff0c;复杂难记 声明方式&#xff1a;k8s当中的yaml文件实现资源管理----声明式GUI:图形化工具的管理。 查看k8s的…

TP-LINK 路由器忘记密码 - 恢复出厂设置

TP-LINK 路由器忘记密码 - 恢复出厂设置 1. 恢复出厂设置2. 创建管理员密码3. 上网设置4. 无线设置5. TP-LINK ID6. 网络状态References 1. 恢复出厂设置 在设备通电的情况下&#xff0c;按住路由器背面的 Reset 按钮直到所有指示灯同时亮起后松开。 2. 创建管理员密码 3. 上网…

解锁小程序UI设计的奥秘:必须知晓的相关事项

一、什么是小程序 2016年张小龙首次提出小程序概念&#xff0c;全称为“微信小程序”&#xff0c;英文名为“微信小程序”MiniProgram它是一种基于微信生态系统、开发周期短、成本低、无需下载安装、即用即走的应用。 到目前为止&#xff0c;小程序不仅是微信独有的&#xff…

lv13 内核模板编译方法 7

1 内核模块基础代码解析 Linux内核的插件机制——内核模块 类似于浏览器、eclipse这些软件的插件开发&#xff0c;Linux提供了一种可以向正在运行的内核中插入新的代码段、在代码段不需要继续运行时也可以从内核中移除的机制&#xff0c;这个可以被插入、移除的代码段被称为内…

喜讯|极狐GitLab 通过信通院汽车软件研发效能成熟度模型能力

12 月 27 日&#xff0c;在由中国信息通信研究院&#xff08;下称信通院&#xff09;、中国通信标准化协会联合主办的2023系统稳定性与精益软件工程大会-汽车云质效专场峰会上&#xff0c;信通院发布了“2023年下半年汽车云评估结果”&#xff0c;极狐GitLab 一体化 DevOps 平台…

thinkphp+vue_mysql汽车租赁管理系统1ma2x

运行环境:phpstudy/wamp/xammp等 开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp5 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat/phpmyadmin 课题主要分为三大模块&#xff1a;即管理员模块、用户模块…

Appstore上架:2023年度总结

今天是2023年的最后一个工作日&#xff0c;今天用来总结一下2023年关于苹果上架的相关政策改动和对应的拒审邮件解决方法。 目录 了解有关Apple开发者的最新活动、故事和资讯指南拒审邮件及解决对应方法 了解有关Apple开发者的最新活动、故事和资讯指南 官方新闻和更新记录 官…

Shell三剑客:awk(awk编辑编程)五

一、前言 AWK 可以使用关联数组这种数据结构&#xff0c;索引可以是数字或字符串。AWK关联数 组也不需要提前声明其大小&#xff0c;因为它在运行时可以自动的增大或减小。 二、数组语法格式 array_name[index]valuearray_name&#xff1a;数组的名称index&#xff1a;数组索…

使用rust读取usb设备ACR122U的nfc卡片id

rust及其高效和安全著称&#xff0c;而且支持跨平台&#xff0c;所以就想使用这个rust开发一个桌面端程序&#xff0c;来读取nfc设备的nfc卡片的id信息&#xff0c;下面就做一个最简单的入门教程吧&#xff0c;也是我写的第三个rust应用。 当你电脑上安装好了rust环境之后&…
最新文章