使用 Envoy Gateway 暴露 Harbor

概述

Envoy Gateway 是一个基于 Gateway API 的 L7 入口和负载均衡器,构建于 Envoy Proxy 之上。在不提供 IngressClass 的环境中,或外部流量路由优先使用 Gateway API 的环境中(例如,在配备 Envoy Gateway Operator 的 Alauda Container Platform 4.3+ 上),Harbor 无法依赖 Ingress 资源,因为 Envoy Gateway 不处理 Kubernetes Ingress API。

此时,Harbor 通过 HTTPRoute(Gateway API)暴露。Harbor Helm Chart 在 expose.type 设置为 route 时,会原生渲染 HTTPRoute,并将其附加到由 Envoy Gateway 管理的用户创建的 Gateway

架构

External Client (docker / browser) https://harbor.example.com


DNS → Envoy Gateway external address


Gateway (Gateway API, HTTPS listener, TLS termination) [L7]


HTTPRoute: /api/ /service/ /v2/ /c/ → harbor-core:80
           /                        → harbor-portal:80


Harbor ClusterIP Services (harbor-core, harbor-portal)
  • TLS 在 Gateway 监听器处终止,而不是由 Harbor 的 Nginx 终止。
  • Harbor Registry API(/v2/)路由到 harbor-core,因此 docker login / push / pull 可通过 Gateway 正常工作。

前提条件

  1. 集群中已安装 Envoy Gateway。
    • 在 Alauda Container Platform 4.3+ 上,通过平台的 Envoy Gateway Operator 安装。创建默认的 EnvoyGatewayCtl 实例(位于 envoy-gateway-operator 命名空间中的 cpaas-default)会自动生成 GatewayClass envoy-gateway-operator-cpaas-default
    • 在其他集群中,安装上游 Envoy Gateway,并自行创建 GatewayClass
  2. 存在 Gateway 资源(请参阅步骤 1:创建 Gateway)。
  3. 域名解析到 Envoy Gateway 的外部地址。
  4. 对于高可用部署,需要与标准 HA 模式相同的外部依赖(HA Redis/PostgreSQL、HA 负载均衡器、超过 2 个节点)。请参阅 Harbor 实例部署

步骤 1:创建 Gateway

与 Harbor 实例相同的命名空间中创建 Gateway,以简化部署(无需 ReferenceGrant)。如需跨命名空间 Gateway,请参阅跨命名空间 Gateway

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: harbor-gateway
  namespace: <harbor instance namespace>
spec:
  gatewayClassName: envoy-gateway-operator-cpaas-default
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: harbor-gateway-tls
      allowedRoutes:
        namespaces:
          from: Same

注意:

  • gatewayClassName 替换为 EnvoyGatewayCtl 实例生成的 GatewayClass。ACP 4.3 上的默认值为 envoy-gateway-operator-cpaas-default;GatewayClass 名称遵循 EnvoyGatewayCtl<namespace>-<name> 模式。
  • HTTPS 监听器要求在 与 Gateway 相同的命名空间中存在 TLS 证书 Secret(请参阅 TLS 配置)。
  • 也可以从 Web Console 创建 Gateway:Networking → Gateway API Gateway,此操作会自动创建配套的 EnvoyProxy 资源。
  • 如果还需要普通 HTTP 访问(例如,为不使用 TLS 的 Registry 提供访问),请在端口 80 上添加 HTTP 监听器。

步骤 2:部署 Harbor

选项 A:使用 Harbor High Availability (Envoy Gateway) 模板

在 Harbor 部署向导中选择 Harbor High Availability (Envoy Gateway) 模板,并填写以下内容:

字段
Domain NameHarbor 域名,例如 harbor.example.com
Gateway NameGateway 资源名称,例如 harbor-gateway
Gateway Namespace当 Gateway 与 Harbor 实例位于同一命名空间时留空;否则填写 Gateway 的命名空间

选项 B:从 YAML 部署

对应的 Harbor 实例配置如下:

apiVersion: operator.alaudadevops.io/v1alpha1
kind: Harbor
metadata:
  name: harbor-ha
  namespace: <harbor instance namespace>
