Skip to content

制品库平台实践-nexus

制品库平台实践-nexus

image-20230626204508801

目录

[TOC]

本节实践

实战名称
💘 实践:配置Maven代理仓库(测试成功)-2022.6.7
💘 实践:创建Maven本地仓库(依赖)-2022.6.10(测试成功)
💘 实践:创建Raw本地仓库(制品)(测试成功)-2022.6.10
💘 实践:使用管理页面上传制品(测试成功)-2023.6.27
💘 实践:使用mvn命令上传制品(自定义pom信息(灵活))-2022.6.11
💘 实践:直接读取pom文件(扩展)-2022.6.11(测试成功)
💘 实践:使用Jenkins插件上传制品(把信息写死)(测试成功)-2023.6.28
💘 实践:使用Jenkins插件上传制品(读取POM文件上传制品)(测试成功)-2023.6.28
💘 扩展: 可以在Jenkins页面添加参数, 让用户输入后进行发布(测试成功)-2023.6.28
💘 实践:Jenkins插件获取包路径-2023.6.29(测试成功)
💘 实践:CI综合实践-2023.6.29(测试成功)

1、Nexus基本功能简介

官网:https://www.sonatype.com/products/nexus-repository?topnav=true 官方文档: https://help.sonatype.com/repomanager3/product-information/release-notes

工作流定义:

  • 集成流水线: 提交代码,构建,单测,代码扫描,上传制品【生成制品】
  • 发布流水线: 输入版本号, 输入部署环境.(从对应环境的制品库中取制品)

制品类型: 二进制制品, docker镜像

核心技能点:

  • 制品库管理规范(创建)
  • 上传制品
  • 下载制品

从3.x开始,它默认支持许多主流的软件包格式。Docker、Maven、Npm:

image-20220604153100167

仓库类型:

  • proxy 代理仓库。
  • hosted 私有仓库。
  • group 仓库组,将多个仓库组合在一起,通过同一个URL对外提供。

代理仓库 : Maven、Npm等。用于存储外网公共仓库中的插件和依赖,不可进行修改和私自上传。

2、Nexus3安装部署配置

见独立md。

3、Nexus制品库应用实践

1.搭建Maven私服(代理仓库)

默认开发同学在进行开发的时候会使用一些包管理工具,例如:mavenantgradle这些都是常见项目编译构建工具 。这些工具可以理解为是一个命令行工具, 本身不会存储任何依赖包,而是通过公网官方的仓库中下载当前项目构建所需要的包。 (内网的速度要比公网快,这会直接影响管道的构建速度)

image-20220605093736331

使用私服,就是在企业内部建立单一的可信源, 例如:我们在公司通过nexus创建一个代理仓库, 将公网仓库中的maven包代理到内网仓库中。 这样整个公司的同学就可以直接访问内网的私服进行下载构建依赖包。(减少了引入不信任依赖的风险)

代理仓库不会一下子把公网仓库中的所有包下载到本地,而是按需缓存。 例如: 此时我需要使用aa这个包, 如果代理仓库中没有, 则请求外部服务器下载这个包并进行缓存。第二次访问的时候,就直接访问代理仓库了。、

安装nexus后,默认存在以下图中的仓库, 这些仓库是官方默认配置好的maven私服。(可以直接使用)

image-20220605094014964

进入其中一个仓库, 可以看到默认的配置。即: 代理公网repo1中的包到本地;

image-20220605094036165

💘 实践:配置Maven代理仓库(测试成功)-2022.6.7

image-20220607071943874

  • 实验环境
bash
nexus3.39.0-01
apache-maven-3.8.5
  • 实验软件(无)
  • 创建proxy-aliyun-maven仓库:仓库类型选择proxy,制品策略类型选择Release,其他默认。

image-20220605124450440

image-20230626220602062

image-20230626220704555

  • 编辑maven构建节点的settings.xml文件,配置阿里云代理仓库地址
bash
[root@devops conf]#vim /usr/local/apache-maven-3.8.5/conf/settings.xml
https://maven.aliyun.com/repository/public

image-20220606075916331

注意:

image-20220606080033508

image-20220607075202686

  • 创建完成后如下:

image-20220606081111248

http://172.29.9.101:8081/repository/proxy-aliyun-maven/

image-20220606081143569

  • 配置maven软件的配置文件
bash
[root@devops conf]#vim /usr/local/apache-maven-3.8.5/conf/settings.xml
<url>http://172.29.9.101:8081/repository/proxy-aliyun-maven/</url>

image-20220606081614053

  • 删除本地缓存:
bash
[root@devops conf]#rm -rf ~/.m2/
[root@devops conf]#rm -rf /data/maven_build_cache/*
bash
[root@devops conf]#vim /usr/local/apache-maven-3.8.5/conf/settings.xml

image-20220606080839726

  • 测试效果:

来到一个java项目里,使用maven进行测试效果:

bash
[root@devops devops4-maven-service-master]#pwd
/root/devops4-maven-service-master
[root@devops devops4-maven-service-master]#ls
build.sh  mvnw  mvnw.cmd  pom.xml  README.md  sonar-project.properties  src
[root@devops devops4-maven-service-master]#mvn clean package

image-20220606083855937

此时会报一个错误的,这里我们要配置下权限:

这种公网的,我们一般不加权限:

image-20220606084007639

再次构建,观察效果:

image-20220606084415074

可以看到能够正常下载。

  • 来到这里也是可以看到下载的包:

image-20220607071721941

测试成功。😘

2.搭建制品库(本地仓库 依赖包)

本地仓库:以Maven为例:

  • RELEASE类型仓库(存放制品稳定版) 这个里面的包一般是需要手动指定的;
  • SNAPSHOT类型仓库(存放制品开发版) 这个里面的包一般是自动生成版本号的;

image-20220610080531022

切记:release类型的仓库只能存放release版本的包。不能将release类型的包上传到snapshot仓库,同理snapshot类型的包也不能上传到release类型的仓库中。

  • 新建raw类型的仓库: raw可以理解为普通的文件存储;

raw格式就是一块普通的存储。(制品就传到这种类型的仓库里去)

image-20220610080614130

① 创建Maven本地仓库(依赖)

💘 实践:创建Maven本地仓库(依赖)-2022.6.10(测试成功)

image-20220610083123941

  • 创建仓库:

image-20220610083338652

选择hosted类型:

image-20220610083417572

  • 定义和配置仓库的信息: 名称、存储、是否允许重新上传:

image-20220610083543123

最后点击Create repositry

image-20220610083621800

  • 观察效果:

image-20220610083703091

image-20220610083719270

  • 同理,我们再创建一个devops4-release的本地仓库:

image-20220610083900543

image-20220610083916573

测试完成。😘

② 创建Raw本地仓库(制品)

💘 实践:创建Raw本地仓库(制品)(测试成功)-2022.6.10

image-20220610083947334

  • 创建仓库:

image-20220610084125579

选择raw(hosted)类型:

image-20220610084200083

填写仓库名,并创建:

image-20220610085056159

  • 观察效果:

image-20220610085108812

  • 模拟上传一个图片:

image-20220610085135142

image-20220610085239466

image-20220610085257865

测试结束。😘

4、CI流水线中集成制品库

1.使用管理页面上传制品

页面很方便上传,但是有时候不太好用...例如出现上传失败等问题(暂时无法解决,不同版本的nexus有些api不对应的坑)。

💘 实践:使用管理页面上传制品(测试成功)-2023.6.27
  • 我们来创建maven-devops6-releasemaven-devops6-snapshot2个maven仓库

image-20230627200855447

image-20230627201750863

  • 这次来手动上传下jenkins agent的agent.jar包。

image-20230627203113365

  • 先来模拟一次报错提示

image-20230627203350336

image-20230627203419478

我们指定jar包Version1.1.1-SNAPSHOT,但是我们往RELEASE类型的仓库上传,肯定就会报错的啦。

  • 我们改变下这里的Version字段内容,然后再次上传,观察下现象

image-20230627203640216

image-20230627203651629

image-20230627203728285

此时,就可以正常上传成功了。

2.使用maven指令上传制品

参考:https://support.sonatype.com/hc/en-us/articles/213465818-How-can-I-programmatically-upload-an-artifact-into-Nexus-2-

image-20230627202427742

image-20230627202518322

