首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

多种 Docker 镜像拉取解决方案与实践

  • 25-02-18 09:21
  • 4033
  • 13984
blog.csdn.net

最近国内 Docker 镜像拉取不太通畅,尝试了几种镜像拉取的方式,写篇博客分享一下。

原以为只是 docker hub 被毙了,上机器一操作,官方的下载地址也被毙了,真是从源头解决问题==。

不过还好目前还有其他源能用,已经有大神把各种源整理成脚本,常用的 Linux 系统可以一个脚本解决。

  • 项目仓库 LinuxMirrors
  • 使用方法

如何修改镜像仓库

在 Docker 中,可以修改daemon.json来修改镜像仓库。

Linux

在 Linux 系统中,Docker 的配置文件通常位于 /etc/docker/daemon.json。

如果用的是无根用户模式 (rootless mode),配置文件则位于 ~/.config/docker/daemon.json。 当然也可以在启动 Docker 守护进程时,通过 --config-file 标志明确指定配置文件的位置。

如果你没有添加过配置,就需要在/etc/docker/创建文件daemon.json, 然后通过 registry-mirrors 来指定镜像仓库。修改完成后,重启服务。示例如下:

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
    "registry-mirrors": [
        "https://docker.m.daocloud.io"
    ]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
群晖

群晖的用户在容器仓库的设置中就可以加了。

几种解决方案

从网盘下载/淘宝购买

不得不佩服国人的商业嗅觉,镜像拉取不通畅后,淘宝上立马就出现了相关的服务。给人远程拉镜像 或 把镜像放到百度网盘等卖。。

这一类是非常不建议去使用的,一方面镜像可以很容易的被修改,安全隐患大;另一方面确实没必要。

使用代理

如果自己会搭梯子,或者有买梯子的同学,就不用折腾了,直接使用全局代理或者给 Docker 服务配置代理即可。

下面是单独给 Docker 服务配置代理的方法。

Liunx

在Linux上,有多种方式给 Docker 配置代理,这里说常用的两种。

  • 一种是直接找到 Docker service 的文件,直接在 [Service] 模块下加入代理配置,如:
# 通过 systemctl status 可以看到 Service 文件目录 
$ sudo systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: enabled)

$ sduo vi /usr/lib/systemd/system/docker.service

# 在 `[Service]` 模块加入配置即可
[Service]
Environment="HTTP_PROXY=http://192.168.10.1:7890" # 换成你的代理地址
Environment="HTTPS_PROXY=http://192.168.10.1:7890" # 换成你的代理地址
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fd00::/8,example.com"  # 不需要走代理的地址。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 还可以通过systemd的 drop-in 文件来配置HTTP代理, 操作如下:
# 创建 /etc/systemd/system/docker.service.d/http-proxy.conf 文件
$ sudo mkdir -p /etc/systemd/system/docker.service.d/
$ sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf

# 在其中加入下面的内容
[Service]
Environment="HTTP_PROXY=http://192.168.10.1:7890" # 换成你的代理地址
Environment="HTTPS_PROXY=http://192.168.10.1:7890" # 换成你的代理地址
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fd00::/8,example.com" # 不需要走代理的地址。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

配置好后,重启服务。

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

# 通过这个命令查看是否配置成功
$ sudo systemctl show --property=Environment docker
Environment=HTTP_PROXY=http://192.168.10.1:7890 HTTPS_PROXY=http://192.168.10.1:7890 ....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
群晖

群晖系统就是基于Linux改的,找到服务的位置,和Linux一样的修改即可。

开启SSH,SSH登录后获取root权限,然后修改下面的文件以及在[Service]下面添加代理内容。添加完成后重启 docker/ContainerManager 服务。

  • 群晖 7.2 以前
$ vi /usr/local/lib/systemd/system/pkg-Docker-dockerd.service

[Service]
Environment="HTTP_PROXY=http://192.168.10.1:7890" # 换成你的代理地址
Environment="HTTPS_PROXY=http://192.168.10.1:7890" # 换成你的代理地址
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fd00::/8,example.com" # 不需要走代理的地址。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 群晖 7.2 及以后
$ vi /usr/local/lib/systemd/system/pkg-ContainerManager-dockerd.service

[Service]
Environment="HTTP_PROXY=http://192.168.10.1:7890" # 换成你的代理地址
Environment="HTTPS_PROXY=http://192.168.10.1:7890" # 换成你的代理地址
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fd00::/8,example.com" # 不需要走代理的地址。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用镜像服务

目前还有一些第三方提供的,目前还能使用(随时可能被?),如 https://docker.m.daocloud.io 等。

可以配置多个,防止都扑街。

使用 Cloudflare Workers 代理

