问题背景

基于 rancher v2.8.5 搭建 rke2 集群时,默认情况下 containerd 默认拉取的各种镜像如下片段所示:

......
docker.io/rancher/rke2-runtime:v1.28.10-rke2r1
docker.io/rancher/klipper-lb:v0.4.7
docker.io/rancher/mirrored-pause:3.6
......

因为网络原因,服务器不能直接从 dockerhub 上拉取镜像。
我们有自己搭建的Harbor镜像私服,harbor 中创建了 dockerhub 的代理项目,名为 dockerhub_proxy,私服可以正常代理 dockerhub 上的镜像,假设我们的 harbor 私服域名是 https://harbor.shanhy.com

所以我们通过 harbor 拉取上面的镜像就需要将 docker.io 修改为 harbor.shanhy.com/dockerhub_proxy,示例如下:

harbor.shanhy.com/dockerhub_proxy/rancher/rke2-runtime:v1.28.10-rke2r1
harbor.shanhy.com/dockerhub_proxy/rancher/klipper-lb:v0.4.7
harbor.shanhy.com/dockerhub_proxy/rancher/mirrored-pause:3.6

虽然 Rancher 在创建新的 K8s 集群时,允许配置自己的私服,但是因为规范要求,私服地址只能是合法的域名地址,不能带有 path 后缀,所以不允许我们填写 harbor.shanhy.com/dockerhub_proxy 这样的带有 path 路径的 url 作为私服地址的。

Rancher 截稿 v2.8.5 虽然提供了 rewrite 镜像地址的功能,但是存在 bug ,不能用,希望后续版本能解决。

如果我们只填写 harbor.shanhy.com,拉取镜像自动拼接后的地址 harbor.shanhy.com/rancher/rke2-runtime:v1.28.10-rke2r1 是无法拉取镜像的。因为 harbor 的设计必须要带有项目名称 dockerhub_proxy 这一段路径尾巴(截稿时 harbor v2.10.x,我给官方提了 issue#20829issue#17791,被官方拒绝了) 。

有人对我这个 issue 进行评论,提供了通过 nginx 重写 location 的方式来变通解决这个问题(感谢 issue#8082_issuecomment-725694016)。

段落总结:

我希望通过独立的域名 harbor-dockerhub-proxy.shanhy.com 不带有 path 后缀,来直接访问 harbor 中的项目镜像。

即将 harbor-dockerhub-proxy.shanhy.com/rancher/mirrored-pause:3.6 对应到原始地址 harbor.shanhy.com/dockerhub_proxy/rancher/mirrored-pause:3.6 的实际效果。

Nginx 配置实战

通过在 nginx 中重写 path 的方式,实现上文需求。

1、登录 harbor,创建机器人账户,因为我们需要将密码配置到配置文件中,所以使用机器人账户缩小权限是有必要的。

为 harbor 项目配置独立域名访问(去除 path 路径中的项目名称小尾巴)_字符串

  • 系统权限:不需要选择
  • 项目权限:选择所需要项目,权限全选或者按需勾选
  • 有效期:永不过期或者按需设置
  • 密码:生成的随机字符串密码拷贝记录下来

机器人账户类似一个临时的账户(也可以作为长期的来使用),机器人账户无法登录 Harbor 界面,机器人账户可以在命令行使用 docker login 进行登录。

2、将账户名和密码拼接起来生成base64字符串

按照格式 robot$robot-dockerhub:P2Ac8mcme88sdwefsdfs0xxlXGD3 拼接成字符串,使用 Linux 命令或者其他工具生成 base64 字符串。

echo 'robot$robot-dockerhub:P2Ac8mcme88sdwefsdfs0xxlXGD3'|base64

生成的 base64 字符串,在 nginx 配置文件中设置 header Authorization 时使用。

3、nginx 配置文件

以独立域名 harbor-dockerhub-proxy.shanhy.com 对应 harbor 项目 dockerhub_proxy 为例的配置文件如下:

server {
    listen 443 ssl;
    server_name harbor-dockerhub-proxy.shanhy.com;
    ssl_certificate /etc/letsencrypt/shanhy.com/cert.pem;  # 指定证书的位置,绝对路径
    ssl_certificate_key /etc/letsencrypt/shanhy.com/key.pem;  # 绝对路径,同上

    # https://ssl-config.mozilla.org/#server=nginx&version=1.17.7&config=intermediate&openssl=1.1.1d&guideline=5.6
    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;
    ssl_prefer_server_ciphers off;

    location ~ ^/v2/(?<path>.*)$ {
        if ($path != "") {
            # inject project and source domain into uri
            set $path "dockerhub_proxy/$path";
        }

        proxy_pass            https://harbor.shanhy.com/v2/$path;
        proxy_cache           off;
        proxy_set_header      Host                            $http_host;   # required for docker client's sake
        proxy_set_header      X-Real-IP                       $remote_addr; # pass on real client's IP
        proxy_set_header      X-Forwarded-For                 $proxy_add_x_forwarded_for;
        proxy_set_header      X-Forwarded-Proto               $scheme;
        proxy_set_header      Authorization                   "Basic cm9ib3QkVpMdTdOZTZ6V2FHYWxYRlYyCg==";
        proxy_read_timeout    900;
        # prepare delegate auth
        proxy_hide_header     Www-Authenticate;
    }
    location ~ ^/service/ {
        if ($args ~* "^(.*)(scope=repository%3A)(.*)$") {
            # inject project and source domain into the auth scope
            set $args "$1$2dockerhub_proxy%2F$3";
        }
        proxy_pass            https://harbor.shanhy.com$uri$is_args$args;
        proxy_cache           off;
        proxy_set_header      Host                            $http_host;   # required for docker client's sake
        proxy_set_header      X-Real-IP                       $remote_addr; # pass on real client's IP
        proxy_set_header      X-Forwarded-For                 $proxy_add_x_forwarded_for;
        proxy_set_header      X-Forwarded-Proto               $scheme;
        # /service/ 对Authorization的配置不是必要的
        # proxy_set_header      Authorization                   "Basic cm9ib3QkVpMdTdOZTZ6V2FHYWxYRlYyCg==";
        proxy_read_timeout    900;
    }
}

主要是 server_name 和 两段 location 的配置,有关 ssl 证书的配置和常规 nginx 配置证书一样,没有特殊之处。


(END)