如果是0.0.1或者0.0.1-RELEASE,就是RELEASE版本。

🍀 扩展:

如果是maven类型的具有源码的项目, 可以直接使用mvn命令上传,更加方便。

groovy
//上传制品使用maven命令
def PushArtifactsByMvn(repoName,filePath ){
    sh """
        mvn deploy:deploy-file \
            -DgeneratePom=false \
            -DrepositoryId="maven-hosted" \
            -Durl=http://192.168.1.200:8081/repository/"${repoName}" \
            -DpomFile=pom.xml \
            -Dfile="${filePath}"
    """
}

PushArtifactsByMvn("${params.repoName}","target/${pkg}")

① 方法1:使用mvn命令上传制品(自定义pom信息(灵活))

💘 实践:使用mvn命令上传制品(自定义pom信息(灵活))-2022.6.11
  • 实验环境
bash
sonatype/nexus3:3.53.0
apache-maven-3.9.2
  • 实验软件(无)

  • 上传制品之前, 肯定得确定目标仓库是存在的。 如果不存在我们可以新建一个 hosted类型的maven仓库。

image-20220611074336030

  • 仓库已经有了, 需要更新maven的配置文件,在settings.xml中添加仓库的认证信息。如下:
groovy
    <server>
      <id>mymaven</id>
      <username>admin</username>
      <password>admin123</password>
    </server>
bash
[root@devops ~]#vim /usr/local/apache-maven-3.8.5/conf/settings.xml
    <server>
      <id>mymaven</id>
      <username>admin</username>
      <password>admin123</password>
    </server>

image-20220611074718292

  • 上传制品前记得先要编译构建下的:
bash
[root@devops ~]#cd
[root@devops ~]#cd devops4-maven-service-master
[root@devops devops4-maven-service-master]#ls
build.sh  mvnw  mvnw.cmd  pom.xml  README.md  sonar-project.properties  src  target
[root@devops devops4-maven-service-master]#mvn clean package

image-20220611074901435

bash
[root@devops devops4-maven-service-master]#pwd
/root/devops4-maven-service-master
[root@devops devops4-maven-service-master]#ls
build.sh  mvnw  mvnw.cmd  pom.xml  README.md  sonar-project.properties  src  target
[root@devops devops4-maven-service-master]#ls target/
classes  demo-0.0.1-SNAPSHOT.jar  demo-0.0.1-SNAPSHOT.jar.original  maven-archiver
  • 开始上传:

注意使用mvn deploy 发布时,-DrepositoryId参数的值要与上面配置文件中的<server>标签中的<id>一致。不然会出现401,用户认证失败的问题。

bash
mvn deploy:deploy-file
-DgroupId=xxxxxx #pom中的groupId
-DartifactId=xxxxxx #pom中的artifactId
-Dversion=xxxxxx #pom中的版本号version
-Dpackaging=xxxxxx #pom中打包方式
-Dfile=xxxxxx #本地文件
-Durl=xxxxxx #仓库url
-DrepositoryId=xxxxxx #对应的是setting.xml(认证)

Maven上传报错, 401 可以确定是认证的错误。 需要检查认证信息。

bash
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy-file (default-cli) on project demo: Failed to deploy artifacts: Could not transfer artifact com.devops:zeyang:jar:1.1.1 from/to remote-repository (http://192.168.1.200:8081/repository/devops-maven/): authentication failed for http://192.168.1.200:8081/repository/devops-maven/com/devops/zeyang/1.1.1/zeyang-1.1.1.jar, status: 401 Unauthorized -> [Help 1]

image-20220611080853857

替换参数, 执行命令开始上传制品。

bash
mvn deploy:deploy-file \
-DgroupId=com.devops \
-DartifactId=xyy \
-Dversion=1.1.1-snapshot \
-Dpackaging=jar  \
-Dfile=target/demo-0.0.1-SNAPSHOT.jar \
-Durl=http://172.29.9.101:8081/repository/devops4-snapshot/  \
-DrepositoryId=mymaven

image-20220611080206055

  • 我们再次修改下参数观察下效果:
bash
mvn deploy:deploy-file \
-DgroupId=com.devops \
-DartifactId=xyy \
-Dversion=1.1.1-SNAPSHOT \
-Dpackaging=jar  \
-Dfile=target/demo-0.0.1-SNAPSHOT.jar \
-Durl=http://172.29.9.101:8081/repository/devops4-snapshot/  \
-DrepositoryId=mymaven


#注意:默认是会生成pom文件的。
#如果不想生成pom文件,则可以使用如下选项
-DgeneratePom=false

这里的-Dversion=1.1.1-SNAPSHOT SNAPSHOT一定要大写才行的!否则会被认为是release类型的制品,当然也就不能上传到snapshot仓库了。

可以看到,本次上传成功了:

image-20220611080330752

备注:Pom文件就是个坐标。

image-20230627210305228

  • 验证:制品已经上传成功了。

image-20220611080424283

测试成功。😘

② 方法2:直接读取pom文件(扩展)

💘 实践:直接读取pom文件(扩展)-2022.6.11(测试成功)
  • 实验环境
  • 默认项目下是已经有pom.xml文件了:
bash
sonatype/nexus3:3.53.0
apache-maven-3.9.2
  • 实验软件(无)
bash
[root@devops devops4-maven-service-master]#ls
build.sh  mvnw  mvnw.cmd  pom.xml  README.md  sonar-project.properties  src  target
[root@devops devops4-maven-service-master]#cat pom.xml

image-20220611082707223

  • 这里直接使用命令上传:
bash
mvn deploy:deploy-file \
-DgeneratePom=true \
-DrepositoryId=mymaven \
-Durl=http://172.29.9.101:8081/repository/devops4-snapshot/ \
-DpomFile=pom.xml \
-Dfile=target/demo-0.0.1-SNAPSHOT.jar

image-20220611082838359

image-20220611082849107

  • 验证:

image-20220611082956079

测试成功。😘

⚠️ FAQ:

release类型的仓库只能上传release版本的包。如果你尝试用snapshot包上传到release类型的仓库时会遇到这些错误的。

bash
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy-file (default-cli) on project demo: Failed to deploy artifacts: Could not transfer artifact com.example:demo:jar:0.0.1 from/to maven-hosted (http://192.168.1.200:8081/repository/maven-zeyang-test/): transfer failed for http://192.168.1.200:8081/repository/maven-zeyang-test/com/example/demo/0.0.1/demo-0.0.1.jar, status: 400 Repository version policy: SNAPSHOT does not allow version: 0.0.1 -> [Help 1]

image-20220610214300519

解决方法: 1. 更新pom中的版本号 2. 对号入座,上传到对应类型的仓库。

groovy
<groupId>com.example</groupId>
<artifactId>myapp</artifactId>
<version>0.0.2-SNAPSHOT</version>    //改成0.0.2-RELEASE

3.使用Jenkins插件上传制品

① 方法1:使用Jenkins插件上传制品(把信息写死)

💘 实践:使用Jenkins插件上传制品(把信息写死)(测试成功)-2023.6.28
  • 安装插件:

Nexus Aritifact Uploader

image-20220611151557186

  • 使用片段生成器生成DSL:

找一个pipeline项目,生成流水线脚本:

image-20230628071119368

新建一个nexus凭据:

image-20230628070803438

image-20230628071150459

生成代码:

groovy
nexusArtifactUploader artifacts: [[artifactId: 'demo-app', classifier: '', file: 'target/xxx.jar', type: 'jar']], credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', groupId: 'com.devops6', nexusUrl: '172.29.9.101:8081', nexusVersion: 'nexus3', protocol: 'http', repository: 'maven-devops6-release', version: '1.1.1'

调整下格式:

groovy
nexusArtifactUploader artifacts: [[artifactId: 'demo-app', 
                                   classifier: '', 
                                   file: 'target/xxx.jar', 
                                   type: 'jar']], 
    				credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
    				groupId: 'com.devops6', 
    				nexusUrl: '172.29.9.101:8081', 
                    nexusVersion: 'nexus3', 
                    protocol: 'http', 
                    repository: 'maven-devops6-release', 
                    version: '1.1.1'
  • 这边来到jenkins的devops6-maven-service项目里,先跑一次流水线看下有问题没:

image-20230628071448543

image-20230628071703695

可以看到流水线是ok的。

  • 我们再来写一下流水线代码:
groovy
stage("PushArtifact"){
	steps{
		script{
			PushArtifactByPlugin()
		}
	}


}
def PushArtifactByPlugin(){
    nexusArtifactUploader artifacts: [[artifactId: 'demo-app', 
                                    classifier: '', 
                                    file: 'target/demo-0.0.1-SNAPSHOT.jar', 
                                    type: 'jar']], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: 'com.devops6', 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: 'maven-devops6-release', 
                        version: '1.1.1'
}

image-20230628072038684

然后把这个代码放到上面项目的回放里,跑一次:

groovy
@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()
def sonar = new org.devops.Sonar()

currentBuild.displayName = "${env.branchName}-commitID"
currentBuild.description = "Trigger by user jenkins \n branch: ${env.branchName}"

pipeline {
    agent {label "build"}
    options {
        skipDefaultCheckout true
    }    
    

    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            when {
                environment name: 'skipSonar', value: 'false'
            }
            
            steps{
                script{

                    sonar.SonarScannerByPlugin()        

                }
            }
        }
        
        stage("PushArtifact"){
            steps{
                script{
                    PushArtifactByPlugin()
                }
            }
    
        }

    }
}


