K8S项目部署-部署Nexus仓库并在Maven/Gradle当中配置Nexus仓库

1. 创建Nexus的Namespace并准备好PV 1.1 创建Namespace apiVersion: v1 kind: Namespace metadata: name: devops-tools 1.2 创建Nex

1. 创建Nexus的Namespace并准备好PV

1.1 创建Namespace

apiVersion: v1
kind: Namespace
metadata:
  name: devops-tools

1.2 创建Nexus的PVC基于StorageClass创建PV

我们这里基于准备好的storageClass(nfs-storage)去进行PVC的创建,在生成PVC的过程中,storageClass会自动为其创建并分配一个合适的PV持久卷。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nexus-pvc
  namespace: devops-tools
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi
  storageClassName: nfs-storage

2. Nexus服务的部署和配置

2.1 基于Deployment部署Nexus服务

Nexus天生不支持分布式,只能单机部署,不支持多个节点部署(replicas必须指定为1),使用普通的多个节点部署会有数据的一致性的问题。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nexus
  namespace: devops-tools
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nexus
  template:
    metadata:
      annotations:
        kubesphere.io/imagepullsecrets: '{"nexus":"devops-tools-harbor"}'
      labels:
        app: nexus
    spec:
      containers:
      - name: nexus
        image: wanna1314y.top:1443/library/nexus3:latest
        ports:
        - containerPort: 8081
        volumeMounts:
        - name: nexus-data
          mountPath: /nexus-data
        env:
        - name: INSTALL4J_ADD_VM_PARAMS
          value: "-Xms400m -Xmx600m"
      volumes:
      - name: nexus-data
        persistentVolumeClaim:
          claimName: nexus-pvc

镜像来源如下:

docker pull sonatype/nexus3:latest

2.2 Nexus的Service配置支持Nexus服务外网访问

我们使用NodePort的方式将Nexus映射到宿主机的31111端口号上,方便我们通过31111端口进行访问,在后续调试完成之后,我们可以将类型改为ClusterIp并通过域名的方式进行对外访问。

apiVersion: v1
kind: Service
metadata:
  name: nexus
  namespace: devops-tools
spec:
  ports:
    - name: http
      port: 8081
      targetPort: 8081
      nodePort: 31111
  selector:
    app: nexus
  type: NodePort

配置之后,可以使用31111端口去访问Nexus服务。

cb0d7959d0228f74e6ba76c9fedc8729.png

Nexus服务的默认管理员登录密码为admin,密码在/nexus-data/admin.password当中可以通过命令cat /nexus-data/admin.password去查看文件当中的默认密码,Nexus在第一次登录时会让修改新密码。

3.配置Ingress支持使用域名方式访问Nexus

新增如下的Ingress配置,将指定的域名的请求转发到nexus服务,需要将<domain>替换成为真实的域名,比如xxx.com

kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
  name: devops-tools-nginx-ingress
  namespace: devops-tools
  annotations:
    nginx.ingress.kubernetes.io/affinity: cookie
    nginx.ingress.kubernetes.io/proxy-body-size: 100m
    nginx.ingress.kubernetes.io/session-cookie-max-age: '86400'
    nginx.ingress.kubernetes.io/session-cookie-name: SESSION
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - <domain>
      secretName: <domain-ssl-secret>
  rules:
    - host: <domain>
      http:
        paths:
          - path: /
            pathType: ImplementationSpecific
            backend:
              service:
                name: nexus
                port:
                  number: 8081

如果需要创建HTTPS的Ingress配置,那么需要新增如下的配置项,如果不需要创建HTTPS的Ingress配置,那么直接删除这项即可。

配置当中的<domain>是HTTPS证书的域名,<domain-ssl-secret>是该域名的SSL证书的Secret。

  tls:
    - hosts:
        - <domain>
      secretName: <domain-ssl-secret>

SSL证书的Secret创建方式可以参考如下的命令,需要使用-n指定TLS证书配置所在的Namespace,需要通过--cert参数指定证书文件路径,通过--key参数指定私钥文件路径。

