一、概述
在国内网络环境下,在拉取 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 官方镜像(如 nginx, postgres)时无需添加前缀,Docker 守护进程会自动重定向至加速服务。
2. 使用镜像前缀拉取 (非 Docker Hub 或自定义规则)
对于非 Docker Hub 的镜像源(如 GHCR, GCR 等),或者未配置 Mirror 的情况下,可以直接使用完整的代理地址进行拉取。
# docker hub Registry
## 源:redis:latest
## 替换
[root@wordpress ~]# docker pull your_registry_url/library/redis:latest