又有大佬写了更强大的部署脚本 cloudflare-docker-proxy

Workers

如果自己有域名,将域名NS到Cloudflare,就可以使用(白嫖)很多Cloudflare的服务了。这里,已经有大佬写好了workers的脚本,只需要配置一下就可使用。实际使用了一下,速度还不错,比我自己搭建的 Nginx 转发快==。

在登陆Cloudflare后,在Workers 和 Pages > 创建 > 创建Workers 创建 workers ,步骤都在下面图中。

其中代码如下:

'use strict'

const hub_host = 'registry-1.docker.io'
const auth_url = 'https://auth.docker.io'
const workers_url = 'https://你的域名'
const workers_host = '你的域名'
const home_page_url = 'https://example.com/'

/** @type {RequestInit} **/
const PREFLIGHT_INIT = {
    status: 204,
    headers: new Headers({
        'access-control-allow-origin': '*',
        'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS',
        'access-control-max-age': '1728000',
    }),
}

/**
 * @param {any} body
 * @param {number} status
 * @param {Object} headers
 */
function makeRes(body, status = 200, headers = {}) {
    headers['access-control-allow-origin'] = '*'
    return new Response(body, {status, headers})
}


/**
 * @param {string} urlStr
 */
function newUrl(urlStr) {
    try {
        return new URL(urlStr)
    } catch (err) {
        return null
    }
}


addEventListener('fetch', e => {
    const ret = fetchHandler(e)
        .catch(err => makeRes('cfworker error:\n' + err.stack, 502))
    e.respondWith(ret)
})


/**
 * @param {FetchEvent} e
 */
async function fetchHandler(e) {
    const getReqHeader = (key) => e.request.headers.get(key);

    let url = new URL(e.request.url);

    if (url.pathname === '/') {
        // Fetch and return the home page HTML content with replacement
        let response = await fetch(home_page_url);
        let text = await response.text();
        text = text.replace(/{workers_host}/g, workers_host);
        return new Response(text, {
            status: response.status,
            headers: response.headers
        });
    }

    if (url.pathname === '/token') {
        let token_parameter = {
            headers: {
                'Host': 'auth.docker.io',
                'User-Agent': getReqHeader("User-Agent"),
                'Accept': getReqHeader("Accept"),
                'Accept-Language': getReqHeader("Accept-Language"),
                'Accept-Encoding': getReqHeader("Accept-Encoding"),
                'Connection': 'keep-alive',
                'Cache-Control': 'max-age=0'
            }
        };
        let token_url = auth_url + url.pathname + url.search
        return fetch(new Request(token_url, e.request), token_parameter)
    }

    url.hostname = hub_host;

    let parameter = {
        headers: {
            'Host': hub_host,
            'User-Agent': getReqHeader("User-Agent"),
            'Accept': getReqHeader("Accept"),
            'Accept-Language': getReqHeader("Accept-Language"),
            'Accept-Encoding': getReqHeader("Accept-Encoding"),
            'Connection': 'keep-alive',
            'Cache-Control': 'max-age=0'
        },
        cacheTtl: 3600
    };

    if (e.request.headers.has("Authorization")) {
        parameter.headers.Authorization = getReqHeader("Authorization");
    }

    let original_response = await fetch(new Request(url, e.request), parameter)
    let original_response_clone = original_response.clone();
    let original_text = original_response_clone.body;
    let response_headers = original_response.headers;
    let new_response_headers = new Headers(response_headers);
    let status = original_response.status;

    if (new_response_headers.get("Www-Authenticate")) {
        let auth = new_response_headers.get("Www-Authenticate");
        let re = new RegExp(auth_url, 'g');
        new_response_headers.set("Www-Authenticate", response_headers.get("Www-Authenticate").replace(re, workers_url));
    }

    if (new_response_headers.get("Location")) {
        return httpHandler(e.request, new_response_headers.get("Location"))
    }

    let response = new Response(original_text, {
        status,
        headers: new_response_headers
    })
    return response;

}


/**
 * @param {Request} req
 * @param {string} pathname
 */
function httpHandler(req, pathname) {
    const reqHdrRaw = req.headers

    // preflight
    if (req.method === 'OPTIONS' &&
        reqHdrRaw.has('access-control-request-headers')
    ) {
        return new Response(null, PREFLIGHT_INIT)
    }

    let rawLen = ''

    const reqHdrNew = new Headers(reqHdrRaw)

    const refer = reqHdrNew.get('referer')

    let urlStr = pathname

    const urlObj = newUrl(urlStr)

    /** @type {RequestInit} */
    const reqInit = {
        method: req.method,
        headers: reqHdrNew,
        redirect: 'follow',
        body: req.body
    }
    return proxy(urlObj, reqInit, rawLen, 0)
}


