במאמר הזה מוסבר איך לפרוס AI Gateway בסביבות מבודדות (air-gapped) של Google Distributed Cloud (GDC). השער מבוסס על Envoy Gateway, הטמעה של Kubernetes Gateway API שמבוססת על Envoy proxy, ועל Envoy Agent Router (לשעבר Envoy AI Gateway), התוסף שהופך את Envoy Gateway לנקודת כניסה מאוחדת שתואמת ל-OpenAI עבור תנועה של מודלים גדולים של שפה (LLM). המדריך כולל הסברים על הטמעה של קובצי אימג' של קונטיינרים ושל תרשימי Helm במאגר המקומי של Harbor, התקנה של שני מישורי בקרה באשכול רגיל, הכנה של קצה עורפי אופציונלי להגבלת קצב מבוססת-טוקן ואימות ההתקנה באמצעות עומסי עבודה לדוגמה.
העורפים (back-end) של פרסום המודלים (Ollama, vLLM) נפרסים עם קבוצת המדריכים הנלווים Open Weight Models on GDC עם air gap. במדריך Body-based routing with Envoy Agent Router user guide מוסבר איך לנתב בקשות אליהם לפי שם המודל.
ארכיטקטורה
הפתרון פועל באשכול רגיל של GDC. תחנת עבודה של אדמין מאכלסת את התמונות ואת תרשימי Helm במאגר Harbor של הפרויקט ומתקינה את שתי מישורי הבקרה: בקר Envoy Gateway, שמתכנת את מישור הנתונים של Envoy proxy ממקורות Gateway API (GatewayClass, Gateway, HTTPRoute), ובקר Envoy Agent Router, שמרחיב את מישור הנתונים הזה באמצעות מעבד חיצוני (ExtProc) לתנועת AI (AIGatewayRoute, AIServiceBackend, InferencePool). לקוחות של אפליקציות שולחים בקשות שתואמות ל-OpenAI ל-Envoy proxy, שמנתב אותן לשרתי קצה של מודלים או ל-InferencePool. מופע Redis אופציונלי שמאחסן את המונים של שירות הגבלת הקצב של Envoy להגבלת קצב מבוססת-טוקן.