spec:
  version: 2.14.3
  helmValues:
    externalURL: https://harbor.example.com
    expose:
      type: route
      tls:
        enabled: false
      route:
        parentRefs:
          - name: harbor-gateway
            namespace: <harbor instance namespace>
            sectionName: https
            group: gateway.networking.k8s.io
            kind: Gateway
        hosts:
          - harbor.example.com
    persistence:
      enabled: true
      persistentVolumeClaim:
        registry:
          storageClass: <storage class>
          accessMode: ReadWriteMany
          size: 10Gi
        jobservice:
          jobLog:
            storageClass: <storage class>
            accessMode: ReadWriteMany
            size: 1Gi
        trivy:
          storageClass: <storage class>
          accessMode: ReadWriteMany
          size: 5Gi
    # High availability replicas, external PostgreSQL/Redis and credentials follow the standard HA configuration.
    # See "Deploying from YAML" in the deployment guide.

实例部署完成后,Operator 会渲染一个 HTTPRoute(默认命名为 harbor-route),并将其附加到所引用的 Gateway。

步骤 3:验证

  1. 检查 HTTPRoute 是否已被 Gateway 接受:

    kubectl get httproute -n <harbor instance namespace>
    kubectl get httproute harbor-route -n <harbor instance namespace> -o yaml

路由条件应显示 Accepted: TrueResolvedRefs: True

  1. 访问 Harbor UI:https://harbor.example.com

  2. 使用 Docker 客户端验证 Registry API(在没有 DNS 的测试环境中,使用 --add-host 映射域名):

    docker login harbor.example.com -u admin -p <password>
    docker pull busybox
    docker tag busybox harbor.example.com/library/busybox
    docker push harbor.example.com/library/busybox

TLS 配置

route 模式下,TLS 在 Gateway 处终止,因此证书应配置在 Gateway 监听器上,而不是 Harbor 实例上:

  1. Gateway 的命名空间中创建 TLS 证书 Secret

    apiVersion: v1
    kind: Secret
    metadata:
      name: harbor-gateway-tls
      namespace: <gateway namespace>
    type: kubernetes.io/tls
    data:
      tls.crt: <base64 encoded cert>
      tls.key: <base64 encoded key>
  2. 在 Gateway 的 HTTPS 监听器中引用该证书(请参阅步骤 1)。

  3. 保持 Harbor 自身的 expose.tls.enabledfalse,并将 externalURL 设置为 https://<domain>

证书续期时,更新 Secret;Gateway 监听器会获取新证书,无需重新部署 Harbor。

跨命名空间 Gateway

如果 Gateway 与 Harbor 实例位于不同命名空间中:

  1. Gateway 监听器必须允许来自 Harbor 命名空间的路由,例如:

    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            cpaas.io/project: <project name>
  2. Gateway 的命名空间中创建 ReferenceGrant,允许 Harbor 命名空间访问:

    apiVersion: gateway.networking.k8s.io/v1
    kind: ReferenceGrant
    metadata:
      name: allow-harbor-route
      namespace: <gateway namespace>
    spec:
      from:
        - group: gateway.networking.k8s.io
          kind: HTTPRoute
          namespace: <harbor instance namespace>
      to:
        - group: gateway.networking.k8s.io
          kind: Gateway

故障排除

症状可能原因解决方法
HTTPRoute 未被接受parentRefs 名称或命名空间错误,或者监听器不允许路由所在的命名空间确认 Gateway 存在、gatewayClassName 正确,并且 allowedRoutes 覆盖 Harbor 命名空间;跨命名空间时创建 ReferenceGrant
Gateway 没有外部地址Envoy Gateway 数据平面服务保持为 Pending(裸机或气隙集群中没有 MetalLB / 云 LoadBalancer)通过 NodePort 暴露数据平面:创建带有 envoyService.type: NodePortEnvoyProxy 并从 Gateway 引用它;请参阅下文无 LoadBalancer 访问
Gateway 返回 404没有匹配的主机名/路径规则确认 hosts 与请求的域名匹配,并且 HTTPRouteAccepted
TLS 握手失败证书 Secret 缺失或位于错误的命名空间确认 Secret 存在于 Gateway 的命名空间中,并且已被 certificateRefs 引用
docker login 失败并出现 x509 错误Harbor externalURL 使用的 scheme/domain 与 Gateway 路由不同确保 externalURL: https://<domain> 与 Gateway 主机名保持一致

无 LoadBalancer 访问

在没有 MetalLB 或云 LoadBalancer 的裸机或气隙集群中,Envoy proxy Service 默认为 type: LoadBalancer 并保持为 Pending,因此 Gateway 永远无法获得外部地址。创建 EnvoyProxy(通过 infrastructure.parametersRef 从 Gateway 引用),并将其 envoyService.type 设置为 NodePort,然后通过 https://<nodeIP>:<nodePort> 访问 Harbor。

这是 Envoy Gateway 的通用能力;完整的 EnvoyProxy 配置参考请参阅 Envoy Gateway 文档: