Implementación de referencia de AI Gateway con Envoy Agent Router en GDC aislado

En este documento, se proporcionan instrucciones paso a paso para implementar una puerta de enlace de IA en entornos aislados de Google Distributed Cloud (GDC). La puerta de enlace se compila a partir de Envoy Gateway, la implementación de la API de Kubernetes Gateway basada en el proxy de Envoy, y Envoy Agent Router (anteriormente, Envoy AI Gateway), la extensión que convierte Envoy Gateway en un punto de entrada unificado y compatible con OpenAI para el tráfico de modelos de lenguaje grandes (LLM). Se explica cómo propagar las imágenes de contenedor y los gráficos de Helm en el registro local de Harbor, instalar ambos planos de control en un clúster estándar, preparar el backend opcional de límite de frecuencia basada en tokens y validar la instalación con cargas de trabajo de muestra.

Los backends de entrega de modelos (Ollama, vLLM) se implementan con el conjunto complementario de guías Modelos de peso abierto en GDC aislado. La guía del usuario de Body-based routing with Envoy Agent Router muestra cómo enrutar solicitudes a ellos por nombre de modelo.

Arquitectura

La solución se ejecuta en un clúster estándar de GDC. Una estación de trabajo del administrador inicializa las imágenes y los gráficos de Helm en el registro de Harbor del proyecto, y luego instala los dos planos de control: el controlador de Envoy Gateway, que programa el plano de datos del proxy de Envoy a partir de los recursos de la API de Gateway (GatewayClass, Gateway, HTTPRoute), y el controlador del enrutador del agente de Envoy, que extiende ese plano de datos con un procesador externo (ExtProc) para el tráfico de IA (AIGatewayRoute, AIServiceBackend, InferencePool). Los clientes de la aplicación envían solicitudes compatibles con OpenAI al proxy de Envoy, que las enruta a los backends de entrega de modelos o a los InferencePools. Una instancia de Redis opcional almacena los contadores del servicio de límite de frecuencia de Envoy para la limitación de frecuencia basada en tokens.

Arquitectura de referencia de AI Gateway con Envoy Agent Router en GDC aislado.

Puerta de enlace de Envoy

Envoy Gateway es un proyecto de código abierto creado en el proxy de Envoy que simplifica la adopción, el uso y la administración del proxy de Envoy como una puerta de enlace de la API de Kubernetes. Implementa y extiende la API de Kubernetes Gateway, sucesora de la API de Ingress: Los recursos GatewayClass y Gateway describen los puntos de entrada, los recursos de ruta, como HTTPRoute, describen cómo se hace coincidir y se reenvía el tráfico, y el diseño orientado a roles separa las responsabilidades de los equipos de infraestructura y de aplicaciones. Envoy Gateway agrega sus propias APIs de extensión, por ejemplo, EnvoyProxy (configuración del plano de datos), Backend (extremos fuera del clúster o a los que se hace referencia por FQDN) y ClientTrafficPolicy (configuración de conexión, como límites de búfer).

Enrutador de agentes de Envoy

Envoy Agent Router (anteriormente, Envoy AI Gateway) es un proyecto de código abierto que usa Envoy Gateway para controlar el tráfico de solicitudes de los clientes de aplicaciones a los servicios de IA generativa. Proporciona una capa unificada para enrutar y administrar el tráfico de LLM con enrutamiento compatible con el modelo, autenticación upstream, límite de frecuencia basada en tokens y observabilidad, y se integra con la extensión de Gateway API Inference (InferencePool, Endpoint Picker) para la selección de extremos basada en métricas. El controlador supervisa los recursos de aigateway.envoyproxy.io/v1beta1 y, luego, inserta un procesador externo junto al proxy de Envoy. El procesador externo analiza el cuerpo de la solicitud (por ejemplo, el campo model de una solicitud de finalización de chat de OpenAI), establece encabezados de enrutamiento, como x-ai-eg-model, y traduce entre esquemas de API cuando es necesario.

Antes de comenzar

Antes de continuar con la implementación, asegúrate de que tu entorno cumpla con todos los requisitos previos necesarios y de que las utilidades de línea de comandos requeridas estén configuradas correctamente. Configurar estas herramientas en tu estación de trabajo es fundamental para administrar registros de contenedores, interactuar con clústeres y automatizar el proceso de implementación.

  • El entorno aislado de GDC 1.16.2-hf1 o posterior está disponible con un clúster estándar que ejecuta Kubernetes v1.32.13-gke.400 o posterior.
  • Se creó un clúster estándar con recursos suficientes. Los componentes de la puerta de enlace solo se ejecutan en la CPU. Los backends de entrega de modelos tienen sus propios requisitos de acelerador (consulta las guías de Modelos de código abierto en GDC con aislamiento de aire).
  • La instancia de Harbor está disponible y se puede acceder a ella.
  • Se aplicó el IAM necesario.
  • Estación de trabajo con la conectividad necesaria para el entorno y la Internet

Configuración del entorno

La configuración del entorno abarca las identidades y los permisos, la estación de trabajo y el acceso al entorno de GDC y al clúster.

Identity and Access Management

Asegúrate de que las cuentas, los roles y los permisos de IAM necesarios estén configurados correctamente.

Roles de usuario de GDC en project (RoleBinding en el espacio de nombres del proyecto, otorgado por un administrador de IAM del proyecto):

  • Visualizador de instancias de Harbor (harbor-instance-viewer)
  • Creador del proyecto de Harbor (harbor-project-creator, solo si el proyecto de Harbor aún no existe)
  • Administrador de clúster estándar (standard-cluster-admin, obligatorio para gdcloud clusters get-credentials)

Rol de Usuario de GDC en el clúster estándar: Los roles de proyecto anteriores no otorgan permisos dentro del clúster. Además, un administrador de IAM del proyecto debe vincular al usuario a StandardClusterRole cluster-admin con un StandardClusterRoleBinding en el espacio de nombres del proyecto en el servidor de la API de administración. La vinculación se propaga a los clústeres estándar del proyecto en cuestión de segundos (status.clusters[].conditions muestra Propagated=True). Se requieren permisos para todo el clúster porque en esta guía se instalan definiciones de recursos personalizados, ClusterRole y un GatewayClass.

cat <<EOF | kubectl --kubeconfig MANAGEMENT_API_SERVER apply -f -
apiVersion: iam.gdc.goog/v1
kind: StandardClusterRoleBinding
metadata:
  name: user-USER-cluster-admin
  namespace: PROJECT
spec:
  roleRef:
    apiGroup: iam.gdc.goog
    kind: StandardClusterRole
    name: cluster-admin
  subjects:
    - apiGroup: rbac.authorization.k8s.io
      kind: User
      name: USER
EOF

Reemplaza lo siguiente:

  • MANAGEMENT_API_SERVER: Es la ruta al archivo kubeconfig del servidor de la API de administración.
  • USER: Usuario.
  • PROJECT: proyecto.

Permisos de la cuenta de robot de Harbor crane (crane):

  • List Repository
  • Extraer repositorio
  • Repositorio de envío
  • Leer artefacto
  • List Artifact
  • Crear etiqueta
  • Etiqueta de lista