kubectl create secret tls app-tls-secret --cert=/path/to/<domain>_bundle.crt --key=/path/to/<domain>.key -n <namespace>

4.在Maven和Gradle当中配置Nexus仓库

首先我们需要知道Nexus会有Snapshot仓库和Release,分别用来存放Snapshot版本的Jar包和Release版本的Jar包。

  • Nexus默认提供的Snapshot版本的Jar包仓库地址为${nexus-repository-url}/repository/maven-snapshots/
  • Nexus默认提供的Release版本的Jar包的仓库地址为${nexus-repository-url}/repository/maven-releases/
  • Nexus默认提供的中央仓库地址为${nexus-repository-url}/repository/maven-central/,是通过代理的方式去代理的中央仓库,配置这个地址相当于可以从中央仓库进行Jar包的拉取。
  • Nexus默认提供了一个MavenGroup,名为maven-public,地址为${nexus-repository-url}/repository/maven-public/
    • maven-public默认配置了maven-releasesmaven-snapshotsmaven-central三个仓库。

66ce31ed8aceba6f767192d058e15a38.png

对于仓库地址的配置,需要注意的是:

  • 对于拉取Jar包的仓库配置,可以直接使用maven-publicmaven-public能支持从maven-releasesmaven-snapshotsmaven-central这三个仓库进行Jar包的拉取。
  • 对于Jar包的推送的仓库配置,比如明确指定推送的地址是maven-releases还是maven-snapshots,不能指定为maven-public

4.1 在Maven当中配置远程Nexus仓库

(1) 通过Maven配置Jar包拉取仓库地址

Maven当中需要通过Maven的配置文件~/.m2/settings.xml配置远程仓库,我们可以修改settings.xml,将其修改为如下内容(修改之前记得备份),配置为本地的Nexus仓库。

注意:需要将${nexus-repository-url}替换成为真实的仓库地址,${username}替换成为用户名,${password}替换成为密码。

<?xml version="1.0"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

    <servers>
        <server>
            <id>local-nexus-public</id>
            <username>${username}</username>
            <password>${password}</password>
            <filePermissions>664</filePermissions>
            <directoryPermissions>775</directoryPermissions>
        </server>
    </servers>
    <mirrors>
        <mirror>
            <id>local-nexus-public</id>
            <name>Local Nexus Public Mirror</name>
            <url>${nexus-repository-url}/repository/maven-public/</url>
            <mirrorOf>*</mirrorOf>
        </mirror>
    </mirrors>
    <profiles>
        <profile>
            <id>local-nexus</id>
            <repositories>
                <repository>
                    <id>local-nexus-public</id>
                    <url>${nexus-repository-url}/repository/maven-public/</url>
                    <snapshots>
                        <enabled>true</enabled>
                        <checksumPolicy>fail</checksumPolicy>
                        <updatePolicy>always</updatePolicy>
                    </snapshots>
                </repository>
            </repositories>
            <pluginRepositories>
                <pluginRepository>
                    <id>local-nexus-public</id>
                    <url>${nexus-repository-url}/repository/maven-public/</url>
                    <snapshots>
                        <enabled>true</enabled>
                        <checksumPolicy>fail</checksumPolicy>
                        <updatePolicy>always</updatePolicy>
                    </snapshots>
                </pluginRepository>
            </pluginRepositories>
        </profile>
    </profiles>

    <activeProfiles>
        <activeProfile>local-nexus</activeProfile>
    </activeProfiles>
</settings>

在项目的pom.xml当中,新增如下的依赖项,拉取Jar包依赖。

        <dependency>
            <groupId>com.wanna.project</groupId>
            <artifactId>nativeproject</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

如果出现如下的异常,拉取不到依赖时,需要注意classifier是否正确:

The following artifacts could not be resolved: com.wanna.project:nativeproject:jar:0.0.1-SNAPSHOT (absent)

比如在Nexus仓库当中看到了如下的Jar包名字时,那么就说明存在有classifier=plain。

492dfe380b80e63284684fbf2060f8c3.png

