在 GDC 网闸隔离配置上使用 Envoy 代理路由器参考实现的 AI 网关

本文档提供了在 Google Distributed Cloud (GDC) 网闸隔离环境中部署 AI Gateway 的分步说明。该网关由 Envoy Gateway(基于 Envoy 代理的 Kubernetes Gateway API 实现)和 Envoy Agent Router(前身为 Envoy AI Gateway,可将 Envoy Gateway 转换为与 OpenAI 兼容的大语言模型 [LLM] 流量统一入口点)构建而成。内容涵盖了将容器映像和 Helm 图表植入本地 Harbor 注册表、在标准集群上安装两个控制平面、准备可选的基于令牌的速率限制后端,以及使用示例工作负载验证安装。

模型部署后端(Ollama、vLLM)通过配套指南集在 GDC 网闸隔离配置上部署开放权重模型进行部署;使用 Envoy 代理路由器进行基于正文的路由用户指南展示了如何按模型名称将请求路由到这些后端。

架构

该解决方案在 GDC 标准集群中运行。管理员工作站会将映像和 Helm 图表植入项目的 Harbor 注册表中,并安装两个控制平面:Envoy Gateway 控制器(用于根据 Gateway API 资源 [GatewayClass、Gateway、HTTPRoute] 对 Envoy 代理数据平面进行编程)和 Envoy Agent Router 控制器(用于通过外部处理器 [ExtProc] 扩展该数据平面,以处理 AI 流量 [AIGatewayRoute、AIServiceBackend、InferencePool])。应用客户端向 Envoy 代理发送与 OpenAI 兼容的请求,Envoy 代理会将这些请求路由到模型部署后端或 InferencePool。可选的 Redis 实例存储 Envoy 速率限制服务的计数器,用于基于令牌的速率限制。

GDC 网闸隔离配置上采用 Envoy 代理路由器的 AI Gateway 参考架构。

Envoy Gateway

Envoy Gateway 是一个基于 Envoy 代理构建的开源项目,可简化 Envoy 代理作为 Kubernetes API Gateway 的采用、使用和管理。它实现并扩展了 Kubernetes Gateway API(Ingress API 的后继者):GatewayClass 和 Gateway 资源描述了入口点,HTTPRoute 等路由资源描述了流量的匹配和转发方式,而面向角色的设计则分离了基础架构团队和应用团队的职责。Envoy Gateway 会添加自己的扩展 API,例如 EnvoyProxy(数据平面设置)、Backend(集群外部或由 FQDN 引用的端点)和 ClientTrafficPolicy(连接设置,例如缓冲区限制)。

Envoy 代理路由器

Envoy Agent Router(以前称为 Envoy AI Gateway)是一个开源项目,它使用 Envoy Gateway 处理从应用客户端到生成式 AI 服务的请求流量。它提供了一个统一层,用于通过模型感知路由、上游身份验证、基于令牌的速率限制和可观测性来路由和管理 LLM 流量,并与 Gateway API Inference Extension (InferencePool, Endpoint Picker) 集成,以实现指标感知端点选择。控制器会监视 aigateway.envoyproxy.io/v1beta1 资源,并在 Envoy 代理旁边注入一个外部处理器;外部处理器会解析请求正文(例如 OpenAI 聊天补全请求的 model 字段),设置 x-ai-eg-model 等路由标头,并在需要时在 API schema 之间进行转换。

准备工作

在继续部署之前,请确保您的环境满足所有必要的前提条件,并且所需的命令行实用程序已正确配置。在工作站上设置这些工具对于管理 Container Registry、与集群交互以及自动执行部署流程至关重要。

  • GDC 网闸隔离配置 1.16.2-hf1 或更高版本环境可与运行 Kubernetes v1.32.13-gke.400 或更高版本的标准集群搭配使用。
  • 已创建具有充足资源的标准集群。网关组件仅在 CPU 上运行;模型部署后端有自己的加速器要求(请参阅在 GDC 网闸隔离配置上运行开放权重模型指南)。
  • Harbor 实例可用且可访问。
  • 已应用必要的 IAM。
  • 与环境和互联网具有必要连接的工作站

环境配置

