早期我司使用 Harbor 自建 Docker Registry,后来阿里云推出了容器镜像服务,服务还处于公测阶段,使用免费,看起来不错,但是两者的 URI 是不一样的:

  • 我司:registry.example.com/NAMESPACE/REPOSITORY
  • 阿里云:registry.cn-shenzhen.aliyuncs.com/PREFIX_NAMESPACE/REPOSITORY

除了域名不一样,两者的 namespace 也不一样,阿里云比较矬,它的 RDS、Redis 服务提供的域名是每个用户不一样的,而 docker registry 却选择了一个固定域名,于是不同租户的 namespace 名字会冲突,非常不幸的是我司用的 namespace 名字已经被人占用了,阿里云推荐的解决办法是加一个公司名字前缀。 由于这个原因,我们不能把 registry.example.com以 DNS CNAME 的方式直接映射到 registry.cn-shenzhen.aliyuncs.com,而是要借助一层 Nginx 代理:

http {
  map $args $mapped_args {
    # 在认证时把 NAMESPACE/REPOS 映射到 PREFIX_NAMESPACE/REPOS
    ~(.*)scope=repository%3A(.*)   $1scope=repository%3APREFIX_$2;
    default                        $args;
  }

  server {
    listen 443 ssl;
    server_name registry.example.com;

    tcp_nodelay on;

    # this is necessary for us to be able to disable request buffering in all cases
    proxy_http_version 1.1;

    # SSL
    ssl_certificate cert/registry.example.com.pem;
    ssl_certificate_key cert/registry.example.com.key;

    # Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
    ssl_protocols TLSv1.1 TLSv1.2;
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    # disable any limits to avoid HTTP 413 for large image uploads
    client_max_body_size 0;

    # required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
    chunked_transfer_encoding on;

    proxy_buffering off;
    proxy_request_buffering off;

    # 拦截服务端返回的认证服务器地址
    location = /v2/ {
      # 更理想的是替换服务端返回的 http reader,但 Nginx 标准模块没这个功能,所幸 service 参数貌似是固定的
      add_header Www-Authenticate 'Bearer realm="https://registry.example.com/auth",service="registry.aliyuncs.com:cn-shenzhen:26842"' always;

      proxy_pass https://registry.cn-shenzhen.aliyuncs.com/v2/;
      # 丢掉服务端返回的 Www-Authenticate 头部
      proxy_hide_header Www-Authenticate;
    }

    # 改写 NAMESPACE/REPOS 为实际的 PREFIX_NAMESPACE/REPOS 以获取 access token
    location /auth {
      #rewrite_log on;

      set $args $mapped_args;
      rewrite ^(.*)$ $1 break;

      proxy_pass https://dockerauth.cn-hangzhou.aliyuncs.com;
    }

    # 将 /v2/NAMESPACE/... 重写成 /v2/PREFIX_NAMESPACE/...
    location /v2/ {
      #rewrite_log on;
      rewrite ^/v2/(?:PREFIX_)?(.+) /v2/PREFIX_$1 break;
      proxy_pass https://registry.cn-shenzhen.aliyuncs.com;
    }
  }

}

这个 hack 的起因虽然是针对阿里云容器镜像服务,但应该是通用的,Docker registry 的网络协议是由 Docker 官方明文规定的,只要是想改变 docker image 的 NAMESPACE 部分,都可以用上面的办法代理转换一把。