基于Jenkins实现的CI/CD方案

基于Jenkins实现的CI/CD方案

前言

最近基于Jenkins的基座,搭建了一套适用于我们项目小组的持续集成环境。现在把流程整理分享出来,希望可以给大家提供一些帮助和思路。

使用到的组件和版本

组件名称组件版本作用
Harbor2.7.3镜像仓库
Jenkins2.319.2持续集成工具
Pipeline2.6Jenkins插件,编排流水线脚本
SSH Pipeline Steps2.0.0Jenkins插件,提供远程执行ssh能力
Git4.10.1Jenkins插件,提供拉取git代码仓库的能力
Httpd2.4.18HTTP服务器,用于归档编译后的软件包,镜像包
Maven3.6.3后端代码构建工具
Node8.17.0前端代码构建工具
Docker20.10.0容器版本
Docker Composev2.18.1容器编排

我这边是基于Jenkins的Pipeline+Docker的方式进行的任务编排,Jenkins是找的一个别人做好的,内置了绝大多数插件的容器版本,链接地址:https://hub.docker.com/r/h1kkan/jenkins-docker。这个做好的镜像里面没有SSH Pipeline Steps这个插件,需要自己额外下载一下,这边需要注意一下插件和Jenkins的对应版本。

基本流程图


这边有几个需要注意的地方,简单说明一下

  1. 首先需要配置代码仓库的webhook,这个网上有很多资料,可以自行参考配置一下

  2. dockerfile,docker-compose.yml这些文件需要内置在代码仓库中或者服务器内(我们是dockerfile文件内置在代码仓库,docker-compose.yml文件放在服务器里面固定目录)

  3. 在第五步,执行远端ssh时,需要去更改docker-compose.yml中的image节点,一开始准备用shell去做的,但是实现有点难度,然后就内置了一个python脚本,用yaml这个库去实现的,具体脚本updateImageLabel.py参考:

    import yaml
    import sys
    
    filename = sys.argv[1]
    # 加载docker-compose.yml文件
    with open(filename,'r') as f:
        data = yaml.load(f)
    # 更新镜像标签
    data['services'][sys.argv[2]]['image'] = sys.argv[3]
    with open(filename, 'w') as yaml_file:
        yaml_file.write(yaml.dump(data, default_flow_style=False))
    

    使用时,传入三个参数,分别是docker-compose.yml文件路径,需要更新的服务,更新后的镜像标签

    python3 updateImageLabel.py ${dockerComposePath}  ${service}  ${image}
    
  4. Harbor的安装可以参考我之前的博客,安装完成如果是http的话,还需要配置一下insecure-registries仓库信息,并且执行docker login命令登录镜像仓库,用于拉取Harbor仓库镜像

    {
      "insecure-registries": [
        "harbor服务器地址"
      ]
    }
    
  5. 第七步是可选的,做这个是为了方便每一个版本的归档,这边可以归档软件包或者save之后的镜像包

具体实现步骤

下面具体写一下实现的步骤,因为涉及的东西很多,有一些能找到的通用的步骤我就先不写了,大家可以自行去百度或者Google。

基础中间件部署

这边只讲Jenkins和Httpd的部署,Harbor部署可以参考我之前的文档。

Jenkins部署

通过docker的方式拉起Jenkins

docker run -u root -e TZ=Asia/Shanghai --name=jenkins -d -e TZ="Asia/Shanghai" -p 8080:8080 -p 50000:50000 -v /data/jenkins:/var/jenkins_home -v /run/docker.sock:/var/run/docker.sock -v /data/archive:/data/archive h1kkan/jenkins-docker:2.319.2

容器起来之后,需要进入到容器内部执行一下docker login的命令,在Jenkins容器内部也生成一套Harbor的凭证。

第三个挂载目录是用来开给Httpd服务器的,用于归档软件包和镜像包。

Httpd服务器部署

通过docker拉起Httpd服务器

docker run -p 8001:80 -v /data/archive:/usr/local/apache2/htdocs/ -d --name httpd httpd:2.4.18 

这边挂载目录就是Jenkins开出来的目录

Maven、Node、JDK等基础镜像

可以先从外网拉取对应版本,然后本地打成tar包之后上传到服务器解压,再tag之后推送到自己的Harbor仓库。

在Jenkins配置Git凭证

这个网上也很多,就不细致展开了,可以自己查一下

Pipeline流水线编排

后端流水线pipeline脚本
import java.text.SimpleDateFormat
import java.util.TimeZone

// 构建版本
def createVersion() {
    def simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
    simpleDateFormat.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
    return simpleDateFormat.format(new Date()) + "_${env.branch}"
}

def getTime() {
    def simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
    simpleDateFormat.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
    return simpleDateFormat.format(new Date())
}