环境配置涵盖身份和权限、工作站以及对 GDC 环境和集群的访问权限。

Identity and Access Management

确保已正确配置必要的 IAM 账号、角色和权限。

项目上的 GDC 用户角色(项目命名空间中的 RoleBinding,由 IAM 项目管理员授予):

  • Harbor Instance Viewer (harbor-instance-viewer)
  • Harbor Project Creator(harbor-project-creator,仅当 Harbor 项目尚不存在时)
  • 标准集群管理员(standard-cluster-admin,gdcloud clusters get-credentials 必需)

标准集群上的 GDC 用户角色:上述项目角色不会授予集群内的任何权限。项目 IAM 管理员还必须在管理 API 服务器上的项目命名空间中,使用 StandardClusterRoleBinding 将用户绑定到 StandardClusterRole cluster-admin;绑定会在几秒钟内传播到项目的标准集群(status.clusters[].conditions 显示 Propagated=True)。由于本指南会安装自定义资源定义、ClusterRole 和 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

替换以下内容:

  • MANAGEMENT_API_SERVER:管理 API 服务器 kubeconfig 文件的路径。
  • USER:用户。
  • PROJECT:项目。

Harbor crane (crane) 机器人账号权限:

  • 列出代码库
  • 拉取代码库
  • 推送代码库
  • 读取制品
  • 列出制品
  • 创建标记
  • 列表代码

Harbor Kubernetes 映像拉取 (kubernetes-image-puller) 机器人账号权限:

  • 列出代码库
  • 拉取代码库
  • 读取制品
  • 列出制品
  • 列表代码

工作站

除非某个步骤另有说明,否则请在具有所需工具且可连接到环境和互联网的工作站上运行本指南中的所有命令。

要求

工作站上需要安装以下工具:

  • crane:在注册表之间管理和复制容器映像和 OCI 工件(文档)。
  • gdcloud:用于管理 GDC 资源的命令行界面 (CLI)(文档)。
  • kubectl:用于与 Kubernetes 集群通信和管理 Kubernetes 集群的命令行界面 (CLI)。
  • helm:Kubernetes 的软件包管理器,版本 3.8 或更高版本(OCI 注册表支持)(文档)。
  • curl:用于通过网址传输数据的命令行工具。
  • jq:轻量级且灵活的命令行 JSON 处理器。
  • yq:可移植的命令行 YAML 处理器。

除非某个步骤另有说明,否则请从工作站运行本指南中的所有命令。

工作站配置

工作站配置需要以下有关环境的信息:

  • GDC_STANDARD_CLUSTER_NAME:GDC 标准集群的名称。
  • GDC_DOMAIN_SUFFIX:GDC 环境的域名后缀(例如 gdc.example.com)。
  • GDC_ORG:GDC 组织的名称。
  • GDC_PROJECT:GDC 项目的名称。
  • GDC_ZONE:GDC 部署可用区的名称。
  • GDC_HARBOR_INSTANCE_NAME:项目中 Harbor 实例的名称。

  • GDCS_HARBOR_PROJECT_NAME:要用于映像的 Harbor 项目的名称(默认:solutions)

  • GDCS_HARBOR_CRANE_ROBOT_NAME:Harbor crane 机器人账号的名称。

  • GDCS_HARBOR_CRANE_ROBOT_TOKEN:Harbor crane 机器人账号的身份验证令牌。

  • GDCS_HARBOR_K8S_ROBOT_NAME:Harbor Kubernetes 映像拉取机器人账号的名称。

  • GDCS_HARBOR_K8S_ROBOT_TOKEN:Harbor Kubernetes 映像拉取机器人账号的身份验证令牌。

