国内Docker镜像拉取优化:基于Docker-Proxy的自建加速方案实战

一、概述

在国内网络环境下,在拉取 Docker 镜像时,常面临网络延迟高、连接频繁中断以及由于匿名访问导致的限流问题,严重影响了开发与部署效率。本文将介绍一种基于开源项目 Docker-Proxy 的自建 Docker 镜像加速服务方案。通过在本地或公有云环境部署反向代理服务,可以有效加速/优化镜像拉取,实现 Docker Hub、GHCR、GCR、Quay 等主流镜像仓库的高速拉取。
开源项目地址:https://github.com/dqzboy/Docker-Proxy
本文提供的加速服务地址为:https://docker.nebulait.cn

二、 性能验证:自建服务的速度实测

在正式部署之前,我们通过实际测试来验证该加速方案的效果。以下是在国内服务器环境下,使用加速地址拉取 postgres:15-alpine 镜像的实测数据。

拉取速度测试:

[root@wordpress ~]# time docker pull docker.nebulait.cn/library/postgres:15-alpine
15-alpine: Pulling from library/postgres
589002ba0eae: Pull complete 
80880d84c7ee: Pull complete 
52ab15f7caf3: Pull complete 
3f0f76fbbf7f: Pull complete 
13b5376b3898: Pull complete 
3058f5761809: Pull complete 
89acc9a02475: Pull complete 
76724a94cd79: Pull complete 
dc34e1889eb4: Pull complete 
2e54eccd7e45: Pull complete 
b9ae69fca244: Pull complete 
Digest: sha256:1d1095b0503145be79c79239372aefadfacf6b7c33c9245bbf99ce4b6b89c737
Status: Downloaded newer image for docker.nebulait.cn/library/postgres:15-alpine
docker.nebulait.cn/library/postgres:15-alpine

real    0m12.561s
user    0m0.042s
sys     0m0.036s

从测试结果可见, Layers 并行拉取且完整无误,总耗时仅约 12.5 秒(包含网络传输与解压),验证了加速方案的稳定性与高效性。

[root@wordpress ~]# docker images | grep "postgres"
docker.nebulait.cn/library/postgres       15-alpine    bb506a9728d2   2 days ago    274MB

三、 架构部署:基于 Docker Compose 的服务编排

本方案采用 Docker Compose 编排多个 Registry 代理服务,支持 Docker Hub、GitHub Container Registry (GHCR)、Google Container Registry (GCR)、Kubernetes (K8s) 及 Quay 等多个镜像源。同时集成了 Web UI 管理界面,便于可视化管理。

1. 环境准备

首先创建项目目录并进入:

[root@wordpress ~]# mkdir registry-proxy && cd registry-proxy

2. 配置 docker-compose.yaml

创建 docker-compose.yaml 文件。以下配置精选了常用的镜像库。如需全量部署,可参考开源项目提供的配置文件进行修改。

[root@wordpress ~]# vim docker-compose.yaml