// 获取远端服务器信息 
def GetRemoteServer(ip, username, password) {
    def remote = [:]
    remote.name = ip
    remote.host = ip
    remote.user = username
    remote.password = password
    remote.allowAnyHosts = true
    return remote
}

pipeline {
    agent none
    environment {
        _version = createVersion()
        _time = getTime()
    }
    stages {
        stage('Git Checkout') {
            agent any
            steps {
                checkout([
	                $class: 'GitSCM', 
	                branches: [[name: "${branch}"]], 
	                doGenerateSubmoduleConfigurations: false, 
	                extensions: [], 
	                gitTool: 'Default', 
	                submoduleCfg: [], 
	                userRemoteConfigs: [[url: '{{git_url}}',credentialsId: '{{credentialsId}}',]]
                ])
            }
        }
        stage('Source Package') {
            agent {
                docker { 
                    image 'local-maven:3.6.3-openjdk-8' 
                    args '-v maven-repo:/usr/share/maven/ref'
                }
            }
            steps {
                sh 'mvn clean install -Dmaven.test.skip=true'
            }
        }
      
        stage('Build Image') {
            agent any
            steps {
                sh 'docker build -f $WORKSPACE/CI/dockerfile --build-arg JARNAME=backend.jar -t 127.0.0.1:18080/library/backend:${_version} $WORKSPACE/backend/target/'
                sh 'docker push 127.0.0.1:18080/library/backend:${_version}'
                sh 'docker rmi 127.0.0.1:18080/library/backend:${_version}'
            }
        }

        stage('Publish To Env') {
            agent any
            steps {
                script {
                    def remote = [:]
                    remote = GetRemoteServer('127.0.0.1', 'username', 'password')
                  	sshCommand remote: remote, command: "python3 updateImageLabel.py docker-compose.yml backend 127.0.0.1:18080/library/backend:${_version}"
                  	sshCommand remote: remote, command: "docker-compose -f docker-compose.yml up -d --build backend"
                }
            }
        }
          
        stage('Archive Package') {
            agent any
            steps {
                sh 'mkdir -p /data/archive/backend/${branch}/${_time}'
                sh 'cp $WORKSPACE/backend/backend.jar /data/archive/backend/${branch}/${_time}'
            }
        }
    }
}

解释一下这个脚本

  • 首选镜像的版本是时间戳+分支的格式,类似20231026095511_dev,可以根据项目组的要求进行生成。

  • GetRemoteServer方法主要是提供获取远端服务器的信息,主要使用了SSH Pipeline Steps这个插件的能力,传入ip,username,password三个参数,这边密码是明文写死在脚本里面的,这个插件也支持密码加密保存,如果安全性要求比较高的可以换成加密的方式。

  • Git Checkout 这个stage主要是进行代码的下载,根据${branch}这个参数来指定代码仓库的版本,参数的具体配置可以看下Jenkins参数化构建的相关指导和文章。

  • Source Package就是代码仓库的打包,这边指定使用Docker镜像作为执行的Agent,只要指定镜像和编译命令就可以。这边我还挂载了一个maven-repo的volume,主要是为了缓存Jar包,不用每次都去下载。

  • Build Image步骤进行代码的构建,通过docker-build命令去生成镜像,这边的dockerfile文件是内置在我们代码库中的,参考如下:

    FROM 127.0.0.1:18080/library/java:8.0
    ARG JARNAME
    COPY ${JARNAME} /data/
    
  • Publish To Env 推送到环境,这边核心就是连接到远程服务器,通过修改镜像标签的脚本去更新docker-compose.yml文件中的镜像标签,然后重新构建容器

  • Archive Package 是否归档,演示的脚本里面只是归档了编译后的文件,还可以归档镜像等等。归档的路径为分支名/时间戳
    在这里插入图片描述

前端流水线pipeline脚本
import java.text.SimpleDateFormat
import java.util.TimeZone

// 构建版本
def createVersion() {
    def simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
    simpleDateFormat.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
    return simpleDateFormat.format(new Date()) + "_${env.branch}"
}

def getTime() {
    def simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
    simpleDateFormat.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
    return simpleDateFormat.format(new Date())
}

// 获取远端服务器信息 
def GetRemoteServer(ip, username, password) {
    def remote = [:]
    remote.name = ip
    remote.host = ip
    remote.user = username
    remote.password = password
    remote.allowAnyHosts = true
    return remote
}

