이 문서에서는 Google Distributed Cloud (GDC) 오프라인 환경에 AI Gateway를 배포하는 방법을 단계별로 설명합니다. 게이트웨이는 Envoy 프록시를 기반으로 하는 Kubernetes Gateway API 구현인 Envoy Gateway와 Envoy Gateway를 대규모 언어 모델 (LLM) 트래픽을 위한 통합된 OpenAI 호환 진입점으로 전환하는 확장 프로그램인 Envoy Agent Router (이전 명칭: Envoy AI Gateway)로 빌드됩니다. 컨테이너 이미지와 Helm 차트를 로컬 Harbor 레지스트리에 시딩하고, 표준 클러스터에 두 제어 영역을 모두 설치하고, 선택적 토큰 기반 비율 제한 백엔드를 준비하고, 샘플 워크로드로 설치를 검증하는 방법을 다룹니다.
모델 서빙 백엔드 (Ollama, vLLM)는 GDC 에어갭의 공개 가중치 모델 가이드의 컴패니언 세트로 배포됩니다. Envoy 에이전트 라우터 사용자 가이드의 본문 기반 라우팅에서는 모델 이름으로 요청을 라우팅하는 방법을 보여줍니다.
아키텍처
솔루션은 GDC Standard 클러스터에서 실행됩니다. 관리자 워크스테이션은 이미지와 Helm 차트를 프로젝트의 Harbor 레지스트리에 시딩하고 두 개의 컨트롤 플레인(게이트웨이 API 리소스(GatewayClass, Gateway, HTTPRoute)에서 Envoy 프록시 데이터 플레인을 프로그래밍하는 Envoy 게이트웨이 컨트롤러와 AI 트래픽(AIGatewayRoute, AIServiceBackend, InferencePool)을 위한 외부 프로세서(ExtProc)로 데이터 플레인을 확장하는 Envoy 에이전트 라우터 컨트롤러)를 설치합니다. 애플리케이션 클라이언트는 OpenAI 호환 요청을 Envoy 프록시에 전송하며, Envoy 프록시는 이를 모델 제공 백엔드 또는 InferencePool에 라우팅합니다. 선택적 Redis 인스턴스는 토큰 기반 비율 제한을 위한 Envoy 비율 제한 서비스의 카운터를 저장합니다.