def PushArtifactByPlugin(){
    nexusArtifactUploader artifacts: [[artifactId: 'demo-app', 
                                    classifier: '', 
                                    file: 'target/demo-0.0.1-SNAPSHOT.jar', 
                                    type: 'jar']], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: 'com.devops6', 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: 'maven-devops6-release', 
                        version: '1.1.1'
}

image-20230628072431427

可以看到,上传制品成功了:

image-20230628072328051

image-20230628072347527

  • 下载制品,点击链接,下载就ok了。

image-20230628072506697

测试成功。😘

② 方法2:使用Jenkins插件上传制品(读取POM文件上传制品)

💘 实践:使用Jenkins插件上传制品(读取POM文件上传制品)(测试成功)-2023.6.28
  • devops6-maven-servicegitlab项目里编辑下pom.xml文件,添加packaging选项并提交。
bash
<packaging>jar</packaging>

image-20230628075950652

  • 编写pipeline代码
groovy
@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()
def sonar = new org.devops.Sonar()

currentBuild.displayName = "${env.branchName}-commitID"
currentBuild.description = "Trigger by user jenkins \n branch: ${env.branchName}"

pipeline {
    agent {label "build"}
    options {
        skipDefaultCheckout true
    }    
    

    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            when {
                environment name: 'skipSonar', value: 'false'
            }
            
            steps{
                script{

                    sonar.SonarScannerByPlugin()        

                }
            }
        }
        
        stage("PushArtifact"){
            steps{
                script{
                    //PushArtifactByPlugin()
                    PushArtifactByPluginPOM()
                }
            }
    
        }

    }
}

def PushArtifactByPlugin(){
    nexusArtifactUploader artifacts: [[artifactId: 'demo-app', 
                                    classifier: '', 
                                    file: 'target/demo-0.0.1-SNAPSHOT.jar', 
                                    type: 'jar']], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: 'com.devops6', 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: 'maven-devops6-release', 
                        version: '1.1.1'
}

def PushArtifactByPluginPOM(){
    POM = readMavenPom file: 'pom.xml'
    println(POM)
    println("GroupID: ${POM.groupId}")
    println("ArtifactID: ${POM.artifactId}")
    println("Version: ${POM.version}")
    println("Packaging: ${POM.packaging}")

    env.artifactId = "${POM.artifactId}"
    env.packaging = "${POM.packaging}"
    env.groupId = "${POM.groupId}"
    env.art_version = "${POM.version}"
    env.art_name = "${env.artifactId}-${env.art_version}.${env.packaging}"
    nexusArtifactUploader   artifacts: [[artifactId: "${env.artifactId}", 
                                        classifier: '', 
                                        file: "target/${env.art_name}", 
                                        type: "${env.packaging}"]], 
                            credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                            groupId: "${env.groupId}", 
                            nexusUrl: '172.29.9.101:8081', 
                            nexusVersion: 'nexus3', 
                            protocol: 'http', 
                            repository: 'maven-devops6-release', 
                            version: "${env.art_version}"
}

image-20230628124328780

  • 在回放里运行

image-20230628124518879

image-20230628124446169

发现报错:

将项目里的pom文件改成RELEASE版本。

image-20230628124620477

改成

image-20230628124726497

然后再次运行:

image-20230628124841495

又一次报错,提示:status: 400 Repository does not allow updating assets: maven-devops6-release

应该是nexus3仓库里之前已经存在了这个包。

我们这里再改下version:

image-20230628125003359

再次运行:

image-20230628125116468

image-20230628125100794

这次就运行成功了。

image-20230628125142740

  • 然后写到共享库里,再次运行测试

image-20230628133855172

image-20230628133845430

构建成功。

  • 也可以修改上面这个版本,利用函数传参来演示

pipeline代码:

groovy
@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()
def sonar = new org.devops.Sonar()

currentBuild.displayName = "${env.branchName}-commitID"
currentBuild.description = "Trigger by user jenkins \n branch: ${env.branchName}"

pipeline {
    agent {label "build"}
    options {
        skipDefaultCheckout true
    }    
    

    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            when {
                environment name: 'skipSonar', value: 'false'
            }
            
            steps{
                script{

                    sonar.SonarScannerByPlugin()        

                }
            }
        }
        
        stage("PushArtifact"){
            steps{
                script{
                    //PushArtifactByPlugin()
                    // PushArtifactByPluginPOM()
                    pomData = readMavenPom file: 'pom.xml'

                    buName = "${JOB_NAME}".split("-")[0]
                    repoName = "maven-${buName}-release"
                    file = "target/${pomData.artifactId}-${pomData.version}.${pomData.packaging}"
        			PushArtifactByPluginPOM(pomData.artifactId, file, pomData.packaging, pomData.groupId, repoName, pomData.version)
                }
            }
    
        }

    }
}

def PushArtifactByPlugin(){
    nexusArtifactUploader artifacts: [[artifactId: 'demo-app', 
                                    classifier: '', 
                                    file: 'target/demo-0.0.1-SNAPSHOT.jar', 
                                    type: 'jar']], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: 'com.devops6', 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: 'maven-devops6-release', 
                        version: '1.1.1'
}


// def PushArtifactByPluginPOM(){
//     POM = readMavenPom file: 'pom.xml'
//     println(POM)
//     println("GroupID: ${POM.groupId}")
//     println("ArtifactID: ${POM.artifactId}")
//     println("Version: ${POM.version}")
//     println("Packaging: ${POM.packaging}")

//     env.artifactId = "${POM.artifactId}"
//     env.packaging = "${POM.packaging}"
//     env.groupId = "${POM.groupId}"
//     env.art_version = "${POM.version}"
//     env.art_name = "${env.artifactId}-${env.art_version}.${env.packaging}"
//     nexusArtifactUploader   artifacts: [[artifactId: "${env.artifactId}", 
//                                         classifier: '', 
//                                         file: "target/${env.art_name}", 
//                                         type: "${env.packaging}"]], 
//                             credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
//                             groupId: "${env.groupId}", 
//                             nexusUrl: '172.29.9.101:8081', 
//                             nexusVersion: 'nexus3', 
//                             protocol: 'http', 
//                             repository: 'maven-devops6-release', 
//                             version: "${env.art_version}"
// }

//函数方式
def PushArtifactByPluginPOM(artifactId, file, type, groupId, repoName, version){
    println(artifactId)
    //demo
    println("${file}, ${type}, ${groupId}, ${repoName}, ${version}")
    //target/demo-0.0.1-SNAPSHOT.jar, jar, com.example, devops4-release, 0.0.1-SNAPSHOT
    nexusArtifactUploader artifacts: [[artifactId: artifactId, 
                                    classifier: '', 
                                    file: file, 
                                    type: type]], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: groupId, 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: repoName, 
                        version: version    
}

image-20230629091623624

image-20230629091707573

再次运行:

image-20230629091435615

image-20230629091413823

image-20230629091343925

发现这个方法也是ok的。(haha,函数方式感觉nb一点,那这里就采用函数方式了哦。😂)

然后把这部分代码放到共享库里,再次测试下:

