Cloudflare Workers 自建 Docker 镜像代理

网上找别人的镜像太麻烦有时候还不稳定,不如自建(用Cloudflare Workers)

部署代码:


<div>
<div>addEventListener("fetch", (event) => {</div>
<div>  event.passThroughOnException();</div>
<div>  event.respondWith(handleRequest(event.request));</div>
<div>});</div>
<div>const dockerHub = "https://registry-1.docker.io";</div>
<div>const HTML = `</div>
<div><!DOCTYPE html></div>
<div><html lang="zh-CN"></div>
<div><head></div>
<div>    <meta charset="utf-8"></div>
<div>    <meta name="viewport" content="width=device-width, initial-scale=1"></div>
<div>    <link rel="shortcut icon" href="https://xiaowangye.org/assets/img/favicons/favicon.ico"></div>
<div>    <title>Docker 镜像代理使用说明</title></div>
<div>    <style></div>
<div>    body{font-family:'Roboto',sans-serif;margin:0;padding:0;background-color:#f4f4f4;}.header{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;padding:.1rem 0;text-align:center;box-shadow:0 2px 4px rgba(0,0,0,0.1);}.container{max-width:800px;margin:40px auto;padding:20px;background-color:#fff;box-shadow:0 4px 8px rgba(0,0,0,0.1);border-radius:10px;}.content{margin-bottom:20px;}.footer{text-align:center;padding:20px 0;background-color:#333;color:#fff;}pre{background-color:#272822;color:#f8f8f2;padding:15px;border-radius:5px;overflow-x:auto;}code{font-family:'Source Code Pro',monospace;}a{font-weight:bold;color:#ffffff;text-decoration:none;}a:hover{text-decoration:underline;}@media (max-width:600px){.container{margin:20px;padding:15px;}.header{padding:15px 0;}}</div>
<div>    </style></div>
<div>    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&family=Source+Code+Pro:wght@400;700&display=swap" rel="stylesheet"></div>
<div></head></div>
<div><body></div>
<div><div class="header"></div>
<div><h1>Docker 镜像代理使用说明</h1></div>
<div></div></div>
<div><div class="container"></div>
<div><div class="content"></div>
<div><h3>带镜像仓库地址使用说明</h3><p>使用例子</p></div>
<div><pre><code># 拉取某个官方镜像(不带命名空间)</div>
<div>docker pull stilleshan/frpc:latest</div>
<div># 拉取 代理拉取镜像</div>
<div>docker pull hub.songwqs.top/stilleshan/frpc:latest</div>
<div># 重命名镜像</div>
<div>docker tag hub.songwqs.top/stilleshan/frpc:latest stilleshan/frpc:latest</div>
<div># 删除代理镜像</div>
<div>docker rmi hub.songwqs.top/stilleshan/frpc:latest</div>
<div></code></pre><h3>镜像源方式使用说明</h3><p>1.添加镜像源</p></div>
<div>          <pre><code># 添加镜像代理到 Docker 镜像源</div>
<div>sudo tee /etc/docker/daemon.json &lt;&lt; EOF</div>
<div>{</div>
<div>  "registry-mirrors": ["https://hub.songwqs.top"]</div>
<div>}</div>
<div>EOF</code></pre><p>2.拉取镜像</p></div>
<div><pre><code># 拉取 redis 官方镜像</div>
<div>docker pull redis</div>
<div># 拉取 rabbitmq 非官方镜像</div>
<div>docker pull bitnami/rabbitmq</div>
<div># 拉取 postgresql 官方镜像</div>
<div>docker pull postgresql</code></pre></div>
<div></div></div></div>
<div></body></div>
<div></html></div>
<div>`</div>
<div>const routes = {</div>
<div>  // 替换为你的域名</div>
<div>  "hub.songwqs.top": dockerHub,</div>
<div>};</div>
<div>function routeByHosts(host) {</div>
<div>  if (host in routes) {</div>
<div>    return routes[host];</div>
<div>  }</div>
<div>  return "";</div>
<div>}</div>
<div>async function handleRequest(request) {</div>
<div>  const url = new URL(request.url);</div>
<div>  if (url.pathname == "/") {</div>
<div>    return handleHomeRequest(url.host);</div>
<div>  }</div>
<div>  const upstream = routeByHosts(url.hostname);</div>
<div>  if (!upstream) {</div>
<div>    return createNotFoundResponse(routes);</div>
<div>  }</div>
<div>  const isDockerHub = upstream == dockerHub;</div>
<div>  const authorization = request.headers.get("Authorization");</div>
<div>  if (url.pathname == "/v2/") {</div>
<div>    return handleFirstRequest(upstream, authorization, url.hostname);</div>
<div>  }</div>
<div>  // get token</div>
<div>  if (url.pathname == "/v2/auth") {</div>
<div>    return handleAuthRequest(upstream, url, isDockerHub, authorization);</div>
<div>  }</div>
<div>  // redirect for DockerHub library images</div>
<div>  // Example: /v2/busybox/manifests/latest => /v2/library/busybox/manifests/latest</div>
<div>  if (isDockerHub) {</div>
<div>    const pathParts = url.pathname.split("/");</div>
<div>    if (pathParts.length == 5) {</div>
<div>      pathParts.splice(2, 0, "library");</div>
<div>      const redirectUrl = new URL(url);</div>
<div>      redirectUrl.pathname = pathParts.join("/");</div>
<div>      return Response.redirect(redirectUrl.toString(), 301);</div>
<div>    }</div>
<div>  }</div>
<div>  return handlePullRequest(upstream, request);</div>
<div>}</div>
<div>function parseAuthenticate(authenticateStr) {</div>
<div>  // sample: Bearer realm="https://auth.ipv6.docker.com/token",service="registry.docker.io"</div>
<div>  // match strings after =" and before "</div>
<div>  const re = /(?<=\=")(?:\\.|[^"\\])*(?=")/g;</div>
<div>  const matches = authenticateStr.match(re);</div>
<div>  if (matches == null || matches.length < 2) {</div>
<div>    throw new Error(`invalid Www-Authenticate Header: ${authenticateStr}`);</div>
<div>  }</div>
<div>  return {</div>
<div>    realm: matches[0],</div>
<div>    service: matches[1],</div>
<div>  };</div>
<div>}</div>
<div>async function fetchToken(wwwAuthenticate, scope, authorization) {</div>
<div>  const url = new URL(wwwAuthenticate.realm);</div>
<div>  if (wwwAuthenticate.service.length) {</div>
<div>    url.searchParams.set("service", wwwAuthenticate.service);</div>
<div>  }</div>
<div>  if (scope) {</div>
<div>    url.searchParams.set("scope", scope);</div>
<div>  }</div>
<div>  const headers = new Headers();</div>
<div>  if (authorization) {</div>
<div>    headers.set("Authorization", authorization);</div>
<div>  }</div>
<div>  return await fetch(url, { method: "GET", headers: headers });</div>
<div>}</div>
<div>function handleHomeRequest(host) {</div>
<div>  // 使用正则表达式将所有{{host}}的出现替换为host变量</div>
<div>  const updatedHTML = HTML.replace(/{{host}}/g, host);</div>
<div></div>
<div>  return new Response(updatedHTML, {</div>
<div>    status: 200,</div>
<div>    headers: {</div>
<div>      "content-type": "text/html",</div>
<div>    }</div>
<div>  });</div>
<div>}</div>
<div>async function handlePullRequest(upstream, request) {</div>
<div>  const url = new URL(request.url);</div>
<div>  const newUrl = new URL(upstream + url.pathname);</div>
<div>  const newReq = new Request(newUrl, {</div>
<div>    method: request.method,</div>
<div>    headers: request.headers,</div>
<div>    redirect: "follow",</div>
<div>  });</div>
<div>  return await fetch(newReq);</div>
<div>}</div>
<div>async function handleFirstRequest(upstream, authorization, hostname) {</div>
<div>  const newUrl = new URL(upstream + "/v2/");</div>
<div>  const headers = new Headers();</div>
<div>  if (authorization) {</div>
<div>    headers.set("Authorization", authorization);</div>
<div>  }</div>
<div>  // check if need to authenticate</div>
<div>  const resp = await fetch(newUrl.toString(), {</div>
<div>    method: "GET",</div>
<div>    headers: headers,</div>
<div>    redirect: "follow",</div>
<div>  });</div>
<div>  if (resp.status === 401) {</div>
<div>      headers.set(</div>
<div>        "Www-Authenticate",</div>
<div>        `Bearer realm="https://${hostname}/v2/auth",service="cloudflare-docker-proxy"`</div>
<div>      );</div>
<div>    return new Response(JSON.stringify({ message: "Unauthorized" }), {</div>
<div>      status: 401,</div>
<div>      headers: headers,</div>
<div>    });</div>
<div>  } else {</div>
<div>    return resp;</div>
<div>  }</div>
<div>}</div>
<div>async function handleAuthRequest(upstream, url, isDockerHub, authorization) {</div>
<div>  const newUrl = new URL(upstream + "/v2/");</div>
<div>  const resp = await fetch(newUrl.toString(), {</div>
<div>    method: "GET",</div>
<div>    redirect: "follow",</div>
<div>  });</div>
<div>  if (resp.status !== 401) {</div>
<div>    return resp;</div>
<div>  }</div>
<div>  const authenticateStr = resp.headers.get("WWW-Authenticate");</div>
<div>  if (authenticateStr === null) {</div>
<div>    return resp;</div>
<div>  }</div>
<div>  const wwwAuthenticate = parseAuthenticate(authenticateStr);</div>
<div>  let scope = url.searchParams.get("scope");</div>
<div>  // autocomplete repo part into scope for DockerHub library images</div>
<div>  // Example: repository:busybox:pull => repository:library/busybox:pull</div>
<div>  if (scope && isDockerHub) {</div>
<div>    let scopeParts = scope.split(":");</div>
<div>    if (scopeParts.length == 3 && !scopeParts[1].includes("/")) {</div>
<div>      scopeParts[1] = "library/" + scopeParts[1];</div>
<div>      scope = scopeParts.join(":");</div>
<div>    }</div>
<div>  }</div>
<div>  return await fetchToken(wwwAuthenticate, scope, authorization);</div>
<div>}</div>
<div>const createNotFoundResponse = (routes) => new Response(</div>
<div>  JSON.stringify({ routes }),</div>
<div>  {</div>
<div>    status: 404,</div>
<div>    headers: {</div>
<div>      "Content-Type": "application/json",</div>
<div>    },</div>
<div>  }</div>
<div>);</div>
</div>

点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注