使用Nexus3 + Nginx搭建Jenkins插件代理仓库
前言
因为公司内部环境无法上网,搭建Jenkins环境后,存在如下情况:
- 默认插件仓库偶尔会出现网络连接异常,无法下载的问题
- 因为是内网环境,只能在申请搭建环境的这几天有权限连外网(在一个单独隔离的环境中),无法一次性将以后可能需要的所有插件都更新
环境说明
Sonatype Nexus : OSS 3.30.0-01
Jenkins: 2.286
Nginx: 1.19.9
操作系统: Windows 10 (因先自行尝试,所以使用Windows、Linux环境原理其实一样)
问题一、解决方案
-
使用 Jenkins 中文社区 的 更新中心镜像: https://updates.jenkins-zh.cn/update-center.json
PS: 具体说明可查看 中文社区官网: https://jenkins-zh.cn/
今天(2021-04-02)尝试用这个的时候报错(应该是资源问题,待他们修复即可):
检查更新中心: IOException: Server returned HTTP response code: 403 for URL: https://cdn.jsdelivr.net/gh/jenkins-zh/update-center-mirror/tsinghua/dynamic-2.283/update-center.json 时发生错误
问题二、解决方案(包括问题一)
为了解决以上问题,想到可以将 Jenkins 插件仓库用Nexus代理并缓存(就像Maven仓库一样),了解到Nexus3 添加支持 RAW类型仓库,于是做了如下尝试:
1、Nexus 添加代理仓库,代理国内清华镜像源
清华镜像源地址: https://mirrors.tuna.tsinghua.edu.cn/jenkins/
1-1、Nexus 添加 Jenkins Raw Proxy仓库:
1-2、尝试请求文件,验证 Nexus 是否代理成功
请求链接: http://localhost:8081/repository/jenkins/TIME
文件可正常下载,表示代理配置成功:
Nexus3 上文件亦存在:
2、修改Jenkins 更新中心镜像地址
尝试方案一(验证失败 PASS)
从Jenkins 更新中心镜像地址配置https://updates.jenkins.io/update-center.json
,配置文件为Json。 因内部插件下载地址都是从此文件读取,所以第一个想法就是把这个文件地址改了不就成了。
说做就做,先将此文件下载到本地,修改内容如下:
- 因为后续不能连外网,所以检测连接的地址也改成Nexus服务地址
"connectionCheckUrl":"http://www.google.com/"
修改为"connectionCheckUrl":"http://localhost:8081/"
- 将所有带
.hpi
结尾的URL前缀https://updates.jenkins.io/download/plugins/
替换为http://localhost:8081/repository/jenkins/plugins/
(共1805个)
因 Jenkins 升级站点,不支持本地文件路径,只好在 Nexus3上新建一个名为 jenkins-hosted 的 Raw-hosted 仓库,并上传修改后的 update-center.json, 新的站点URL为: http://localhost:8081/repository/jenkins-hosted/update-center.json
将Jenkins修改升级站点URL, 点击提交、立即获取,(Oh my God…报错了):
检查更新中心: SHA-512 digest mismatch: expected=cbf60c71f19642b29fd3a385cea599200ccc0884054a98398018cfa6f4b02246e5c761de5290dde7bf3659b556325c029b9a9bcba88f931092a484815e531627 in 'update site 'default'' 时发生错误
原来Jenkins 会校验站点证书,看来这方案不行。。。
尝试方案二 ( 验证成功)
参考《Jenkins配置国内插件下载代理 https://blog.51cto.com/13812615/2505580》使用 Nginx 代理;在不修改原 update-center.json内容情况下,直接修改hosts文件将对应域名指定至本地。
但之前都是 Http 协议,可以直接代理转发就行; 现在都是 https, 所以需要生成站点证书配置。
生成证书 (使用 openssl )
进入Nginx目录,新建 ssl 文件夹,执行以下命令:
### 生成 Key 文件,输入对应密码
openssl genrsa -des3 -out jenkins-io.key 1024
### 生成 csr 文件
openssl req -new -key .\jenkins-io.key -out .\jenkins-io.csr
#此步注意, 在 Common Name 时要填写, updates.jenkins.io, 否则在连接时证书验证会报 No name matching updates.jenkins.io found
Common Name (e.g. server FQDN or YOUR name) []:updates.jenkins.io
### 执行以下两步,去除密码(否则 Nginx 启动要求输入密码)
copy .\jenkins-io.key .\jenkins-io.key.org
openssl rsa -in .\jenkins-io.key.org -out .\jenkins-io.key
### 生成证书文件
openssl x509 -req -days 365 -in .\jenkins-io.csr -signkey .\jenkins-io.key -out .\jenkins-io.crt
执行完成后,文件如下图所示:
修改 hosts
127.0.0.1 www.google.com
127.0.0.1 updates.jenkins.io
配置 Nginx.conf 添加 https 域名代理
# 默认的 www.google.com 验证网络连接,也代理至 Nexus3 服务
server {
listen 80;
server_name www.google.com;
location / {
proxy_pass http://localhost:8081/;
}
}
server {
listen 443 ssl;
server_name updates.jenkins.io;
# ssl 证书配置
ssl_certificate D:/CI/nginx/nginx-1.19.9/ssl/jenkins-io.crt;
ssl_certificate_key D:/CI/nginx/nginx-1.19.9/ssl/jenkins-io.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location /download/ {
proxy_pass http://localhost:8081/repository/jenkins/;
}
# 此处原update-center.json访问,使用本地路径(也可上传至 Nexus3 Raw仓库)
location /update-center.json {
root D:/CI/Jenkins/;
}
}
验证 Jenkins 是否可正常连接下载插件
将 Jenkins 升级站点URL 还原为 https://updates.jenkins.io/update-center.json ,点击提交、立即检查(…天啊,还是报错)
检查更新中心: SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 时发生错误
于是又各种查资料,发现是因为生成的证书没有被信任的问题,试着将 jenkins-io.crt 导入系统,发现还是不行。忽然想到 Jenkins 是Java 程序,应该将证书导入 jre 才可以;于是执行以下命令:
PS D:\CI\nginx\nginx-1.19.9\ssl> keytool.exe -importcert -file .\jenkins-io.crt -keystore D:\Env\Java\corretto-1.8.0_275\jre\lib\security\cacerts -storepass changeit
添加后(重新启动Jenkins),再次验证 Jenkins , 已成功连接并可正常下载插件,如下图:
Nexus3 上 Jenkins 仓库也已有对应插件文件:
至此, Nexus3 代理 Jenkins 插件仓库的所有环境及配置已搭建完毕。
3、将插件仓库所有可用插件,都缓存至 Nexus3
nexus 仓库缓存的原理需要有URL请求去请求对应资源,才会将文件下载缓存至Nexus私服。
既然如此,于是想到个较简单也是比较笨的办法,挨个请求一遍。
python 脚本遍历请求
既然所有插件的下载URL路径,都在update-center.json里,那就写个简单的 python 脚本,遍历的请求一遍就好了。
花了几分钟把脚本写好,等了大概半小时终于将所有插件都缓存到 Nexus私服了。
以下为 python 脚本:
# coding=utf-8
import re, urllib.request, time, _thread
def requestUrl(idx, url, threadName):
try:
urllib.request.urlopen(url)
except BaseException as e:
print('Error: 待 18 秒后,继续尝试 ' + threadName + ' -- ' + str(idx) + ' -- ' + url + ': ' + str(e))
time.sleep(18)
requestUrl(idx, url, threadName)
def requestList(urlList, startIdx, step, threadName):
for idx, url in enumerate(urlList):
if idx < startIdx or (idx - startIdx) % step != 0:
continue
# Url 替换为 Nexus 的路径
nurl = url.replace('https://updates.jenkins.io/download/', 'http://localhost:8081/repository/jenkins/')
print(threadName + ' -- ' + str(idx) + ' -- ' + nurl)
requestUrl(idx, nurl, threadName)
time.sleep(1)
print(threadName + '============= 执行完成!!!')
# 读取文件内容
file1 = open('../update-center.json', 'r', encoding='utf-8')
txt = file1.read()
# 正则匹配 URL
links = re.findall('"url":"(\S*.hpi)"', txt)
try:
_thread.start_new_thread(requestList, (links, 0, 3, 'thread-1'))
_thread.start_new_thread(requestList, (links, 1, 3, 'thread-2'))
_thread.start_new_thread(requestList, (links, 2, 3, 'thread-3'))
except BaseException as e:
print('线程启动异常', str(e))
while 1:
pass
离线完毕,可见Nexus3 上已缓存文件大小: 5 GB
离线验证,Nexus 私服是否可用
如下图所示, Jenkins 可正常下载新插件:
以上代码工程,已上传Gitee.com: https://gitee.com/xhal/jenkins-mirror
有需要的小伙伴可以自行 clone 试试。
参考资料
- Sonatype Raw 仓库类型 https://help.sonatype.com/repomanager3/formats/raw-repositories
- Windows下Nginx配置SSL实现Https访问(包含证书生成) https://blog.youkuaiyun.com/qq_34924407/article/details/80544253
- Jenkins配置国内插件下载代理 https://blog.51cto.com/13812615/2505580