image-20230629094435751

image-20230629094339247

image-20230629094451374

符合预期。

此时共享库里玩转代码如下: PushArtifact.groovy文件:

groovy
package org.devops

def PushArtifactByPlugin(){
    nexusArtifactUploader artifacts: [[artifactId: 'demo-app', 
                                    classifier: '', 
                                    file: 'target/demo-0.0.1-SNAPSHOT.jar', 
                                    type: 'jar']], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: 'com.devops6', 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: 'maven-devops6-release', 
                        version: '1.1.1'
}

// def PushArtifactByPluginPOM(){
//     POM = readMavenPom file: 'pom.xml'
//     println(POM)
//     println("GroupID: ${POM.groupId}")
//     println("ArtifactID: ${POM.artifactId}")
//     println("Version: ${POM.version}")
//     println("Packaging: ${POM.packaging}")

//     env.artifactId = "${POM.artifactId}"
//     env.packaging = "${POM.packaging}"
//     env.groupId = "${POM.groupId}"
//     env.art_version = "${POM.version}"
//     env.art_name = "${env.artifactId}-${env.art_version}.${env.packaging}"
//     nexusArtifactUploader   artifacts: [[artifactId: "${env.artifactId}", 
//                                         classifier: '', 
//                                         file: "target/${env.art_name}", 
//                                         type: "${env.packaging}"]], 
//                             credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
//                             groupId: "${env.groupId}", 
//                             nexusUrl: '172.29.9.101:8081', 
//                             nexusVersion: 'nexus3', 
//                             protocol: 'http', 
//                             repository: 'maven-devops6-release', 
//                             version: "${env.art_version}"
// }

//函数方式
def PushArtifactByPluginPOM(artifactId, file, type, groupId, repoName, version){
    println(artifactId)
    //demo
    println("${file}, ${type}, ${groupId}, ${repoName}, ${version}")
    //target/demo-0.0.1-SNAPSHOT.jar, jar, com.example, devops4-release, 0.0.1-SNAPSHOT
    nexusArtifactUploader artifacts: [[artifactId: artifactId, 
                                    classifier: '', 
                                    file: file, 
                                    type: type]], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: groupId, 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: repoName, 
                        version: version    
}

Jenkinsfile文件:

groovy
@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()
def sonar = new org.devops.Sonar()
def pushartifact = new org.devops.PushArtifact()

currentBuild.displayName = "${env.branchName}-commitID"
currentBuild.description = "Trigger by user jenkins \n branch: ${env.branchName}"

pipeline {
    agent {label "build"}
    options {
        skipDefaultCheckout true
    }    
    

    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            when {
                environment name: 'skipSonar', value: 'false'
            }
            
            steps{
                script{

                    sonar.SonarScannerByPlugin()        

                }
            }
        }

         stage("PushArtifact"){
            steps{
                script{
                    //PushArtifactByPlugin()
                    // PushArtifactByPluginPOM()
                    pomData = readMavenPom file: 'pom.xml'

                    buName = "${JOB_NAME}".split("-")[0]
                    repoName = "maven-${buName}-release"
                    file = "target/${pomData.artifactId}-${pomData.version}.${pomData.packaging}"
        			pushartifact.PushArtifactByPluginPOM(pomData.artifactId, file, pomData.packaging, pomData.groupId, repoName, pomData.version)
                }
            }
    
        }       

    }
}

测试完成。😘

扩展: 可以在Jenkins页面添加参数, 让用户输入后进行发布

💘 扩展: 可以在Jenkins页面添加参数, 让用户输入后进行发布(测试成功)-2023.6.28

tstmp_20230629102803

测试过程如下:

  • 改写代码:

image-20220612085854177

groovy
@Library("mylib@main") _     //加载共享库
import org.devops.*						// 导入库

def checkout = new Checkout()    //New实例化
def build = new Build()
def unittest = new UnitTest()
def sonar = new Sonar()
def gitcli = new GitLab()

//env.buildType = "${JOB_NAME}".split("-")[1]

//流水线
pipeline {
    agent { label "build" }

    options {
        skipDefaultCheckout true
    }

    stages{
        stage("Checkout"){
            steps{
                script {
                    println("GetCode")
                    checkout.GetCode("${env.srcUrl}", "${env.branchName}")
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    println("Build")
                    //build.CodeBuild("${env.buildType}")
                    sh "${env.buildShell}"
                   
                }
            }
        }

        /*stage("UnitTest"){
            steps{
                script{
                    unittest.CodeTest("${env.buildType}")
                }
            }
        }*/

        stage("CodeScan"){
            when {
                environment name: 'skipSonar', value: 'false'
            }
            steps{
                script{
                    profileName = "${JOB_NAME}".split("-")[0]
                    sonar.Init("${JOB_NAME}", "java", profileName)


                    //commit-status
                    commitID = gitcli.GetCommitID()
                    groupName =profileName
                    projectID = gitcli.GetProjectID("${JOB_NAME}", groupName)
                    sonar.CodeScan("${env.branchName}", commitID, projectID)
                    }
                    
                }
        }
        
        stage("PushArtifact"){
        	steps{
        		script{
                    // 读取pom文件获取坐标信息
                    // pomData = readMavenPom file: 'pom.xml'
                    // println(pomData)
                    // println(pomData.getClass())
                    buName = "${JOB_NAME}".split("-")[0]
                    repoName = "${buName}-snapshot"
                    file = "target/${env.artifactId}-${env.version}.${env.packaging}"
                    // 用户输入获取坐标信息
        			PushArtifactByNexusPlugin(env.artifactId, file, env.packaging ,env.groupId, repoName, env.version)
        		}
        	}


        }
        
    }
}


def PushArtifactByNexusPlugin(artifactId, file, type, groupId, repoName, version){
    println(artifactId)
    //demo
    println("${file}, ${type}, ${groupId}, ${repoName}, ${version}")
    //target/demo-0.0.1-SNAPSHOT.jar, jar, com.example, devops4-release, 0.0.1-SNAPSHOT
    nexusArtifactUploader artifacts: [[artifactId: artifactId, 
                                    classifier: '', 
                                    file: file, 
                                    type: type]], 
                        credentialsId: '2b44f51a-2374-4275-aeed-b720f4fbf937', 
                        groupId: groupId, 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: repoName, 
                        version: version    
}
  • 在流水线里添加字符参数和选项参数:

image-20220612090001655

image-20220612090013761

  • 开始构建:

image-20220612085627306

观察效果:

image-20220612085733756

可以看到制品被成功上传到制品库了:

image-20220612085656120

测试结束。😘

Jenkins插件获取包路径

发布其实就是下载制品,然后将制品发送到目标主机,最后通过脚本或者指令启动程序。

这个其实没多大意义。

💘 实践:Jenkins插件获取包路径-2023.6.29(测试成功)
  • 实验环境
bash
gitlab-ce:15.0.3-ce.0
jenkins:2.346.3-2-lts-jdk11
sonarqube:9.9.0-community
nexus3:3.53.0
  • 实验软件(无)

下面是下载制品的示例:

bash
curl http://192.168.1.200:8081/repository/devops-maven/com/example/demo/1.1.10/demo-1.1.10.jar -o app.jar -uadmin:admin123

或者:

  • 安装插件Maven Artifact ChoiceListProvider (Nexus)

image-20230629104351223

  • 创建一个流水线

image-20230629104433653

创建选项参数,选择扩展选项参数,并配置:

image-20230629105505198

image-20230629105520483

  • 点击构建,就出现效果了

用户选择制品后, 点击构建。此时可以想象,Jenkins下载这个包, 然后通过salt、ansible进行发布部署。

image-20230629105548928

测试结束。😘

GitLabCI

💘 实践:GitLabCI的Nexus的CI/CD(测试成功)-2022.6.21

1.Nexus