/**
 *
 * @param {URL} urlObj
 * @param {RequestInit} reqInit
 */
async function proxy(urlObj, reqInit, rawLen) {
    const res = await fetch(urlObj.href, reqInit)
    const resHdrOld = res.headers
    const resHdrNew = new Headers(resHdrOld)

    // verify
    if (rawLen) {
        const newLen = resHdrOld.get('content-length') || ''
        const badLen = (rawLen !== newLen)

        if (badLen) {
            return makeRes(res.body, 400, {
                '--error': `bad len: ${newLen}, except: ${rawLen}`,
                'access-control-expose-headers': '--error',
            })
        }
    }
    const status = res.status
    resHdrNew.set('access-control-expose-headers', '*')
    resHdrNew.set('access-control-allow-origin', '*')
    resHdrNew.set('Cache-Control', 'max-age=1500')

    resHdrNew.delete('content-security-policy')
    resHdrNew.delete('content-security-policy-report-only')
    resHdrNew.delete('clear-site-data')

    return new Response(res.body, {
        status,
        headers: resHdrNew
    })
}
  • 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
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199

自建 Nginx 反向代理

找个海外的服务器,跑个Nginx,自己配置一个域名和证书,就可以搭建一个自己的镜像服务了。

我用 docker-compose 跑了一个,不过速度不太理想。贴一下配置:

  • docker-compose.yml
version: '3.5'
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    network_mode: host
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./certs:/certs:ro
      - ./html:/var/www/html
      - /var/log/nginx:/var/log/nginx
    restart: always
  
  # 用acme申请证书
  # https://github.com/acmesh-official/acme.sh/wiki/Run-acme.sh-in-docker#3-run-acmesh-as-a-docker-daemon
   acme:
    image: neilpang/acme.sh:3.0.7
    restart: always
    container_name: acme.sh
    command: ["daemon"]
    volumes:
      - ./html:/webroot
      - ./acme.sh:/acme.sh
      - ./certs:/ssl
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • ./nginx/conf.d/80.conf
server {
    listen 80;
    server_tokens off;

    access_log /var/log/nginx/access_80.log;
    error_log /var/log/nginx/error_80.log;

    location  /.well-known/acme-challenge {
        default_type "text/plain";
        root /var/www/html;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • ./nginx/conf.d/docker.conf
server {
    listen 443 ssl;
    server_name hub.razeen.me;
    http2 on;

    ssl_certificate /certs/cert.pem;
    ssl_certificate_key /certs/key.pem;

    access_log /var/log/nginx/access_hub.log;
    error_log /var/log/nginx/error_hub.log;

    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;

    # OCSP stapling
    # ssl_stapling on;
    # ssl_stapling_verify on;

    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    # ssl_trusted_certificate /certs/ca.pem;

    location / {
        proxy_pass https://registry-1.docker.io;  # Docker Hub 的官方镜像仓库
        proxy_set_header Host registry-1.docker.io;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_buffering off;

        proxy_set_header Authorization $http_authorization;
        proxy_pass_header  Authorization;

        # 对 upstream 状态码检查,实现 error_page 错误重定向
        proxy_intercept_errors on;
        # error_page 指令默认只检查了第一次后端返回的状态码,开启后可以跟随多次重定向。
        recursive_error_pages on;
        # 根据状态码执行对应操作,以下为301、302、307状态码都会触发
        error_page 301 302 307 = @handle_redirect;
    }
    
    location @handle_redirect {
        resolver 1.1.1.1;
        set $saved_redirect_location '$upstream_http_location';
        proxy_pass $saved_redirect_location;
    }
}
  • 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

自建镜像加速服务

还有大佬基于官方的 registry 做了镜像加速的服务,GitHub 项目 Docker-Proxy 可以一键部署Docker、K8s、Quay、Ghcr等镜像加速,而且还有UI管理。

有海外服务器的小伙伴可以试试,Github上有详细教程,这里就不展开了。下面是他的UI界面。

为了拉镜像,大家可谓是各显神通,一些我没尝试的就没写进来了,全放在文末的参考文档中。

参考文档及其他方式

  • 群晖 Docker/Container Manager 拉取镜像使用代理加速
  • 总结目前国内加速拉取 docker 镜像的几种方法
  • public-image-mirror
  • 使用Github Action将国外的Docker镜像转存到阿里云私有仓库
  • 自建镜像加速 Docker-Proxy
  • DaoCloud CRProxy 通用的 Image 代理
  • cloudflare-docker-proxy
  • CF-Workers-docker.io
  • acme.sh
注:本文转载自blog.csdn.net的@comefly的文章"https://blog.csdn.net/weixin_74313592/article/details/145215879"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

115
云原生
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top