pipeline {
    agent none
    environment {
        _version = createVersion()
        _time = getTime()
    }
    stages {
        stage('Git Checkout') {
            agent any
            steps {
                checkout([
	                $class: 'GitSCM', 
	                branches: [[name: "${branch}"]], 
	                doGenerateSubmoduleConfigurations: false, 
	                extensions: [], 
	                gitTool: 'Default', 
	                submoduleCfg: [], 
	                userRemoteConfigs: [[url: '{{git_url}}',credentialsId: '{{credentialsId}}',]]
                ])
            }
        }
        stage('Source Package') {
            agent {
                docker { 
                    image 'node:8.17.0' 
                }
            }
            steps {
                script {
                    sh 'npm install --registry=http://registry.npm.taobao.org'
                }
            }
        }
        stage('Image Build') {
            agent any
            steps {
                sh 'docker build -f $WORKSPACE/CI/dockerfile -t 127.0.0.1:18080/library/frontend:${_version} $WORKSPACE/target/'
                sh 'docker push 127.0.0.1:18080/library/frontend:${_version}'
                sh 'docker rmi 127.0.0.1:18080/library/frontend:${_version}'
            }
        }
        stage('Publish To Env') {
            agent any
            steps {
                script {
                    def remote = [:]
                    remote = GetRemoteServer('127.0.0.1', 'username', 'password')
                    // 更新docker-compose.yml文件,修改镜像
                    sshCommand remote: remote, command: "python3 updateImageLabel.py docker-compose.yml frontend 127.0.0.1:18080/library/frontend:${_version}"
                    sshCommand remote: remote, command: "docker-compose -f docker-compose.yml up -d --build frontend"

                }
            }
        }
        stage('Archive Package') {
            agent any
            steps {
                sh 'mkdir -p /data/archive/frontend/${branch}/${environment}/${_time}'
                sh 'cd $WORKSPACE/target/dist/ && zip -r dist.zip *'
                sh 'cp $WORKSPACE/target/dist/dist.zip /data/archive/frontend/${branch}/${environment}/${_time}'
            }
        }
    }
}

前端和后端除了构建方式不一样,其他基本都相同。

结语

参考地址:

https://www.jenkins.io/doc/book/pipeline/

https://www.jenkins.io/doc/pipeline/steps/ssh-steps/

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

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

相关文章

C++ Primer 笔记(总结,摘要,概括)——第5章 语句

目录 5.1 简单语句 5.2 语句作用域 5.3 条件语句 5.3.1 if语句 5.3.2 switch语句 5.4 迭代语句 5.4.1 while语句 5.4.2 传统的for语句 5.4.3 范围for语句 5.4.4 do while语句 5.5 跳转语句 5.5.1 break语句 5.5.2 continue语句 5.5.3 goto语句 5.6 try语句块和异常处理 5…

http和https的区别(简述)