groovy
.pushartifact:
  tags:
    - "${RUNNER_TAG}"
  stage: pushartifact  
  rules:
    - if: '$RUN_TYPE == "CI"'
      when: always
    - when: never
  script:
    |- 
      if [[ ${PROJECT_TYPE} == "java" ]];then
          pkgName=`ls target/ | grep -e "jar$"`
          cd target/
          newPkgName=${CI_PROJECT_NAME}-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHORT_SHA}.jar
          filePath=${CI_PROJECT_ROOT_NAMESPACE}/${CI_PROJECT_NAME}/${CI_COMMIT_BRANCH}-${CI_COMMIT_SHORT_SHA}
          #Dir /buName/serviceName/version/serviceName-version.xxx
          mv ${pkgName} ${newPkgName}
          curl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=devops4-local" \
          -H "accept: application/json" \
          -H "Content-Type: multipart/form-data" \
          -F "raw.directory=${filePath}" \
          -F "raw.asset1=@${newPkgName};type=application/java-archive" \
          -F "raw.asset1.filename=${newPkgName}" -u admin:admin123
      else
          echo "PROJECT_TYPE ERROR [java]"
      fi

📍 本次测试过程

image-20220620151909202

image-20220620151947603

  • 先跑一次流水线,看下效果:

image-20220620152352927

报错了:

image-20220620152415887

这里之前是配置过用户名和密码的了:

image-20220620152530009

image-20220620160306424

image-20220620160231714

后面还要看下这个容器:……要重装下的!!!!

image-20220620160632431

  • 这里先把代码扫描给跳过:

image-20220620160548403

再跑一次流水线:

image-20220620160701753

  • 现在开始写代码:

找一下gitlabci的预定义环境变量

image-20220620162129426

image-20220620163728227

image-20220620163747263

此时,在RELEASE-1.1.1里加一些代码,并提交:

image-20220620163837618

image-20220620163850033

image-20220620163924957

符合预期。

  • 此时有个问题,那么jenkins里再跑一次CD,会下载哪个包呢?

image-20220620164008701

可以看到拿的是最新那个包。

以上完整代码如下:

groovy
.pipelineInit:
  tags:
    - "${RUNNER_TAG}"
  stage: .pre
  variables:
    GIT_CHECKOUT: "true"   ##局部开启作业的代码下载
  script:
    - ls -l 

.cibuild:
  tags:
    - "${RUNNER_TAG}"
  stage: build
  script:
    - echo "${BUILD_SHELL}"
    - ${BUILD_SHELL}
  artifacts:
     paths:
      - ${ARTIFACT_PATH}

.citest:
  tags:
    - "${RUNNER_TAG}"
  stage: test
  script:
    - echo "${TEST_SHELL}"
    - ${TEST_SHELL}
  # artifacts:
  #   reports:
  #     junit: ${TEST_REPORTS}

.codescan:
  tags: 
    - "${RUNNER_TAG}"
  stage: codescan
  script:
    |-
      /usr/local/sonar-scanner/sonar-scanner-4.7.0.2747-linux/bin/sonar-scanner \
      -Dsonar.login=${SONAR_USER} \
      -Dsonar.password=${SONAR_PASSWD} \
      -Dsonar.projectVersion=${CI_COMMIT_BRANCH}\
      -Dsonar.branch.name=${CI_COMMIT_BRANCH} \
      -Dsonar.gitlab.commit_sha=${CI_COMMIT_SHA} \
      -Dsonar.gitlab.ref_name=${CI_COMMIT_BRANCH} \
      -Dsonar.gitlab.project_id=${CI_PROJECT_ID} \
      -Dsonar.dynamicAnalysis=reuseReports \
      -Dsonar.gitlab.failure_notification_mode=commit-status \
      -Dsonar.gitlab.url=http://172.29.9.101 \
      -Dsonar.gitlab.user_token=${GITLAB_TOKEN} \
      -Dsonar.gitlab.api_version=v4

.pushartifact:
  tags:
    - "${RUNNER_TAG}"
  stage: pushartifact  
  script:
    |- 
      if [[ ${PROJECT_TYPE} == "java" ]];then
          pkgName=`ls target/ | grep -e "jar$"`
          cd target/

          #Dir /buName/serviceName/version/serviceName-version.xxx
          mv ${pkgName} ${CI_PROJECT_NAME}-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHORT_SHA}.jar
          curl -X POST "http://172.29.9.101:8081/service/rest/v1/components?repository=devops4-local" \
          -H "accept: application/json" \
          -H "Content-Type: multipart/form-data" \
          -F "raw.directory=/${CI_PROJECT_ROOT_NAMESPACE}/${CI_PROJECT_NAME}/${CI_COMMIT_BRANCH}-${CI_COMMIT_SHORT_SHA}"/ \
          -F "raw.asset1=@${CI_PROJECT_NAME}-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHORT_SHA}.jar;type=application/java-archive" \
          -F "raw.asset1.filename=${CI_PROJECT_NAME}-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHORT_SHA}.jar" -u admin:admin123
      else
          echo "PROJECT_TYPE ERROR [java]"
      fi
groovy
include:
  - project: 'devops4/devops4-gitlablib-service'
    ref: main
    file: 
      - '/jobs/CI.yaml'

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "web"
      when: always
    - if: $CI_COMMIT_BEFORE_SHA == "0000000000000000000000000000000000000000"
      when: never
    - when: always

variables:
  GIT_CHECKOUT: "false"   ## 全局关闭作业代码下载
  BUILD_SHELL: "sh -x build.sh"  ## 构建命令
  TEST_SHELL: "/usr/local/apache-maven-3.8.5/bin/mvn test "                        ## 测试命令
  ARTIFACT_PATH: "target/*jar"      ## 制品路径  
  # TEST_REPORTS: "target/surefire-reports/TEST-*.xml" ##测试报告
  RUNNER_TAG: "builder"
  PROJECT_TYPE: "java"

stages:
  - build
  - test
  - pushartifact
  # - codescan

pipelineInit:
  extends: 
    - .pipelineInit

cibuild:
  extends:
    - .cibuild

citest:
  extends:
    - .citest

# codescan:
#   extends:
#   - .codescan
pushartifact:
  extends:
    - .pushartifact
  • 这里在把之前的jenkins的CD代码优化下:

image-20220620165147061

符合预期:

image-20220620165128632

  • gitlabCI完成了,如何完成CD呢?

这里改造下代码:

如何限制作业的运行?

image-20220620165648295

配置代码:

image-20220621071125310

运行测试:

image-20220621070925184

image-20220621071044158

image-20220621070955952

符合预期。

  • 现在开始写CD:

把这一部分拿过来:

image-20220621072037732

image-20220621072152521

配置如下:

image-20220621072737895

image-20220621072800401

image-20220621072812042

运行测试:

image-20220621072649397

此时发现,他把代码给下载下来了,但是我们不需要下载代码,这个该如何优化下呢?

我们可以借助这里来配置下:

image-20220621073535102

但是这里已经全局关闭了代码下载的,为什么CD里还会再下一次代码呢:

image-20220621073622329

我们这里再配置下,并观察效果:

image-20220621073729392

经观察,这里还是会下载代码的:……

image-20220621073833782

至此,GITLABCI的CI/CD实验结束。😘

2.GitLab Package【扩展】

image-20220620151621582

bash
curl --header "PRIVATE-TOKEN: apF1R9s9JJBYJzLF5mYd" \
     --upload-file sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar \
     "http://192.168.1.200/api/v4/projects/33/packages/generic/devops03-maven-service/0.0.1/sonar-gitlab-plugin-4.1.0-SNAPSHOT.jar?status=default"
bash
.pushartforgitlab:
  tags:
    - build
  stage: pushartifact  
  script:
    |- 
      if [[ ${PROJECT_TYPE} == "java" ]];then
          newPkgName=${CI_PROJECT_NAME}-${CI_COMMIT_SHA}.jar
          pkgName=`ls target/ | grep -e "jar$"`
          cd target/
          mv ${pkgName} ${newPkgName}
          curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
          --upload-file ${newPkgName} \
          "http://192.168.1.200/api/v4/projects/${CI_PROJECT_ID}/packages/generic/${CI_PROJECT_NAME}/${CI_COMMIT_SHA}/${newPkgName}?status=default"
      else
          echo "PROJECT_TYPE ERROR [java]"
      fi

5、Nexus REST API

http://172.29.9.101:8081/#admin/system/api

image-20220612221857210

NexusAPI调试方法

进入设置页面, 找到System > API , 即可进入API调试页面。

image-20220612221917426

调试API /v1/components, 点击Try it out才能填写信息。

image-20220612221937373

Maven格式仓库

  • 填写信息

image-20230629135742734

image-20230629135823606

