Cilium Cilium

Expón Services con el soporte de Gateway API de Cilium

GatewayClass, Gateway y HTTPRoute: enrutamiento por host, coincidencia de rutas, reescrituras de URL y TLS, sin Pods de gateway separados.

14 min Intermedio

Kubernetes Gateway API es el sucesor estandarizado de Ingress, y Cilium lo implementa directamente usando su propio plano de datos eBPF más un Envoy embebido para el procesamiento de capa 7. Esta guía cubre los recursos principales, GatewayClass, Gateway, HTTPRoute, y los patrones de enrutamiento construidos con ellos, asumiendo que Cilium ya está instalado con Gateway API habilitado (consulta la guía anterior).

GatewayClass: declarar Cilium como controlador disponible

Un GatewayClass le dice al clúster “este controlador está disponible para que lo usen los Gateways”:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: cilium
spec:
  controllerName: io.cilium/gateway-controller

Si estableciste gatewayAPI.gatewayClass.create: false durante la instalación (como recomienda la guía anterior), este es el recurso que creas tú mismo para cubrir ese hueco.

Personaliza la clase con CiliumGatewayClassConfig

Cilium ofrece su propio CRD, CiliumGatewayClassConfig, para los ajustes que deben aplicarse a todos los Gateways que usen una clase dada, con más frecuencia el tipo de Service de Kubernetes a través del cual se expone el Gateway:

apiVersion: cilium.io/v2alpha1
kind: CiliumGatewayClassConfig
metadata:
  name: gateway-api-config
  namespace: default
spec:
  service:
    type: NodePort
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: cilium
spec:
  controllerName: io.cilium/gateway-controller
  parametersRef:
    group: cilium.io
    kind: CiliumGatewayClassConfig
    name: gateway-api-config
    namespace: default

NodePort aquí es específicamente una concesión para clústeres kind, que se explica más abajo; en un clúster con un balanceador de carga real en la nube disponible, LoadBalancer es la opción más típica, y guías posteriores de esta serie sobre el anuncio de rutas BGP cubren cómo obtener una dirección enrutable sin el balanceador de carga de un proveedor de nube.

Crea un Gateway

Un Gateway es la configuración real de los listeners, que hace referencia a la clase anterior:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: demo-gateway
  namespace: default
spec:
  gatewayClassName: cilium
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: All
    - name: https
      protocol: HTTPS
      port: 443
      allowedRoutes:
        namespaces:
          from: All
      tls:
        mode: Terminate
        certificateRefs:
          - name: demo-tls-secret

Sin Pods de gateway: aquí es donde Cilium se diferencia

Con la mayoría de las implementaciones de Gateway API, crear un Gateway produce un Deployment y Pods dedicados para esa instancia de gateway. Con Cilium no ocurre así: el tráfico se intercepta mediante eBPF a nivel de nodo y se enruta según la configuración del Gateway, y solo aparece un Service de Kubernetes, sin Pods dedicados al Gateway en sí.

kubectl get gateway demo-gateway
kubectl get service -l gateway.networking.k8s.io/gateway-name=demo-gateway

En un clúster kind en concreto, un Service NodePort recibe por defecto un puerto aleatorio, no los puertos que realmente quieres (30080/30443 de la configuración anterior del clúster kind). Parchéalo explícitamente:

kubectl patch service <gateway-service-name> --type='json' -p='[
  {"op": "replace", "path": "/spec/ports/0/nodePort", "value": 30080},
  {"op": "replace", "path": "/spec/ports/1/nodePort", "value": 30443}
]'

Este paso de parcheo es únicamente un workaround de clústeres kind por su falta de un balanceador de carga real, no una limitación general de Cilium. En un clúster con una implementación real de LoadBalancer en la nube, o con los anuncios L2 de Cilium o el anuncio de rutas BGP (una guía posterior de esta serie), el Service del Gateway recibe una dirección enrutable sin parcheo manual.

Enruta por nombre de host

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: python-route
  namespace: default
spec:
  parentRefs:
    - name: demo-gateway
  hostnames:
    - "python.example.com"
  rules:
    - backendRefs:
        - name: python-service
          port: 5000

parentRefs vincula esta ruta al Gateway (deben estar en el mismo namespace, o la ruta necesita una política explícita de referencia entre namespaces). Cualquier petición que llegue con el encabezado Host python.example.com se envía a python-service.

Enruta por ruta: coincidencia exacta vs. por prefijo

  rules:
    - matches:
        - path:
            type: Exact
            value: /
      backendRefs:
        - name: python-service
          port: 5000

Exact coincide solo con esa ruta literal; una petición a /status no coincidiría con una regla limitada a /. Para cualquier cosa más allá de una sola ruta fija, PathPrefix coincide con el prefijo dado y reenvía el resto de la ruta sin cambios:

  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /

La distinción importa en la práctica: una API con varios endpoints detrás de una sola ruta necesita PathPrefix, no Exact, o cada endpoint más allá de la ruta literal coincidente devuelve un route-not-found en lugar de llegar al backend.

Reescrituras de URL: una ruta externa limpia, otra distinta internamente

Un patrón habitual es exponer varios servicios backend bajo un único nombre de host consolidado con un esquema de rutas ordenado, mientras cada backend mantiene sus propias rutas internas sin conocer ese prefijo externo:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: python-route
  namespace: default
spec:
  parentRefs:
    - name: demo-gateway
  hostnames:
    - "example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api/python
      filters:
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /
      backendRefs:
        - name: python-service
          port: 5000

Una petición a example.com/api/python/status coincide con el prefijo /api/python, se reescribe a /status antes de llegar a python-service, de modo que el backend nunca necesita conocer el prefijo /api/python que ve el exterior. Define una ruta paralela para cada servicio backend (/api/go hacia un servicio en Go, y así sucesivamente) para construir un único gateway de API consolidado a partir de varios backends desarrollados de forma independiente, cada uno sin conocer la estructura de rutas que el gateway presenta externamente.

TLS: dos cosas deben coincidir

Servir tráfico HTTPS requiere configurar dos piezas de forma coherente:

  1. El listener HTTPS del Gateway (mostrado arriba) referencia un Secret TLS y establece mode: Terminate.
  2. Cada HTTPRoute que deba aceptar tráfico HTTPS necesita que su entrada parentRefs apunte realmente a ese listener HTTPS por nombre, no solo al Gateway en conjunto:
  parentRefs:
    - name: demo-gateway
      sectionName: https

Sin sectionName apuntando específicamente al listener HTTPS, una ruta se vincula al listener (o listeners) que por defecto encajen con sus criterios de coincidencia, que puede no incluir el que pretendías. Crea el Secret TLS antes de aplicar un Gateway que lo referencie:

kubectl create secret tls demo-tls-secret \
  --cert=path/to/cert.pem \
  --key=path/to/key.pem \
  -n default

kubectl describe gateway demo-gateway es el primer lugar donde mirar si un listener no acepta tráfico como se espera; un Secret TLS ausente o mal formado es un motivo común por el que un listener aparece como no aceptado.

Guías populares