基于 Cloudflare Workers 构建 Docker Registry 代理服务
边缘代理架构设计
在容器化工作流中,直接访问公共镜像仓库常受限于区域网络策略,导致拉取速率低下或连接中断。通过部署边缘计算节点作为中转层,可有效解决此类问题。Cloudflare Workers 提供基于 V8 引擎的无服务器运行环境,具备天然的全球分布式特性,非常适合作为轻量级容器镜像代理网关。
该架构的核心优势在于:
- 请求就近路由:客户端请求自动指向地理距离最近的边缘节点,大幅降低网络延迟。
- 免运维基础设施:无需预配虚拟机或容器集群,代码随请求触发执行,支持自动弹性扩缩容。
- 按需计量计费:费用完全基于实际处理的请求数量与执行时长,适合流量波动较大的镜像分发场景。
部署 Worker 实例
登录 Cloudflare 控制台,进入 Workers & Pages 模块,创建新的 Worker 服务。分配具有语义的名称(例如 docker-mirror),完成初始部署后跳转至在线代码编辑器。
核心代理逻辑实现
将以下 JavaScript 代码替换至编辑器中。该脚本实现了上游路由分发、鉴权协商、请求头重写及静态资源拦截等核心功能。
// 定义上游注册表与认证端点
const DEFAULT_REGISTRY_HOST = 'registry-1.docker.io';
const AUTH_SERVER_URL = 'https://auth.docker.io';
let WORKER_BASE_URL = '';
// 路由映射配置:依据子域前缀选择目标上游
const UPSTREAM_MAP = {
'quay': 'quay.io',
'gcr': 'gcr.io',
'k8s-gcr': 'k8s.gcr.io',
'k8s': 'registry.k8s.io',
'ghcr': 'ghcr.io',
'cloudsmith': 'docker.cloudsmith.io',
'test': 'registry-1.docker.io'
};
function resolveUpstream(targetHost) {
const prefix = targetHost.split('.')[0];
const mappedHost = UPSTREAM_MAP[prefix] || DEFAULT_REGISTRY_HOST;
const isDefaultRoute = !UPSTREAM_MAP[prefix];
return { host: mappedHost, isDefault: isDefaultRoute };
}
// 响应构建辅助函数
function buildResponse(content, statusCode = 200, extraHeaders = {}) {
const hdrs = new Headers(extraHeaders);
hdrs.set('Access-Control-Allow-Origin', '*');
return new Response(content, { status: statusCode, headers: hdrs });
}
// 校验 UUID 格式
function checkUuidFormat(str) {
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(str);
}
// 生成占位 HTML
function generatePlaceholderHtml() {
return '<!DOCTYPE html><html><head><title>Mirror Service</title></head><body><h1>Proxy Active</h1></body></html>';
}
export default {
async fetch(request, env, ctx) {
const requestUrl = new URL(request.url);
const clientAgent = (request.headers.get('User-Agent') || '').toLowerCase();
const currentDomain = requestUrl.hostname;
WORKER_BASE_URL = `https://${currentDomain}`;
// 动态合并环境变量中的拦截列表
let activeBlockedAgents = ['netcraft'];
if (env.UA_FILTER) {
const extra = env.UA_FILTER.split(',').map(s => s.trim()).filter(Boolean);
activeBlockedAgents = [...activeBlockedAgents, ...extra];
}
const routeConfig = resolveUpstream(currentDomain);
const targetRegistry = routeConfig.host;
// 拦截指定 UA 请求
if (activeBlockedAgents.some(agent => clientAgent.includes(agent))) {
return buildResponse(generatePlaceholderHtml(), 200, { 'Content-Type': 'text/html; charset=utf-8' });
}
const reqPath = requestUrl.pathname;
const isSpecialPath = [
reqPath === '/', reqPath === '/favicon.ico',
reqPath.includes('/_catalog'), reqPath.includes('/v2/_'),
reqPath.includes('/v2/user'), reqPath.includes('/v2/orgs'),
reqPath.includes('/search'), reqPath.includes('/source')
].some(Boolean);
// 处理根路径、特殊 API 或 UUID 请求
if ((isSpecialPath || checkUuidFormat(reqPath.split('/')[1] || '')) && (routeConfig.isDefault || currentDomain.startsWith('docker'))) {
if (env.REDIRECT_TARGET) {
return Response.redirect(env.REDIRECT_TARGET, 302);
}
if (env.FALLBACK_URL) {
if (env.FALLBACK_URL.toLowerCase() === 'nginx') {
return buildResponse(generatePlaceholderHtml(), 200, { 'Content-Type': 'text/html; charset=utf-8' });
}
return fetch(new Request(env.FALLBACK_URL, request));
}
// 转发至官方 Hub 首页
const hubRequest = new Request(`https://registry.hub.docker.com${reqPath}${requestUrl.search}`, {
method: request.method,
headers: new Headers({ ...Object.fromEntries(request.headers), 'Host': 'registry.hub.docker.com' }),
body: ['GET', 'HEAD'].includes(request.method) ? null : await request.blob(),
redirect: 'follow'
});
return fetch(hubRequest);
}
// 处理 URL 编码规范化 (%3A -> %3Alibrary/)
let normalizedUrl = new URL(requestUrl.toString());
if (normalizedUrl.search.includes('%3A') && !normalizedUrl.search.includes('%2F')) {
normalizedUrl.search = normalizedUrl.search.replace(/%3A(?=.*?&)/, '%3Alibrary%2F');
}
// 代理 OAuth Token 认证请求
if (normalizedUrl.pathname.includes('/token')) {
const authRequest = new Request(`${AUTH_SERVER_URL}${normalizedUrl.pathname}${normalizedUrl.search}`, request);
authRequest.headers.set('Host', AUTH_SERVER_URL.replace('https://', ''));
return fetch(authRequest);
}
// 自动补全 Docker Hub 默认命名空间
if (/^\/v2\/[^/]+\/[^/]+\/[^/]+$/.test(normalizedUrl.pathname) && !normalizedUrl.pathname.startsWith('/v2/library')) {
normalizedUrl.pathname = normalizedUrl.pathname.replace(/^\/v2\//, '/v2/library/');
}
// 切换目标主机名
normalizedUrl.hostname = targetRegistry;
// 构建代理请求头
const proxyHeaders = new Headers(request.headers);
proxyHeaders.set('Host', targetRegistry);
proxyHeaders.set('Connection', 'keep-alive');
proxyHeaders.set('Cache-Control', 'max-age=0');
const proxyRequest = new Request(normalizedUrl, {
method: request.method,
headers: proxyHeaders,
body: ['GET', 'HEAD'].includes(request.method) ? null : await request.blob(),
redirect: 'follow'
});
// 执行上游请求
const upstreamResponse = await fetch(proxyRequest);
const responseHeaders = new Headers(upstreamResponse.headers);
// 重写认证服务器地址指向 Worker
const wwwAuth = responseHeaders.get('Www-Authenticate');
if (wwwAuth) {
responseHeaders.set('Www-Authenticate', wwwAuth.replace(/https:\/\/auth\.docker\.io/g, WORKER_BASE_URL));
}
// 处理重定向响应
const redirectLocation = responseHeaders.get('Location');
if (redirectLocation) {
const redirectReq = new Request(redirectLocation, request);
return fetch(redirectReq);
}
// 注入标准响应头
responseHeaders.set('Access-Control-Allow-Origin', '*');
responseHeaders.set('Access-Control-Expose-Headers', '*');
responseHeaders.set('Cache-Control', 'max-age=1500');
responseHeaders.delete('Content-Security-Policy');
responseHeaders.delete('Clear-Site-Data');
return new Response(upstreamResponse.body, { status: upstreamResponse.status, headers: responseHeaders });
}
};
粘贴代码后点击【部署】,服务即刻生效。
域名绑定与证书配置
返回 Worker 控制台,进入"设置"标签页下的"触发器"区域。点击"添加自定义域",输入预设的域名(例如 docker.yourdomain.com)。Cloudflare 会自动完成 DNS 解析验证并签发 SSL 证书,无需额外配置。
容器客户端适配
在宿主机中编辑 Docker 守护进程配置文件 /etc/docker/daemon.json,将代理地址注入镜像源列表:
{
"registry-mirrors": ["https://docker.yourdomain.com"],
"insecure-registries": ["docker.yourdomain.com"]
}
重载系统配置并重启 Docker 服务以应用变更:
sudo systemctl daemon-reload
sudo systemctl restart docker