image-20230629135901205

  • 点击Execute

image-20230629135931507

此时,包就上传到nexus仓库了,然后可以拷贝代码,放到流水线里集成。

image-20230629140515739

bash
curl -X 'POST' \
  'http://172.29.9.101:8081/service/rest/v1/components?repository=maven-devops6-release' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -H 'NX-ANTI-CSRF-TOKEN: 0.6366498537413283' \
  -H 'X-Nexus-UI: true' \
  -F 'maven2.groupId=com.devops6' \
  -F 'maven2.artifactId=demo-app' \
  -F 'maven2.version=1.20.1' \
  -F 'maven2.generate-pom=true' \
  -F 'maven2.packaging=jar' \
  -F 'maven2.asset1=@agent.jar' \
  -F 'maven2.asset1.extension=jar'

Raw格式仓库

==上传jar包==

  • 填写参数信息

image-20220612221953430

image-20220612222002041

  • 点击img执行操作, 204表示成功。 我们可以复用这里的CURL指令, 最后封装到Jenkins流水线当中。

image-20220612222022480

==上传图片==

  • 填写参数:

image-20220618101311885

image-20220618101425444

  • 点击Excute上传:

image-20220618101459427

bash
curl -X 'POST' \
  'http://172.29.9.101:8081/service/rest/v1/components?repository=devops6' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -H 'NX-ANTI-CSRF-TOKEN: 0.6366498537413283' \
  -H 'X-Nexus-UI: true' \
  -F 'raw.directory=/devops/demo' \
  -F 'raw.asset1=@xyyhg.png;type=image/png'
  • 观察效果:

image-20220618101542111

上传制品成功。

上传制品

  • curl -u admin:admin123 如果Nexus开启了认证需要配置认证信息才能正常访问。(不同的包的类型不同
bash
##PNG
curl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=myrepo" \
-H "accept: application/json" \
-H "Content-Type: multipart/form-data" \
-F "raw.directory=/tmp" \
-F "raw.asset1=@默认标题_自定义px_2020-10-01-0.png;type=image/png" \
-F "raw.asset1.filename=默认标题_自定义px_2020-10-01-0.png"


## tar.gz & ZIP
curl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=myrepo" \
-H "accept: application/json" \
-H "Content-Type: multipart/form-data" \
-F "raw.directory=/tmp" \
-F "raw.asset1=@nexus-3.30.0-01-unix.tar.gz;type=application/x-gzip" \
-F "raw.asset1.filename=aaa.tar.gz"


curl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=myrepo" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "raw.directory=/tmp" -F "raw.asset1=@waypoint_0.1.5_linux_amd64.zip;type=application/x-gzip" -F "raw.asset1.filename=waypoint_0.1.5_linux_amd64.zip"


## Jar file 
curl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=myrepo" \
-H "accept: application/json" \
-H "Content-Type: multipart/form-data" \
-F "raw.directory=/tmp" \
-F "raw.asset1=@aopalliance-1.0.jar;type=application/java-archive" \
-F "raw.asset1.filename=aopalliance-1.0.jar"
  • 上传制品(maven类型的制品):
bash
 curl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=devops-maven" \
 -H "accept: application/json" \
 -H "Content-Type: multipart/form-data" \
 -F "maven2.groupId=com.newdevops" \
 -F "maven2.artifactId=devopsapp" \
 -F "maven2.version=1.1.5" \
 -F "maven2.packaging=jar" \
 -F "maven2.asset1=@demo-0.0.1-SNAPSHOT.jar;type=application/java-archive" \
 -F "maven2.asset1.extension=demo-0.0.1-SNAPSHOT.jar" \
 -u admin:admin123

下载制品

cURL:

bash
curl -u admin:admin123 http://192.168.1.200:8081/repository/anyops/com/anyops/a
nyops-devops-service/1.1.1/anyops-devops-service-1.1.1.jar -o anyops-devops-service-1.1.1.jar

Wget:

bash
wget --http-user=admin --http-passwd=admin123 http://192.168.1.200:8081/repos
itory/anyops/com/anyops/anyops-devops-service/1.1.1/anyops-devops-service-1.1.1.jar

6、CI综合实践

💘 实践:CI综合实践-2023.6.29(测试成功)
  • 实验环境
groovy
gitlab-ce:15.0.3-ce.0
jenkins:2.346.3-2-lts-jdk11
sonarqube:9.9.0-community
nexus3:3.53.0
  • 实验软件

链接:https://pan.baidu.com/s/1lxP-jO5gHKsvoNaJi7PE6A?pwd=0820 提取码:0820 2023.6.29-实践:CI综合实践(测试成功)

image-20230629162259969

==制品仓库规范==

在开始引入制品的时候,就应该制定制品库的管理和使用规范。 有了标准化的规范之后, 就很容易实现自动化。(为什么有些工作无法做成自动化? -无标准)

  • 版本号: 1.2.3 主次修

    bash
    主版本号:表示项目的重大架构变更。
    次版本号:表示较大范围的功能增加和变化。
    修订版本号:表示重大Bug的修复。
    里程碑版本:表示某一个版本的里程碑。
  • 仓库名称: 每个项目组一个仓库 devops6

img

  • 应用

    • 应用1: devops6-maven-service
    • 应用2: devops6-gradle-service
  • 包名: 应用名称-版本号-类型

    • 应用1: devops6-maven-service-1.2.3.jar
    • 应用2: devops6-gradle-service-1.2.3.jar
  • 仓库目录结构: /应用名称/版本号/包名

    • 应用1: /devops6-maven-service/1.2.3/devops6-maven-service-1.2.3.jar
    • 应用2: /devops6-gradle-service/1.2.3/devops6-gradle-service-1.2.3.jar

创建nexus制品仓库

  • 创建一个raw类型的仓库存放制品

image-20230629140946508

image-20230629140955955

创建Jenkins作业

这里复用之前的项目。

image-20230629141232572

项目仓库创建版本分支

  • 基于main分支创建一个版本分支

image-20230629141420885

  • 然后在Jenkins上修改选项参数branchName,添加RELEASE-1.1.1内容

image-20230629141553736

image-20230629141608566

编写Jenkins代码

groovy
@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()
def sonar = new org.devops.Sonar()

currentBuild.displayName = "${env.branchName}-commitID"
currentBuild.description = "Trigger by user jenkins \n branch: ${env.branchName}"

env.branchName = "RELEASE-6.1.1"
println(env.branchName)

pipeline {
    agent {label "build"}
    options {
        skipDefaultCheckout true
    }    
    

    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            when {
                environment name: 'skipSonar', value: 'false'
            }
            
            steps{
                script{

                    sonar.SonarScannerByPlugin()        

                }
            }
        }
        
        stage("PushArtifact"){
            steps{
                script{
                    //PushArtifactByPlugin()
                    //PushArtifactByPluginPOM()

                    // init package info
                    appName = "${JOB_NAME}" //devops6-maven-service
                    repoName = appName.split('-')[0]   //devops6
                    appVersion = "${env.branchName}".split("-")[-1] // RELEASE-1.1.1   1.1.1
                    targetDir="${JOB_NAME}/${appVersion}"
                    

                    // 通过pom文件获取包名称
                    POM = readMavenPom file: 'pom.xml'
                    env.artifactId = "${POM.artifactId}"
                    env.packaging = "${POM.packaging}"
                    env.groupId = "${POM.groupId}"
                    env.art_version = "${POM.version}"
                    sourcePkgName = "${env.artifactId}-${env.art_version}.${env.packaging}"
                    
                    pkgPath = "target"
                    targetPkgName = "${appName}-${appVersion}.${env.packaging}"
                    PushNexusArtifact(repoName, targetDir, pkgPath, sourcePkgName,targetPkgName)

                }
            }
    
        }

    }
}

//通过nexus api上传制品--综合实践
def PushNexusArtifact(repoId, targetDir, pkgPath, sourcePkgName,targetPkgName){
    //nexus api 
    withCredentials([usernamePassword(credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', \
                                    passwordVariable: 'PASSWD', 
                                    usernameVariable: 'USERNAME')]) {
        sh """
            curl -X 'POST' \
              "http://172.29.9.101:8081/service/rest/v1/components?repository=${repoId}" \
              -H 'accept: application/json' \
              -H 'Content-Type: multipart/form-data' \
              -F "raw.directory=${targetDir}" \
              -F "raw.asset1=@${pkgPath}/${sourcePkgName};type=application/java-archive" \
              -F "raw.asset1.filename=${targetPkgName}" \
              -u ${USERNAME}:${PASSWD}
        """
    }
}