services:
  ## docker hub
  dockerhub:
    container_name: reg-docker-hub
    image: dqzboy/registry:latest
    restart: always
    environment:
      - OTEL_TRACES_EXPORTER=none
        #- http_proxy=http://172.19.0.1:7890
        #- https_proxy=http://172.19.0.1:7890
    volumes:
      - ./registry/data:/var/lib/registry
      - ./registry-hub.yml:/etc/distribution/config.yml
      #- ./htpasswd:/auth/htpasswd
    ports:
      - 51000:5000
    networks:
      - registry-net

  ## ghcr.io
  ghcr:
    container_name: reg-ghcr
    image: dqzboy/registry:latest
    restart: always
    environment:
      - OTEL_TRACES_EXPORTER=none
        #- http_proxy=http://172.19.0.1:7890
        #- https_proxy=http://172.19.0.1:7890
    volumes:
      - ./registry/data:/var/lib/registry
      - ./registry-ghcr.yml:/etc/distribution/config.yml
      #- ./htpasswd:/auth/htpasswd
    ports:
      - 52000:5000
    networks:
      - registry-net

  ## gcr.io
  gcr:
    container_name: reg-gcr
    image: dqzboy/registry:latest
    restart: always
    environment:
      - OTEL_TRACES_EXPORTER=none
        #- http_proxy=http://172.19.0.1:7890
        #- https_proxy=http://172.19.0.1:7890
    volumes:
      - ./registry/data:/var/lib/registry
      - ./registry-gcr.yml:/etc/distribution/config.yml
      #- ./htpasswd:/auth/htpasswd
    ports:
      - 53000:5000
    networks:
      - registry-net

  ## registry.k8s.io
  k8s:
    container_name: reg-k8s
    image: dqzboy/registry:latest
    restart: always
    environment:
      - OTEL_TRACES_EXPORTER=none
        #- http_proxy=http://172.19.0.1:7890
        #- https_proxy=http://172.19.0.1:7890
    volumes:
      - ./registry/data:/var/lib/registry
      - ./registry-k8s.yml:/etc/distribution/config.yml
      #- ./htpasswd:/auth/htpasswd
    ports:
      - 55000:5000
    networks:
      - registry-net

  ## quay.io
  quay:
    container_name: reg-quay
    image: dqzboy/registry:latest
    restart: always
    environment:
      - OTEL_TRACES_EXPORTER=none
        #- http_proxy=http://172.19.0.1:7890
        #- https_proxy=http://172.19.0.1:7890
    volumes:
      - ./registry/data:/var/lib/registry
      - ./registry-quay.yml:/etc/distribution/config.yml
      #- ./htpasswd:/auth/htpasswd
    ports:
      - 56000:5000
    networks:
      - registry-net

  ## UI
  registry-ui:
    container_name: registry-ui
    image: dqzboy/docker-registry-ui:latest
    environment:
      - DOCKER_REGISTRY_URL=http://reg-docker-hub:5000
      # [必须]使用 openssl rand -hex 16 生成唯一值
      - SECRET_KEY_BASE=8fcd446810534c1636831a9fe5158419
      # 启用Image TAG 的删除按钮
      - ENABLE_DELETE_IMAGES=true
      - NO_SSL_VERIFICATION=true
    restart: always
    ports:
      - 50000:8080
    networks:
      - registry-net

  hubcmd-ui:
    container_name: hubcmd-ui
    image: dqzboy/hubcmd-ui:latest
    restart: always
    # environment:
      # HTTP代理配置(可选)
      #- HTTP_PROXY=http://proxy.example.com:8080
      #- HTTPS_PROXY=https://proxy.example.com:8080
      #- NO_PROXY=localhost,127.0.0.1,.local

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      # SQLite数据库文件
      - ./data:/app/data
    ports:
      - 30080:3000

networks:
  registry-net:

3. 启动服务

执行以下命令启动所有容器:

[root@wordpress ~]# docker compose up -d

四、 网关配置:Nginx 反向代理与路由规则

为了统一入口并支持 HTTPS 访问,建议使用 Nginx 作为反向代理。以下配置展示了如何根据 URL 路径将请求分发至不同的后端容器端口,并处理 SSL 卸载与 Header 重写。

配置文件路径示例:/etc/nginx/conf.d/docker.nebulait.cn