收集完所有必需变量的值后,继续生成环境变量文件。创建后,您可以随时手动修改该文件。

  1. 创建根解决方案目录和密钥文件夹:

    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. 创建平台环境配置文件:

    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
    

    替换以下内容:

    • STANDARD_CLUSTER_NAME:GDC 标准集群名称。
    • DOMAIN_SUFFIX:GDC 域名后缀。
    • ORG:GDC 组织。
    • PROJECT:GDC 项目。
    • ZONE:GDC 可用区。
    • HARBOR_INSTANCE_NAME:GDC Harbor 实例名称。
  3. 创建注册表环境配置文件:

    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
    

    替换以下内容:

    • HARBOR_CRANE_ROBOT_NAME:GDC Harbor 机器人账号名称。
    • HARBOR_K8S_ROBOT_NAME:GDC Harbor 机器人账号名称。
  4. 将令牌添加到 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
    

    替换以下内容:

    • CRANE_ROBOT_TOKEN:吊车机器人令牌。
    • KUBERNETES_ROBOT_TOKEN:Kubernetes 机器人令牌。
  5. 创建根环境加载器文件:

    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
    

配置解决方案变量

  1. 创建解决方案实现目录:

    mkdir -p ${HOME}/gdcag-solutions/ai-gateway/envoy/env.d
    
  2. 创建解决方案环境配置文件:

    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. 创建实现环境加载器文件:

    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. 使用您首选的编辑器修改和查看环境文件:

    ${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. 获取环境文件:

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

    输出类似于以下内容:

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

GDC

本指南假定您的工作站已配置为信任 GDC 环境和 Harbor 实例的 TLS 证书。

  1. 配置 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. 向 GDC 环境进行身份验证:

    gdcloud auth login
    

集群

  1. 检索集群凭据:

    gdcloud clusters get-credentials "${GDC_STANDARD_CLUSTER_NAME}" \
    --project="${GDC_PROJECT}" \
    --standard \
    --zone="${GDC_ZONE}"
    
  2. 验证与集群的连接:

    kubectl get nodes -L node.cluster.private.gdc.goog/machine-class
    

制品迁移准备工作

  1. 为 crane 创建 Docker 配置文件。机器人账号用于推送大型映像层,以避免在使用托管 Harbor 服务 (MHS) 凭据辅助程序 (docker-credential-mhs) 和用户账号时出现身份验证令牌超时:

    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. 为 Kubernetes 创建 Docker 配置文件:

    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. 使用 Kubernetes 映像拉取机器人账号通过 helm 登录 Harbor OCI 注册表。helm 会保留自己的注册表凭据,并且需要这些凭据才能从 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
    

    输出类似于以下内容:

    Login Succeeded
    
  4. 创建 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. 创建 seed_charts.sh 脚本。作为 OCI 工件发布的 Helm 图表也会通过 crane 进行复制,无需选择平台,也无需使用容器映像代码库所用的 create 标记:

    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"
    

    替换以下内容:

    • REGISTRY_HOST:注册表主机。
    • REPOSITORY:代码库。
    • CHART_VERSION:图表版本。
  6. 定义解决方案所需的容器映像列表:

    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. 将所需的容器映像植入到制品库:

    ${GDCS_IMPLEMENTATION_HOME}/seed_registry.sh
    
  1. 定义解决方案所需的 Helm 图表列表:

    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. 将所需的 Helm 图表植入到制品库:

    ${GDCS_IMPLEMENTATION_HOME}/seed_charts.sh
    
  3. 验证是否可以从 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):'
    

    输出类似于以下内容:

    name: gateway-helm
    version: v1.8.5
    name: ai-gateway-helm
    version: v1.1.0
    
  4. 下载 Gateway API 推理扩展程序清单。它们作为发布资产(而非图表)发布:

    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"
    

    输出类似于以下内容:

    4
    

Envoy Gateway

首先安装 Envoy Gateway,并使用上游快速入门自行验证;下一部分将介绍 Envoy Agent Router 集成。

命名空间

  1. 为 Envoy Gateway 创建命名空间。每个 Gateway 的 Envoy 代理 Deployment 也会在此命名空间中创建:

    kubectl create namespace "${GDCS_ENVOY_GATEWAY_NAMESPACE}"
    
  2. 添加 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 -
    

自定义资源定义

  1. 从种子图表中安装 Gateway API(标准渠道)和 Envoy Gateway 自定义资源定义 (CRD):

    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. 验证 CRD 是否已注册:

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

控制器

  1. 为 Envoy Gateway 创建 Helm 值文件。这些映像是使用映像拉取密钥 crds.enabled=false 从 Harbor 中拉取的,因为 CRD 是单独安装的:

    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. 安装 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. 等待 Envoy Gateway 控制器变为“Available”(可用):

    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. 验证控制器及其证书生成作业的映像是否来自 Harbor:

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

