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服务。
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-releases
、maven-snapshots
和maven-central
三个仓库。
对于仓库地址的配置,需要注意的是:
- 对于拉取Jar包的仓库配置,可以直接使用
maven-public
,maven-public
能支持从maven-releases
、maven-snapshots
和maven-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。
比如我推送到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包。
常见的classifier
有sources
、plain
、javadoc
:
- 一般不带任何的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.id
为local-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.kts
的repositories
配置当中,新增如下的远程仓库配置即可。只需要将<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包的仓库地址),进入对应的仓库地址页面。
比如我们上传的是snapshot快照版本,那么我们就可以选择maven-snapshots
,接着就进入到下面的页面,就可以查看到刚刚我们通过./gradlew publish
上传的Jar包。
对于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_USERNAME
和NEXUS_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>