比如我推送到Nexus仓库的Jar包依赖可能是plain版本,那么我就需要使用classifier=plain去进行获取,不指定classifier则无法获取到。

        <dependency>
            <groupId>com.wanna.project</groupId>
            <artifactId>nativeproject</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <classifier>plain</classifier>
        </dependency>

对于Maven仓库(本地Maven仓库/远程Nexus仓库)当中的Jar包的文件名称命名格式如下:

  • 正常的Release的Jar包来说,文件名格式是{artifactId}-{version}.jar,如果指定了classifier,那么应该变成{artifactId}-{version}-{classfier}.jar
  • 对于Snapshot的Jar包来说,文件名格式是{artifactId}-{version}-SNAPSHOT.jar,如果指定了classfier,那么应该变成{artifactId}-{version}-{classfier}-SNAPSHOT.jar
  • 对于Maven仓库当中的快照jar包,如果需要保证唯一的版本号的话,那么名称会变成{artifactId}-{version}-{timestamp}-{buildNumber}-SNAPSHOT.jar,如果指定了classifier,那么应该变成{artifactId}-{version}-{timestamp}-{buildNumber}-{classifier}-SNAPSHOT.jar

通常情况,Maven发布插件,在进行Artifact的发布时都会默认加上时间戳和构建版本号,快照Jar包默认情况下都会在版本号后面拼接上时间戳来保证Jar包版本唯一性,比如0.0.1-SNAPSHOT这个版本就可能被发布多次,如果允许覆盖的话第二次发布可能会覆盖第一次的Jar包,默认情况Nexus会保存所有的版本的Jar包。

常见的classifiersourcesplainjavadoc

  • 一般不带任何的classififer的是最终产物。
  • classifier=sources代表这是一个源码包的Jar包。
  • classifier=plain代表这是一个原始的Jar包,不经过任何处理,直接编译得到的产物。一般我们都使用不带任何classifier的Jar包,它会经过其他编译插件处理,比如会通过Manifest去指定主启动类,比如在SpringBoot的编译插件当中会生成fatjar大jar包。
  • classifier=javadoc代表这是一个Javadoc的Jar包。

(2) 通过Maven配置发布推送仓库地址

只需要在项目pom.xml当中新增如下的配置项即可:

    <distributionManagement>
        <repository>
            <id>local-nexus-repository</id>
            <url>https://nexus.wanna1314y.top:31889/repository/maven-snapshots/</url>
            <snapshots>
                <enabled>true</enabled>
                <checksumPolicy>fail</checksumPolicy>
                <updatePolicy>always</updatePolicy>
            </snapshots>
            <releases>
                <enabled>true</enabled>
                <checksumPolicy>warn</checksumPolicy>
            </releases>
        </repository>
    </distributionManagement>

需要注意的是:distributionManagement配置当中的repositoy.id需要指向settings.xml当中的已经定义好server的账号密码的配置

比如我们在settings.xml当中已经定义了如下的server配置,那么我们可以在distributionManagement.repository当中去配置id为local-nexus-repository,就代表这个仓库会用server.idlocal-nexus-repository所对应的账号密码进行授权,如果没有权限那么可能会出现无法推送到远程Nexus仓库的情况(比如出现HTTP 401的状态码)。

        <server>
            <id>local-nexus-repository</id>
            <username>${username}</username>
            <password>${password}</password>
            <filePermissions>664</filePermissions>
            <directoryPermissions>775</directoryPermissions>
        </server>

通过执行mvn deploy命令,将Jar包推送到远程的Nexus仓库。

mvn deploy

4.2 Gradle当中配置Nexus仓库

(1) 将Nexus作为拉取Jar包的远程Maven仓库

我们只需要在build.gradle.ktsrepositories配置当中,新增如下的远程仓库配置即可。只需要将<nexus-repository-url>替换成为自己的Nexus仓库的地址,将<username><password>换成自己的Nexus仓库的用户名和密码,即可作为Maven仓库进行Jar包的拉取。

repositories {
    mavenCentral()

    // 远程仓库
    maven {
        url = uri("<nexus-repository-url>/repository/maven-snapshots/")
        credentials {
            username = "<username>"
            password = "<password>"
        }
    }
}

