Nexus 制品库集成


Nexus 制品库集成

一、Nexus 是什么?

1.1 使用场景

私有仓库,主要使用场景有两个

  • 私服仓库:企业内私有制品仓库服务
  • 代理仓库:公网第三方仓库缓存代理

1.2 基本概念

1.2.1 Components 组件

简单理解的话,就是仓库中的每个包,常见的格式包括:jarwarziptar.gzexeshapk,如下示例:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

1.2.2 Assets 资产

简单来说即使一个项目的集合,如图所示:

1.2.3 Central Repository 中央仓库

中央仓库(Central Repository)是用于 Java 组件下载的内置存储库,由 Sonatype 管理,每天为数百万的 Maven 用户提供服务。Maven 工具在默认情况下,它就会从预定义的中央存储库检索组件

除了默认的中央存储库,还有其他组织维护其特有的存储库,例如 Redhat、Oracle、Apache 软件基金会,这些存储库通常拥有以下几个属性:

  • 组件元数据:存储库中保存所有软件组件的元数据,如名称、版被号、依赖等
  • 不可变:一旦组件(及元数据)发布到存储库,那么便具有不可更改特性(行政手段)
  • 组件安全:存储库包含 哈希 和 PGP 加密签名,用于验证软件组件的真实性和完整性,并支持通过 HTTPS 安全加密协议下载访问
  • 性能:存储库面向全球用户开发,性能自是不必多说(除了网络问题)

1.2.4 Components Coordinate 组件坐标

制品坐标为制品(组件)的唯一标识符,以 Maven 为例,Maven 以 groupId(组ID)artifactId(构件ID)version(版本号)packaging,这组坐标通常称为 GAV 坐标

**GAV 坐标 **是 Maven 管理依赖关系的基础,我们再认真看下

  • groupId:组标识符,通常使用 组标识符 将某一特定组织产生的构件逻辑分组,例如:Apache 软件基金会 发布的组件组标识为 org.apache.maven
  • artifactId:组件标识符,用于标识组件 groupIdartifactId 的组合应是唯一
  • version:组件版本号,应遵循组织内版本标识规范
  • packaging:描述组件格式,包括任何二进制软件格式的内容,例如:ziprarwarjar

构建工具通过将 GAV(P) 坐标 转换为与 Maven 存储库中对应的 URL,例如:

<repoURL>/org/apache/commons/commons-lang/1.2.0/commons-lang-1.2.0.jar</repoURL>

1.2.5 发布(release)/快照(snapshot)

Maven 存储库包含两种类型的组件,releasesnapshot,前者代表稳定的静态发布组件、后者代表频繁更新的组件,下面我们仔细看下

  • release:代表坚固、稳定、不会随着时间推移而变更的组件,例如:commons-lang-1.2.0.jar
  • snapshot:代表开发过程中的产生的组件,通常包含 版本号 + 时间戳,例如:commons-lang-1.3.0-20090314.182342-1.jar

二、Nexus 部署

2.1 Docker 部署

启动容器

$ mkdir -p /prodata/nexus-data && chown -R 200 /prodata/nexus-data
$ docker run -d -p 9081:8081 --name nexus -v /prodata/nexus-data:/nexus-data sonatype/nexus3

查看日志

$ docker logs -f nexus
...
2021-12-01 11:30:37,466+0000 INFO  [jetty-main-1] *SYSTEM org.eclipse.jetty.server.Server - Started @78725ms
2021-12-01 11:30:37,468+0000 INFO  [jetty-main-1] *SYSTEM org.sonatype.nexus.bootstrap.jetty.JettyServer - 
-------------------------------------------------

Started Sonatype Nexus OSS 3.37.0-01

-------------------------------------------------

登录平台,默认用户名 admin,密码保存在 /prodata/nexus-data/admin.password

访问 WebUI,初始化 Nexus 并重置密码

三、Nexus & Jenkins