Permisos de la cuenta de robot de extracción de imágenes de Kubernetes de Harbor (kubernetes-image-puller):

  • List Repository
  • Extraer repositorio
  • Leer artefacto
  • Enumera artefactos
  • Etiqueta de lista

Estación de trabajo

Esta guía requiere una estación de trabajo con la conectividad necesaria para el entorno y la Internet.

Requisitos

Las siguientes herramientas deben instalarse en la estación de trabajo:

  • crane: Administra y copia imágenes de contenedores y artefactos de OCI entre registros (documentación).
  • gdcloud: Es la interfaz de línea de comandos (CLI) para administrar recursos de GDC (documentación).
  • kubectl: Es la interfaz de línea de comandos (CLI) que se usa para comunicarse con un clúster de Kubernetes y administrarlo.
  • helm: Administrador de paquetes para Kubernetes, versión 3.8 o posterior (compatibilidad con el registro de OCI) (documentación).
  • curl: Herramienta de línea de comandos para transferir datos con URLs.
  • jq: Es un procesador de JSON de línea de comandos ligero y flexible.
  • yq: Es un procesador de YAML portátil de línea de comandos.

Ejecuta todos los comandos de esta guía desde la estación de trabajo, a menos que se indique lo contrario en un paso.

Configuración de la estación de trabajo

Se requiere la siguiente información sobre el entorno para la configuración de la estación de trabajo:

  • GDC_STANDARD_CLUSTER_NAME: Es el nombre del clúster estándar de GDC.
  • GDC_DOMAIN_SUFFIX: Es el sufijo de dominio para el entorno de GDC (por ejemplo, gdc.example.com).
  • GDC_ORG: Es el nombre de la organización de GDC.
  • GDC_PROJECT: Es el nombre del proyecto de GDC.
  • GDC_ZONE: Es el nombre de la zona de implementación de GDC.
  • GDC_HARBOR_INSTANCE_NAME: Es el nombre de la instancia de Harbor en el proyecto.

  • GDCS_HARBOR_PROJECT_NAME: Es el nombre del proyecto de Harbor que se usará para la imagen (el valor predeterminado es solutions).

  • GDCS_HARBOR_CRANE_ROBOT_NAME: Es el nombre de la cuenta de robot de crane de Harbor.

  • GDCS_HARBOR_CRANE_ROBOT_TOKEN: Es el token de autenticación para la cuenta de robot de crane de Harbor.

  • GDCS_HARBOR_K8S_ROBOT_NAME: Es el nombre de la cuenta de robot de extracción de imágenes de Kubernetes de Harbor.

  • GDCS_HARBOR_K8S_ROBOT_TOKEN: Es el token de autenticación para la cuenta de robot de extracción de imágenes de Kubernetes de Harbor.