HTTP(HyperText Transfer Protocol)和HTTPS(HTTP Secure)都是用于在客户端和服务器之间传输数据的协议,但它们在安全性方面有重要的区别。 1.HTTP: 概述: HTTP是一种用于传输超文本的协议(超文…

前端基础自学整理|DOM树

DOM,文档对象模型(Document Object Model),简单的说,DOM是一种理念,一种思想,一个与系统平台和编程语言无关的接口,一种方法, 使 Web开发人员可以访问HTML元素!不是具体方…

D6208——双向马达驱动电路芯片,噪声低 内设马达驱动功率晶体管,工作电源电压范围宽用 TTL 逻辑信号直接控制

D6208 是一块单片双向马达驱动电路,它使用TTL电平的逻辑信号就能控制卡式录音机和其它电子设备中的双向马达。该电路由一个逻辑部分和一个功率输出部分组成。逻辑部分控制马达正、反转向及制动,功率输出部分根据逻辑控制能提供100mA(典型值&a…

【python】 脚本检查文本里是否包含特殊字符

【python】 脚本检查文本里是否包含特殊字符 完整代码: # 代码片段功能: 检查文本里是否包含特殊字符 # 将utf-8格式文本,先转为16进制格式 # 检查完成,再将16进制格式转为utf-8格式。 import re# 包含特殊字符的字符串 sample "I arg…

当别人在用AI一分钟画几十张图,你还在埋头苦苦设计?AI时代一定要学会和AI共存!

AI绘画即指人工智能绘画,是一种计算机生成绘画的方式。是AIGC应用领域内的一大分支。 AI绘画主要分为两个部分,一个是对图像的分析与判断,即“学习”,一个是对图像的处理和还原,即“输出”。 人工智能通过对数以万计…

数据分析(二)自动生成分析报告

1. 报告生成思路概述 怎么快速一份简单的数据分析报告,注意这个报告的特点: --网页版,可以支持在线观看或者分享HTML文件 --标题,动图,原始数据应有尽有 --支持交互,比如plotly交互画面,数据…

Windows安装PHP及在VScode中配置插件,使用PHP输出HelloWorld

安装PHP PHP官网下载地址(8.3版本):PHP For Windows:二进制文件和源代码发布 点击下载.zip格式压缩包: 历史版本在Old archives中下载。推荐在Documentation download中下载官方文档,方便学习。 下载完成后在一个顺眼的地方解压压…

net反射

1.1 查找dll文件 Load需要把dll放到程序当前路径加载,也可以读取字符串形式。LoadFrom需要写全路径,如果test1.dll引用了test2.dll,同时也会加载test2.dll进来。LoadFile不会加载test2.dll。 Assembly assembly1 Assembly.Load("DllTe…

互联网加竞赛 大数据疫情分析及可视化系统

文章目录 0 前言2 开发简介3 数据集4 实现技术4.1 系统架构4.2 开发环境4.3 疫情地图4.3.1 填充图(Choropleth maps)4.3.2 气泡图 4.4 全国疫情实时追踪4.6 其他页面 5 关键代码最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 大数据疫…

Open AI — Sora 如何发挥其魔力 — 近距离观察该技术

OpenAI 的大模型 Sora 可以制作一整分钟的高质量视频。他们的工作成果表明,使视频生成模型更大是为现实世界创建多功能模拟器的好方法。Sora 是一种灵活的可视化数据模型。它可以创建不同长度、形状和大小的视频和图片,甚至可以创建长达一分钟的高清视频。我阅读了 OpenAI 的…

[C#]winform使用引导APSF和梯度自适应卷积增强夜间雾图像的可见性算法实现夜间雾霾图像的可见度增强

【算法介绍】 提升夜间雾霾图像可见度的技术研究:引导APSF与梯度自适应卷积的应用 随着城市化的快速发展,雾霾现象日益严重,尤其是在夜间,雾霾对图像的可见度造成了极大的影响。因此,提升夜间雾霾图像的可见度成为了…

ElasticSearch聚合操作

目录 ElasticSearch聚合操作 基本语法 聚合的分类 后续示例数据 Metric Aggregation Bucket Aggregation ES聚合分析不精准原因分析 提高聚合精确度 ElasticSearch聚合操作 Elasticsearch除搜索以外,提供了针对ES 数据进行统计分析的功能。聚合(aggregation…

一文彻底搞懂JVM垃圾回收算法

文章目录 1. 标记-清除算法(Mark and Sweep)2. 复制算法(Copying)3. 标记-整理算法(Mark and Compact)4. 分代算法(Generational)4.1 执行流程 1. 标记-清除算法(Mark an…

《雾锁王国》超简单0成本自建个人专属16人联机服务器教程

阿里云雾锁王国服务器搭建教程是基于计算巢服务,3分钟即可成功创建Enshrouded游戏服务器,阿里云8核32G雾锁王国专用游戏服务器90元1个月、271元3个月,阿里云服务器网aliyunfuwuqi.com亲自整理雾锁王国服务器详细搭建教程: 一、前…

「实战应用」如何使用图表控件LightningChart创建数据采集系统?(一)

LightningChart.NET完全由GPU加速,并且性能经过优化,可用于实时显示海量数据-超过10亿个数据点。 LightningChart包括广泛的2D,高级3D,Polar,Smith,3D饼/甜甜圈,地理地图和GIS图表以及适用于科学…

了解电力测试中负载箱的重要性?

电力测试是电力系统运行和维护的重要环节,其中负载箱作为一种重要的测试设备,其重要性不言而喻。负载箱主要用于模拟实际的电力负载,对电力设备进行性能测试和故障诊断,以确保电力系统的稳定运行。 负载箱可以模拟实际的电力负载。…

电商网站的大规模网页抓取 (终极指南)

电商网站的大规模网页抓取|电商数据采集API接口 与小型项目相比,大规模的网页抓取带来了一系列截然不同的挑战,例如基础结构搭建、管理资源成本、绕过爬虫检测措施等。 本文将指导您完成大规模数据收集,并以电商领域为重点。 Oxylabs 网页…

安卓系统和iOS系统的手机备忘录同步数据方法

在这个智能手机时代,安卓与iOS系统犹如两位王者,各自拥有庞大的用户群体。有人钟情于安卓的开放与多样,有人偏爱iOS的流畅与稳定。甚至,有些人为了满足不同需求,同时使用着两个系统的手机。我就是其中的一员。 工作中…

PDF控件Spire.PDF for .NET【安全】演示:获取 PDF 签名中的所有证书

Spire.PDF for .NET 是一款独立 PDF 控件,用于 .NET 程序中创建、编辑和操作 PDF 文档。使用 Spire.PDF 类库,开发人员可以新建一个 PDF 文档或者对现有的 PDF 文档进行处理,且无需安装 Adobe Acrobat。 E-iceblue 功能类库Spire 系列文档处…