Envoy 代理和 Gateway 类

  1. 为 EnvoyProxy 数据平面模板和 GatewayClass 创建清单。EnvoyProxy 设置代理映像、映像拉取密钥和代理 Pod 的资源请求;GatewayClass 由用户指南共享:

    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. 应用 EnvoyProxy 和 GatewayClass 的清单:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/gateway-class.yaml"
    
  3. 验证 GatewayClass 是否已接受:

    kubectl get gatewayclass "${GDCS_GATEWAY_CLASS_NAME}"
    

    输出类似于以下内容:

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

验证

验证会将 Envoy Gateway 快速入门(HTTPRoute 后面的 echo 后端)部署到 Envoy Gateway 命名空间中,并在之后将其移除。

  1. 为快速入门工作负载创建清单:

    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. 应用快速入门工作负载的清单:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/eg-quickstart.yaml" \
    --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"
    
  3. 等待 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. 等待后端 Deployment 变为可用状态:

    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. 使用端口转发通过网关发送测试请求。Gateway 的 Envoy Service 通过其所属网关标签找到:

    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}
    

    输出类似于以下内容:

    {
      "path": "/get",
      "host": "www.example.com",
      "method": "GET",
      ...
      "namespace": "envoy-gateway-system",
      "pod": "eg-quickstart-backend-...",
      ...
    }
    
  6. 移除快速入门工作负载:

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

Envoy 代理路由器

Envoy Agent Router 会安装到其自己的命名空间中;然后,Envoy Gateway 会重新配置为将其作为扩展服务器进行调用。

命名空间

  1. 为 Envoy Agent Router 控制器创建命名空间:

    kubectl create namespace "${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}"
    
  2. 添加 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 -
    

自定义资源定义

  1. 从种子图表安装 Envoy Agent Router CRD:

    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. 通过下载的清单安装 Gateway API 推理扩展程序 CRD(InferencePool、InferenceObjective):

    kubectl apply --server-side \
    --filename="${GDCS_IMPLEMENTATION_HOME}/manifests/gateway-api-inference-extension-${GDCS_GATEWAY_API_INFERENCE_EXTENSION_VERSION}.yaml"
    
  3. 验证 CRD 是否已注册:

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

    输出类似于以下内容:

    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

基于令牌的速率限制由 Envoy 速率限制服务强制执行,该服务将其计数器存储在 Redis 中。本指南部署了一个没有持久性的单副本 Redis;您也可以通过更改下一部分中的 rateLimit.backend.redis.url 值来改用现有 Redis 服务。

  1. 为 Redis Deployment 创建清单:

    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. 应用 Redis Deployment 的清单:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/redis.yaml" \
    --namespace="${GDCS_REDIS_NAMESPACE}"
    
  3. 等待 Redis Deployment 变为可用状态:

    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'"
    

控制器

  1. 为 Envoy 代理路由器创建 Helm 值文件:

    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. 安装 Envoy Agent Router:

    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. 等待 Envoy Agent Router 控制器变为可用状态:

    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'"
    

Envoy Gateway 集成