//通过jenkins的nexus插件上传制品
def PushArtifactByPlugin(){
    nexusArtifactUploader artifacts: [[artifactId: 'demo-app', 
                                    classifier: '', 
                                    file: 'target/demo-0.0.1-SNAPSHOT.jar', 
                                    type: 'jar']], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: 'com.devops6', 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: 'maven-devops6-release', 
                        version: '1.1.1'
}

// 通过jenkins的nexus插件上传制品(读取pom文件)
// def PushArtifactByPluginPOM(){
//     POM = readMavenPom file: 'pom.xml'
//     println(POM)
//     println("GroupID: ${POM.groupId}")
//     println("ArtifactID: ${POM.artifactId}")
//     println("Version: ${POM.version}")
//     println("Packaging: ${POM.packaging}")

//     env.artifactId = "${POM.artifactId}"
//     env.packaging = "${POM.packaging}"
//     env.groupId = "${POM.groupId}"
//     env.art_version = "${POM.version}"
//     env.art_name = "${env.artifactId}-${env.art_version}.${env.packaging}"
//     nexusArtifactUploader   artifacts: [[artifactId: "${env.artifactId}", 
//                                         classifier: '', 
//                                         file: "target/${env.art_name}", 
//                                         type: "${env.packaging}"]], 
//                             credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
//                             groupId: "${env.groupId}", 
//                             nexusUrl: '172.29.9.101:8081', 
//                             nexusVersion: 'nexus3', 
//                             protocol: 'http', 
//                             repository: 'maven-devops6-release', 
//                             version: "${env.art_version}"
// }

// 通过jenkins的nexus插件上传制品(读取pom文件)-函数方式
def PushArtifactByPluginPOM(artifactId, file, type, groupId, repoName, version){
    println(artifactId)
    //demo
    println("${file}, ${type}, ${groupId}, ${repoName}, ${version}")
    //target/demo-0.0.1-SNAPSHOT.jar, jar, com.example, devops4-release, 0.0.1-SNAPSHOT
    nexusArtifactUploader artifacts: [[artifactId: artifactId, 
                                    classifier: '', 
                                    file: file, 
                                    type: type]], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: groupId, 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: repoName, 
                        version: version    
}
  • 运行,观察效果:

image-20230629144621839

image-20230629144632097

测试是ok的。

  • 将代码集成到共享库

image-20230629152917367

Jenkinsfile文件

groovy
@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()
def sonar = new org.devops.Sonar()
def artifact = new org.devops.Artifact()

currentBuild.displayName = "${env.branchName}-commitID"
currentBuild.description = "Trigger by user jenkins \n branch: ${env.branchName}"

env.branchName = "RELEASE-6.1.1"
println(env.branchName)

pipeline {
    agent {label "build"}
    options {
        skipDefaultCheckout true
    }    
    

    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            when {
                environment name: 'skipSonar', value: 'false'
            }
            
            steps{
                script{

                    sonar.SonarScannerByPlugin()        

                }
            }
        }

         stage("PushArtifact"){
            steps{
                script{
                    //PushArtifactByPlugin()
                    //PushArtifactByPluginPOM()

                    // init package info
                    appName = "${JOB_NAME}" //devops6-maven-service
                    repoName = appName.split('-')[0]   //devops6
                    appVersion = "${env.branchName}".split("-")[-1] // RELEASE-1.1.1   1.1.1
                    targetDir="${JOB_NAME}/${appVersion}"
                    

                    // 通过pom文件获取包名称
                    POM = readMavenPom file: 'pom.xml'
                    env.artifactId = "${POM.artifactId}"
                    env.packaging = "${POM.packaging}"
                    env.groupId = "${POM.groupId}"
                    env.art_version = "${POM.version}"
                    sourcePkgName = "${env.artifactId}-${env.art_version}.${env.packaging}"
                    
                    pkgPath = "target"
                    targetPkgName = "${appName}-${appVersion}.${env.packaging}"
                    artifact.PushNexusArtifact(repoName, targetDir, pkgPath, sourcePkgName,targetPkgName)
                }
            }
    
        }       

    }
}

Artifact.groovy文件

groovy
package org.devops


//通过nexus api上传制品--综合实践
def PushNexusArtifact(repoId, targetDir, pkgPath, sourcePkgName,targetPkgName){
    //nexus api 
    withCredentials([usernamePassword(credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', \
                                    passwordVariable: 'PASSWD', 
                                    usernameVariable: 'USERNAME')]) {
        sh """
            curl -X 'POST' \
              "http://172.29.9.101:8081/service/rest/v1/components?repository=${repoId}" \
              -H 'accept: application/json' \
              -H 'Content-Type: multipart/form-data' \
              -F "raw.directory=${targetDir}" \
              -F "raw.asset1=@${pkgPath}/${sourcePkgName};type=application/java-archive" \
              -F "raw.asset1.filename=${targetPkgName}" \
              -u ${USERNAME}:${PASSWD}
        """
    }
}


//通过jenkins的nexus插件上传制品
def PushArtifactByPlugin(){
    nexusArtifactUploader artifacts: [[artifactId: 'demo-app', 
                                    classifier: '', 
                                    file: 'target/demo-0.0.1-SNAPSHOT.jar', 
                                    type: 'jar']], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: 'com.devops6', 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: 'maven-devops6-release', 
                        version: '1.1.1'
}

// 通过jenkins的nexus插件上传制品(读取pom文件)
// def PushArtifactByPluginPOM(){
//     POM = readMavenPom file: 'pom.xml'
//     println(POM)
//     println("GroupID: ${POM.groupId}")
//     println("ArtifactID: ${POM.artifactId}")
//     println("Version: ${POM.version}")
//     println("Packaging: ${POM.packaging}")

//     env.artifactId = "${POM.artifactId}"
//     env.packaging = "${POM.packaging}"
//     env.groupId = "${POM.groupId}"
//     env.art_version = "${POM.version}"
//     env.art_name = "${env.artifactId}-${env.art_version}.${env.packaging}"
//     nexusArtifactUploader   artifacts: [[artifactId: "${env.artifactId}", 
//                                         classifier: '', 
//                                         file: "target/${env.art_name}", 
//                                         type: "${env.packaging}"]], 
//                             credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
//                             groupId: "${env.groupId}", 
//                             nexusUrl: '172.29.9.101:8081', 
//                             nexusVersion: 'nexus3', 
//                             protocol: 'http', 
//                             repository: 'maven-devops6-release', 
//                             version: "${env.art_version}"
// }

// 通过jenkins的nexus插件上传制品(读取pom文件)-函数方式
def PushArtifactByPluginPOM(artifactId, file, type, groupId, repoName, version){
    println(artifactId)
    //demo
    println("${file}, ${type}, ${groupId}, ${repoName}, ${version}")
    //target/demo-0.0.1-SNAPSHOT.jar, jar, com.example, devops4-release, 0.0.1-SNAPSHOT
    nexusArtifactUploader artifacts: [[artifactId: artifactId, 
                                    classifier: '', 
                                    file: file, 
                                    type: type]], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: groupId, 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: repoName, 
                        version: version    
}
  • 运行,观察结果

image-20230629145427129

image-20230629152716367

image-20230629152745158

image-20230629145603391

符合预期。😘

如何自动识别项目的分支名选择进行构建

  • Jenkins安装Git Parameters插件

image-20230629153141949

  • 我们在devops6-maven-service上基于main分支创建版本分支RELEASE-6.2.1

image-20230629153238101

  • 编辑Jenkins项目devops6-maven-service,添加一个Git Parameter参数。

image-20230629153516566

image-20230629153953485

image-20230629154044474

默认什么都不过滤的话,保存后,点击参数化构建,现象如下:

image-20230629153906797

可以看到构建分支上回出现所有分支。

这里过滤下,只允许出现版本分支:

image-20230629154253428

image-20230629154303489

  • 再次编辑共享库代码,重新修改下分支名
groovy
//使用git 参数需要格式化
env.branchName = "${env.branchName}" - "origin/"
println(env.branchName)

