当入口不在 80/443 上时,克隆 URL 和链接缺少端口

问题描述

实例通过 Ingress 或 Gateway API 暴露,但用户通过非默认端口访问它(HTTP 使用 80,HTTPS 使用 443)。例如,当 Ingress 控制器或 Gateway Service 为 NodePort 时,或者外部负载均衡器监听自定义端口时,就会出现这种情况。

  • Web UI 可通过 http://<domain>:<port> 正常打开,并且可以正常登录。
  • 项目页面上显示以及 API 返回的克隆 URL 会省略端口,例如显示为 http://<domain>/group/project.git 而不是 http://<domain>:<port>/group/project.git。使用此类 URL 执行 git clone 会失败或访问到其他服务。
  • GitLab 根据其配置生成的其他绝对链接(例如克隆图表中的 HTTP 克隆 URL,以及通知电子邮件和 webhook 负载中的链接)也会省略端口。
  • 当 SSH 通过主机端口访问时,SSH 克隆 URL 会以相同方式省略 SSH 端口。

根本原因

在浏览器中查看的页面会跟随当前请求的地址,因此会保留端口。GitLab 根据自身配置生成的所有 URL 则使用 global.hosts.gitlab.name 和 global.appConfig.gitlabPort。实例无法从外部检测用户通过哪个端口访问它:Ingress 控制器、Gateway 或负载均衡器会在请求到达 GitLab 之前转换端口。当未设置 gitlabPort 时,GitLab 会假定使用 80(使用 HTTPS 时为 443),并在生成的 URL 中省略端口。

SSH 也存在相同情况:SSH 克隆 URL 根据 global.shell.port 生成,而不是根据 gitlab-shell Service 生成。

故障排查

  1. 找出用户实际通过哪个端口访问实例。

    • Ingress:Ingress 控制器 Service 的 HTTP/HTTPS 端口,例如:

      kubectl -n <ingress-controller-namespace> get svc
    • Gateway API:为 Gateway 创建的 Service 的端口。在默认部署模式下使用 Envoy Gateway 时,Service 位于 Envoy Gateway 控制器所在的命名空间中,其名称以 envoy-<namespace>-<gateway name>- 开头;在其他模式或使用其他控制器时,请检查 Gateway 的 status.addresses 以及控制器文档。例如:

      kubectl get svc -A | grep envoy-<instance-namespace>-<gateway-name>
  2. 检查 GitLab 生成 URL 时使用的端口。在实例的 webservice 或 sidekiq pod 中运行:

    kubectl -n <instance-namespace> exec <sidekiq-pod> -c sidekiq -- \
      sh -c 'cd /srv/gitlab && bin/rails runner "puts Gitlab.config.gitlab.url; puts Gitlab.config.gitlab_shell.ssh_path_prefix"'

    如果输出为 http://<domain>(无端口),而用户通过其他端口访问实例,则本文适用。

  3. 检查实例配置:

    kubectl -n <instance-namespace> get gitlabofficial <instance-name> \
      -o jsonpath='{.spec.externalURL}{"\n"}{.spec.helmValues.global.appConfig.gitlabPort}{"\n"}{.spec.helmValues.global.shell.port}{"\n"}'

    空的 gitlabPort 或不包含端口的 externalURL 可确认原因。

解决方案

选择以下任一方案。

选项 1:在默认端口上暴露入口

在 80/443 上暴露 Ingress controller、Gateway 或负载均衡器,例如通过 LoadBalancer Service 或平台负载均衡器,然后让用户访问不带端口的 http://<domain> 或 https://<domain>。无需更改实例。

选项 2:告知实例外部端口

设置用户访问实例时使用的端口,并将其包含在 spec.externalURL 中:

spec:
  externalURL: http://gitlab.example.com:31069 # Include the external port
  helmValues:
    global:
      appConfig:
        gitlabPort: 31069 # External HTTP (or HTTPS) port of the Ingress controller / Gateway / load balancer
      shell:
        port: 30000 # External SSH port, when SSH is exposed through a NodePort

更改后,等待 webservice 和 sidekiq pod 根据新配置重新创建,然后再次运行 故障排查第 2 步中的检查;此时 URL 应包含端口。现有克隆仍可正常使用;对于复制了不带端口的克隆 URL 的用户,需要更新其远程仓库:

git remote set-url origin http://gitlab.example.com:31069/group/project.git

提示

  • 使用 Gateway API 时,还请参阅 Gateway API 访问 中的说明,包括非默认端口上的 HTTP 到 HTTPS 重定向。
  • gitlabPort 中的端口必须是用户看到的端口,而不是 webservice 的容器端口(8080),也不是 Gateway listener 的端口(80)。

GitLab 是 GitLab Inc. 的商标。

Alauda 是独立供应商。本产品与 GitLab Inc. 没有关联,也未获得其认可或赞助。所有商标均归其各自所有者所有,本文使用这些商标仅用于标识目的。