当前位置: 首页 > news >正文

使用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 repetitiondef 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" ]; thenecho "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_FILEecho "Created file: $NEW_FILE"
doneecho "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: 1strategy:rollingUpdate:maxSurge: 1maxUnavailable: 0selector:matchLabels:app: game-ucentertemplate:metadata:labels:app: game-ucenterspec:containers:- name: game-ucenterimage: xxxx/xxx/game-ucenter:{data}envFrom:- configMapRef:name: deployports:- containerPort: 80resources:requests:memory: "4096M"cpu: "2000m"limits:memory: "4096M"cpu: "2000m" livenessProbe:httpGet:scheme: HTTPpath: /test.htmlport: 80initialDelaySeconds: 20periodSeconds: 120successThreshold: 1failureThreshold: 3readinessProbe:httpGet:scheme: HTTPpath: /test.htmlport: 80initialDelaySeconds: 20periodSeconds: 120imagePullSecrets:                                              - name: xxx
---apiVersion: v1
kind: Service
metadata:name: game-ucenterlabels:app: game-ucenter
spec:ports:- port: 80protocol: TCPtargetPort: 80selector: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集群进行相应的调整。在生产环境中部署之前,请确保进行充分的测试。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • openpose+smplx
  • 轮廓检测与处理
  • wpf-动态设置组件【按钮为例】样式
  • Ubuntu 18.04搭建RISCV和QEMU环境
  • 磁盘阵列raid
  • 【机器学习】深度学习概论(二)
  • Java中final、finally和finalize方法的区别
  • Python实现张万森下雪了的效果
  • Linux 编写脚本定时发送天气预报
  • count distinct在spark中的运行机制
  • 大数据应用发展史:从搜索引擎时代到机器学习时代
  • AI赋能金融创新:技术驱动的未来金融革命
  • 水库大坝安全监测设计与施工经验
  • Golang 通用代码生成器仙童已发布 2.4.0 电音仙女尝鲜版二及其介绍视频,详细介绍了三大部分生成功能群
  • 城市分站优化系统源码:提升百度关键排名 附带完整的搭建教程
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • Elasticsearch 参考指南(升级前重新索引)
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • Mocha测试初探
  • Node + FFmpeg 实现Canvas动画导出视频
  • React-生命周期杂记
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • 对象引论
  • 解析 Webpack中import、require、按需加载的执行过程
  • 嵌入式文件系统
  • 软件开发学习的5大技巧,你知道吗?
  • 跳前端坑前,先看看这个!!
  • #{}和${}的区别是什么 -- java面试
  • #if 1...#endif
  • #QT(一种朴素的计算器实现方法)
  • %check_box% in rails :coditions={:has_many , :through}
  • (11)MATLAB PCA+SVM 人脸识别
  • (12)Hive调优——count distinct去重优化
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (九)One-Wire总线-DS18B20
  • (利用IDEA+Maven)定制属于自己的jar包
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (学习日记)2024.02.29:UCOSIII第二节
  • (一)基于IDEA的JAVA基础12
  • (一一四)第九章编程练习
  • (转)iOS字体
  • .equals()到底是什么意思?
  • .NET 8.0 发布到 IIS
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .NET构架之我见
  • @Validated和@Valid校验参数区别
  • @软考考生,这份软考高分攻略你须知道
  • [2019.2.28]BZOJ4033 [HAOI2015]树上染色
  • [AIGC] Nacos:一个简单 yet powerful 的配置中心和服务注册中心
  • [Algorithm][动态规划][子序列问题][最长递增子序列][摆动序列]详细讲解
  • [android] 看博客学习hashCode()和equals()
  • [Android]Android P(9) WIFI学习笔记 - 扫描 (1)
  • [Angular 基础] - 自定义指令,深入学习 directive
  • [Angularjs]asp.net mvc+angularjs+web api单页应用之CRUD操作