接着,我们在dependencies配置当中,添加我们刚刚上传的Maven依赖的GAV坐标配置,即可从我们自己的远程Nexus仓库当中拉取到Jar包。

dependencies {
    implementation("com.wanna.project:nativeproject:0.0.1-SNAPSHOT")
}

对于Gradle GroovyDSL的情况,需要的配置信息和上面的Kotlin DSL的配置完全一致。

repositories {
    mavenCentral()

    // 远程仓库
    maven {
        url = uri("<nexus-repository-url>/repository/maven-snapshots/")
        credentials {
            username = "<username>"
            password = "<password>"
        }
    }
}

dependencies {
    implementation("com.wanna.project:nativeproject:0.0.1-SNAPSHOT")
}

(2)将Nexus作为Jar包的推送目标仓库

Gradle当中发布到Maven仓库,需要用到Maven的发布插件maven-publish,首先需要在plugins当中去声明这个插件。

下面是使用KotlinDSL的Gradle配置,我们只需要在build.gradle.kts当中新增如下的配置即可。(只需要将<nexus-repository-url>替换成为自己的Nexus仓库的地址,将<username><password>换成自己的Nexus仓库的用户名和密码,即可完成上传。)

plugins {
    ......
    `maven-publish`
}

publishing {
    publications {
        create<MavenPublication>("mavenJava") {
            from(components["java"])
        }
    }
    repositories {
        maven {
            // Nexus 发布仓库 URL
            url = if (version.toString().endsWith("-SNAPSHOT")) {
                uri("<nexus-repository-url>/repository/maven-snapshots/")
            } else {
                uri("<nexus-repository-url>/repository/maven-releases/")
            }
            credentials {
                username = "<username>"
                password = "<password>"
            }
        }
    }
}

我们只需要执行./gradlew publish命令,即可将本地仓库的Jar包上传到远程Nexus仓库。

上传成功之后,我们可以通过"Browse",找到右侧的maven-releases/maven-snapshots(分别对应的是release的jar包和snapshot的jar包的仓库地址),进入对应的仓库地址页面。

f5066165223e966751f0ebfad7c90ad5.png

比如我们上传的是snapshot快照版本,那么我们就可以选择maven-snapshots,接着就进入到下面的页面,就可以查看到刚刚我们通过./gradlew publish上传的Jar包。

522f8081813141ce52fe4a51c7d3edf7.png

对于Gradle GroovyDSL的配置完全类似,可以使用如下的配置。

plugins {
    id 'maven-publish'
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
    repositories {
        maven {
            if (version.toString().endsWith("-SNAPSHOT")) {
                url uri("<nexus-repository-url>/repository/maven-snapshots/")
            } else {
                url uri("<nexus-repository-url>/repository/maven-releases/")
            }
            credentials {
                username = '<username>'
                password = '<password>'
            }
        }
    }
}

(3) 基于配置文件/环境变量动态取身份配置信息(避免直接写入到build.gradle)

直接将username和password写入到build.gradle当中是不合理的,一方面是项目涉及到多人协作,还可能公开暴露给别人,直接写到build.gradle当中是非常危险的,我们应该避免直接写入。

为了避免直接将username和password直接写入到builld.gradle配置文件当中,我们可以将用户名和密码尝试使用配置文件/环境变量的方式进行读取。这样只需要我们编辑配置文件/环境变量当中的NEXUS_USERNAMENEXUS_PASSWORD,即可对所有的Gradle项目均生效。

        credentials {
            username = (project.findProperty("NEXUS_USERNAME") ?: System.getenv("NEXUS_USERNAME") ?: "").toString()
            password = (project.findProperty("NEXUS_PASSWORD") ?: System.getenv("NEXUS_PASSWORD") ?: "").toString()
        }

通过上面的配置,我们会优先从Gradle的配置文件(例如~/.gradle/gradle.properties)当中去进行读取,如果读取不到,可以尝试从环境变量当中去进行获取。

我们可以在配置文件当中写入如下内容,即可被自动被Gradle的build.gralde(build.gradle.kts)进行获取到。

NEXUS_USERNAME=<username>
NEXUS_PASSWORD=<password>
Comment