Envoy Gateway
Envoy Gateway는 Envoy 프록시를 기반으로 구축된 오픈소스 프로젝트로, Envoy 프록시를 Kubernetes API 게이트웨이로 채택, 사용, 관리하는 작업을 간소화합니다. 인그레스 API의 후속 API인 Kubernetes Gateway 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, 엔드포인트 선택기)과 통합됩니다. 컨트롤러는 aigateway.envoyproxy.io/v1beta1 리소스를 모니터링하고 Envoy 프록시 옆에 외부 프로세서를 삽입합니다. 외부 프로세서는 요청 본문 (예: OpenAI 채팅 완료 요청의 model 필드)을 파싱하고, x-ai-eg-model와 같은 라우팅 헤더를 설정하고, 필요한 경우 API 스키마 간에 변환합니다.
시작하기 전에
배포를 진행하기 전에 환경이 필요한 모든 기본 요건을 충족하고 필수 명령줄 유틸리티가 올바르게 구성되어 있는지 확인하세요. 워크스테이션에서 이러한 도구를 설정하는 것은 컨테이너 레지스트리를 관리하고, 클러스터와 상호작용하고, 배포 프로세스를 자동화하는 데 필수적입니다.
- GDC 오프라인 1.16.2-hf1 이상 환경은 Kubernetes v1.32.13-gke.400 이상을 실행하는 표준 클러스터에서 사용할 수 있습니다.
- 충분한 리소스로 생성된 Standard 클러스터 게이트웨이 구성요소는 CPU에서만 실행됩니다. 모델 서빙 백엔드에는 자체 액셀러레이터 요구사항이 있습니다 (GDC 에어갭의 개방형 가중치 모델 가이드 참고).
- Harbor 인스턴스를 사용할 수 있고 액세스할 수 있습니다.
- 필요한 IAM이 적용되었습니다.
- 환경 및 인터넷에 필요한 연결이 있는 워크스테이션
환경 구성
환경 구성은 ID 및 권한, 워크스테이션, GDC 환경 및 클러스터에 대한 액세스를 다룹니다.
Identity and Access Management
필요한 IAM 계정, 역할, 권한이 올바르게 구성되어 있는지 확인합니다.
프로젝트의 GDC 사용자 역할 (프로젝트 네임스페이스의 RoleBinding, 프로젝트 IAM 관리자가 부여함):
- Harbor 인스턴스 뷰어 (
harbor-instance-viewer) - Harbor 프로젝트 생성자 (
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 (크레인) 로봇 계정 권한:
- 저장소 목록
- 저장소 가져오기
- 저장소 푸시
- 아티팩트 읽기
- 아티팩트 나열
- 태그 만들기
- 목록 태그
Harbor Kubernetes 이미지 가져오기 (kubernetes-image-puller) 로봇 계정 권한:
- 저장소 목록
- 저장소 가져오기
- 아티팩트 읽기
- 아티팩트 나열
- 목록 태그
워크스테이션
이 가이드에는 환경 및 인터넷에 필요한 연결이 있는 워크스테이션이 필요합니다.
요구사항
워크스테이션에 다음 도구를 설치해야 합니다.
crane: 레지스트리 간에 컨테이너 이미지와 OCI 아티팩트를 관리하고 복사합니다(문서).gdcloud: GDC 리소스 관리를 위한 명령줄 인터페이스 (CLI)(문서)kubectl: Kubernetes 클러스터와 통신하고 이를 관리하는 데 사용되는 명령줄 인터페이스 (CLI)입니다.helm: Kubernetes용 패키지 관리자, 버전 3.8 이상 (OCI 레지스트리 지원) (문서)curl: URL을 사용하여 데이터를 전송하는 명령줄 도구입니다.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: Harborcrane로봇 계정의 이름입니다.GDCS_HARBOR_CRANE_ROBOT_TOKEN: Harborcrane로봇 계정의 인증 토큰입니다.GDCS_HARBOR_K8S_ROBOT_NAME: Harbor Kubernetes 이미지 풀 로봇 계정의 이름입니다.GDCS_HARBOR_K8S_ROBOT_TOKEN: Harbor Kubernetes 이미지 가져오기 로봇 계정의 인증 토큰입니다.
필요한 모든 변수의 값을 수집한 후 환경 변수 파일 생성을 진행합니다. 생성된 후 언제든지 파일을 수동으로 수정할 수 있습니다.
루트 솔루션 디렉터리와 보안 비밀 폴더를 만듭니다.
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/*플랫폼 환경 구성 파일을 만듭니다.
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 인스턴스 이름입니다.
레지스트리 환경 구성 파일을 만듭니다.
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 로봇 계정 이름입니다.
보안 비밀 파일에 토큰을 추가합니다.
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 로봇 토큰입니다.
루트 환경 로더 파일을 만듭니다.
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
솔루션 변수 구성
솔루션 구현 디렉터리를 만듭니다.
mkdir -p ${HOME}/gdcag-solutions/ai-gateway/envoy/env.d솔루션 환경 구성 파일을 만듭니다.
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구현 환경 로더 파일을 만듭니다.
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원하는 편집기로 환경 파일을 수정하고 검토합니다.
${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환경 파일을 소싱합니다.
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 인증서를 신뢰하도록 구성되어 있다고 가정합니다.
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}"GDC 환경에 인증합니다.
gdcloud auth login
클러스터
클러스터 사용자 인증 정보를 검색합니다.
gdcloud clusters get-credentials "${GDC_STANDARD_CLUSTER_NAME}" \ --project="${GDC_PROJECT}" \ --standard \ --zone="${GDC_ZONE}"클러스터에 대한 연결을 확인합니다.
kubectl get nodes -L node.cluster.private.gdc.goog/machine-class모든 노드가 이 가이드의 시작하기 전에 섹션에서 요구하는 Kubernetes 버전을 실행하는지 확인합니다.
kubectl get nodes -o custom-columns='NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion'
아티팩트 마이그레이션 준비
- 아티팩트 레지스트리의 연결 및 구성을 확인합니다.
크레인용 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 historyKubernetes용 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 historyKubernetes 이미지 풀 로봇 계정을 사용하여
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 Succeededseed_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"seed_charts.sh스크립트를 만듭니다. OCI 아티팩트로 게시된 Helm 차트도 플랫폼 선택 없이 컨테이너 이미지 저장소에 사용되는create태그 없이crane로 복사됩니다.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: 차트 버전입니다.
솔루션에 필요한 컨테이너 이미지 목록을 정의합니다.
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)필요한 컨테이너 이미지를 아티팩트 레지스트리에 시드합니다.
${GDCS_IMPLEMENTATION_HOME}/seed_registry.sh
솔루션에 필요한 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)필요한 Helm 차트를 아티팩트 레지스트리에 시드합니다.
${GDCS_IMPLEMENTATION_HOME}/seed_charts.shHarbor에서 차트를 다시 읽을 수 있는지 확인합니다.
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.0Gateway 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 통합은 다음 섹션에 나옵니다.
네임스페이스
Envoy Gateway의 네임스페이스를 만듭니다. 모든
Gateway의 Envoy 프록시Deployment도 이 네임스페이스에 생성됩니다.kubectl create namespace "${GDCS_ENVOY_GATEWAY_NAMESPACE}"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 -
커스텀 리소스 정의
시드 차트에서 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=-CRD가 등록되었는지 확인합니다.
kubectl get crd | grep -E 'gateway.networking.k8s.io|gateway.envoyproxy.io'
컨트롤러
Envoy Gateway의 Helm 값 파일을 만듭니다. 이미지는 이미지 가져오기 비밀번호를 사용하여 Harbor에서 가져옵니다. CRD가 별도로 설치되었기 때문입니다.
crds.enabled=falsecat <<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} EOFEnvoy 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}"Envoy Gateway 컨트롤러가 사용 가능해질 때까지 기다립니다.
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'"컨트롤러 이미지와 인증서 생성 작업이 Harbor에서 제공되는지 확인합니다.
kubectl get pods --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" \ --output=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}'
Envoy 프록시 및 게이트웨이 클래스
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} EOFEnvoyProxy및GatewayClass의 매니페스트를 적용합니다.kubectl apply \ --filename="${GDCS_IMPLEMENTATION_HOME}/gateway-class.yaml"GatewayClass이 수락되었는지 확인합니다.kubectl get gatewayclass "${GDCS_GATEWAY_CLASS_NAME}"출력은 다음과 비슷합니다.
NAME CONTROLLER ACCEPTED AGE envoy-ai-gateway gateway.envoyproxy.io/gatewayclass-controller True 5s
유효성 검사
유효성 검사는 Envoy Gateway 네임스페이스에 Envoy Gateway 빠른 시작 (HTTPRoute 뒤에 있는 에코 백엔드)을 배포한 후 삭제합니다.
빠른 시작 워크로드의 매니페스트를 만듭니다.
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빠른 시작 워크로드의 매니페스트를 적용합니다.
kubectl apply \ --filename="${GDCS_IMPLEMENTATION_HOME}/eg-quickstart.yaml" \ --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"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'"백엔드
Deployment이 Available이 될 때까지 기다립니다.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'"포트 전달을 사용하여 게이트웨이를 통해 테스트 요청을 전송합니다.
Gateway의 EnvoyService는 소유 게이트웨이 라벨로 찾습니다.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-...", ... }빠른 시작 워크로드를 삭제합니다.
kubectl delete \ --filename="${GDCS_IMPLEMENTATION_HOME}/eg-quickstart.yaml" \ --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"
Envoy 에이전트 라우터
Envoy 에이전트 라우터가 자체 네임스페이스에 설치됩니다. 그러면 Envoy 게이트웨이가 확장 프로그램 서버로 호출하도록 재구성됩니다.
네임스페이스
Envoy 에이전트 라우터 컨트롤러의 네임스페이스를 만듭니다.
kubectl create namespace "${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}"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 -
커스텀 리소스 정의
시드된 차트에서 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=-다운로드한 매니페스트에서 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"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
토큰 기반 비율 제한은 Redis에 카운터를 저장하는 Envoy 비율 제한 서비스에 의해 적용됩니다. 이 가이드에서는 지속성이 없는 단일 복제본 Redis를 배포합니다. 다음 섹션에서 rateLimit.backend.redis.url 값을 변경하여 기존 Redis 서비스를 대신 사용할 수 있습니다.
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 EOFRedis
Deployment의 매니페스트를 적용합니다.kubectl apply \ --filename="${GDCS_IMPLEMENTATION_HOME}/redis.yaml" \ --namespace="${GDCS_REDIS_NAMESPACE}"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'"
컨트롤러
Envoy Agent Router의 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} EOFEnvoy 에이전트 라우터를 설치합니다.
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}"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 게이트웨이는 Envoy 에이전트 라우터 컨트롤러를 확장 서버로 호출하여 InferencePool 리소스를 백엔드로 수락하고 Redis 지원 비율 제한 서비스를 사용하도록 재구성해야 합니다.
통합의 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두 값 파일로 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}"Envoy Gateway 컨트롤러가
InferencePool리소스를 모니터링할 수 있도록 하는ClusterRole의 매니페스트를 만듭니다. 차트는 이 권한을 부여하지 않습니다. 별도의ClusterRoleBinding는 차트 관리ClusterRole패치와 달리 차트 업그레이드에서 유지됩니다.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} EOFClusterRole의 매니페스트를 적용합니다.kubectl apply \ --filename="${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-inferencepool-rbac.yaml"새 구성과 권한을 적용하도록 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속도 제한 서비스가 배포되었으며 사용 가능한지 확인합니다.
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 에이전트 라우터 네임스페이스에 Envoy 에이전트 라우터 기본 예시 (AIGatewayRoute 뒤에 있는 모의 OpenAI 호환 업스트림)를 배포한 후 삭제합니다.
검증 워크로드의 매니페스트를 만듭니다.
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유효성 검사 워크로드의 매니페스트를 적용합니다.
kubectl apply \ --filename="${GDCS_IMPLEMENTATION_HOME}/aieg-basic.yaml" \ --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}"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'"모의 업스트림
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'"Envoy Agent Router에 의해 삽입된 외부 프로세서 사이드카가
Gateway의 Envoy 프록시Pod를 실행하는지 확인합니다.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포트 포워딩을 사용하여 게이트웨이를 통해 채팅 완성 요청을 전송합니다. 외부 프로세서는 요청 본문의
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": { ... } }검증 워크로드를 삭제합니다.
kubectl delete \ --filename="${GDCS_IMPLEMENTATION_HOME}/aieg-basic.yaml" \ --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}"
작업
게이트웨이 컨트롤 플레인의 2일차 작업입니다.
업그레이드
- 새 버전의 이미지와 차트를 시드하고 (
env.sh에서 버전 변수를 업데이트하고seed_registry.sh및seed_charts.sh를 다시 실행)kubectl apply --server-side로 새 CRD 차트를 적용한 후 새--version로 동일한helm upgrade --install명령어를 실행합니다. Envoy 에이전트 라우터 호환성 매트릭스에서 타겟 출시에서 지원하는 Envoy 게이트웨이 및 게이트웨이 API 버전을 확인합니다. Envoy 에이전트 라우터 1.1.0은 Envoy 게이트웨이 1.8을 기반으로 빌드됩니다.
제거
- 먼저 사용자 가이드의
Gateway,AIGatewayRoute,InferencePool리소스를 삭제한 다음helm uninstall aieg --namespace="${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}",helm uninstall eg --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}",ClusterRoleenvoy-gateway-inferencepool-reader,GatewayClass, RedisDeployment, 마지막으로 CRD를 삭제합니다.
문제 해결
| 증상 | 가능한 원인 | 작업 |
|---|---|---|
helm show chart 또는 helm upgrade가 unauthorized 또는 FetchReference와 함께 실패함 |
helm에 Harbor 사용자 인증 정보가 없습니다 (crane Docker 구성을 사용하지 않음). |
Kubernetes 이미지 가져오기 로봇 계정으로 helm registry login 단계를 다시 실행합니다. |
Envoy Gateway 또는 Envoy Agent Router Pod가 ImagePullBackOff에 유지됨 |
이미지가 시드되지 않았거나 네임스페이스에 이미지 가져오기 보안 비밀이 누락됨 | kubectl describe pod를 확인하고 이미지 경로를 crane ls "${GDCS_HARBOR_PROJECT_URI}/envoyproxy/gateway"와 비교하고 ${GDCS_ENVOY_GATEWAY_NAMESPACE} 및 ${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}에 비밀이 있는지 확인합니다. |
통합 후에도 Gateway이 Programmed=False으로 유지됩니다. |
Envoy Gateway 컨트롤러가 확장 프로그램 서버에 연결할 수 없거나 업그레이드 후 다시 시작되지 않음 | kubectl logs deployment/envoy-gateway --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"을 실행하고 ${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE}에 service/ai-gateway-controller이 있고 포트 1063이 나열되어 있는지 확인한 후 출시 다시 시작을 반복합니다. |
InferencePool을 참조하는 AIGatewayRoute은 허용되지 않습니다. |
Envoy Gateway에 inferencepools를 읽을 권한이 없거나 InferencePool CRD가 누락됨 |
ClusterRoleBinding envoy-gateway-inferencepool-reader 및 inferencepools.inference.networking.k8s.io CRD를 확인하고 forbidden의 컨트롤러 로그를 확인합니다. |
채팅 완료 요청이 413를 반환하거나 큰 프롬프트의 경우 연결이 재설정됨 |
기본 Envoy 버퍼 한도 (32KiB)가 AI 페이로드에 비해 너무 작음 | connection.bufferLimit (검증에서는 50Mi 사용)을 사용하여 ClientTrafficPolicy를 Gateway에 연결합니다. |
비율 제한 서비스 Pod이 CrashLoopBackOff입니다. |
구성된 URL에서 Redis에 연결할 수 없음 | kubectl get service redis --namespace="${GDCS_REDIS_NAMESPACE}": 통합 값 파일에서 rateLimit.backend.redis.url를 수정하고 다시 업그레이드합니다. |
추가 자료
- Envoy Agent Router 문서
- Envoy Agent Router 출시 노트 및 호환성 매트릭스
- Envoy Gateway 문서
- Kubernetes Gateway API
- 게이트웨이 API 추론 확장 프로그램
- Standard 클러스터 만들기 | Google Distributed Cloud(에어 갭 적용형)