server {
    listen 80;
    server_name docker.nebulait.cn;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name docker.nebulait.cn;

    ssl_certificate /data/certs/allnebulait/full_chain.pem;
    ssl_certificate_key /data/certs/allnebulait/cert.key;

    ssl_session_tickets off;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;
    ssl_buffer_size 8k;

    client_max_body_size 0;
    chunked_transfer_encoding on;

    proxy_connect_timeout 600;
    proxy_send_timeout    600;
    proxy_read_timeout    600;
    send_timeout          600;

    # 全局代理头(除 Host 相关外)
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Nginx-Proxy true;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_http_version 1.1;
    proxy_buffering off;
    proxy_ssl_server_name on;

    # ==================== UI 界面 ====================
    location ^~ /ui/ {
        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-Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_http_version 1.1;
        proxy_pass http://127.0.0.1:50000/;
    }

    location ^~ /assets/ {
        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-Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_http_version 1.1;
        proxy_pass http://127.0.0.1:50000;
    }

    location ^~ /repo/ {
        # 不剥离前缀,让后端看到完整路径
        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-Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_http_version 1.1;
        proxy_pass http://127.0.0.1:50000;  # ✅ 不带 /,保留完整路径
    }

    # ==================== Docker Registry API ====================
    # ghcr.io 代理 → 52000
    location ~ ^/v2/ghcr\.io($|/.*) {
        rewrite ^/v2/ghcr\.io$        /v2/ break;
        rewrite ^/v2/ghcr\.io(/.*)$   /v2$1 break;
        proxy_set_header Host ghcr.io;
        proxy_set_header X-Forwarded-Host ghcr.io;  # ✅ 关键:必须同时设置
        proxy_pass http://127.0.0.1:52000;
    }

    # gcr.io 代理 → 53000
    location ~ ^/v2/gcr\.io($|/.*) {
        rewrite ^/v2/gcr\.io$        /v2/ break;
        rewrite ^/v2/gcr\.io(/.*)$   /v2$1 break;
        proxy_set_header Host gcr.io;
        proxy_set_header X-Forwarded-Host gcr.io;  # ✅ 关键
        proxy_pass http://127.0.0.1:53000;
    }

    # k8s.gcr.io 代理 → 54000
    location ~ ^/v2/k8s\.gcr\.io($|/.*) {
        rewrite ^/v2/k8s\.gcr\.io$        /v2/ break;
        rewrite ^/v2/k8s\.gcr\.io(/.*)$   /v2$1 break;
        proxy_set_header Host k8s.gcr.io;
        proxy_set_header X-Forwarded-Host k8s.gcr.io;  # ✅ 关键
        proxy_pass http://127.0.0.1:54000;
    }

    # registry.k8s.io 代理 → 55000
    location ~ ^/v2/registry\.k8s\.io($|/.*) {
        rewrite ^/v2/registry\.k8s\.io$        /v2/ break;
        rewrite ^/v2/registry\.k8s\.io(/.*)$   /v2$1 break;
        proxy_set_header Host registry.k8s.io;
        proxy_set_header X-Forwarded-Host registry.k8s.io;  # ✅ 关键
        proxy_set_header Authorization $http_authorization; # ✅ 新增:透传 token
        proxy_pass http://127.0.0.1:55000;
    }

    # quay.io 代理 → 56000
    location ~ ^/v2/quay\.io($|/.*) {
        rewrite ^/v2/quay\.io$        /v2/ break;
        rewrite ^/v2/quay\.io(/.*)$   /v2$1 break;
        proxy_set_header Host quay.io;
        proxy_set_header X-Forwarded-Host quay.io;  # ✅ 关键
        proxy_set_header Docker-Distribution-API-Version registry/2.0;
        proxy_pass http://127.0.0.1:56000;
    }

    # 兜底:主 Registry → 51000
    location /v2/ {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_pass http://127.0.0.1:51000;
    }

    # 兜底:其他路径
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_pass http://127.0.0.1:30080/;
    }

    access_log  /home/wwwlogs/docker.nebulait.cn.log;
}

五、 客户端接入:Docker 守护进程配置与使用方法

部署完成后,客户端可以通过两种方式接入该加速服务。

1. 配置 Docker 守护进程 (daemon.json)

修改 Docker 的配置文件 daemon.json,将自建服务配置为 Registry Mirror。此方式对拉取 Docker Hub 镜像最为便捷。

[root@wordpress ~]# vim /etc/docker/daemon.json
{
    "registry-mirrors": [ "https://your_registry_url" ],
    "log-opts": {
      "max-size": "100m",
      "max-file": "5"
    }
}

修改配置后,需重启 Docker 服务使配置生效:

[root@wordpress ~]# systemctl daemon-reload systemctl restart docker

说明: 配置 registry-mirrors 后,拉取 Docker Hub 官方镜像(如 nginxpostgres)时无需添加前缀,Docker 守护进程会自动重定向至加速服务。

2. 使用镜像前缀拉取 (非 Docker Hub 或自定义规则)

对于非 Docker Hub 的镜像源(如 GHCR, GCR 等),或者未配置 Mirror 的情况下,可以直接使用完整的代理地址进行拉取。

# docker hub Registry
## 源:redis:latest
## 替换

[root@wordpress ~]# docker pull your_registry_url/library/redis:latest
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

给TA打赏
共{{data.count}}人
人已打赏
LinuxOps工具

阿里云ossfs部署完整指南 - Linux系统挂载OSS教程

2025-8-7 12:38:29

Kubernetes云原生

Ingress-Nginx高级配置

2025-7-15 6:20:02

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索
本站支持IPv6访问