手动部署 docker 镜像加速服务

由于近期 hub.docker.com 等容器镜像站在国内已经被墙,并且现在看来也无重新可用的可能性了。所以手里有闲置服务器的佬友可以选择自己搭建一个 Docker 镜像加速服务,以便能够中转对 Docker 官方镜像仓库的请求,解决国内无法访问或访问缓慢的问题。本文主要讲述如何使用一台海外 VPS,手动搭建一台镜像加速服务器。

1. 准备工作

  • 海外 VPS 一台安装好主流的操作系统 (CentOS、 RHEL、Almalinux 或者 Rocky)

  • 域名一个,做好相关的解析

2. 获取一个海外 vps

这是使用的是一台Racknerd VPS

配置:3 vCore 2 GB RAM

本教教程使用的操作系统为: Almalinux 8

3. 基础环境安装

3.1. 安装 docker

安装 docker, 请参考博客 - 安装并配置 docker

3.2. 安装防火墙

很多操作系统默认没有安装防火墙,或者防火墙没有开启,建议安装防火墙并开启,毕竟将重要端口暴露在公网会导致服务器被莫名地攻击。

1
2
3
4
sudo dnf install -y firewalld

systemctl enable firewalld --now

4. 部署镜像仓库代理

4.1. 添加 docker-compose.yml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[root@proxy ~]# mkdir -p /data/registry-proxy &&  cd $_
[root@proxy registry-proxy]# vi 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://host:port
#- https_proxy=http://host:port
volumes:
- ./registry/data:/var/lib/registry
- ./registry-hub.yml:/etc/distribution/config.yml
#- ./htpasswd:/auth/htpasswd
ports:
- 51000:5000
networks:
- registry-net
## registry UI
registry-ui:
container_name: registry-ui
image: dqzboy/docker-registry-ui:latest
environment:
# UI所关联的REGISTRY容器服务地址
- DOCKER_REGISTRY_URL=http://reg-docker-hub:5000
# [必须]使用 openssl rand -hex 16 生成唯一值
- SECRET_KEY_BASE=9f18244a1e1179fa5aa4a06a335d01b2
# 启用Image TAG 的删除按钮
- ENABLE_DELETE_IMAGES=true
- NO_SSL_VERIFICATION=true
restart: always
ports:
- 50000:8080
networks:
- registry-net

networks:
registry-net:

每个容器挂载对应的 config.yml,这里名称需要与上面 compose.yml 文件定义的挂载的名称保持一致(例如上面挂载的配配置文件名称为 docker-hub.yml);下面只是其中一个示例配置,其他的配置也一样,只需要更改 remoteurl 代理的地址即可!
如果你上面开启了密码认证,那么下面配置中 auth 块的内容需要把注释取消掉!!!

创建 docker-hub 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

[root@proxy registry-proxy]# vim docker-hub.yml
version: 0.1
log:
fields:
service: registry
storage:
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
cache:
blobdescriptor: inmemory
blobdescriptorsize: 10000
maintenance:
uploadpurging:
enabled: true
age: 168h
interval: 24h
dryrun: false
readonly:
enabled: false
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
Access-Control-Allow-Origin: ['*']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
Access-Control-Allow-Headers: ['Authorization', 'Accept', 'Cache-Control']
Access-Control-Max-Age: [1728000]
Access-Control-Allow-Credentials: [true]
Access-Control-Expose-Headers: ['Docker-Content-Digest']

#auth:
# htpasswd:
# realm: basic-realm
# path: /auth/htpasswd

health:
storagedriver:
enabled: true
interval: 10s
threshold: 3

proxy:
remoteurl: https://registry-1.docker.io
username:
password:
ttl: 168h

4.2. 启动容器服务

1
2
3
4
5
6
7
8
9

[root@proxy registry-proxy]# docker compose up -d

# 检查启动容器状态
[root@proxy registry-proxy]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a07955ecac3a dqzboy/docker-registry-ui:latest "/docker-entrypoint.…" 10 minutes ago Up 10 minutes 8443/tcp, 0.0.0.0:50000->8080/tcp, [::]:50000->8080/tcp registry-ui
00a58a54a203 dqzboy/registry:latest "/entrypoint.sh /etc…" 10 minutes ago Up 4 minutes 0.0.0.0:51000->5000/tcp, [::]:51000->5000/tcp reg-docker-hub

防火墙放行 registry-ui 监听端口

1
2
3
4
5

[root@proxy registry-proxy]# firewall-cmd --add-port=50000/tcp --permanent

[root@proxy registry-proxy]# firewall-cmd --reload