Envoy Gateway
Envoy Gateway הוא פרויקט קוד פתוח שמבוסס על Envoy proxy, ומפשט את ההטמעה, השימוש והניהול של Envoy proxy כשער API של Kubernetes. הוא מיישם ומרחיב את Kubernetes Gateway API, המחליף של Ingress API: משאבי GatewayClass ו-Gateway מתארים את נקודות הכניסה, משאבי מסלולים כמו HTTPRoute מתארים איך התנועה מותאמת ומועברת, והתכנון מבוסס-התפקידים מפריד בין האחריות של צוותי התשתית והאפליקציות. Envoy Gateway מוסיף ממשקי API משלו להרחבות, למשל EnvoyProxy (הגדרות של מישור הנתונים), Backend (נקודות קצה מחוץ לאשכול או שמופנות על ידי FQDN) ו-ClientTrafficPolicy (הגדרות חיבור כמו מגבלות מאגר).
נתב Envoy Agent
Envoy Agent Router (לשעבר Envoy AI Gateway) הוא פרויקט קוד פתוח שמשתמש ב-Envoy Gateway כדי לטפל בתנועת בקשות מלקוחות של אפליקציות לשירותי AI גנרטיבי. הוא מספק שכבה מאוחדת לניתוב ולניהול תנועה של מודלים גדולים של שפה (LLM) עם ניתוב מודע למודל, אימות במעלה הזרם, הגבלת קצב מבוססת-טוקנים ויכולת צפייה, והוא משתלב עם Gateway API Inference Extension (InferencePool, Endpoint Picker) לבחירת נקודות קצה מודעות למדדים. הבקר עוקב אחרי המשאבים של aigateway.envoyproxy.io/v1beta1 ומזריק מעבד חיצוני לצד ה-proxy של Envoy. המעבד החיצוני מנתח את גוף הבקשה (לדוגמה, השדה model של בקשה להשלמת צ'אט של OpenAI), מגדיר כותרות ניתוב כמו x-ai-eg-model ומתרגם בין סכימות של API לפי הצורך.
לפני שמתחילים
לפני שממשיכים בהטמעה, צריך לוודא שהסביבה עומדת בכל הדרישות המוקדמות ושהגדרתם את כלי השירות הנדרשים של שורת הפקודה בצורה נכונה. הגדרת הכלים האלה בתחנת העבודה חיונית לניהול של מאגרי תמונות של קונטיינרים, לאינטראקציה עם אשכולות ולאוטומציה של תהליך הפריסה.
- סביבת GDC עם air gap בגרסה 1.16.2-hf1 ואילך זמינה עם אשכול רגיל שפועל ב-Kubernetes בגרסה v1.32.13-gke.400 ואילך.
- נוצר אשכול רגיל עם מספיק משאבים. רכיבי השער פועלים רק במעבד (CPU). לשרתי הקצה של המודלים יש דרישות משלהם לגבי מאיץ (ראו את המדריכים בנושא מודלים של משקלים פתוחים ב-GDC עם פערים בבידוד).
- מופע Harbor זמין ונגיש.
- הגדרנו את ה-IAM הנדרש.
- תחנת עבודה עם הקישוריות הנדרשת לסביבה ולאינטרנט
הגדרת הסביבה
הגדרת הסביבה כוללת את הזהויות וההרשאות, את תחנת העבודה ואת הגישה לסביבת GDC ולתצורה.
ניהול זהויות והרשאות גישה
מוודאים שחשבונות, תפקידים והרשאות ה-IAM הנדרשים מוגדרים בצורה תקינה.
תפקידי GDC User בפרויקט (RoleBinding במרחב השמות של הפרויקט, שמוקצים על ידי אדמין IAM של הפרויקט):
- צפייה במופע Harbor (
harbor-instance-viewer) - Harbor Project Creator (
harbor-project-creator, רק אם פרויקט Harbor עדיין לא קיים) - אדמין רגיל של אשכול (
standard-cluster-admin, נדרש לgdcloud clusters get-credentials)
התפקיד GDC User באשכול רגיל: התפקידים הקודמים בפרויקט לא מעניקים הרשאות בתוך האשכול. בנוסף, אדמין של IAM בפרויקט צריך לקשר את המשתמש אל StandardClusterRole cluster-admin עם StandardClusterRoleBinding במרחב השמות של הפרויקט בשרת של Management API. הקישור מועבר לאשכולות הרגילים של הפרויקט תוך שניות (status.clusters[].conditions מציג Propagated=True). נדרשות הרשאות ברמת האשכול כי במדריך הזה מותקנים Custom Resource Definitions (CRD), 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: הנתיב לקובץ kubeconfig של שרת Management API. -
USER: משתמש. -
PROJECT: project.
הרשאות חשבון הרובוט של Harbor crane (crane):
- רשימת מאגרים
- שליפת נתונים ממאגר
- מאגר Push
- קריאת ארטיפקט
- ארטיפקט של רשימה
- יצירת תג
- תג רשימה
הרשאות של חשבון רובוט (robot account) למשיכת תמונות של Kubernetes מ-Harbor (kubernetes-image-puller):
- רשימת מאגרים
- שליפת נתונים ממאגר
- קריאת ארטיפקט
- ארטיפקט של רשימה
- תג רשימה
תחנת עבודה
כדי להשתמש במדריך הזה, צריך תחנת עבודה עם קישוריות לסביבה ולאינטרנט.
דרישות
צריך להתקין את הכלים הבאים בתחנת העבודה:
-
crane: ניהול והעתקה של קובצי אימג' בקונטיינרים ושל ארטיפקטים של OCI בין רשומות (מסמכי עזרה). -
gdcloud: ממשק שורת פקודה (CLI) לניהול משאבי GDC (מסמכי עזר). -
kubectl: ממשק שורת פקודה (CLI) שמשמש לתקשורת עם אשכול Kubernetes ולניהול שלו. -
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 image pull.
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
במדריך הזה אנחנו יוצאים מנקודת הנחה שתחנת העבודה שלכם מוגדרת כך שהיא נותנת אמון באישורי ה-TLS של סביבת GDC ושל מופע Harbor.
הגדרה של
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'
הכנות להעברת ארטיפקטים
- צריך לוודא שיש קישוריות ושההגדרות של Artifact Registry תקינות.
יוצרים קובץ תצורה של Docker עבור crane. חשבון רובוט משמש להעברת שכבות גדולות של תמונות כדי למנוע פסק זמן של אסימוני אימות כשמשתמשים בכלי העזר לפרטי כניסה (docker-credential-mhs) של Managed Harbor Service (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יוצרים קובץ תצורה של Docker ל-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נכנסים למאגר Harbor OCI באמצעות
helmבאמצעות חשבון הרובוט של Kubernetes image pull. 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יוצרים את הסקריפט
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"יוצרים את הסקריפט
seed_charts.sh. תרשימי Helm שפורסמו כארטיפקטים של OCI מועתקים גם הם באמצעות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: גרסת התרשים.
-
מגדירים את רשימת האימג'ים הנדרשים של הקונטיינרים לפתרון:
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)מזינים את קובצי האימג' של הקונטיינרים הנדרשים ל-Artifact Registry:
${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.shמוודאים שאפשר לקרוא את התרשימים מ-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מורידים את קובצי המניפסט של Gateway API Inference Extension. הם מתפרסמים כנכס של מהדורה, ולא כתרשים:
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 ומאמתים אותו בנפרד באמצעות ההפעלה המהירה של upstream. בשלב הבא, בקטע הבא, מתבצעת השילוב של Envoy Agent Router.
מרחב שמות
יוצרים את מרחב השמות עבור Envoy Gateway. גם שרתי ה-proxy של Envoy
Deploymentשל כלGatewayנוצרים במרחב השמות הזה: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 Custom Resource Definitions (CRDs) מהתרשים המאכלס:
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'
שלט רחוק
יוצרים את קובץ הערכים של Helm עבור Envoy Gateway. התמונות נמשכות מ-Harbor באמצעות סוד משיכת התמונה;
crds.enabled=falseכי ה-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מתקינים את 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}"ממתינים עד שהבקר של 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'"מוודאים שהתמונות של בקר ה-Controller והעבודה ליצירת האישור שלו מגיעות מ-Harbor:
kubectl get pods --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}" \ --output=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}'
Envoy proxy ו-Gateway class
יוצרים את קובץ המניפסט עבור תבנית מישור הנתונים
EnvoyProxyואתGatewayClass. EnvoyProxyמגדיר את תמונת ה-proxy, את סוד המשיכה של התמונה ואת בקשות המשאבים של ה-proxyPods. 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מחילים את המניפסט על
EnvoyProxyועל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 (קצה עורפי של הד Echo מאחורי HTTPRoute) במרחב השמות של Envoy Gateway ומסיר אותו לאחר מכן.
יוצרים את קובץ המניפסט של עומס העבודה במדריך למתחילים:
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יהיה זמין: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'"שולחים בקשת בדיקה דרך השער באמצעות העברה ליציאה אחרת. Envoy
ServiceשלGatewayנמצא לפי התוויות של שער הבעלים: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 Agent
Envoy Agent Router מותקן במרחב שמות משלו, ואז Envoy Gateway מוגדר מחדש כדי לקרוא לו כשרת ההרחבות שלו.
מרחב שמות
יוצרים את מרחב השמות של בקר נתב Envoy Agent:
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 -
הגדרות מותאמות אישית של משאבים
מתקינים את ה-CRD של נתב הסוכן של Envoy מהתרשים המוגדר מראש:
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=-מתקינים את ה-CRD של תוסף ההסקה של Gateway API (
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
הגבלת קצב של יצירת בקשות מבוססת-אסימון נאכפת על ידי שירות הגבלת הקצב של Envoy, שמאחסן את המונים שלו ב-Redis. במדריך הזה מפעילים Redis עם עותק יחיד ללא שמירת נתונים. אפשר להשתמש בשירות Redis קיים במקום זאת, על ידי שינוי הערך rateLimit.backend.redis.url בקטע הבא.
יוצרים את קובץ המניפסט של 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מחילים את המניפסט על Redis
Deployment:kubectl apply \ --filename="${GDCS_IMPLEMENTATION_HOME}/redis.yaml" \ --namespace="${GDCS_REDIS_NAMESPACE}"ממתינים עד שמצב Redis
Deploymentיהיה Available (זמין):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'"
שלט רחוק
יוצרים את קובץ הערכים של Helm עבור 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התקנת נתב Envoy Agent:
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 יהיה זמין:
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.
יוצרים את קובץ הערכים של 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}"יוצרים את קובץ המניפסט של
ClusterRoleשמאפשר לבקר Envoy Gateway לעקוב אחרי משאביInferencePool. התרשים לא מעניק את ההרשאה הזו.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} EOFמחילים את המניפסט על
ClusterRole:kubectl apply \ --filename="${GDCS_IMPLEMENTATION_HOME}/envoy-gateway-inferencepool-rbac.yaml"מפעילים מחדש את בקר Envoy Gateway כדי שיזהה את ההגדרות וההרשאות החדשות, ומחכים עד שהסטטוס שלו יהיה Available:
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 Agent (דוגמה מדומה של מקור תואם ל-OpenAI מאחורי AIGatewayRoute) במרחב השמות של נתב Envoy Agent, ומסיר אותה לאחר מכן.
יוצרים את המניפסט של עומס העבודה של האימות:
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'"מחכים עד שה-mock upstream
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 proxy
PodשלGatewayמפעיל את מעבד הצד החיצוני שהוזרק על ידי Envoy Agent Router: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של גוף הבקשה ומנתב אותו לשרת ה-upstream המדומה: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}"
תפעול
משימות ליום השני של מישורי הבקרה של השער.
שדרוג
- מזינים את התמונות והתרשימים של הגרסאות החדשות (מעדכנים את משתני הגרסה ב-
env.sh, מריצים מחדש אתseed_registry.shואתseed_charts.sh), מחילים את התרשימים החדשים של ה-CRD באמצעותkubectl apply --server-side, ואז מריצים את אותן פקודותhelm upgrade --installעם--versionהחדש. במטריצת התאימות של Envoy Agent Router אפשר לבדוק את הגרסאות של Envoy Gateway ושל Gateway API שנתמכות בגרסת היעד. הגרסה Envoy Agent Router 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}", ClusterRoleenvoy-gateway-inferencepool-reader, GatewayClass, RedisDeploymentולבסוף את ה-CRD.
פתרון בעיות
| תיאור הבעיה | סיבה סבירה | פעולה |
|---|---|---|
helm show chart או helm upgrade נכשל עם unauthorized או FetchReference |
ל-helm אין פרטי כניסה ל-Harbor (הוא לא משתמש בהגדרת Docker crane) |
מריצים שוב את שלב helm registry login עם חשבון הרובוט של משיכת תמונות Kubernetes. |
Envoy Gateway או Envoy Agent Router Pods נשארים ב-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 controller אין גישה לשרת התוסף, או שהוא לא הופעל מחדש אחרי השדרוג | 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, או שחסרים CRD של InferencePool |
מוודאים את ClusterRoleBinding envoy-gateway-inferencepool-reader ואת inferencepools.inference.networking.k8s.io CRD, ובודקים את יומני הבקרה של forbidden. |
בקשת השלמה של צ'אט מחזירה 413 או שהחיבור מאופס להנחיות גדולות |
מגבלת ברירת המחדל של מאגר הנתונים הזמני של Envoy (32KiB) קטנה מדי בשביל נתוני AI | מצרפים ClientTrafficPolicy עם connection.bufferLimit (האימות משתמש ב-50Mi) אל Gateway. |
הגבלת קצב של יצירת בקשות בשירות Pod היא CrashLoopBackOff |
אין גישה ל-Redis בכתובת ה-URL שהוגדרה | kubectl get service redis --namespace="${GDCS_REDIS_NAMESPACE}"; מתקנים את rateLimit.backend.redis.url בקובץ הערכים של השילוב ומשדרגים שוב. |
חומרים נוספים
- מאמרים בנושא Envoy Agent Router
- הערות מוצר ומטריצת תאימות של Envoy Agent Router
- חומרי עזר בנושא Envoy Gateway
- Kubernetes Gateway API
- Gateway API Inference Extension
- יצירת אשכול רגיל | Google Distributed Cloud במודל Air-gapped
יצירת פרויקטים ב-Harbor | Google Distributed Cloud במודל Air-gapped