3.1 创建 nexus 仓库

接下来,我们让 Jenkins 与 Nexus 联动起来,首先,我们创建一个仓库

![](I:\技术学习\【优点】\【优点】Jenkins 实践\个人笔记\图片\创建 maven-hosted 仓库.gif)

3.2 配置 nexus 仓库

拷贝仓库地址

3.3 创建 nexus 用户

3.4 配置 Maven

配置认证

$ vim /usr/local/apache-maven-3.8.3/conf/settings.xml
# ...
  <servers>
    <server>
      <id>maven-hostd</id>
      <username>jenkins</username>
      <password>jenk1n5</password>
    </server>
  </servers>
# ...

3.5 测试上传制品

执行命令

$ mvn deploy:deploy-file -Dmaven.test.skip=true \
-Dfile=target/my-app.jar \
-DgroupId=com.mycompany.app \
-DartifactId=my-app \
-Dversion=1.1-SNAPSHOT \
-Dpackaging=jar \
-DrepositoryId=maven-hostd \
-Durl=http://192.144.227.61:9081/repository/maven-hostd

执行效果

[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< com.mycompany.app:my-app >----------------------
[INFO] Building my-app 1.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-deploy-plugin:2.7:deploy-file (default-cli) @ my-app ---
Downloading from maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/1.1-SNAPSHOT/maven-metadata.xml
Uploading to maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/1.1-SNAPSHOT/my-app-1.1-20211204.082307-1.jar
Uploaded to maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/1.1-SNAPSHOT/my-app-1.1-20211204.082307-1.jar (1.9 kB at 4.2 kB/s)
Uploading to maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/1.1-SNAPSHOT/my-app-1.1-20211204.082307-1.pom
Uploaded to maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/1.1-SNAPSHOT/my-app-1.1-20211204.082307-1.pom (404 B at 1.6 kB/s)
Downloading from maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/maven-metadata.xml
Uploading to maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/1.1-SNAPSHOT/maven-metadata.xml
Uploaded to maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/1.1-SNAPSHOT/maven-metadata.xml (767 B at 4.7 kB/s)
Uploading to maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/maven-metadata.xml
Uploaded to maven-hostd: http://192.144.227.61:9081/repository/maven-hostd/com/mycompany/app/my-app/maven-metadata.xml (281 B at 2.4 kB/s)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.058 s
[INFO] Finished at: 2021-12-04T16:23:09+08:00
[INFO] ------------------------------------------------------------------------

浏览组件

查看组件(制品)详情信息

3.6 Jenkins 集成 Nexus

首先,安装 Nexus Artifact Uploader 插件,使用片段生成器生成 DSL

抽离上传逻辑至共享库:src/org/devops/nexus.groovy

package org.devops

def Upload(nexus_server, credentials_id, repository_id, pom_groupid, pom_artifact, pom_version, pom_packaging){
    def artifact_file = "target/${pom_artifact}.${pom_packaging}"
    result = nexusArtifactUploader artifacts: [[artifactId: "${pom_artifact}", 
                                               classifier: "",
                                               file: "${artifact_file}",
                                               type: "${pom_packaging}"]],
                          credentialsId: "${credentials_id}",
                          groupId: "${pom_groupid}",
                          nexusUrl: "${nexus_server}",
                          nexusVersion: "nexus3",
                          protocol: "http",
                          repository: "${repository_id}",
                          version: "${pom_version}"
    return result
}

Jenkinsfile 流水线定义

@Library('shareLibraryDemo') _
def util_tools = new org.devops.utils()
def toemail = new org.devops.toemail()
def sonar = new org.devops.sonarqube()
def sonarapi = new org.devops.sonarapi()
def nexus = new org.devops.nexus()

String branch_name = "${branch}".split('/')[-1]
String sonarqube_server = "http://192.144.227.61:9000"
String nexus_repo_url = "http://192.144.227.61:9081/repository/maven-hostd"

currentBuild.description = "Trigger by ${committer_name} ${branch_name}"

pipeline {
  agent {
    node {
      label "bj-tencent-lhins-1"
    }
  }
  tools {
    jdk 'Openjdk 11.0.13'
    maven 'M2'
  }
  options {
    timestamps()
  }
  stages {
    stage('拉取代码') {
      steps {
        // branches 只拉取特定仓库的分支
        // branches: [[name: '*/master'],]:
        //     当远程仓库 master  分支提交时,触发 pipeline 拉取 master 分支
        //     当远程仓库 develop 分支提交时,仍然触发 pipeline 拉取 master 分支
        checkout([$class: 'GitSCM', branches: [
            [name: "${branch}"],
        ], extensions   : [], userRemoteConfigs: [
            [credentialsId: 'gitee', url: "${env.GIT_URL}"]
        ]])
      }
    }
    stage("打包应用") {
      steps {
        script {
          util_tools.PrintColorMsg("编译/打包最新代码.", "blue")
          sh "mvn package"
          util_tools.PrintColorMsg("编译/打包完成.", "green")
        }
      }
    }
    stage("上传制品") {
      steps {
        script {
            util_tools.PrintColorMsg("上传构建制品.", "blue")
            def jar_name = sh returnStdout: true, script: "cd target;ls *.jar"
            jar_name = jar_name - "\n"
            def pom = readMavenPom file: 'pom.xml'
            pom_groupid = "${pom.groupId}"
            pom_artifact = "${pom.artifactId}"
            pom_version = "${pom.version}"
            pom_packaging = "${pom.packaging}"
            println("GAV 坐标:${pom_groupid}-${pom_artifact}-${pom_version}-${pom_packaging}")
            nexus_server  = "${nexus_repo_url}".split('/')[2]
            repository_id = "${nexus_repo_url}".split('/')[-1]
            result = nexus.Upload(nexus_server, 'nexus-jenkins-user', repository_id, pom_groupid, pom_artifact, pom_version, pom_packaging)
            println("上传结果: ${result}")
            util_tools.PrintColorMsg("上传制品完成.", "green")
        }
      }
    }
    stage("代码扫描") {
      steps {
        script {
          util_tools.PrintColorMsg("代码扫描,跳过...~", "green")
        }
      }
    }
  }
  post {
    success {
      script {
        util_tools.PrintColorMsg("构建成功~", "green")
        toemail.Email("流水线构建成功了!", committer_email, branch_name)
      }
    }
    failure {
      script {
        util_tools.PrintColorMsg("构建失败!", "red")
        toemail.Email("流水线执行失败了!", committer_email, branch_name)
      }
    }
    aborted {
      script {
        util_tools.PrintColorMsg("构建取消!", "red")
        toemail.Email("流水线被取消了!", committer_email, branch_name)
      }
    }
    always {
      script {
        util_tools.PrintColorMsg("清理临时产物", "green")
        sh "mvn clean"
      }
    }
  }
}

构建日志

Blueocean UI

查看制品

3.7 制品晋级

软件制品是软件企业持续交付的目标产物,其质量是制品交付的重要属性。只有依次通过了代码检查、单元测试、测试(自动化、手工)等多重质量门禁,制品方可由开发库逐步晋级至测试库、待投产库,最终晋级至投产库部署生产

“一次构建,多次部署” 这是单一制品原则,也是“制品晋级”的核心理念,一般是会通过多个制品库(repo)、打标签、增加属性进行标识,但是在流程上需要明确什么时间点,什么动作来触发晋级,制品晋级后供以下一级环境的部署使用

我们梳理下具体流程:

  1. 持续集成流水线:打包构建制品
  2. 持续部署流水线:部署测试制品
  3. 制品晋级流水线:制品晋级(打标、移仓)

https://cloud.tencent.com/developer/article/1830395


文章作者: Da
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Da !
  目录