在 K8s 中部署 Nexus 私服仓库
介绍
Nexus 是用于管理和托管软件包的私有仓库管理系统。它提供了一个集中化的存储位置,用于保存软件组件、库文件、依赖项和其他二进制文件。
仓库类型
Nexus 仓库按用途分为四种类型:
- Blob Stores:用于存储实体数据的仓库。
- Hosted:本地仓库,可用于上传和下载本地构件。
- Proxy:代理仓库,可通过代理仓库下载外部仓库的构件。
- Group:仓库组,可包含本地和代理仓库的集合。
其他参考
Maven 相关配置参考:简书
Nexus 开启 https 参考:cnblogs
部署
Nexus 3.x 版本支持 Docker 镜像仓库,并且使用 OrientDB 数据库来存储仓库数据。部署选择最新版 3.38.1。
准备工作
声明应用名和环境,建立发布文件存放目录:
[root@k8s-101 ~]# export APPNAME=nexus
[root@k8s-101 ~]# export APPENV=local
[root@k8s-101 ~]# mkdir -p /hxz393/${APPENV}/${APPNAME}/apply
创建 SVC
Web 端口和 Docker 上传下载端口都可用 Nginx 做反向代理,直接通过域名操作,但对网络速度要求很高。如果网络不稳,会出现大容量镜像推送时中断,陷入反复重试循环。因此还是优先通过内网端口推送,将端口暴露出来:
[root@k8s-101 ~]# tee /hxz393/${APPENV}/${APPNAME}/apply/${APPNAME}-svc.yaml<<EOF
apiVersion: v1
kind: Service
metadata:
name: ${APPNAME}-svc
namespace: ${APPENV}
spec:
type: NodePort
ports:
- name: web
port: 80
targetPort: web
nodePort: 8081
selector:
app: ${APPNAME}
---
apiVersion: v1
kind: Service
metadata:
name: ${APPNAME}-svc-docker-pull
namespace: ${APPENV}
spec:
type: NodePort
ports:
- name: docker-pull
port: 80
targetPort: docker-pull
nodePort: 10007
selector:
app: ${APPNAME}
---
apiVersion: v1
kind: Service
metadata:
name: ${APPNAME}-svc-docker-push
namespace: ${APPENV}
spec:
type: NodePort
ports:
- name: docker-push
port: 80
targetPort: docker-push
nodePort: 10008
selector:
app: ${APPNAME}
EOF
[root@k8s-101 ~]# kubectl apply -f /hxz393/${APPENV}/${APPNAME}/apply/${APPNAME}-svc.yaml
[root@k8s-101 ~]# kubectl -n ${APPENV} get svc -owide | grep ${APPNAME}
创建 STS
Nexus 对内存和储存消耗很大,在生产环境需要配置大一点:
[root@k8s-101 ~]# tee /hxz393/${APPENV}/${APPNAME}/apply/${APPNAME}-sts.yaml<<EOF
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: ${APPNAME}-sts
namespace: ${APPENV}
spec:
serviceName: ${APPNAME}-svc
replicas: 1
selector:
matchLabels:
app: ${APPNAME}
template:
metadata:
labels:
app: ${APPNAME}
spec:
terminationGracePeriodSeconds: 60
nodeSelector:
NodeEnv: ${APPENV}
containers:
- name: ${APPNAME}
image: sonatype/nexus3:3.38.1
imagePullPolicy: IfNotPresent
resources:
requests:
memory: 500Mi
limits:
memory: 4200Mi
ports:
- name: web
containerPort: 8081
- name: docker-pull
containerPort: 10007
- name: docker-push
containerPort: 10008
env:
- name: TZ
value: Asia/Shanghai
securityContext:
runAsUser: 0
runAsGroup: 0
startupProbe:
tcpSocket:
port: web
timeoutSeconds: 6
failureThreshold: 60
periodSeconds: 5
readinessProbe:
tcpSocket:
port: web
timeoutSeconds: 10
periodSeconds: 35
livenessProbe:
tcpSocket:
port: web
timeoutSeconds: 10
periodSeconds: 60
volumeMounts:
- name: data
mountPath: /nexus-data/
subPath: data/
volumeClaimTemplates:
- metadata:
name: data
spec:
storageClassName: kube-sc
accessModes: [ "ReadWriteMany" ]
resources:
requests:
storage: 50Gi
EOF
[root@k8s-101 ~]# kubectl apply -f /hxz393/${APPENV}/${APPNAME}/apply/${APPNAME}-sts.yaml
[root@k8s-101 ~]# kubectl -n ${APPENV} get sts,po -owide | grep ${APPNAME}
[root@k8s-101 ~]# kubectl -n ${APPENV} logs -f ${APPNAME}-sts-0
第一次部署时间比较长。
配置域名
配置前端访问域名 nexus.x2b.net
:
[root@k8s-101 ~]# vi /hxz393/local/nginx/config/conf.d/nexus.x2b.net.conf
server {
listen 80;
listen 443 ssl;
server_name nexus.x2b.net;
ssl_certificate /cert/x2b.net.pem;
ssl_certificate_key /cert/x2b.net.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1.2 TLSv1.3 SSLv3;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://nexus-svc.local/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto "https";
}
}
配置专用域名 docker.x2b.net
用于 Docker 镜像拉取推送:
[root@k8s-101 ~]# vi /hxz393/local/nginx/config/conf.d/docker.x2b.net.conf
upstream docker_get {
server nexus-svc-docker-pull.local;
}
upstream docker_put {
server nexus-svc-docker-push.local;
}
server {
listen 80;
listen 443 ssl;
server_name docker.x2b.net;
ssl_certificate /cert/x2b.net.pem;
ssl_certificate_key /cert/x2b.net.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1.2 TLSv1.3 SSLv3;
ssl_prefer_server_ciphers on;
chunked_transfer_encoding on;
# 设置默认使用推送代理
set $upstream "docker_put";
# 当请求是GET也就是拉取镜像的时候,这里改为拉取代理,如此便解决了拉取和推送的端口统一
if ( $request_method ~* 'GET') {
set $upstream "docker_get";
}
location / {
proxy_pass http://$upstream;
proxy_set_header Host $host;
proxy_connect_timeout 3600;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffering off;
proxy_request_buffering off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto http;
}
}
[root@k8s-101 ~]# for i in $(kubectl get pod -n local|grep nginx|awk '{print $1}');do kubectl exec -it -n local $i -- nginx -s reload; done
配置
配置仓库和策略等。
修改密码
第一次部署后,获取管理员 admin
默认登录密码:
[root@k8s-101 ~]# kubectl -n local exec -it nexus-sts-0 -- cat /nexus-data/admin.password
b0736b15-cde5-4ab0-b880-b21767b99366
登陆后点击用户管理重设密码。
禁止匿名访问
默认情况下匿名用户可以看到仓库内容。点击设置中的 Security > Anonymous Access,去除勾选允许匿名访问(Allow anonymous users to access the server)再保存。
添加权限
点击设置中的 Security > Realms,将左侧的认证方式全部加入到右边,点击保存。
配置 Docker 仓库
为了不被混淆,先把默认库都删除。
建立 Blob 库
在 Blob Stores 页面点击新建:
- Type:选择 File 表示本地文件储存。
- Name:输入仓库名
docker-blob
。
点击保存完成。
建立 Hosted 库
在 Repositories 页面点击新建,类型选择 docker(hosted)
:
- Name:输入仓库名
docker-hosted
。 - HTTP:设置端口为
10008
。 - Blob store:选择刚才新建的仓库
docker-blob
。
默认策略为 Allow redeploy
允许覆盖上传,点击创建完成。
建立 Proxy 库
在 Repositories 页面点击新建,类型选择 docker(proxy)
:
- Name:输入仓库名
docker-proxy-dockerio
。 - Remote storage:输入官方 docker 仓库地址
https://registry-1.docker.io
。 - Docker Index:选择使用 Docker Hub 索引。
- Blob store:选择仓库
docker-blob
。
点击创建来完成。同理可以创建阿里云镜像仓库代理,地址为用阿里云账号申请的私有地址。
建立 Group 库
在 Repositories 页面点击新建,类型选择 docker(group)
:
- Name:输入仓库名
docker-group
。 - HTTP:设置端口为
10007
。 - Blob store:选择仓库
docker-blob
。 - Member repositories:逐个添加代理和本地仓库到右边成员列表。hosted 库放最上面。
点击创建完成。
创建 Secret
K8s 中使用私有仓库需要指定仓库用户名和密码。先创建 Secret:
[root@k8s-101 ~]# kubectl create secret docker-registry nexussecret --docker-server=192.168.1.253:10007 --docker-username='admin' --docker-password='9pFmd,C@DP2hZJ*O' --namespace=local
[root@k8s-101 ~]# kubectl get secret -n local
用打补丁的方式,把 Secret 分配给不同命名空间默认 SA,这样在发布文件中不需要指定 imagePullSecrets
参数了:
[root@k8s-101 ~]# kubectl patch sa default --namespace=test -p '{"imagePullSecrets": [{"name": "nexussecret"}]}'
创建 Secret(另类)
另外一种更规范创建 Secret 的方式。先登录仓库:
[root@k8s-101 ~]# docker login 192.168.1.253:10007
将用户密码记录文件转为 base64 编码:
[root@k8s-101 ~]# cat /root/.docker/config.json|base64 -w 0
ewoJImF1dGhzIjogewoJCSIxOTIuMTY4LjEuMjUzOjEwMDA3IjogewoJCQkiYXV0aCI6ICJZV1J0YVc0Nk9YQkdiV1FzUTBCRVVESm9Xa29xVHc9PSIKCQl9LAoJCSIxOTIuMTY4LjEuMjUzOjEwMDA4IjogewoJCQkiYXV0aCI6ICJZV1J0YVc0Nk9YQkdiV1FzUTBCRVVESm9Xa29xVHc9PSIKCQl9LAoJCSJkb2NrZXIuemhvbmdtZWl6aHV6YW8uY29tIjogewoJCQkiYXV0aCI6ICJZV1J0YVc0Nk9YQkdiV1Fz
再建立 Secret 将登录信息储存:
[root@k8s-101 ~]# vi secret-login.yaml
apiVersion: v1
kind: Secret
metadata:
name: dockerlogin
namespace: dev
data:
.dockerconfigjson: ewoJImF1dGhzIjogewoJCSIxOTIuMTY4LjEuMjUzOjEwMDA3IjogewoJCQkiYXV0aCI6ICJZV1J0YVc0Nk9YQkdiV1FzUTBCRVVESm9Xa29xVHc9PSIKCQl9LAoJCSIxOTIuMTY4LjEuMjUzOjEwMDA4IjogewoJCQkiYXV0aCI6ICJZV1J0YVc0Nk9YQkdiV1FzUTBCRVVESm9Xa29xVHc9PSIKCQl9LAoJCSJkb2NrZXIuemhvbmdtZWl6aHV6YW8uY29tIjogewoJCQkiYXV0aCI6ICJZV1J0YVc0Nk9YQkdiV1Fz
type: kubernetes.io/dockerconfigjson
之后通过 imagePullSecrets
参数来指定使用。
配置 Maven 仓库
Maven 仓库可以按照实际情况,建立多个 hosted 和 group 库,来区分不同构建环境。
建立 Blob 库
创建一个 Maven 专用 Blob 库:
- Type:选择
file
。 - Name:输入
maven-blob
。
点击保存完成。
建立 Hosted 库
在 Repositories 页面点击新建,类型选择 maven2(hosted)
:
- Name:输入
maven2-hosted-local
。 - Version policy:选择
Mixed
。 - Blob store:选择
maven-blob
。 - Deployment policy:选择
Allow redeploy
来允许重复发布。
点击创建完成。
建立 Proxy 库
在 Repositories 页面点击新建,类型选择 maven2(proxy)
:
- Name:输入
maven2-proxy-aliyun
。 - Remote storage:填阿里云镜像仓库地址
https://maven.aliyun.com/repository/public
。 - Blob store:选择
maven-blob
。
点击创建完成。同理创建一个 maven 官方库代理 maven2-proxy-maven
,地址为 https://repo1.maven.org/maven2/
。
建立 Group 库
在 Repositories 页面点击新建,类型选择 maven2(group)
:
- name:输入
maven2-group-local
。 - Version Policy:选择
Mixed
混合。 - Blob store:选择
maven-blob
。 - Member repositories:将对应仓库以 hosted 库在前,proxy 库在后的方式加入到右边列表。
点击创建完成。
配置 npm 仓库
当构建前端项目的时候,常常会指定镜像地址:--registry=https://registry.npm.taobao.org
,使用私服做代理可以节约流量和时间。
建立 Blob 库
创建一个 npm 专用 Blob 库:
- Type:选择
file
。 - Name:输入
npm-blob
。
点击保存完成。
建立 Hosted 库
在 Repositories 页面点击新建,类型选择 npm(hosted)
:
- Name:输入仓库名
npm-hosted-local
。 - Blob store:选择仓库
npm-blob
。 - Deployment policy:选择
Allow redeploy
允许重复发布。
点击创建完成。
建立 Proxy 库
在 Repositories 页面点击新建,类型选择 npm(proxy)
:
- Name:输入
npm-proxy-npm
。 - Remote storage:填官方仓库地址
https://registry.npmjs.org
。 - Blob store:选择
npm-blob
。
点击创建完成。另外建立一个指向淘宝镜像站 https://registry.npm.taobao.org
的代理仓库 npm-proxy-taobao
。
建立 Group 库
在 Repositories 页面点击新建,类型选择 npm(group)
:
- name:输入
npm-group-local
。 - Blob store:选择
npm-blob
。 - Member repositories:将左侧仓库以 hosted 库在前的方式加入到右边列表。
点击创建完成。
清理策略
私有仓库如果不及时清理,空间会越来越大。在 Cleanup Policies 中,可以建立针对仓库的清理策略。这里只清理 Jar 包:
- 名称:输入名称为
maven2-clean
。 - 格式:选择格式为
maven2
。 - 清理标准:设置清理策略 Component Usage 为 7 天内未被下载。
点击保存完成。返回仓库列表,根据需要对 Hosted 库进行策略配置。
清理任务
清理任务同样可以进行清理操作。在 System > Tasks 中建立清理 maven2 的任务:
- 类型选择(Select a Type):选择
Maven - Delete SNAPSHOT
。 - 任务名称(Task name):输入
clean-maven2-hosted-local
。 - 仓库(Repository):选择
maven2-hosted-local
。 - 最小快照数(Minimum snapshot count):设置为保留 2 个副本。
- 快照保留期限(Snapshot retention days):保留多少天内的包,设置为 0。
- 任务频率(Task frequency):任务运行频率选择每天 01:45 运行。
创建一个清理 Blob 仓库的策略,该策略将清理标记为删除的原始存储数据:
- 类型选择(Select a Type):选择
Admin - Compact blob store
,删除未被使用的数据。 - 任务名称(Task name):任务名称设为
clean-maven-blob
。 - Blob 仓库(Repository):选择仓库
maven-blob
。 - 任务频率 Task frequency):任务运行频率选择每天 02:45 运行。
类似地,可以创建 Docker Blob 仓库清理的任务。Docker 镜像不需要设置清除策略。
清理工具
使用第三方开源工具 nexus-cli 对私服中的镜像进行清理。项目地址:Github
下载
在 release 页面下载适用系统版本的可执行程序。添加权限后测试运行:
[root@k8s-101 ~]# mv nexus-cli-v1.1.0-linux-amd64 nexus-cli && chmod 744 nexus-cli
[root@k8s-101 ~]# ./nexus-cli -h
NAME:
Nexus CLI - Manage Docker Private Registry on Nexus
USAGE:
nexus-cli [global options] command [command options] [arguments...]
AUTHORS:
Mohamed Labouardy <mohamed@labouardy.com>
Alexandr Zaytsev <13rentgen@gmail.com>
COMMANDS:
configure Configure Nexus Credentials
image Manage Docker Images
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
登录
使用 nexus-cli 尝试登录私服:
[root@k8s-101 ~]# ./nexus-cli configure
Enter Nexus Host: http://192.168.1.253:8081
Enter Nexus Repository Name: docker-hosted
Enter Nexus Username: admin
Enter Nexus Password: 9pFmd,C@DP2hZJ*O
工具会在当前目录下生成 .credentials
文件:
[root@k8s-101 ~]# cat .credentials
# Nexus Credentials
nexus_host = "http://192.168.1.253:8081"
nexus_username = "admin"
nexus_password = "9pFmd,C@DP2hZJ*O"
nexus_repository = "docker-hosted"
查询
尝试列出所有镜像:
[root@k8s-101 ~]# ./nexus-cli image ls
base/nginx
base/openjdk
base/python
dev/config-admin-web
test/devops-health-check
查询指定镜像的所有标签:
[root@k8s-101 ~]# ./nexus-cli image tags -name test/devops-health-check
e6befa3 created at 2022-08-16 13:55:39 +0000 GMT
def95a6 created at 2022-08-16 15:44:10 +0000 GMT
...
There are 28 images for test/devops-health-check
删除
可以通过 -keep
参数来设置需要保留的版本个数。例如只保留 test/devops-health-check
镜像 5 个最新镜像:
[root@k8s-101 ~]# ./nexus-cli image delete -name test/devops-health-check -keep 5
test/devops-health-check:e6befa3 image will be deleted ...
test/devops-health-check:e6befa3 has been successful deleted
test/devops-health-check:def95a6 image will be deleted ...
脚本
用脚本对所有镜像进行清理,只保留 5 个版本:
[root@k8s-101 ~]# vi clean.sh
for image in `/nexus-cli image ls|grep -vE "Total images"`
do
/nexus-cli image delete -n $image -k 5
done
[root@k8s-101 ~]# chmod 744 clean.sh
发布
在 Gitlab 上新建 nexus-clean
项目,并将所需文件提交上去:
[root@k8s-101 ~]# mkdir nexus-clean
[root@k8s-101 ~]# mv nexus-cli .credentials clean.sh nexus-clean/
在 Jenkins 流水线目录下新建 Dockerfile
:
[root@k8s-101 ~]# mkdir -p /hxz393/local/jenkins/pipeline/devops-nexus-clean
[root@k8s-101 ~]# vi /hxz393/local/jenkins/pipeline/devops-nexus-clean/Dockerfile
FROM 192.168.1.253:10007/alpine:3.14
COPY . /
CMD [ "sh", "/clean.sh" ]
新建 K8s 发布文件,定义为定时任务类型,每小时运行一次:
[root@k8s-101 ~]# vi /hxz393/local/jenkins/pipeline/devops-nexus-clean/Kubefile.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: <APP_NAME>
namespace: <NAME_SPACE>
labels:
app: <APP_NAME>
annotations:
kubernetes.io/change-cause: "<GIT_TAG>"
spec:
schedule: "* */1 * * *"
jobTemplate:
spec:
template:
metadata:
labels:
app: <APP_NAME>
spec:
restartPolicy: OnFailure
containers:
- name: <APP_NAME>
image: <NEXUS_ADDRESS>/hxz393-<NAME_SPACE>/<APP_NAME>:<GIT_TAG>
新建 Jenkins 发布文件:
[root@k8s-101 ~]# vi /hxz393/local/jenkins/pipeline/devops-nexus-clean/Jenkinsfile
pipeline {
agent {
kubernetes {
yamlFile 'local/jenkins/config/docker-pod.yaml'
}
}
environment{
// ###############################手动填写部分###############################
GITLAB_URL='git@gitlab-svc.test:devops/nexus-clean' // Git项目地址
NAME_SPACE="local" // 命名空间
// ###############################自动生成部分###############################
APP_NAME="${JOB_BASE_NAME}" // 项目名
}
stages {
stage('准备工作') {
steps {
script {
if (env.NAME_SPACE == "local") {
echo "将发布到本地环境"
env.GIT_BRANCH_NAME="master" // 分支名
env.NEXUS_ADDRESS='192.168.1.253:10007' // 私服地址
env.NEXUS_ADDRESS_PUSH='192.168.1.253:10008' // 私服上传地址
env.CREDENTIALS_ID='Nexus' // 私服账号
}
else {
echo "不支持的环境"
}
}
sh "cp -r test/jenkins/pipeline/${APP_NAME}/ ../pipeline"
git branch: "${GIT_BRANCH_NAME}", credentialsId: 'Gitlab', url: "${GITLAB_URL}"
sh 'cp -r ../pipeline/* .'
script {
env.GIT_TAG = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() // 提交哈希
env.GIT_COMMIT_MSG = sh (returnStdout: true, script: 'git log -1 --pretty=%B ${GIT_TAG}').trim() // 提交日志
}
sh '''
sed -i "s/<NEXUS_ADDRESS>/${NEXUS_ADDRESS}/g" Kubefile.yaml
sed -i "s/<APP_NAME>/${APP_NAME}/g" Kubefile.yaml
sed -i "s/<NAME_SPACE>/${NAME_SPACE}/g" Kubefile.yaml
sed -i "s/<GIT_TAG>/${GIT_TAG}/g" Kubefile.yaml
'''
echo "代码拉取完成!"
}
}
stage('构建镜像') {
steps {
container('docker') {
withCredentials([usernamePassword(credentialsId: "${CREDENTIALS_ID}", passwordVariable: 'Password', usernameVariable: 'Username')]) {
sh "docker login ${NEXUS_ADDRESS_PUSH} -u ${Username} -p ${Password}"
sh "docker build --progress=plain --no-cache -t ${NEXUS_ADDRESS_PUSH}/nanruan-${NAME_SPACE}/${APP_NAME}:${GIT_TAG} ."
sh "docker push ${NEXUS_ADDRESS_PUSH}/nanruan-${NAME_SPACE}/${APP_NAME}:${GIT_TAG}"
}
}
echo "镜像构建完成!"
}
}
stage('发布应用') {
steps {
container('kubectl') {
sh "kubectl --kubeconfig /hxz393/${NAME_SPACE}/k8s/config/admin.conf apply -f Kubefile.yaml"
}
sh "mkdir -p /hxz393/${NAME_SPACE}/${APP_NAME}/"
sh "cp Dockerfile Kubefile.yaml Jenkinsfile /hxz393/${NAME_SPACE}/${APP_NAME}/"
echo "应用发布完成!"
}
}
}
}
最后是在 Jenkins 中新建同名任务 devops-nexus-clean
,发布后清理任务会保持运行。
测试
测试私服仓库上传下载功能。在 CI/CD 中应用私服请查看 Jenkins 相关主题。
测试 Docker 仓库
测试登录、拉取和上传。
测试登录
先使用 docker login
来测试是否可以登录。使用 IP 登录时候注意:10007 是 docker pull
端口,10008 是 docker push
端口,登陆时候需要分别认证:
[root@k8s-103 ~]# docker login -u 'admin' -p '9pFmd,C@DP2hZJ*O' docker.x2b.net
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
[root@k8s-103 ~]# docker login -u 'admin' -p '9pFmd,C@DP2hZJ*O' 192.168.1.253:10007
[root@k8s-103 ~]# docker login -u 'admin' -p '9pFmd,C@DP2hZJ*O' 192.168.1.253:10008
登录成。测试完记得删除储存的 json 文件 /root/.docker/config.json
。
测试上传
使用 docker tag
修改镜像标签并上传:
[root@k8s-103 ~]# docker tag mongo:5.0.8 docker.x2b.net/mongo:5.0.8
[root@k8s-103 ~]# docker push docker.x2b.net/mongo:5.0.8
[root@k8s-103 ~]# docker tag redis:5.0.14-alpine 192.168.1.253:10008/redis:5.0.14-alpine
[root@k8s-103 ~]# docker push 192.168.1.253:10008/redis:5.0.14-alpine
测试下载
先在另外一台主机尝试拉取刚刚上传的镜像:
[root@k8s-248 ~]# docker pull docker.x2b.net/mongo:5.0.8
[root@k8s-248 ~]# docker pull 192.168.1.253:10007/redis:5.0.14-alpine
再测试拉取本地和仓库中都没有的镜像:
[root@k8s-248 ~]# docker pull docker.x2b.net/rabbitmq
[root@k8s-248 ~]# docker pull 192.168.1.253:10007/httpd
拉取速度甚至比本地直接拉取还要快。从外部仓库下载的镜像储存在私服 docker-proxy 库的 library 目录中。
测试认证
要测试在 K8s 集群中自动拉取镜像是否工作,先删除所有服务器上的 /root/.docker/config.json
文件:
[root@k8s-248 ~]# rm -rf /root/.docker/config.json
用 kubectl run
命令建立一个临时 Pod,测试从私服拉取本地没有的镜像:
[root@k8s-101 ~]# kubectl -n local run testnexus --image=192.168.1.253:10007/ibmcom/busybox:1.30.1 --command -- ls -la
pod/testnexus created
[root@k8s-101 ~]# kubectl -n test describe po testnexus
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 34s default-scheduler Successfully assigned local/testnexus to k8s-103
Normal Pulling 34s kubelet Pulling image "192.168.1.253:10007/ibmcom/busybox:1.30.1"
Normal Pulled 8s kubelet Successfully pulled image "192.168.1.253:10007/ibmcom/busybox:1.30.1" in 25.424388112s
[root@k8s-101 ~]# kubectl -n local logs testnexus
total 16
drwxr-xr-x 1 root root 17 May 5 12:55 .
drwxr-xr-x 1 root root 17 May 5 12:55 ..
-rwxr-xr-x 1 root root 0 May 5 12:55 .dockerenv
drwxr-xr-x 2 root root 12288 Feb 14 2019 bin
[root@k8s-101 ~]# kubectl -n local delete po testnexus
pod "testnexus" deleted
镜像已经成功拉取并运行。
测试 npm 仓库
可以运行一个 npm 容器来测试 npm 仓库功能:
[root@k8s-101 ~]# docker run --rm -it 192.168.1.253:10007/node:14.19.3 bash
npm 命令参考:CSDN 博客
查看状态
查看全局安装模块的目录:
root@4f5ec7bfe603:/# npm root -g
/usr/local/lib/node_modules
查看当前登录 npm 的账户,用来 publish 发布包时使用:
root@4f5ec7bfe603:/# npm whoami
admin
测试登录
测试登录私服:
root@4f5ec7bfe603:/# npm login --registry=http://192.168.1.253:8081/repository/npm-group-local/
Username: admin
Password: 9pFmd,C@DP2hZJ*O
Email: (this IS public) hxz393@x2b.net
Logged in as admin on http://192.168.1.253:8081/repository/npm-group-local/.
root@4f5ec7bfe603:/# npm login --registry=http://192.168.1.253:8081/repository/npm-hosted-local/
添加用户。在发布前需要添加用户到 hosted 仓库:
root@4f5ec7bfe603:/# npm adduser --registry=http://192.168.1.253:8081/repository/npm-hosted-local/
配置文件
获取全局配置内容:
root@4f5ec7bfe603:/# npm config list -l
; cli configs
long = true
metrics-registry = "https://registry.npmjs.org/"
scope = ""
user-agent = "npm/6.14.17 node/v14.19.3 linux x64"
获取 npm 缓存目录和仓库地址。全局配置中的其他选项都可以通过 npm config get
来获取:
root@4f5ec7bfe603:/# npm config get cache
/root/.npm
root@4f5ec7bfe603:/# npm config get registry
https://registry.npmjs.org/
npm 获取配置的优先级高低顺序:
-
命令行参数,如
--registry=
。 -
环境变量。
-
用户配置文件。在使用命令进行属性配置时被写入的配置文件。路径如下:
root@4f5ec7bfe603:/# npm config get userconfig /root/.npmrc root@4f5ec7bfe603:/# cd && cat .npmrc //192.168.1.253:8081/repository/npm-group-local/:_authToken=NpmToken.7285beb7-2f92-3295-8ccf-8020132d6232 //192.168.1.253:8081/repository/npm-hosted-local/:_authToken=NpmToken.7285beb7-2f92-3295-8ccf-8020132d6232 registry=http://192.168.1.253:8081/repository/npm-group-local/
-
全局配置文件。需要手动建立,或在使用命令配置时加入
--global
参数来写入。路径如下:root@4f5ec7bfe603:~# npm config get globalconfig /usr/local/etc/npmrc
-
自带配置文件。一般是
/usr/local/lib/node_modules/npm/node_modules
下面的npmrc
文件。 -
默认配置参数。
配置仓库
配置镜像仓库地址为私服 group 库的地址:
root@4f5ec7bfe603:~# npm config set registry http://192.168.1.253:8081/repository/npm-group-local/
发布时指定地址为 hosted 库的地址。只在本条命令有效:
root@4f5ec7bfe603:~# npm publish --registry=http://192.168.1.253:8081/repository/npm-hosted-local/
测试打包
建立项目目录后,先生成 package.json
文件:
root@4f5ec7bfe603:~# mkdir xyz-hxz393 && cd xyz-hxz393
root@4f5ec7bfe603:~/xyz-hxz393# npm init -y
Wrote to /root/xyz-hxz393/package.json:
{
"name": "xyz-hxz393",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
生成 index.js
文件:
root@4f5ec7bfe603:~/xyz-hxz393# echo "module.exports = 907;">index.js
试着发布包:
root@4f5ec7bfe603:~/xyz-hxz393# npm publish --registry=http://192.168.1.253:8081/repository/npm-hosted-local/
npm notice
npm notice package: xyz-hxz393@1.0.0
npm notice === Tarball Contents ===
npm notice 22B index.js
npm notice 224B package.json
npm notice === Tarball Details ===
npm notice name: xyz-hxz393
npm notice version: 1.0.0
npm notice package size: 302 B
npm notice unpacked size: 246 B
npm notice shasum: 609171152a95a18623d918869049f5bbcb57aad4
npm notice integrity: sha512-x1KcX1CvXs9cn[...]fmr7Kx1Lr2d9Q==
npm notice total files: 2
npm notice
+ xyz-hxz393@1.0.0
登录 Nexus 仓库网页端,浏览 npm-hosted-local 仓库,此处已经有了上传的包 xyz-hxz393/-/xyz-hxz393-1.0.0.tgz
。
测试下载
测试从私服拉取上传的包,需要重启新的容器:
root@4f5ec7bfe603:~/xyz-hxz393# exit
[root@k8s-101 ~]# docker run --rm -it 192.168.1.253:10007/node:14.19.3 bash
root@26a681c3ecc3:/# npm config get registry
https://registry.npmjs.org/
root@26a681c3ecc3:/# npm config set registry http://192.168.1.253:8081/repository/npm-group-local/
root@26a681c3ecc3:/# cd && npm adduser --registry=http://192.168.1.253:8081/repository/npm-group-local/
Username: admin
Password:
Email: (this IS public) hxz393@x2b.net
Logged in as admin on http://192.168.1.253:8081/repository/npm-group-local/.
下载私有包:
root@26a681c3ecc3:~# npm -loglevel info install xyz-hxz393
+ xyz-hxz393@1.0.0
added 1 package in 1.83s
npm timing npm Completed in 2017ms
npm info ok
下载公共包:
root@26a681c3ecc3:~# npm -d install bootstrap
+ bootstrap@5.2.3
added 1 package from 2 contributors in 3.444s
1 package is looking for funding
run `npm fund` for details
npm timing npm Completed in 3650ms
npm info ok
查询包下载目录:
root@26a681c3ecc3:~# find / -name bootstrap
/root/node_modules/bootstrap
至此功能测试完毕。
管理
Nexus 在 K8s 集群中的管理。
小版本升级
如果没有大版本跳跃,可以直接升级。例如,从版本 3.38.1
升级到最新的 3.54.1
版本,先删除旧版本,修改镜像版本再重新部署:
[root@k8s-101 ~]# kubectl delete -f /hxz393/${APPENV}/${APPNAME}/apply/
statefulset.apps "nexus-sts" deleted
service "nexus-svc" deleted
service "nexus-svc-docker-pull" deleted
service "nexus-svc-docker-push" deleted
[root@k8s-101 ~]# vi /hxz393/${APPENV}/${APPNAME}/apply/nexus-sts.yaml
...
containers:
- name: nexus
image: sonatype/nexus3:3.54.1
imagePullPolicy: IfNotPresent
...
[root@k8s-101 ~]# kubectl apply -f /hxz393/${APPENV}/${APPNAME}/apply/
大版本升级
按照官方指南,2.x 版本不能直接升级到 3.x 版本,需要先升级到 2.x 的最后一个版本(下载地址),然后再升级到 3.x 的最新版本:官网教程
数据迁移
升级迁移可能需要很长时间,可以先运行一个 3.x 版本的 Nexus 副本,新建 proxy 库指向老仓库地址。下面是一个示例:
- 新建 maven2 proxy 仓库。
- Name:输入
maven2-proxy-old-maven-public
。 - Remote storage:填旧仓库组地址
http://192.168.1.55:8081/repository/maven-public/
。 - Blob store:选择
maven-blob
。 - HTTP Authentication:输入旧仓库用户名和密码。
- 创建完毕后,修改现在的 group 库
maven2-group-local
配置,将库maven2-proxy-old-maven-public
加入到激活列表。
这样配置后,通过新库 maven2-group-local
拉取到旧库的组件,会存入到新库 maven-blob
中。
升级步骤
升级流程简单描述如下:
- 先部署一个全新的 3.x 版本 Nexus 容器。
- 在 2.x 版本 Nexus 主页,点击 System > Capabilities > Create capability 来新建 Upgrade:Agent。配置 Token 为
123456
。 - 在 3.x 版本 Nexus 主页,点击 System > Capabilities > Create capability 来新建 Upgrade。
- 在 3.x 版本 Nexus 主页 System 下面多出一个 Upgrade 菜单,点击运行升级向导。在 URL 中输入旧仓库地址,例如:
http://192.168.2.110:8081/nexus
。在 Access Token 中输入123456
。继续点击下一步,选择空白的目标仓库。再下一步,选择旧 Nexus 的源仓库。最后点击开始等待同步完成。