image-20230629154448867

  • 再次运行流水线,观察效果

image-20230629154548639

image-20230629154823918

代码扫描:

image-20230629154851134

CI上传制品:

image-20230629160439246

测试结束。😘

完整共享库代码

image-20230629160027060

Jenkinsfile文件

groovy
@Library("devops06@main") _

//import src/org/devops/Build.groovy
def build = new org.devops.Build()
def sonar = new org.devops.Sonar()
def artifact = new org.devops.Artifact()


//使用git 参数需要格式化
env.branchName = "${env.branchName}" - "origin/"
println(env.branchName)

currentBuild.displayName = "${env.branchName}-commitID"
currentBuild.description = "Trigger by user jenkins \n branch: ${env.branchName}"

pipeline {
    agent {label "build"}
    options {
        skipDefaultCheckout true
    }    
    

    stages{
        stage("CheckOut"){
            steps{
                script{
                    build.CheckOut()
                }
            }
        }

        stage("Build"){
            steps{
                script{
                    build.Build()
                }
            }

        }        

        stage("CodeScan"){
            when {
                environment name: 'skipSonar', value: 'false'
            }
            
            steps{
                script{

                    sonar.SonarScannerByPlugin()        

                }
            }
        }

         stage("PushArtifact"){
            steps{
                script{
                    //PushArtifactByPlugin()
                    //PushArtifactByPluginPOM()

                    // init package info
                    appName = "${JOB_NAME}" //devops6-maven-service
                    repoName = appName.split('-')[0]   //devops6
                    appVersion = "${env.branchName}".split("-")[-1] // RELEASE-1.1.1   1.1.1
                    targetDir="${JOB_NAME}/${appVersion}"
                    

                    // 通过pom文件获取包名称
                    POM = readMavenPom file: 'pom.xml'
                    env.artifactId = "${POM.artifactId}"
                    env.packaging = "${POM.packaging}"
                    env.groupId = "${POM.groupId}"
                    env.art_version = "${POM.version}"
                    sourcePkgName = "${env.artifactId}-${env.art_version}.${env.packaging}"
                    
                    pkgPath = "target"
                    targetPkgName = "${appName}-${appVersion}.${env.packaging}"
                    artifact.PushNexusArtifact(repoName, targetDir, pkgPath, sourcePkgName,targetPkgName)
                }
            }
    
        }       

    }
}

image-20230629160216845

Artifact.groovy文件

groovy
package org.devops


//通过nexus api上传制品--综合实践
def PushNexusArtifact(repoId, targetDir, pkgPath, sourcePkgName,targetPkgName){
    //nexus api 
    withCredentials([usernamePassword(credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', \
                                    passwordVariable: 'PASSWD', 
                                    usernameVariable: 'USERNAME')]) {
        sh """
            curl -X 'POST' \
              "http://172.29.9.101:8081/service/rest/v1/components?repository=${repoId}" \
              -H 'accept: application/json' \
              -H 'Content-Type: multipart/form-data' \
              -F "raw.directory=${targetDir}" \
              -F "raw.asset1=@${pkgPath}/${sourcePkgName};type=application/java-archive" \
              -F "raw.asset1.filename=${targetPkgName}" \
              -u ${USERNAME}:${PASSWD}
        """
    }
}


//通过jenkins的nexus插件上传制品
def PushArtifactByPlugin(){
    nexusArtifactUploader artifacts: [[artifactId: 'demo-app', 
                                    classifier: '', 
                                    file: 'target/demo-0.0.1-SNAPSHOT.jar', 
                                    type: 'jar']], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: 'com.devops6', 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: 'maven-devops6-release', 
                        version: '1.1.1'
}

// 通过jenkins的nexus插件上传制品(读取pom文件)
// def PushArtifactByPluginPOM(){
//     POM = readMavenPom file: 'pom.xml'
//     println(POM)
//     println("GroupID: ${POM.groupId}")
//     println("ArtifactID: ${POM.artifactId}")
//     println("Version: ${POM.version}")
//     println("Packaging: ${POM.packaging}")

//     env.artifactId = "${POM.artifactId}"
//     env.packaging = "${POM.packaging}"
//     env.groupId = "${POM.groupId}"
//     env.art_version = "${POM.version}"
//     env.art_name = "${env.artifactId}-${env.art_version}.${env.packaging}"
//     nexusArtifactUploader   artifacts: [[artifactId: "${env.artifactId}", 
//                                         classifier: '', 
//                                         file: "target/${env.art_name}", 
//                                         type: "${env.packaging}"]], 
//                             credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
//                             groupId: "${env.groupId}", 
//                             nexusUrl: '172.29.9.101:8081', 
//                             nexusVersion: 'nexus3', 
//                             protocol: 'http', 
//                             repository: 'maven-devops6-release', 
//                             version: "${env.art_version}"
// }

// 通过jenkins的nexus插件上传制品(读取pom文件)-函数方式
def PushArtifactByPluginPOM(artifactId, file, type, groupId, repoName, version){
    println(artifactId)
    //demo
    println("${file}, ${type}, ${groupId}, ${repoName}, ${version}")
    //target/demo-0.0.1-SNAPSHOT.jar, jar, com.example, devops4-release, 0.0.1-SNAPSHOT
    nexusArtifactUploader artifacts: [[artifactId: artifactId, 
                                    classifier: '', 
                                    file: file, 
                                    type: type]], 
                        credentialsId: '3404937d-89e3-4699-88cf-c4bd299094ad', 
                        groupId: groupId, 
                        nexusUrl: '172.29.9.101:8081', 
                        nexusVersion: 'nexus3', 
                        protocol: 'http', 
                        repository: repoName, 
                        version: version    
}

image-20230629160242353

疑问

==疑问:本来已经配置了禁止重新部署,但是好像没生效哎……==

有同名的制品的话,流水线可以跑成功,也不报错。但是制品是不会上传的。--可能是bug吧。。。

image-20230630063104821

image-20230630063130770

nexus仓库可以看到,这里的制品生成日期是没变的:

image-20230630063048068

这里也是禁止重新部署的:

image-20230630063236331

FAQ

Deployment Policy含义

image-20230627201100623

image-20230627201344962

image-20230627201837321

这个问题就和docker镜像里的标签latest很像。

这里有个问题: 一般,RELEASE仓库里必须要变更版本号,不然是不允许上传的。开启了这个Aloow redeploy,表示它可以覆盖之前的版本。当你覆盖了,其他应用在引用的时候,可能会出现失败的情况,所以一般我们都把它disable掉。

image-20230627202003554

常见制品库

==① Nexus==

开源的解决方案,nexus肯定是我们首选的,(nexus制品库还是非常稳定的)。

nexus也有企业版本。

软件交付物 == 制品

依赖包

依赖包也是可以通过nexus去管理。

yum也是可以由nexus来管理。

加速编译构建的一种方式:

凡是mirrors、镜像源相关的,我们都可以通过nexus代理仓库(proxy)来代理公网源来加速编译构建。

Maven私服仓库是企业里用的最多的一个了。

nexus本身是支持docker镜像和nexus的,只不过企业里我们一般会搭建harbor来管理docker镜像和helm包的

nexus也支持和ldap做集成。

jenkins本身也支持收集制品的功能,但不建议这么做,因为jenkins已经够重了;

制品:晋级策略 制品库:解决重复编译,构建的问题;

image-20230626204508801

==② JFROG==

在大厂里也用的比较多,商业版本里,这个JFORG也用的比较多。

image-20230627074628691

关于我

我的博客主旨:

  • 排版美观,语言精炼;
  • 文档即手册,步骤明细,拒绝埋坑,提供源码;
  • 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!

🍀 微信二维码 x2675263825 (舍得), qq:2675263825。

image-20211002091450217

🍀 微信公众号 《云原生架构师实战》

image-20211002141739664

🍀 csdn https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

image-20211002092344616

🍀 博客 www.onlyyou520.com

image-20220513150311181

🍀 知乎

https://www.zhihu.com/people/foryouone

🍀 语雀

https://www.yuque.com/books/share/34a34d43-b80d-47f7-972e-24a888a8fc5e?# 《不服来怼:宇宙中最好用的云笔记!》

image-20220625153919603

最后

好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

image-20220625161909047

最近更新