5. 解析域名

  • 将我们在 Nginx 配置的域名,在 DNS 服务商商进行解析,解析到部署镜像代理仓库的服务器上;
  • 通过访问 UI 地址可以查看镜像仓库缓存的镜像;
  • 通过使用对应的代理域名来下载我们之前无法下载的镜像。

6. 配置 Nginx 反代

6.1. 安装 nginx

1
2
3
4
5
6
7
8
9

sudo dnf install -y nginx

# 防火墙放行80和443端口
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https

sudo systemctl reload firewalld

6.2. 申请 SSL 证书

首先安装 acme.sh

1
2
3
4
5
6
7

dnf install -y curl socat tar

curl https://get.acme.sh | sh

source ~/.bashrc

申请 SSL 证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 如果3X-UI监听在80端口上,需要先暂停一下x-ui,

sudo systemctl stop nginx

# ZeroSSL 的 retryafter 时间过长,您可以切换到 Let’s Encrypt CA。
acme.sh --set-default-ca --server letsencrypt

# 使用acme.sh申请证书
acme.sh --issue --standalone -d hub.example.com --force
acme.sh --issue --standalone -d ui.example.com --force

sudo systemctl start nginx

安装证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 创建文件用于存放证书
mkdir -p /etc/nginx/certs/

sudo systemctl start nginx

# 为registry-ui准备证书
acme.sh --installcert -d ui.example.com \
--key-file /etc/nginx/certs/ui.example.com.key \
--fullchain-file /etc/nginx/certs/ui.example.com.crt \
--reloadcmd "systemctl restart nginx" --ecc

# 为docker-hub准备证书
acme.sh --installcert -d hub.example.com \
--key-file /etc/nginx/certs/hub.example.com.key \
--fullchain-file /etc/nginx/certs/hub.example.com.crt \
--reloadcmd "systemctl restart nginx" --ecc

修改 nginx 配置, 为 registry-ui 添加反向代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

[root@proxy registry-proxy]# cd /etc/nginx/conf.d
[root@proxy registry-proxy]# vi registry-ui.conf
# Settings for a TLS enabled server.
## registry-ui
server {
listen 80;
listen [::]:80;
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name ui.example.com;
root /usr/share/nginx/html;
http2 on;

ssl_certificate "/etc/nginx/certs/ui.example.com.crt";
ssl_certificate_key "/etc/nginx/certs/ui.example.com.key";
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_buffer_size 8k;

# Load configuration files for the default server block.
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
location / {
proxy_set_header Host $host;
proxy_set_header Origin $scheme://$host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Ssl on; # Optional
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Host $host;
proxy_pass http://127.0.0.1:50000;
}

}

需要将 ui.example.com 替换成你自己的域名

修改 nginx 配置, 为 docker hub 添加反向代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

[root@proxy registry-proxy]# cd /etc/nginx/conf.d
[root@proxy registry-proxy]# vi docker-hub.conf
# Settings for a TLS enabled server.
## registry-ui
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
server_name hub.example.com;
root /usr/share/nginx/html;
http2 on;

ssl_certificate "/etc/nginx/certs/hub.example.com.crt";
ssl_certificate_key "/etc/nginx/certs/hub.example.com.key";
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_buffer_size 8k;

# Load configuration files for the default server block.
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
location / {
proxy_set_header Host $host;
proxy_set_header Origin $scheme://$host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Ssl on; # Optional
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Host $host;
proxy_pass http://127.0.0.1:51000;
}
}

修改完 nginx 配置后,重启 nginx 服务器

1
2
3
4
5
# 检查ngix配置
nginx -t

# 重启nginx服务器
sudo systemctl restart nginx

此时使用 https 的方式访问 registry-ui 了 https://ui.example.com

使用镜像仓库代理

使用自建的 Registry 地址替换官方的 Registry 地址拉取镜像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

[root@proxy registry-proxy]# vi /etc/docker/daemon.json

{
"registry-mirrors": [
"https://hub.example.com"
],
"live-restore": true,
"debug": true
}

# 重新重启docker使得配置生效

[root@proxy registry-proxy]# systemctl daemon-reload
[root@proxy registry-proxy]# systemctl restart docker

# 拉取一个测试镜像进行测试
[root@proxy registry-proxy]# docker run hello-world:latest

# 或者
[root@proxy registry-proxy]# docker run hub.example.com/library/hello-world:latest

访问 UI 页面就可以看到上面我们下载的镜像已经被缓存了

配置更多代理

安装以上的方法,我们可以继续配置更多代理

