将现有实例从 Ingress 迁移到 Gateway API

本文介绍如何将现有 GitLab 实例的 Web 入口从 Ingress 切换到 Gateway API(HTTPRoute),例如在移除集群的 Ingress controller 之前完成切换。

适用场景:

  • 集群正在迁移到 Gateway API,且 Ingress controller 将被移除。
  • 新集群仅提供 Gateway API,而恢复或迁移到该集群的实例仍使用 Ingress 配置。

新实例可以直接选择 Gateway API。请参阅 Gateway API 访问。

变更内容

项目IngressGateway API
Web 入口对象Ingress <instance name>-webservice-defaultHTTPRoute <instance name>-gitlab
入口地址Ingress controller 的地址Gateway(status.addresses)的地址
实例设置gitlab.webservice.ingress.enabled: true
global.ingress.enabled: true
gitlab.webservice.gatewayRoute.enabled: true
gitlab.webservice.ingress.enabled: false
global.ingress.enabled: false
域名和外部 URL不变只要用户通过与之前相同的端口访问 Gateway,就不会改变;否则请参阅 Gateway API 访问 中关于端口的说明
SSH 访问不变不变

存储库、用户和其他数据不受影响。只有路由对象会发生变化。Ingress 的 NGINX 超时和请求体大小注解不再适用;有关 Gateway 对应设置,请参阅 Gateway API 访问 中的说明。

前提条件

  1. 集群已安装 Gateway API CRD 和 Gateway controller,并且有可用的 GatewayClass:

    kubectl get gatewayclass
  2. 实例所在的 namespace 中存在 Gateway,并且其中包含一个接受实例域名的 HTTP listener。最小示例:

    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: gitlab-gateway
      namespace: <namespace>
    spec:
      gatewayClassName: <gateway-class>
      listeners:
        - name: http
          protocol: HTTP
          port: 80
          allowedRoutes:
            namespaces:
              from: Same

    如果实例通过 HTTPS(global.hosts.gitlab.https: true)提供服务,则 Gateway 还需要包含一个带有证书的 HTTPS listener,并且下方的 sectionName 必须指定该 listener。请参阅 配置 HTTPS。

    等待 Gateway 完成编程并获得地址:

    kubectl -n <namespace> get gateway gitlab-gateway
  3. 备份当前实例定义,以便进行恢复:

    kubectl -n <namespace> get gitlabofficial <instance name> -o yaml > gitlab-before-gateway.yaml

步骤

迁移分为两个阶段完成,以便在切换 DNS 的过程中保持实例可访问。

阶段 1:在 Ingress 旁添加 HTTPRoute

编辑实例,在 gitlab.webservice 下添加 gatewayRoute,并保持 Ingress 启用:

kubectl -n <namespace> edit gitlabofficial <instance name>
spec:
  helmValues:
    gitlab:
      webservice:
        gatewayRoute:
          enabled: true
          gatewayName: gitlab-gateway # Gateway in the instance namespace
          sectionName: http           # Listener name of the Gateway (spec.listeners[].name)

实例完成协调后,检查 HTTPRoute 是否已接受:

kubectl -n <namespace> get httproute <instance name>-gitlab \
  -o jsonpath='{range .status.parents[*].conditions[*]}{.type}={.status}{"\n"}{end}'

预期会看到 Accepted=True 和 ResolvedRefs=True。然后,在不更改 DNS 的情况下,使用 Gateway 的地址测试新的入口:

GATEWAY_ADDRESS=$(kubectl -n <namespace> get gateway gitlab-gateway -o jsonpath='{.status.addresses[0].value}')
curl -I --resolve gitlab.example.com:80:${GATEWAY_ADDRESS} http://gitlab.example.com/users/sign_in

阶段 2:切换 DNS 并移除 Ingress

  1. 将实例域名指向 Gateway 的地址,并等待旧 DNS 记录过期。

  2. 在实例中禁用 Ingress:

    spec:
      helmValues:
        gitlab:
          webservice:
            ingress:
              enabled: false
        global:
          ingress:
            enabled: false
  3. 检查 Ingress 是否已移除,并确认实例仍可访问:

    kubectl -n <namespace> get ingress
    curl -I http://gitlab.example.com/users/sign_in

同时通过 HTTP 验证 git clone / git push,并登录 Web UI。

回滚

恢复备份的定义。首先从 gitlab-before-gateway.yaml 中移除 metadata.resourceVersion、metadata.uid 和 status 等服务器端字段,然后使用该定义替换实例:

kubectl replace -f gitlab-before-gateway.yaml

使用 kubectl replace,而不是 kubectl apply。当使用 kubectl edit 或 kubectl patch 更改实例时,kubectl apply 不会移除备份中缺失的字段,因此 gatewayRoute 会保持启用状态,且 HTTPRoute 不会被移除。

Ingress 会重新创建,并且 HTTPRoute 会被移除。如果 DNS 已更改,请将其重新指向 Ingress controller 的地址。

故障排除

症状检查
HTTPRoute 的 Accepted=False 原因为 NoMatchingParent / NotAllowedByListenersGateway 位于另一个命名空间中,其 listener hostname 与域名不匹配,或者 allowedRoutes 不接受该命名空间。
实例报告 spec.parentRefs[0].sectionName ... must be of type stringsectionName 为空。将其设置为 Gateway 的 listener 名称。
HTTPRoute 的 Accepted=False 原因为 NoMatchingListenerHostname,或没有匹配的 sectionsectionName 与 Gateway 的任何 listener 名称都不匹配。
HTTPRoute 具有 ResolvedRefs=Falsewebservice Service <instance name>-webservice-default 尚不存在;请等待实例部署完成。
实例报告创建 httproutes 时发生权限错误operator 版本不包含 Gateway API 权限;请升级 operator。
来自 Gateway 的 404请求 Host 与 global.hosts.gitlab.name 中配置的域名不匹配。
Gateway 地址在某些节点上超时Gateway 的 Envoy Service 使用 externalTrafficPolicy: Local,因此只有运行 Envoy Pod 的节点会响应,尽管 status.addresses 可能会列出所有节点。请使用 LoadBalancer 地址,将 DNS 指向运行 Envoy 的节点,或在 Gateway 的 EnvoyProxy 中设置 externalTrafficPolicy: Cluster。