简要说明

最近在基于jenkins+docker+harbor做持续集成,开发环境与测试环境在公司内网,可以相通,因此镜像仓库之间的主从复制无问题。而生产环境是客户的云环境上,因此需要远程复制,将测试环境与生产环境的镜像仓库做远程复制。我将远程复制遇到的填坑经验分享一下。

harbor应用不能添加uri_prefix

首先在生产环境的一台机器(192.168.2.58)上搭建docker+harbor环境,也比较麻烦,因为生产环境的内部机器不能直接联网,所以只能离线安装docker,docker-compose和harbor(harbor也是docker镜像安装的)。harbor的nginx配置如下(配置太长,删减了部分):

  server {
    listen 80;
    server_tokens off;  
    client_max_body_size 0;
    include /etc/nginx/conf.d/harbor.http.*.conf;

    location / {
      proxy_pass http://portal/;
      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-Proto $scheme;      
      proxy_buffering off;
      proxy_request_buffering off;
    }

    location /c/ {
       ...
    }

    location /api/ {
      ...
    }

    location /chartrepo/ {
      ...
    }

    location /v1/ {
      return 404;
    }

    location /v2/ {
      ...
    }

    location /service/ {
      ...
    }

    location /service/notifications {
      return 404;
    }
  }
}

其次在生产的入口nginx,添加一个location指向harbor机器上。

upstream harbor_server{
  server 192.168.2.58   #harbor应用
}

server{
  #...

  location /harbor{
    proxy_pass http://harbor_server;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $Remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

问题来了。

问题描述


在本地通过***可以正常访问及操作远端的192.168.2.58的harbor应用。然后通过生产环境的http://www.xxx.com/harbor正常打开harbor登录页面,但是怎么都登录不了。

打开google的Network查看一下,发现登录的login请求url有问题。

Request URL:
http://www.xxx.cn/c/login
Request Method:
POST
Status Code:
404 Not Found

我去。。这个地址能登进去才神奇。入口的nginx哪有配置/c或者/c/login的路径映射。在内部的harbor机器的nginx才有相关配置。入口的nginx已经拦截下来,你后续能访问才怪。

一般类似web应用,请求的路径一般是通过context_path加上相对路径。例如: 部署的应用名称为/app,那么访问c/login,请求的路径应该是http://ip:port/app/c/login。ngnix可以配置成location app{proxy_pass http://ip:port/app},就可以正常访问。

但是harbor并不能直接设置uri_prefix,他提供的访问路径是http://ip:port/直接访问。并未提供应用名称,能否设置uri_prefix,很遗憾,暂时未找到哪个地方设置,直接网上搜索,果然在github上有人提出类似的问题。

https://github.com/goharbor/harbor/issues/6772

问题描述:

To deploy harbor and notary using the same hostname, I need to be able to use custom paths.
Unfortunately, it's impossible yet mainly because the Angular app cannot be configured at runtime before it's first api call.
This issue is related to the following Pull Request : goharbor/harbor-helm#143
Ideally, I wish to able to set or query param, a cookie or an environment variable to make Angular application to use an URI prefix for all of its API calls.
It seems that the first problem is that, in src/app/app-config.service.ts, systemInfoEndpoint, the API endpoint used to get Harbor configuration is hardcoded.
Also I have now easy way to transmit params to the Angular application at runtime (eg. systemInfoEndpoint URL).
Even if I was able to transmit a parameter to customize this URL, there is no way to easily set uri_prefix globally. We have to redefine all endpoints URLs.
In a try to fix this issue rfom the outside, without touching to the Angular app, Iadd additional annotations to the ingress configuration:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/add-base-url: "true"
It works properly for the HTML part of the app but unfortunately not for API calls.
Maybe it is more a feature request than an issue.
Thank you for your help.

作者回答:

@max-k With initial thinking, I believe it's possible to allow user to set prefix, but we need to balance the complexity in configuration, we don't want to add to many settings that may confuse users until we have to.
Could you let us know the motivation of this requirement?
Do you want same or different prefix for registry and notary and Harbor API? I believe docker client will only communicate with /v2/ api, not /prefix/v2.
In addition, I recall there used to be a bug in notary to support prefix in its service and I submitted a patch. We'll also need to double check whether it's included in the latest build of notary.

---“我相信允许用户设置前缀是可能的,但是我们需要平衡配置的复杂性,我们不想添加到许多设置中,这些设置可能会混淆用户,直到我们不得不这样做。”

我去,摆明是不想解决这个issue,这个解决问题的想法不可取啊。

好吧,只能自己来解决。

解决办法


解决思路也很简单:在入口nginx配置location /harbor{}指向Harbor应用,后续的请求则通过referer来判断,如果是来源于/harbor,则直接在转向harbor应用即可。

修改入口的nginx配置

location / {
   root   html;
   index  index.html index.htm;

   proxy_set_header    Host $http_host;
   proxy_set_header    X-Real-IP $remote_addr;
   proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;

  
   if ($http_referer ~* "/harbor"){
     proxy_pass http://harbor_server;
   }
}

使用nginx -s reload重新加载配置,然后再次登录,就OK了。

当然,除了这个解决方法之外,也有其他的解决办法:

  • 一个是用另外一个外网入口(二级域名也行),直接指向harbor,但是需要用域名。一般来说,涉及到域名的基本上都不考虑这种解决办法。

  • 一个是修改harbor应用本身,给他添加应用前缀,但这种耗时费力,不讨好的事情咱不干。

  • 当然如果你不想用harbor也可以,用其他的镜像仓库,如:Nexus、Docker Repository、Dragonfly、Portus。会不会也出现这种问题,我也不知道,没实际使用经验