巧用CF的Workers完美解决Docker镜像国内无法拉取
巧用CF解决Docker hub镜像国内无法拉取
1.前言简述
最近 Docker Hub 仓库国内无法拉取镜像的事情,在安全、运维、开发圈里都在被火热的讨论,也有人提出过解决方案,例如,前面作者使用 Github Action + 阿里云镜像服务 ACR 来实现Docker Hub 镜像的同步拉取(《运维 Tips | Docker Hub 仓库国内无法拉取镜像,如何应对?》),但是,这个方案需要每次更新镜像时都要到Github中进行触发执行Github Action,过程还是过于繁琐,而且对于国内用户来说,访问 Github 速度比较慢。
那还有其他稍微完美一点的解决方案吗,作者都说到这里了当然有了!
今天,作者在某特(X)上看到一个有趣的方案,使用 Cloudflare 提供的免费 Worker 服务,巧妙的来解决 Docker Hub 镜像源地址国内无法拉取的问题,作者实践后发现确实是目前比较完美的方案。
下面,就来看看作者是如何利用 cloudflare 的 Worker 服务,来解决 Docker Hub 镜像源国内无法拉取的问题。
2.前置需求
Domain 域名:域名注册国内的腾讯云
、阿里云、百度云、华为云都是可以的,如果不想花钱注册的也可去注册一个免费的http://eu.org域名(PS: 这也是作者实践使用的域名),申请教程:《免费注册申请永久的http://eu.org顶级域名创建属于自己的域名》, 注册视频:
- 注册地址:https://nic.eu.org
weiyigeek.top-免费http://eu.org域名图
Cloudflare 账号:注册相信大家都能搞定,这里就不再赘述了,选择免费计划(Free Plans)即可(适用于非关键任务个人或业余项目)链接直达:https://dash.cloudflare.com/sign-up?pt=f
weiyigeek.top-注册cloudflare图
3.实践之路
- Step 1.登录 Cloudflare 账号,进入到主页,点击网站,添加站点 w*.eu.org (PS: 由于Cloudflare Workers有每日请求数限制10w,所以这里作者将实践域名打码,作者只针对赞赏以及付费用户开放,其他看友请安装前置需求准备好对应环境,按照后续实践自行部署自己私有镜像拉取域名即可),同样选择免费计划(0/月)即可。
weiyigeek.top-添加站点图
- Step 2.登录 http://nic.eu.org 将添加到 Cloudflare 上的 w*.http://eu.org 域名进行激活,即将 http://eu.org 域名注册面板,将域名解析服务器
指向 Cloudflare 提供的名称服务器。
bayan.ns.cloudflare.com
vita.ns.cloudflare.com
weiyigeek.top-更改域名DNS域名解析服务器图
- Step 3.回到 Cloudflare 主页,点击Workers 和 Pages,然后创建应用程序, 命名为Hub, 按照提示点击完成即可。
weiyigeek.top-创建Workers应用程序图
- Step 4.创建完成后,点击hub进入到项目业中,再点击编辑代码,将代码替换为如下内容,并且保存即可。(PS: 若显示有问题建议在UP主公众号文章中进行获取)
```js
‘usestrict’
consthub_host=’registry-1.docker.io’
constauth_url=’https://auth.docker.io‘
//请将hub.weiyigeek.eu.org替换为自己的域名
constworkers_url=’https://hub.weiyigeek.eu.org‘
constPREFLIGHT_INIT={
status:204,
headers:newHeaders({
‘access-control-allow-origin’:’*’,
‘access-control-allow-methods’:’GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS’,
‘access-control-max-age’:’1728000’,
}),
}
functionmakeRes(body,status=200,headers={}){
headers[‘access-control-allow-origin’]=’*’
returnnewResponse(body,{status,headers})
}
functionnewUrl(urlStr){
try{
returnnewURL(urlStr)
}catch(err){
returnnull
}
}
addEventListener(‘fetch’,e=>{
constret=fetchHandler(e)
.catch(err=>makeRes(‘cfworkererror:\n’+err.stack,502))
e.respondWith(ret)
})
asyncfunctionfetchHandler(e){
constgetReqHeader=(key)=>e.request.headers.get(key);
leturl=newURL(e.request.url);
if(url.pathname===’/token’){
lettoken_parameter={
headers:{
‘Host’:’auth.docker.io’,
‘User-Agent’:getReqHeader(“User-Agent”),
‘Accept’:getReqHeader(“Accept”),
‘Accept-Language’:getReqHeader(“Accept-Language”),
‘Accept-Encoding’:getReqHeader(“Accept-Encoding”),
‘Connection’:’keep-alive’,
‘Cache-Control’:’max-age=0’
}
};
lettoken_url=auth_url+url.pathname+url.search
returnfetch(newRequest(token_url,e.request),token_parameter)
}
url.hostname=hub_host;
letparameter={
headers:{
‘Host’:hub_host,
‘User-Agent’:getReqHeader(“User-Agent”),
‘Accept’:getReqHeader(“Accept”),
‘Accept-Language’:getReqHeader(“Accept-Language”),
‘Accept-Encoding’:getReqHeader(“Accept-Encoding”),
‘Connection’:’keep-alive’,
‘Cache-Control’:’max-age=0’
},
cacheTtl:3600
};
if(e.request.headers.has(“Authorization”)){
parameter.headers.Authorization=getReqHeader(“Authorization”);
}
letoriginal_response=awaitfetch(newRequest(url,e.request),parameter)
letoriginal_response_clone=original_response.clone();
letoriginal_text=original_response_clone.body;
letresponse_headers=original_response.headers;
letnew_response_headers=newHeaders(response_headers);
letstatus=original_response.status;
if(new_response_headers.get(“Www-Authenticate”)){
letauth=new_response_headers.get(“Www-Authenticate”);
letre=newRegExp(auth_url,’g’);
new_response_headers.set(“Www-Authenticate”,response_headers.get(“Www-Authenticate”).replace(re,workers_url));
}
if(new_response_headers.get(“Location”)){
returnhttpHandler(e.request,new_response_headers.get(“Location”))
}
letresponse=newResponse(original_text,{
status,
headers:new_response_headers
})
returnresponse;
}
functionhttpHandler(req,pathname){
constreqHdrRaw=req.headers
//preflight
if(req.method===’OPTIONS’&&
reqHdrRaw.has(‘access-control-request-headers’)
){
returnnewResponse(null,PREFLIGHT_INIT)
}
letrawLen=’’
constreqHdrNew=newHeaders(reqHdrRaw)
constrefer=reqHdrNew.get(‘referer’)
leturlStr=pathname
consturlObj=newUrl(urlStr)
/*@type{RequestInit}/
constreqInit={
method:req.method,
headers:reqHdrNew,
redirect:’follow’,
body:req.body
}
returnproxy(urlObj,reqInit,rawLen,0)
}
asyncfunctionproxy(urlObj,reqInit,rawLen){
constres=awaitfetch(urlObj.href,reqInit)
constresHdrOld=res.headers
constresHdrNew=newHeaders(resHdrOld)
//verify
if(rawLen){
constnewLen=resHdrOld.get(‘content-length’)||’’
constbadLen=(rawLen!==newLen)
if(badLen){
returnmakeRes(res.body,400,{
‘—error’:badlen:${newLen},except:${rawLen},
‘access-control-expose-headers’:’—error’,
})
}
}
conststatus=res.status
resHdrNew.set(‘access-control-expose-headers’,’*’)
resHdrNew.set(‘access-control-allow-origin’,’*’)
resHdrNew.set(‘Cache-Control’,’max-age=1500’)
resHdrNew.delete(‘content-security-policy’)
resHdrNew.delete(‘content-security-policy-report-only’)
resHdrNew.delete(‘clear-site-data’)
returnnewResponse(res.body,{
status,
headers:resHdrNew
})
}
```
weiyigeek.top-Workers反向代理脚本图
- Step 5.点击右上角的部署即可完成部署,然后回到 hub 项目页,点击设置,点击触发器,再点击添加路由,输入hub.weiyigeek.eu.org/路由(注意末尾的/是不可缺少的),区域选择前面添加的weiyigeek.eu.org即可。
weiyigeek.top-为workers添加指定路由图
- Step 6.回到cloudflare主页,点击前面添加的weiyigeek.eu.org域名,点击设置,点击DNS,点击添加A记录,设置名称为hub,地址为8.8.8.8(随意填),但是一定要启用代理哟。
weiyigeek.top-添加DNS解析记录图
- Step 7.配置完成后,在我们的Linux服务器上,执行下述命令验证站点是否正常,以及是否可以正常拉取nginx:latest镜像。
验证
404pagenotfound
拉取
- dockerpullhub.weiyigeek.eu.org/library/nginx:latest
查看
- dockerimages|grep”eu.org”
- weiyigeek.top-使用cf的workers服务拉取镜像图
至此,在Cloudflare 使用 Workers 服务,实现国内 Docker hub 镜像的拉取