Una vez que hayas recopilado los valores de todas las variables obligatorias, continúa con la generación del archivo de variables de entorno. Después de crearlo, puedes editar el archivo manualmente en cualquier momento.

  1. Crea los directorios raíz de la solución y la carpeta de secretos:

    mkdir -p ${HOME}/gdcag-solutions/env.d
    mkdir -p ${HOME}/gdcag-solutions/secrets
    
    touch ${HOME}/gdcag-solutions/secrets/harbor_crane_robot_token
    touch ${HOME}/gdcag-solutions/secrets/harbor_k8s_robot_token
    
    chmod u=rwx,go= ${HOME}/gdcag-solutions/secrets
    chmod -R u=rw,go= ${HOME}/gdcag-solutions/secrets/*
    
  2. Crea el archivo de configuración del entorno de la plataforma:

    cat << 'EOF' > ${HOME}/gdcag-solutions/env.d/platform.sh && echo "Successfully created." || echo "Failed to create!"
    # Infrastructure (Platform Native)
    export GDC_STANDARD_CLUSTER_NAME="STANDARD_CLUSTER_NAME"
    export GDC_DOMAIN_SUFFIX="DOMAIN_SUFFIX"
    export GDC_ORG="ORG"
    export GDC_PROJECT="PROJECT"
    export GDC_ZONE="ZONE"
    export GDC_HARBOR_INSTANCE_NAME="HARBOR_INSTANCE_NAME"
    
    # Derived platform values
    export GDC_ZONAL_HOSTNAME="${GDC_ORG}.${GDC_ZONE}.${GDC_DOMAIN_SUFFIX}"
    export GDC_ZONAL_CONSOLE_URL="https://console.${GDC_ZONAL_HOSTNAME}"
    export GDC_HARBOR_HOST="${GDC_HARBOR_INSTANCE_NAME}-${GDC_PROJECT}.${GDC_ORG}.${GDC_ZONE}.${GDC_DOMAIN_SUFFIX}"
    EOF
    

    Reemplaza lo siguiente:

    • STANDARD_CLUSTER_NAME: Es el nombre del clúster estándar de GDC.
    • DOMAIN_SUFFIX: Sufijo del dominio de GDC.
    • ORG: Organización de la GDC.
    • PROJECT: Proyecto de GDC.
    • ZONE: Es la zona de GDC.
    • HARBOR_INSTANCE_NAME: Es el nombre de la instancia de GDC Harbor.
  3. Crea el archivo de configuración del entorno de registro:

    cat << 'EOF' > ${HOME}/gdcag-solutions/env.d/registry.sh && echo "Successfully created." || echo "Failed to create!"
    # GDC Solutions Registry & Secrets
    export GDCS_HARBOR_PROJECT_NAME="solutions"
    export GDCS_HARBOR_CRANE_ROBOT_NAME="HARBOR_CRANE_ROBOT_NAME"
    export GDCS_HARBOR_CRANE_ROBOT_TOKEN="$(cat ${GDCS_ROOT_HOME}/secrets/harbor_crane_robot_token)"
    export GDCS_HARBOR_K8S_ROBOT_NAME="HARBOR_K8S_ROBOT_NAME"
    export GDCS_HARBOR_K8S_ROBOT_TOKEN="$(cat ${GDCS_ROOT_HOME}/secrets/harbor_k8s_robot_token)"
    export GDCS_HARBOR_K8S_PULL_SECRET="gdcs-image-pull-secret"
    
    # Derived registry values
    export GDCS_HARBOR_PROJECT_URI="${GDC_HARBOR_HOST}/${GDCS_HARBOR_PROJECT_NAME}"
    export GDCS_HARBOR_CHART_OCI_URI="oci://${GDCS_HARBOR_PROJECT_URI}"
    EOF
    

    Reemplaza lo siguiente:

    • HARBOR_CRANE_ROBOT_NAME: Es el nombre de la cuenta del robot de GDC Harbor.
    • HARBOR_K8S_ROBOT_NAME: Es el nombre de la cuenta del robot de GDC Harbor.
  4. Agrega tus tokens a los archivos de Secret:

    set +o history
    
    echo "CRANE_ROBOT_TOKEN" > ${HOME}/gdcag-solutions/secrets/harbor_crane_robot_token
    echo "KUBERNETES_ROBOT_TOKEN" > ${HOME}/gdcag-solutions/secrets/harbor_k8s_robot_token
    
    set -o history
    

    Reemplaza lo siguiente:

    • CRANE_ROBOT_TOKEN: Es el token del robot grúa.
    • KUBERNETES_ROBOT_TOKEN: Es el token de robot de Kubernetes.
  5. Crea el archivo de carga del entorno raíz:

    cat << 'EOF' > ${HOME}/gdcag-solutions/env.sh && echo "Successfully created." || echo "Failed to create!"
    export GDCS_ROOT_HOME="${HOME}/gdcag-solutions"
    echo "GDCS_ROOT_HOME=${GDCS_ROOT_HOME}"
    
    # Sourced in dependency order
    source "${GDCS_ROOT_HOME}/env.d/platform.sh"
    source "${GDCS_ROOT_HOME}/env.d/registry.sh"
    EOF
    

Configura las variables de solución

  1. Crea el directorio de implementación de la solución:

    mkdir -p ${HOME}/gdcag-solutions/ai-gateway/envoy/env.d
    
  2. Crea el archivo de configuración del entorno de solución:

    cat << 'EOF' > ${HOME}/gdcag-solutions/ai-gateway/envoy/env.d/envoy.sh && echo "Successfully created." || echo "Failed to create!"
    # Envoy Gateway
    export GDCS_ENVOY_GATEWAY_NAMESPACE="envoy-gateway-system"
    export GDCS_ENVOY_GATEWAY_VERSION="v1.8.5"
    export GDCS_ENVOY_PROXY_IMAGE_TAG="distroless-v1.38.4"
    export GDCS_ENVOY_RATELIMIT_IMAGE_TAG="8fe6ea42"
    export GDCS_GATEWAY_API_ECHO_IMAGE_TAG="v1.5.1"
    
    # Envoy Agent Router (formerly Envoy AI Gateway; the images and charts keep the ai-gateway names)
    export GDCS_ENVOY_AGENT_ROUTER_NAMESPACE="envoy-ai-gateway-system"
    export GDCS_ENVOY_AGENT_ROUTER_VERSION="v1.1.0"
    
    # Gateway API Inference Extension (InferencePool, Endpoint Picker)
    export GDCS_GATEWAY_API_INFERENCE_EXTENSION_VERSION="v1.5.0"
    
    # Redis (token-based rate limiting backend)
    export GDCS_REDIS_IMAGE_TAG="8.10.2-alpine3.23"
    export GDCS_REDIS_NAMESPACE="${GDCS_ENVOY_GATEWAY_NAMESPACE}"
    
    # Gateway class shared by the user guides
    export GDCS_GATEWAY_CLASS_NAME="envoy-ai-gateway"
    
    # Docker configuration directories for crane and Kubernetes
    export GDCS_HARBOR_CRANE_DOCKER_CONFIG="${GDCS_IMPLEMENTATION_HOME}/docker/crane"
    export GDCS_HARBOR_K8S_DOCKER_CONFIG="${GDCS_IMPLEMENTATION_HOME}/docker/k8s"
    EOF
    
  3. Crea el archivo de carga del entorno de implementación:

    cat << 'EOF' > ${HOME}/gdcag-solutions/ai-gateway/envoy/env.sh && echo "Successfully created." || echo "Failed to create!"
    source "${HOME}/gdcag-solutions/env.sh"
    
    export GDCS_IMPLEMENTATION_HOME="${HOME}/gdcag-solutions/ai-gateway/envoy"
    echo "GDCS_IMPLEMENTATION_HOME=${GDCS_IMPLEMENTATION_HOME}"
    
    # Sourced in dependency order
    source "${GDCS_IMPLEMENTATION_HOME}/env.d/envoy.sh"
    EOF
    
  4. Edita y revisa los archivos del entorno con tu editor preferido:

    ${EDITOR:-vi} ${HOME}/gdcag-solutions/env.d/platform.sh
    ${EDITOR:-vi} ${HOME}/gdcag-solutions/env.d/registry.sh
    ${EDITOR:-vi} ${HOME}/gdcag-solutions/ai-gateway/envoy/env.d/envoy.sh
    
  5. Ejecuta el archivo de entorno:

    source ${HOME}/gdcag-solutions/ai-gateway/envoy/env.sh
    

    El resultado es similar a lo siguiente:

    GDCS_ROOT_HOME=HOME_DIRECTORY_PATH/gdcag-solutions
    GDCS_IMPLEMENTATION_HOME=HOME_DIRECTORY_PATH/gdcag-solutions/ai-gateway/envoy
    

GDC

En esta guía, se supone que tu estación de trabajo está configurada para confiar en los certificados TLS de tu entorno de GDC y tu instancia de Harbor.

  1. Configura gdcloud:

    gdcloud config set core/account "default-user"
    gdcloud config set core/organization_console_url "${GDC_ZONAL_CONSOLE_URL}"
    gdcloud config set core/project "${GDC_PROJECT}"
    gdcloud config set core/zone "${GDC_ZONE}"
    
  2. Autentícate en el entorno de GDC:

    gdcloud auth login
    

Clúster

  1. Recupera las credenciales del clúster:

    gdcloud clusters get-credentials "${GDC_STANDARD_CLUSTER_NAME}" \
    --project="${GDC_PROJECT}" \
    --standard \
    --zone="${GDC_ZONE}"
    
  2. Verifica la conectividad con el clúster:

    kubectl get nodes -L node.cluster.private.gdc.goog/machine-class
    
  3. Verifica que cada nodo ejecute la versión de Kubernetes que se requiere en la sección Antes de comenzar de esta guía:

    kubectl get nodes -o custom-columns='NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion'
    

Preparación de la migración de artefactos

  1. Crea un archivo de configuración de Docker para crane. Se usa una cuenta de robot para enviar capas de imágenes grandes y evitar los tiempos de espera del token de autenticación cuando se usa el asistente de credenciales de Managed Harbor Service (MHS) (docker-credential-mhs) con una cuenta de usuario:

    set +o history
    
    export DOCKER_CONFIG="${GDCS_HARBOR_CRANE_DOCKER_CONFIG}"
    
    crane auth login "${GDC_HARBOR_HOST}" \
    --password="${GDCS_HARBOR_CRANE_ROBOT_TOKEN}" \
    --username="${GDCS_HARBOR_CRANE_ROBOT_NAME}"
    
    set -o history
    
  2. Crea un archivo de configuración de Docker para Kubernetes:

    set +o history
    
    export DOCKER_CONFIG="${GDCS_HARBOR_K8S_DOCKER_CONFIG}"
    
    crane auth login "${GDC_HARBOR_HOST}" \
    --password="${GDCS_HARBOR_K8S_ROBOT_TOKEN}" \
    --username="${GDCS_HARBOR_K8S_ROBOT_NAME}"
    
    set -o history
    
  3. Accede al registro de OCI de Harbor con helm usando la cuenta de robot de extracción de imágenes de Kubernetes. helm mantiene sus propias credenciales de registro y las necesita para recuperar los gráficos de Harbor:

    set +o history
    helm registry login "${GDC_HARBOR_HOST}" \
    --password="${GDCS_HARBOR_K8S_ROBOT_TOKEN}" \
    --username="${GDCS_HARBOR_K8S_ROBOT_NAME}"
    set -o history
    

    El resultado es similar a lo siguiente:

    Login Succeeded
    
  4. Crea la secuencia de comandos seed_registry.sh:

    cat << 'EOF' > ${GDCS_IMPLEMENTATION_HOME}/seed_registry.sh && echo "Successfully created." || echo "Failed to create!"
    #!/bin/bash
    
    # seed_registry.sh: Modular artifact migration for GDC Solutions
    
    # Requires the env.sh file to be sourced first.
    SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    set -o nounset
    source "${SCRIPT_DIR}/env.sh"
    
    # Set the Docker config
    export DOCKER_CONFIG="${GDCS_HARBOR_CRANE_DOCKER_CONFIG}"
    eval "${SERIALIZED_IMAGES}"
    
    # Ensure GDCS_REGISTRY_IMAGES is set
    if [[ ${#GDCS_REGISTRY_IMAGES[@]} -eq 0 ]]; then
      echo "GDCS_REGISTRY_IMAGES must be set, exiting..."
      exit 1
    fi
    
    # Migrate the images
    for source_image in "${GDCS_REGISTRY_IMAGES[@]}"; do
      # Strip the registry host only when the first path segment is a host (contains a dot or a port)
      first_segment="${source_image%%/*}"
      if [[ "${first_segment}" == *.* || "${first_segment}" == *:* ]]; then
        image_path="${source_image#*/}"
      else
        image_path="${source_image}"
      fi
      destination_image="${GDCS_HARBOR_PROJECT_URI}/${image_path}"
      # Ensure the folder structure is created
      crane append \
        --new_layer=<(tar czf - -T /dev/null) \
        --new_tag="${destination_image%:*}:create" \
        --oci-empty-base 2> /dev/null || true
      # Copy the linux/amd64 platform only to avoid transferring multi-arch layers over air-gapped links
      crane copy --platform linux/amd64 "${source_image}" "${destination_image}" 2> /dev/null
    done
    echo "Migration complete: Images are available at ${GDCS_HARBOR_PROJECT_URI}"
    EOF
    chmod u+x "${GDCS_IMPLEMENTATION_HOME}/seed_registry.sh"
    
  5. Crea la secuencia de comandos seed_charts.sh Los gráficos de Helm publicados como artefactos de OCI también se copian con crane, sin selección de plataforma y sin la etiqueta create que se usa para los repositorios de imágenes de contenedor:

    cat << 'EOF' > ${GDCS_IMPLEMENTATION_HOME}/seed_charts.sh && echo "Successfully created." || echo "Failed to create!"
    #!/bin/bash
    
    # seed_charts.sh: OCI Helm chart migration for GDC Solutions
    
    # Requires the env.sh file to be sourced first.
    SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    set -o nounset
    source "${SCRIPT_DIR}/env.sh"
    
    # Set the Docker config
    export DOCKER_CONFIG="${GDCS_HARBOR_CRANE_DOCKER_CONFIG}"
    eval "${SERIALIZED_CHARTS}"
    
    # Ensure GDCS_REGISTRY_CHARTS is set
    if [[ ${#GDCS_REGISTRY_CHARTS[@]} -eq 0 ]]; then
      echo "GDCS_REGISTRY_CHARTS must be set, exiting..."
      exit 1
    fi
    
    # Migrate the charts (source format: REGISTRY_HOST/REPOSITORY:CHART_VERSION)
    for source_chart in "${GDCS_REGISTRY_CHARTS[@]}"; do
      chart_path="${source_chart#*/}"
      destination_chart="${GDCS_HARBOR_PROJECT_URI}/${chart_path}"
      crane copy "${source_chart}" "${destination_chart}" || { echo "Failed to copy ${source_chart} to ${destination_chart}"; exit 1; }
    done
    echo "Migration complete: Charts are available at ${GDCS_HARBOR_CHART_OCI_URI}"
    EOF
    chmod u+x "${GDCS_IMPLEMENTATION_HOME}/seed_charts.sh"
    

    Reemplaza lo siguiente:

    • REGISTRY_HOST: Host del registro.
    • REPOSITORY: repositorio.
    • CHART_VERSION: Es la versión del gráfico.
  6. Define la lista de imágenes de contenedor requeridas para la solución:

    declare -a GDCS_REGISTRY_IMAGES=(
      "docker.io/envoyproxy/gateway:${GDCS_ENVOY_GATEWAY_VERSION}"
      "docker.io/envoyproxy/envoy:${GDCS_ENVOY_PROXY_IMAGE_TAG}"
      "docker.io/envoyproxy/ratelimit:${GDCS_ENVOY_RATELIMIT_IMAGE_TAG}"
      "docker.io/envoyproxy/ai-gateway-controller:${GDCS_ENVOY_AGENT_ROUTER_VERSION}"
      "docker.io/envoyproxy/ai-gateway-extproc:${GDCS_ENVOY_AGENT_ROUTER_VERSION}"
      "docker.io/envoyproxy/ai-gateway-testupstream:${GDCS_ENVOY_AGENT_ROUTER_VERSION}"
      "docker.io/library/redis:${GDCS_REDIS_IMAGE_TAG}"
      "registry.k8s.io/gateway-api/echo-basic:${GDCS_GATEWAY_API_ECHO_IMAGE_TAG}"
    )
    export SERIALIZED_IMAGES=$(declare -p GDCS_REGISTRY_IMAGES)
    
  7. Propaga las imágenes de contenedor necesarias en Artifact Registry:

    ${GDCS_IMPLEMENTATION_HOME}/seed_registry.sh
    
  1. Define la lista de gráficos de Helm necesarios para la solución:

    declare -a GDCS_REGISTRY_CHARTS=(
      "docker.io/envoyproxy/gateway-crds-helm:${GDCS_ENVOY_GATEWAY_VERSION}"
      "docker.io/envoyproxy/gateway-helm:${GDCS_ENVOY_GATEWAY_VERSION}"
      "docker.io/envoyproxy/ai-gateway-crds-helm:${GDCS_ENVOY_AGENT_ROUTER_VERSION}"
      "docker.io/envoyproxy/ai-gateway-helm:${GDCS_ENVOY_AGENT_ROUTER_VERSION}"
    )
    export SERIALIZED_CHARTS=$(declare -p GDCS_REGISTRY_CHARTS)
    
  2. Propaga los gráficos de Helm necesarios en el registro de artefactos:

    ${GDCS_IMPLEMENTATION_HOME}/seed_charts.sh
    
  3. Verifica que los gráficos se puedan leer desde Harbor:

    helm show chart "${GDCS_HARBOR_CHART_OCI_URI}/envoyproxy/gateway-helm" --version "${GDCS_ENVOY_GATEWAY_VERSION}" | grep -E '^(name|version):'
    helm show chart "${GDCS_HARBOR_CHART_OCI_URI}/envoyproxy/ai-gateway-helm" --version "${GDCS_ENVOY_AGENT_ROUTER_VERSION}" | grep -E '^(name|version):'
    

    El resultado es similar a lo siguiente:

    name: gateway-helm
    version: v1.8.5
    name: ai-gateway-helm
    version: v1.1.0
    
  4. Descarga los manifiestos de la extensión de inferencia de la API de Gateway. Se publican como un recurso de versión, no como un gráfico:

    mkdir -p "${GDCS_IMPLEMENTATION_HOME}/manifests"
    
    curl --fail --location --show-error --silent \
    --output "${GDCS_IMPLEMENTATION_HOME}/manifests/gateway-api-inference-extension-${GDCS_GATEWAY_API_INFERENCE_EXTENSION_VERSION}.yaml" \
    "https://github.com/kubernetes-sigs/gateway-api-inference-extension/releases/download/${GDCS_GATEWAY_API_INFERENCE_EXTENSION_VERSION}/manifests.yaml"
    
    grep --count '^kind: CustomResourceDefinition' "${GDCS_IMPLEMENTATION_HOME}/manifests/gateway-api-inference-extension-${GDCS_GATEWAY_API_INFERENCE_EXTENSION_VERSION}.yaml"
    

    El resultado es similar a lo siguiente:

    4
    

Puerta de enlace de Envoy

Primero, se instala Envoy Gateway y se valida por sí solo con la guía de inicio rápido de upstream. La integración del enrutador de Envoy Agent se explica en la siguiente sección.

Espacio de nombres

  1. Crea el espacio de nombres para Envoy Gateway. Los proxies de Envoy Deployment de cada Gateway también se crean en este espacio de nombres:

    kubectl create namespace "${GDCS_ENVOY_GATEWAY_NAMESPACE}"
    
  2. Agrega el imagePullSecret:

    kubectl create secret docker-registry "${GDCS_HARBOR_K8S_PULL_SECRET}" \
    --dry-run=client \
    --from-file=.dockerconfigjson=${GDCS_HARBOR_K8S_DOCKER_CONFIG}/config.json \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" \
    --output=yaml | kubectl apply -f -
    

Definiciones de recursos personalizados

  1. Instala la API de Gateway (canal estándar) y las definiciones de recursos personalizados (CRD) de Envoy Gateway desde el gráfico inicial:

    helm template eg-crds "${GDCS_HARBOR_CHART_OCI_URI}/envoyproxy/gateway-crds-helm" \
    --set crds.gatewayAPI.channel=standard \
    --set crds.gatewayAPI.enabled=true \
    --set crds.envoyGateway.enabled=true \
    --version "${GDCS_ENVOY_GATEWAY_VERSION}" | kubectl apply --server-side --filename=-
    
  2. Verifica que las CRD estén registradas:

    kubectl get crd | grep -E 'gateway.networking.k8s.io|gateway.envoyproxy.io'
    

Controlador

  1. Crea el archivo de valores de Helm para Envoy Gateway. Las imágenes se extraen de Harbor con el secreto de extracción de imágenes; crds.enabled=false porque los CRD se instalaron por separado:

    cat <<EOF > "${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-values.yaml" && echo "Successfully created." || echo "Failed to create!"
    config:
      envoyGateway:
        extensionApis:
          enableBackend: true
          enableEnvoyPatchPolicy: true
        gateway:
          controllerName: gateway.envoyproxy.io/gatewayclass-controller
        logging:
          level:
            default: info
        provider:
          type: Kubernetes
    crds:
      enabled: false
    global:
      imagePullSecrets:
        - name: ${GDCS_HARBOR_K8S_PULL_SECRET}
      imageRegistry: ${GDCS_HARBOR_PROJECT_URI}
      images:
        envoyProxy:
          image: ${GDCS_HARBOR_PROJECT_URI}/envoyproxy/envoy:${GDCS_ENVOY_PROXY_IMAGE_TAG}
          pullSecrets:
            - name: ${GDCS_HARBOR_K8S_PULL_SECRET}
    EOF
    
  2. Instala Envoy Gateway:

    helm upgrade --install eg "${GDCS_HARBOR_CHART_OCI_URI}/envoyproxy/gateway-helm" \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" \
    --values="${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-values.yaml" \
    --version="${GDCS_ENVOY_GATEWAY_VERSION}"
    
  3. Espera a que el controlador de Envoy Gateway esté disponible:

    watch --color --interval 5 --no-title \
    "kubectl get deployment/envoy-gateway \
    --namespace=${GDCS_ENVOY_GATEWAY_NAMESPACE} | GREP_COLORS='mt=01;92' egrep --color=always -e '^' -e '1/1     1            1'"
    
  4. Verifica que las imágenes del controlador y su trabajo de generación de certificados provengan de Harbor:

    kubectl get pods --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" \
    --output=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}'
    

Proxy de Envoy y clase Gateway

  1. Crea el manifiesto para la plantilla del plano de datos EnvoyProxy y el GatewayClass. El EnvoyProxy establece la imagen del proxy, el secreto de extracción de la imagen y las solicitudes de recursos de los Pod del proxy; el GatewayClass se comparte en las guías del usuario:

    cat <<EOF > "${GDCS_IMPLEMENTATION_HOME}/gateway-class.yaml" && echo "Successfully created." || echo "Failed to create!"
    apiVersion: gateway.envoyproxy.io/v1alpha1
    kind: EnvoyProxy
    metadata:
      name: ${GDCS_GATEWAY_CLASS_NAME}
      namespace: ${GDCS_ENVOY_GATEWAY_NAMESPACE}
    spec:
      provider:
        type: Kubernetes
        kubernetes:
          envoyDeployment:
            container:
              image: ${GDCS_HARBOR_PROJECT_URI}/envoyproxy/envoy:${GDCS_ENVOY_PROXY_IMAGE_TAG}
              resources:
                limits:
                  memory: 2Gi
                requests:
                  cpu: 250m
                  memory: 512Mi
            pod:
              imagePullSecrets:
                - name: ${GDCS_HARBOR_K8S_PULL_SECRET}
    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: GatewayClass
    metadata:
      name: ${GDCS_GATEWAY_CLASS_NAME}
    spec:
      controllerName: gateway.envoyproxy.io/gatewayclass-controller
      parametersRef:
        group: gateway.envoyproxy.io
        kind: EnvoyProxy
        name: ${GDCS_GATEWAY_CLASS_NAME}
        namespace: ${GDCS_ENVOY_GATEWAY_NAMESPACE}
    EOF
    
  2. Aplica el manifiesto para EnvoyProxy y GatewayClass:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/gateway-class.yaml"
    
  3. Verifica que el GatewayClass esté aceptado:

    kubectl get gatewayclass "${GDCS_GATEWAY_CLASS_NAME}"
    

    El resultado es similar a lo siguiente:

    NAME               CONTROLLER                                      ACCEPTED   AGE
    envoy-ai-gateway   gateway.envoyproxy.io/gatewayclass-controller   True       5s
    

Validación

La validación implementa la guía de inicio rápido de Envoy Gateway (un backend de eco detrás de un HTTPRoute) en el espacio de nombres de Envoy Gateway y, luego, lo quita.

  1. Crea el manifiesto para la carga de trabajo de inicio rápido:

    cat <<EOF > "${GDCS_IMPLEMENTATION_HOME}/eg-quickstart.yaml" && echo "Successfully created." || echo "Failed to create!"
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: eg-quickstart
    spec:
      gatewayClassName: ${GDCS_GATEWAY_CLASS_NAME}
      listeners:
        - name: http
          protocol: HTTP
          port: 80
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: eg-quickstart-backend
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: eg-quickstart-backend
      labels:
        app: eg-quickstart-backend
    spec:
      ports:
        - name: http
          port: 3000
          targetPort: 3000
      selector:
        app: eg-quickstart-backend
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: eg-quickstart-backend
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: eg-quickstart-backend
      template:
        metadata:
          labels:
            app: eg-quickstart-backend
        spec:
          serviceAccountName: eg-quickstart-backend
          containers:
            - image: ${GDCS_HARBOR_PROJECT_URI}/gateway-api/echo-basic:${GDCS_GATEWAY_API_ECHO_IMAGE_TAG}
              imagePullPolicy: IfNotPresent
              name: backend
              ports:
                - containerPort: 3000
              env:
                - name: POD_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.name
                - name: NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
          imagePullSecrets:
            - name: ${GDCS_HARBOR_K8S_PULL_SECRET}
    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: eg-quickstart-backend
    spec:
      parentRefs:
        - name: eg-quickstart
      hostnames:
        - "www.example.com"
      rules:
        - backendRefs:
            - group: ""
              kind: Service
              name: eg-quickstart-backend
              port: 3000
              weight: 1
          matches:
            - path:
                type: PathPrefix
                value: /
    EOF
    
  2. Aplica el manifiesto para la carga de trabajo de inicio rápido:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/eg-quickstart.yaml" \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"
    
  3. Espera a que se programe el Gateway:

    watch --color --interval 5 --no-title \
    "kubectl get gateway/eg-quickstart \
    --namespace=${GDCS_ENVOY_GATEWAY_NAMESPACE} | GREP_COLORS='mt=01;92' egrep --color=always -e '^' -e 'True'"
    
  4. Espera a que el backend Deployment esté disponible:

    watch --color --interval 5 --no-title \
    "kubectl get deployment/eg-quickstart-backend \
    --namespace=${GDCS_ENVOY_GATEWAY_NAMESPACE} | GREP_COLORS='mt=01;92' egrep --color=always -e '^' -e '1/1     1            1'"
    
  5. Envía una solicitud de prueba a través de la puerta de enlace con el reenvío de puertos. El Envoy Service de un Gateway se encuentra por las etiquetas de la puerta de enlace propietaria:

    export ENVOY_SERVICE=$(kubectl get service --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" --selector="gateway.envoyproxy.io/owning-gateway-namespace=${GDCS_ENVOY_GATEWAY_NAMESPACE},gateway.envoyproxy.io/owning-gateway-name=eg-quickstart" --output=jsonpath='{.items[0].metadata.name}')
    echo "ENVOY_SERVICE=${ENVOY_SERVICE}"
    
    kubectl port-forward "service/${ENVOY_SERVICE}" \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" 8888:80 &
    PF_PID=$!
    
    sleep 2
    
    curl --header "Host: www.example.com" \
    --no-progress-meter \
    --show-error \
    http://127.0.0.1:8888/get | jq
    
    kill -9 ${PF_PID}
    

    El resultado es similar a lo siguiente:

    {
      "path": "/get",
      "host": "www.example.com",
      "method": "GET",
      ...
      "namespace": "envoy-gateway-system",
      "pod": "eg-quickstart-backend-...",
      ...
    }
    
  6. Quita la carga de trabajo de inicio rápido:

    kubectl delete \
    --filename="${GDCS_IMPLEMENTATION_HOME}/eg-quickstart.yaml" \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"
    

Enrutador de agentes de Envoy

El enrutador del agente de Envoy se instala en su propio espacio de nombres. Luego, se vuelve a configurar la puerta de enlace de Envoy para que la llame como su servidor de extensión.

Espacio de nombres

  1. Crea el espacio de nombres para el controlador del enrutador del agente de Envoy:

    kubectl create namespace "${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}"
    
  2. Agrega el imagePullSecret:

    kubectl create secret docker-registry "${GDCS_HARBOR_K8S_PULL_SECRET}" \
    --dry-run=client \
    --from-file=.dockerconfigjson=${GDCS_HARBOR_K8S_DOCKER_CONFIG}/config.json \
    --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}" \
    --output=yaml | kubectl apply -f -
    

Definiciones de recursos personalizados

  1. Instala las CRD del Envoy Agent Router desde el gráfico inicial:

    helm template aieg-crds "${GDCS_HARBOR_CHART_OCI_URI}/envoyproxy/ai-gateway-crds-helm" \
    --version "${GDCS_ENVOY_AGENT_ROUTER_VERSION}" | kubectl apply --server-side --filename=-
    
  2. Instala las CRD de la extensión de inferencia de la API de Gateway (InferencePool, InferenceObjective) desde los manifiestos descargados:

    kubectl apply --server-side \
    --filename="${GDCS_IMPLEMENTATION_HOME}/manifests/gateway-api-inference-extension-${GDCS_GATEWAY_API_INFERENCE_EXTENSION_VERSION}.yaml"
    
  3. Verifica que las CRD estén registradas:

    kubectl get crd | grep -E 'aigateway.envoyproxy.io|inference.networking'
    

    El resultado es similar a lo siguiente:

    aigatewayroutes.aigateway.envoyproxy.io                 ...
    aiservicebackends.aigateway.envoyproxy.io               ...
    backendsecuritypolicies.aigateway.envoyproxy.io         ...
    gatewayconfigs.aigateway.envoyproxy.io                  ...
    inferencemodelrewrites.inference.networking.x-k8s.io    ...
    inferenceobjectives.inference.networking.x-k8s.io       ...
    inferencepoolimports.inference.networking.x-k8s.io      ...
    inferencepools.inference.networking.k8s.io              ...
    mcproutes.aigateway.envoyproxy.io                       ...
    quotapolicies.aigateway.envoyproxy.io                   ...
    

Redis

El servicio de límite de frecuencia de Envoy, que almacena sus contadores en Redis, aplica el límite de frecuencia basado en tokens. En esta guía, se implementa un servidor Redis de una sola réplica sin persistencia. En su lugar, se puede usar un servicio Redis existente cambiando el valor de rateLimit.backend.redis.url en la siguiente sección.

  1. Crea el manifiesto para el Deployment de Redis:

    cat <<EOF > "${GDCS_IMPLEMENTATION_HOME}/redis.yaml" && echo "Successfully created." || echo "Failed to create!"
    apiVersion: v1
    kind: Service
    metadata:
      name: redis
      labels:
        app: redis
    spec:
      ports:
        - name: redis
          port: 6379
      selector:
        app: redis
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: redis
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: redis
      template:
        metadata:
          labels:
            app: redis
        spec:
          containers:
            - image: ${GDCS_HARBOR_PROJECT_URI}/library/redis:${GDCS_REDIS_IMAGE_TAG}
              imagePullPolicy: IfNotPresent
              name: redis
              ports:
                - name: redis
                  containerPort: 6379
              resources:
                limits:
                  memory: 512Mi
                requests:
                  cpu: 100m
                  memory: 128Mi
          imagePullSecrets:
            - name: ${GDCS_HARBOR_K8S_PULL_SECRET}
          restartPolicy: Always
    EOF
    
  2. Aplica el manifiesto para el Deployment de Redis:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/redis.yaml" \
    --namespace="${GDCS_REDIS_NAMESPACE}"
    
  3. Espera a que el Deployment de Redis esté disponible:

    watch --color --interval 5 --no-title \
    "kubectl get deployment/redis \
    --namespace=${GDCS_REDIS_NAMESPACE} | GREP_COLORS='mt=01;92' egrep --color=always -e '^' -e '1/1     1            1'"
    

Controlador

  1. Crea el archivo de valores de Helm para Envoy Agent Router:

    cat <<EOF > "${GDCS_IMPLEMENTATION_HOME}/envoy-agent-router-values.yaml" && echo "Successfully created." || echo "Failed to create!"
    controller:
      image:
        repository: ${GDCS_HARBOR_PROJECT_URI}/envoyproxy/ai-gateway-controller
      imagePullSecrets:
        - name: ${GDCS_HARBOR_K8S_PULL_SECRET}
    envoyGateway:
      namespace: ${GDCS_ENVOY_GATEWAY_NAMESPACE}
    extProc:
      image:
        repository: ${GDCS_HARBOR_PROJECT_URI}/envoyproxy/ai-gateway-extproc
      imagePullSecrets:
        - name: ${GDCS_HARBOR_K8S_PULL_SECRET}
    global:
      imagePullSecrets:
        - name: ${GDCS_HARBOR_K8S_PULL_SECRET}
    EOF
    
  2. Instala el enrutador del agente de Envoy:

    helm upgrade --install aieg "${GDCS_HARBOR_CHART_OCI_URI}/envoyproxy/ai-gateway-helm" \
    --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}" \
    --values="${GDCS_IMPLEMENTATION_HOME}/envoy-agent-router-values.yaml" \
    --version="${GDCS_ENVOY_AGENT_ROUTER_VERSION}"
    
  3. Espera a que el controlador del router del agente de Envoy esté disponible:

    watch --color --interval 5 --no-title \
    "kubectl get deployment/ai-gateway-controller \
    --namespace=${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE} | GREP_COLORS='mt=01;92' egrep --color=always -e '^' -e '1/1     1            1'"
    

Integración de Envoy Gateway

La puerta de enlace de Envoy debe reconfigurarse para llamar al controlador del enrutador del agente de Envoy como su servidor de extensión, aceptar recursos InferencePool como backends y usar el servicio de límite de frecuencia respaldado por Redis.

  1. Crea el archivo de valores de Helm para la integración:

    cat <<EOF > "${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-agent-router-values.yaml" && echo "Successfully created." || echo "Failed to create!"
    config:
      envoyGateway:
        extensionManager:
          backendResources:
            - group: inference.networking.k8s.io
              kind: InferencePool
              version: v1
          hooks:
            xdsTranslator:
              post:
                - Translation
                - Cluster
                - Route
              translation:
                cluster:
                  includeAll: true
                listener:
                  includeAll: true
                route:
                  includeAll: true
                secret:
                  includeAll: true
          service:
            fqdn:
              hostname: ai-gateway-controller.${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}.svc.cluster.local
              port: 1063
        rateLimit:
          backend:
            redis:
              url: redis.${GDCS_REDIS_NAMESPACE}.svc.cluster.local:6379
            type: Redis
    EOF
    
  2. Actualiza Envoy Gateway con ambos archivos de valores:

    helm upgrade --install eg "${GDCS_HARBOR_CHART_OCI_URI}/envoyproxy/gateway-helm" \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" \
    --values="${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-values.yaml" \
    --values="${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-agent-router-values.yaml" \
    --version="${GDCS_ENVOY_GATEWAY_VERSION}"
    
  3. Crea el manifiesto para el ClusterRole que permite que el controlador de Envoy Gateway observe los recursos InferencePool. El gráfico no otorga este permiso; un ClusterRoleBinding independiente sobrevive a las actualizaciones del gráfico, a diferencia de un parche del ClusterRole administrado por el gráfico:

    cat <<EOF > "${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-inferencepool-rbac.yaml" && echo "Successfully created." || echo "Failed to create!"
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: envoy-gateway-inferencepool-reader
    rules:
      - apiGroups:
          - inference.networking.k8s.io
        resources:
          - inferencepools
        verbs:
          - get
          - list
          - watch
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: envoy-gateway-inferencepool-reader
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: envoy-gateway-inferencepool-reader
    subjects:
      - kind: ServiceAccount
        name: envoy-gateway
        namespace: ${GDCS_ENVOY_GATEWAY_NAMESPACE}
    EOF
    
  4. Aplica el manifiesto para ClusterRole:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-inferencepool-rbac.yaml"
    
  5. Reinicia el controlador de Envoy Gateway para que adopte la nueva configuración y los permisos, y espera a que esté disponible:

    kubectl rollout restart deployment/envoy-gateway \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"
    
    kubectl rollout status deployment/envoy-gateway \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" \
    --timeout=5m
    
  6. Verifica que el servicio de límite de frecuencia se haya implementado y esté disponible:

    watch --color --interval 5 --no-title \
    "kubectl get deployment/envoy-ratelimit \
    --namespace=${GDCS_ENVOY_GATEWAY_NAMESPACE} | GREP_COLORS='mt=01;92' egrep --color=always -e '^' -e '1/1     1            1'"
    

Validación

La validación implementa el ejemplo básico del router de Envoy Agent (un upstream simulado compatible con OpenAI detrás de un AIGatewayRoute) en el espacio de nombres del router de Envoy Agent y lo quita después.

  1. Crea el manifiesto para la carga de trabajo de validación:

    cat <<EOF > "${GDCS_IMPLEMENTATION_HOME}/aieg-basic.yaml" && echo "Successfully created." || echo "Failed to create!"
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: aieg-basic
    spec:
      gatewayClassName: ${GDCS_GATEWAY_CLASS_NAME}
      listeners:
        - name: http
          protocol: HTTP
          port: 80
    ---
    apiVersion: gateway.envoyproxy.io/v1alpha1
    kind: ClientTrafficPolicy
    metadata:
      name: aieg-basic-buffer-limit
    spec:
      targetRefs:
        - group: gateway.networking.k8s.io
          kind: Gateway
          name: aieg-basic
      connection:
        bufferLimit: 50Mi
    ---
    apiVersion: aigateway.envoyproxy.io/v1beta1
    kind: AIGatewayRoute
    metadata:
      name: aieg-basic
    spec:
      parentRefs:
        - name: aieg-basic
          kind: Gateway
          group: gateway.networking.k8s.io
      rules:
        - matches:
            - headers:
                - type: Exact
                  name: x-ai-eg-model
                  value: some-cool-self-hosted-model
          backendRefs:
            - name: aieg-basic-testupstream
    ---
    apiVersion: aigateway.envoyproxy.io/v1beta1
    kind: AIServiceBackend
    metadata:
      name: aieg-basic-testupstream
    spec:
      schema:
        name: OpenAI
      backendRef:
        name: aieg-basic-testupstream
        kind: Backend
        group: gateway.envoyproxy.io
    ---
    apiVersion: gateway.envoyproxy.io/v1alpha1
    kind: Backend
    metadata:
      name: aieg-basic-testupstream
    spec:
      endpoints:
        - fqdn:
            hostname: aieg-basic-testupstream.${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}.svc.cluster.local
            port: 80
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aieg-basic-testupstream
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aieg-basic-testupstream
      template:
        metadata:
          labels:
            app: aieg-basic-testupstream
        spec:
          containers:
            - name: testupstream
              image: ${GDCS_HARBOR_PROJECT_URI}/envoyproxy/ai-gateway-testupstream:${GDCS_ENVOY_AGENT_ROUTER_VERSION}
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 8080
              env:
                - name: TESTUPSTREAM_ID
                  value: test
              readinessProbe:
                httpGet:
                  path: /health
                  port: 8080
                initialDelaySeconds: 5
                periodSeconds: 10
              livenessProbe:
                httpGet:
                  path: /health
                  port: 8080
                initialDelaySeconds: 10
                periodSeconds: 20
          imagePullSecrets:
            - name: ${GDCS_HARBOR_K8S_PULL_SECRET}
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aieg-basic-testupstream
    spec:
      selector:
        app: aieg-basic-testupstream
      ports:
        - protocol: TCP
          port: 80
          targetPort: 8080
      type: ClusterIP
    EOF
    
  2. Aplica el manifiesto para la carga de trabajo de validación:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/aieg-basic.yaml" \
    --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}"
    
  3. Espera a que se programe el Gateway:

    watch --color --interval 5 --no-title \
    "kubectl get gateway/aieg-basic \
    --namespace=${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE} | GREP_COLORS='mt=01;92' egrep --color=always -e '^' -e 'True'"
    
  4. Espera a que el upstream simulado Deployment esté disponible:

    watch --color --interval 5 --no-title \
    "kubectl get deployment/aieg-basic-testupstream \
    --namespace=${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE} | GREP_COLORS='mt=01;92' egrep --color=always -e '^' -e '1/1     1            1'"
    
  5. Verifica que el proxy de Envoy Pod del Gateway ejecute el sidecar del procesador externo insertado por el enrutador del agente de Envoy:

    kubectl get pods --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" \
    --selector="gateway.envoyproxy.io/owning-gateway-name=aieg-basic" \
    --output=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].name}{"\n"}{end}'
    

    El resultado es similar a lo siguiente:

    envoy-envoy-ai-gateway-system-aieg-basic-...   envoy shutdown-manager ai-gateway-extproc
    
  6. Envía una solicitud de finalización de chat a través de la puerta de enlace con el reenvío de puertos. El procesador externo lee el campo model del cuerpo de la solicitud y lo enruta al upstream simulado:

    export ENVOY_SERVICE=$(kubectl get service --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" --selector="gateway.envoyproxy.io/owning-gateway-namespace=${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE},gateway.envoyproxy.io/owning-gateway-name=aieg-basic" --output=jsonpath='{.items[0].metadata.name}')
    echo "ENVOY_SERVICE=${ENVOY_SERVICE}"
    
    kubectl port-forward "service/${ENVOY_SERVICE}" \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" 8888:80 &
    PF_PID=$!
    
    sleep 2
    
    curl http://127.0.0.1:8888/v1/chat/completions \
    --data '{"model": "some-cool-self-hosted-model", "messages": [{"role": "user", "content": "Say this is a test."}]}' \
    --header "Content-Type: application/json" \
    --no-progress-meter \
    --show-error | jq
    
    kill -9 ${PF_PID}
    

    El resultado es similar a lo siguiente:

    {
      "choices": [
        {
          "index": 0,
          "message": {
            "role": "assistant",
            "content": "..."
          },
          "finish_reason": "stop"
        }
      ],
      "usage": {
        ...
      }
    }
    
  7. Quita la carga de trabajo de validación:

    kubectl delete \
    --filename="${GDCS_IMPLEMENTATION_HOME}/aieg-basic.yaml" \
    --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}"
    

Operaciones

Tareas del segundo día para los planos de control de la puerta de enlace.

Actualizar

  • Propaga las imágenes y los gráficos de las versiones nuevas (actualiza las variables de versión en env.sh, vuelve a ejecutar seed_registry.sh y seed_charts.sh), aplica los nuevos gráficos de CRD con kubectl apply --server-side y, luego, ejecuta los mismos comandos de helm upgrade --install con el nuevo --version. Consulta la matriz de compatibilidad del enrutador de Envoy Agent para conocer las versiones de Envoy Gateway y de la API de Gateway que admite la versión de destino. El enrutador de Envoy Agent 1.1.0 se compila en Envoy Gateway 1.8.

Desinstalar

  • Primero, borra los recursos Gateway, AIGatewayRoute y InferencePool de las guías del usuario y, luego, helm uninstall aieg --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}", helm uninstall eg --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}", el ClusterRole envoy-gateway-inferencepool-reader, el GatewayClass, el Deployment de Redis y, por último, las CRD.

Soluciona problemas

Síntoma Causa probable Acción
helm show chart o helm upgrade falla con unauthorized o FetchReference helm no tiene credenciales para Harbor (no usa la configuración de Docker crane). Vuelve a ejecutar el paso helm registry login con la cuenta de robot de extracción de imágenes de Kubernetes.
Envoy Gateway o Envoy Agent Router Pods permanecen en ImagePullBackOff No se propagó la imagen o falta el secreto de extracción de la imagen en el espacio de nombres Verifica kubectl describe pod, compara la ruta de acceso a la imagen con crane ls "${GDCS_HARBOR_PROJECT_URI}/envoyproxy/gateway" y verifica que el secreto exista en ${GDCS_ENVOY_GATEWAY_NAMESPACE} y ${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}.
Gateway permanece Programmed=False después de la integración El controlador de Envoy Gateway no puede acceder al servidor de extensión o no se reinició después de la actualización. kubectl logs deployment/envoy-gateway --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"; confirma que service/ai-gateway-controller existe en ${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE} y que el puerto 1063 aparece en la lista; repite el reinicio de la implementación.
No se acepta la referencia AIGatewayRoute a un InferencePool Envoy Gateway no tiene permiso para leer inferencepools, o faltan los CRD de InferencePool Verifica el CRD ClusterRoleBinding envoy-gateway-inferencepool-reader y el CRD inferencepools.inference.networking.k8s.io; revisa los registros del controlador para forbidden.
La solicitud de finalización de chat devuelve 413 o se restablece la conexión para instrucciones grandes El límite predeterminado del búfer de Envoy (32 KiB) es demasiado pequeño para las cargas útiles de IA. Adjunta un ClientTrafficPolicy con connection.bufferLimit (la validación usa 50 Mi) al Gateway.
El servicio de límite de frecuencia Pod es CrashLoopBackOff No se puede acceder a Redis en la URL configurada kubectl get service redis --namespace="${GDCS_REDIS_NAMESPACE}"; corrige rateLimit.backend.redis.url en el archivo de valores de integración y vuelve a actualizar.

Materiales adicionales