在 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 的源仓库。最后点击开始等待同步完成。