源站替代 Registry平台
docker.iohub.example.comdocker hub
gcr.iogcr.example.comGoogle Container Registry
ghcr.ioghcr.example.comGitHub Container Registry
k8s.gcr.iok8s-gcr.example.comKubernetes Container Registry
registry.k8s.iok8s.example.comKubernetes’s container image registry
quay.ioquay.example.comQuay Container Registry
mcr.microsoft.commcr.example.comMicrosoft Container Registry
docker.elastic.coelastic.example.comElastic Stack
nvcr.ionvcr.example.comNVIDIA Container Registry

下面以 gcr 代理为例进行讲解

配置 gcr 镜像加速

首先从 docker-compose.yaml文件中获取 gcr 容器相关的 docker compose 配置。

将其添加到上文提到的 docker-compose.yml 文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## gcr.io
gcr:
container_name: reg-gcr
image: dqzboy/registry:latest
restart: always
environment:
- OTEL_TRACES_EXPORTER=none
#- http=http://host:port
#- https=http://host:port
volumes:
- ./registry/data:/var/lib/registry
- ./registry-gcr.yml:/etc/distribution/config.yml
#- ./htpasswd:/auth/htpasswd
ports:
- 53000:5000
networks:
- registry-net

然后在config目录下对应的 yml 文件到并将其上传到 docker-compose.yml 同级目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

[root@proxy registry-proxy]# vi /data/registry-proxy/registry-gcr.yml

version: 0.1
log:
accesslog:
disabled: true
level: info
formatter: text
fields:
service: registry
environment: staging
storage:
cache:
# 改为 blan 禁用 blob 描述符缓存,可解决docker pull error:unexpected EOF
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
#inmemory: # 此存储驱动程序不会在运行期间保留任何数据,适合磁盘空间小的机器使用(但是会使用内存开销,只适合测试)
maintenance:
uploadpurging:
enabled: false
tag:
concurrencylimit: 8
delete:
enabled: true

http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
Access-Control-Allow-Origin: ['*']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
Access-Control-Allow-Headers: ['Authorization', 'Accept', 'Cache-Control']
Access-Control-Max-Age: [1728000]
Access-Control-Allow-Credentials: [true]
Access-Control-Expose-Headers: ['Docker-Content-Digest']

health:
storagedriver:
enabled: true
interval: 10s
threshold: 3

proxy:
remoteurl: https://gcr.io
username:
password:
ttl: 168h


[root@proxy registry-proxy]# vi /etc/nginx/conf.d/registry-gcr.conf

server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
server_name gcr.example.com;
root /usr/share/nginx/html;
http2 on;

ssl_certificate "/etc/nginx/certs/gcr.example.com.crt";
ssl_certificate_key "/etc/nginx/certs/gcr.example.com.key";
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_buffer_size 8k;

# Load configuration files for the default server block.
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
location / {
proxy_set_header Host $host;
proxy_set_header Origin $scheme://$host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Ssl on; # Optional
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Host $host;
proxy_pass http://127.0.0.1:53000;
}

}

[root@proxy registry-proxy]# systemctl stop nginx

# 使用acme.sh申请证书
[root@proxy registry-proxy]# acme.sh --issue --standalone -d gcr.example.com --force

[root@proxy registry-proxy]# systemctl start nginx

[root@proxy registry-proxy]# acme.sh --installcert -d gcr.example.com \
--key-file /etc/nginx/certs/gcr.example.com.key \
--fullchain-file /etc/nginx/certs/gcr.example.com.crt

[root@proxy registry-proxy]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@proxy registry-proxy]# systemctl restart nginx

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

[root@proxy registry-proxy]# sudo vi /etc/docker/daemon.json

{
"registry-mirrors": [
"https://hub.example.com",
"https://gcr.example.com"
],
"live-restore": true,
"debug": true
}

[root@proxy registry-proxy]# sudo systemctl daemon-reload
[root@proxy registry-proxy]# sudo systemctl restart docker

# 拉取一个测试镜像进行测试
[root@proxy registry-proxy]# sudo docker pull google-containers/pause:1.0
# 或者
[root@proxy registry-proxy]# sudo docker pull gcr.example.com/google-containers/pause:1.0
1.0: Pulling from google-containers/pause
a3ed95caeb02: Pull complete
4964c72cd024: Pull complete
Digest: sha256:a78c2d6208eff9b672de43f880093100050983047b7b0afe0217d3656e1b0d5f
Status: Downloaded newer image for gcr.example.com/google-containers/pause:1.0
gcr.example.com/google-containers/pause:1.0

7. 参考文档

自建 Docker 镜像加速服务:加速与优化镜像管理

Docker-Proxy

手动部署 docker 镜像加速服务

https://pengtech.net/docker/install_docker_proxy.html

作者

鹏叔

发布于

2025-03-17

更新于

2025-03-20

许可协议

评论