必须重新配置 Envoy Gateway,以调用 Envoy Agent Router 控制器作为其扩展服务器,接受 InferencePool 资源作为后端,并使用 Redis 支持的速率限制服务。

  1. 为集成创建 Helm 值文件:

    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. 使用这两个值文件升级 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" \
    --values="${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-agent-router-values.yaml" \
    --version="${GDCS_ENVOY_GATEWAY_VERSION}"
    
  3. 为 ClusterRole 创建清单,以便 Envoy Gateway 控制器监控 InferencePool 资源。该图表不会授予此权限;与图表管理的 ClusterRole 的补丁不同,单独的 ClusterRoleBinding 在图表升级后仍然存在:

    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. 应用 ClusterRole 的清单:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-inferencepool-rbac.yaml"
    
  5. 重启 Envoy Gateway 控制器,使其获取新的配置和权限,然后等待其变为“可用”状态:

    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. 验证速率限制服务是否已部署且可用:

    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'"
    

验证

验证过程会将 Envoy Agent Router 基本示例(AIGatewayRoute 后面的模拟 OpenAI 兼容上游)部署到 Envoy Agent Router 命名空间中,并在验证后将其移除。

  1. 为验证工作负载创建清单:

    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. 应用验证工作负载的清单:

    kubectl apply \
    --filename="${GDCS_IMPLEMENTATION_HOME}/aieg-basic.yaml" \
    --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}"
    
  3. 等待 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. 等待模拟上游 Deployment 变为可用状态:

    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. 验证 Gateway 的 Envoy 代理 Pod 是否运行由 Envoy Agent Router 注入的外部处理器 Sidecar:

    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}'
    

    输出类似于以下内容:

    envoy-envoy-ai-gateway-system-aieg-basic-...   envoy shutdown-manager ai-gateway-extproc
    
  6. 使用端口转发通过网关发送对话补全请求。 外部处理器读取请求正文的 model 字段,并将其路由到模拟上游:

    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}
    

    输出类似于以下内容:

    {
      "choices": [
        {
          "index": 0,
          "message": {
            "role": "assistant",
            "content": "..."
          },
          "finish_reason": "stop"
        }
      ],
      "usage": {
        ...
      }
    }
    
  7. 移除验证工作负载:

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

操作

网关控制平面的日常任务。

升级

  • 为新版本的映像和图表设定种子(更新 env.sh 中的版本变量,重新运行 seed_registry.sh 和 seed_charts.sh),使用 kubectl apply --server-side 应用新的 CRD 图表,然后使用新的 --version 运行相同的 helm upgrade --install 命令。查看 Envoy 代理路由器兼容性矩阵,了解目标版本支持的 Envoy Gateway 和 Gateway API 版本;Envoy 代理路由器 1.1.0 是针对 Envoy Gateway 1.8 构建的。

卸载

  • 首先删除用户指南的 Gateway、AIGatewayRoute 和 InferencePool 资源,然后删除 helm uninstall aieg --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}"、helm uninstall eg --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"、ClusterRole envoy-gateway-inferencepool-reader、GatewayClass、Redis Deployment,最后删除 CRD。

问题排查

症状 可能的原因 操作
helm show chart 或 helm upgrade 失败,并显示 unauthorized 或 FetchReference helm 没有 Harbor 的凭据(它不使用 crane Docker 配置) 使用 Kubernetes 映像拉取机器人账号再次运行 helm registry login 步骤。
Envoy Gateway 或 Envoy Agent Router Pod 仍处于 ImagePullBackOff 阶段 映像未播种,或命名空间中缺少映像拉取 Secret 检查 kubectl describe pod,将映像路径与 crane ls "${GDCS_HARBOR_PROJECT_URI}/envoyproxy/gateway" 进行比较,并验证 ${GDCS_ENVOY_GATEWAY_NAMESPACE} 和 ${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE} 中是否存在该 Secret。
集成后,Gateway 仍为 Programmed=False Envoy Gateway 控制器无法访问扩展服务器,或者在升级后未重启 kubectl logs deployment/envoy-gateway --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}";确认 service/ai-gateway-controller 存在于 ${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE} 中,并且列出了端口 1063;重复发布重启。
引用 InferencePool 的 AIGatewayRoute 不被接受 Envoy Gateway 缺少读取 inferencepools 的权限,或者缺少 InferencePool CRD 验证 ClusterRoleBinding envoy-gateway-inferencepool-reader 和 inferencepools.inference.networking.k8s.io CRD;检查 forbidden 的控制器日志。
Chat 补全请求返回 413,或者连接因提示过大而重置 默认 Envoy 缓冲区限制 (32 KiB) 对于 AI 载荷而言太小 将具有 connection.bufferLimit(验证使用 50Mi)的 ClientTrafficPolicy 挂接到 Gateway。
速率限制服务 Pod 为 CrashLoopBackOff 无法通过配置的网址访问 Redis kubectl get service redis --namespace="${GDCS_REDIS_NAMESPACE}";请在集成值文件中修正 rateLimit.backend.redis.url,然后重新升级。

其他资料