במאמר הזה מוסבר איך לפרוס שער AI בסביבות עם Air Gap ב-Google Distributed Cloud (GDC). השער מורכב מ-Envoy Gateway, הטמעה של Kubernetes Gateway API שמבוססת על שרת proxy של Envoy, ומ-Envoy Agent Router (לשעבר Envoy AI Gateway), התוסף שהופך את Envoy Gateway לנקודת כניסה מאוחדת שתואמת ל-OpenAI לתעבורת נתונים של מודל שפה גדול (LLM). המדריך כולל הסברים על העברת קובצי האימג' של הקונטיינרים ותרשימי Helm ל-Harbor Registry המקומי, התקנת שני מישורי הבקרה באשכול רגיל, הכנת הקצה העורפי האופציונלי להגבלת קצב של יצירת בקשות שמבוססת על טוקנים ואימות ההתקנה באמצעות עומסי עבודה לדוגמה.
קצוות עורפיים לפרסום מודלים (Ollama, vLLM) נפרסים באמצעות קבוצת המדריכים הנלווים מודלים של משקלים פתוחים ב-GDC עם air gap. המדריך למשתמש בנושא ניתוב מבוסס-גוף באמצעות Envoy Agent Router מראה איך לנתב אליהם בקשות לפי שם המודל.
ארכיטקטורה
הפתרון פועל באשכול רגיל של GDC. תחנת עבודה של אדמין מפיצה את קובצי האימג' ואת תרשימי Helm למאגר Harbor של הפרויקט, ומתקינה את שני מישורי הבקרה: הבקר של Envoy Gateway, שמתכנת את מישור הנתונים של שרת ה-proxy של Envoy ממשאבי Gateway API (GatewayClass, Gateway, HTTPRoute), והבקר של Envoy Agent Router, שמרחיב את מישור הנתונים הזה באמצעות מעבד חיצוני (ExtProc) לתנועת AI (AIGatewayRoute, AIServiceBackend, InferencePool). לקוחות של אפליקציות שולחים בקשות תואמות ל-OpenAI לשרת ה-proxy של Envoy, שמנתב אותן לקצוות העורפיים של פרסום המודל או ל-InferencePool. מכונה אופציונלית של Redis מאחסנת את המונים של שירות הגבלת הקצב של Envoy, לצורך הגבלת קצב מבוססת-אסימון.

Envoy Gateway
Envoy Gateway הוא פרויקט קוד פתוח שמבוסס על שרת proxy של Envoy, שמפשט את ההטמעה, השימוש והניהול של שרת proxy של Envoy כשער API של Kubernetes. הוא מטמיע ומרחיב את Kubernetes Gateway API, שמחליף את Ingress API: משאבי GatewayClass ו-Gateway מתארים את נקודות הכניסה, משאבי מסלול כמו HTTPRoute מתארים איך התנועה מותאמת ומועברת, והעיצוב מבוסס התפקידים מפריד בין תחומי האחריות של צוותי התשתית והאפליקציה. ל-Envoy Gateway יש ממשקי API משלו להרחבה, למשל EnvoyProxy (הגדרות של מישור הנתונים), Backend (נקודות קצה מחוץ לאשכול או כאלה שמוזכרות על ידי FQDN) ו-ClientTrafficPolicy (הגדרות חיבור כמו מגבלות מאגר נתונים).
Envoy Agent Router
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 במקרה הצורך.
לפני שמתחילים
לפני שמתחילים בפריסה, חשוב לוודא שהסביבה עומדת בכל הדרישות המוקדמות, ושכלי שורת הפקודה הנדרשים מוגדרים כמו שצריך. חשוב להגדיר את הכלים האלה בתחנת העבודה כדי לנהל את Container Registry, לתקשר עם אשכולות ולהפוך את תהליך הפריסה לאוטומטי.
- סביבת GDC עם air gap בגרסה 1.16.2-hf1 ואילך זמינה עם אשכול רגיל שפועל בו Kubernetes בגרסה v1.32.13-gke.400 ואילך.
- אשכול רגיל שנוצר עם מספיק משאבים. רכיבי השער פועלים רק ב-CPU. לקצוות העורפיים של פרסום המודל יש דרישות משלהם למאיצים (ראו את המדריכים בנושא מודלים של משקולות פתוחות ב-GDC עם air gap).
- מכונה של 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 באשכול הרגיל: התפקידים הקודמים בפרויקט לא מעניקים הרשאות בתוך האשכול. בנוסף, אדמין של IAM בפרויקט צריך לקשר את המשתמש ל-StandardClusterRole cluster-admin באמצעות StandardClusterRoleBinding במרחב השמות של הפרויקט בשרת ה-API לניהול. הקישור מופץ לאשכולות הרגילים של הפרויקט תוך כמה שניות (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: הנתיב לקובץ kubeconfig של שרת ה-API לניהול. -
USER: משתמש. -
PROJECT: פרויקט.
הרשאות לחשבון רובוט של Harbor crane (crane):
- מאגר רשימות
- מאגר שליפה
- מאגר Push
- קריאת ארטיפקט
- רשימת פריטי מידע שנוצרו בתהליך הפיתוח (Artifact)
- יצירת תג
- תג רשימה
הרשאות של חשבון רובוט לשליפת תמונות של Harbor Kubernetes (kubernetes-image-puller):
- מאגר רשימות
- מאגר שליפה
- קריאת ארטיפקט
- רשימת פריטי מידע שנוצרו בתהליך הפיתוח (Artifact)
- תג רשימה
תחנת עבודה
אלא אם צוין אחרת, צריך להריץ את כל הפקודות במדריך הזה מתחנת עבודה עם הכלים הנדרשים וקישוריות לסביבה ולאינטרנט.
דרישות
צריך להתקין את הכלים הבאים בתחנת העבודה:
-
crane: ניהול והעתקה של קובצי אימג' של קונטיינרים ופריטי מידע שנוצרו בתהליך הפיתוח (Artifact) של 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: אסימון האימות של חשבון הרובוט לשליפת קובצי אימג' של Kubernetes ב-Harbor.
אחרי שאוספים את הערכים של כל המשתנים הנדרשים, ממשיכים ליצירת קובץ משתני הסביבה. אחרי שהקובץ נוצר, אפשר לערוך אותו ידנית בכל שלב.
יוצרים את תיקיות השורש של הפתרון ואת תיקיית הסודות:
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.
-
יוצרים את קובץ טעינת הסביבה ברמה הבסיסית (root):
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
הכנות לקראת העברת פריטי המידע שנוצר (Artifact)
- בודקים את הקישוריות וההגדרות של מאגר פריטי המידע שנוצרו בתהליך הפיתוח (Artifact Registry).
יוצרים קובץ תצורה של Docker ל-crane. חשבון רובוט משמש לדחיפת שכבות גדולות של קובצי אימג', כדי למנוע זמן קצוב לתפוגה של אסימון אימות כשמשתמשים בכלי העזר לאימות של Managed Harbor Service (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יוצרים קובץ תצורה של 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. ל-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 הנדרשים ל-Artifact Registry:
${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 (ערוץ רגיל) ואת הגדרות המשאבים בהתאמה אישית (CRD) של Envoy Gateway מהתרשים שהוזן:
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'"מוודאים שהתמונות של הבקר ומשימת יצירת האישורים שלו מגיעות מ-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, את הסוד למשיכת קובץ האימג' ואת בקשות המשאבים של שרתי ה-proxy מסוג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מחילים את המניפסט על
EnvoyProxyועלGatewayClass:kubectl apply \ --filename="${GDCS_IMPLEMENTATION_HOME}/gateway-class.yaml"מוודאים שהסטטוס של
GatewayClassהוא Accepted (התקבל):kubectl get gatewayclass "${GDCS_GATEWAY_CLASS_NAME}"הפלט אמור להיראות כך:
NAME CONTROLLER ACCEPTED AGE envoy-ai-gateway gateway.envoyproxy.io/gatewayclass-controller True 5s
אימות
האימות פורס את ההתחלה המהירה של Envoy Gateway (קצה עורפי של הד מאחורי 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'"שולחים בקשת בדיקה דרך השער באמצעות העברה ליציאה אחרת.
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 Router
Envoy Agent Router מותקן במרחב שמות משלו, ואז Envoy Gateway מוגדר מחדש כדי לקרוא לו כשרת התוספים שלו.
מרחב שמות
יוצרים את מרחב השמות של הבקר של Envoy Agent Router:
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 Agent Router מהתרשים שהוזן:
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 Inference Extension (
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יהיה זמין: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 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}"מחכים עד שהבקר של 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.
יוצרים את קובץ הערכים של 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 כדי שיקבל את ההגדרות וההרשאות החדשות, וממתינים עד שהוא יהיה זמין:
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 Router (דמה של OpenAI-compatible upstream מאחורי AIGatewayRoute) במרחב השמות של Envoy Agent Router, ואז מסיר אותו.
יוצרים את המניפסט לעומס העבודה של האימות:
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'"מוודאים ששרת ה-proxy של Envoy
PodשלGatewayמריץ את המעבד החיצוני של ה-sidecar שהוחדר על ידי 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בגוף הבקשה ומנתב אותו ל-mock 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 לא יכול להגיע לשרת התוסף, או שלא הופעל מחדש אחרי השדרוג | kubectl logs deployment/envoy-gateway --namespace="${GDCS_ENVOY_GATEWAY_NAMESPACE}"; מוודאים ש-service/ai-gateway-controller קיים ב-${GDCS_ENVOY_AGENT_ROUTER_NAMESPACE} ושיציאה 1063 מופיעה ברשימה; חוזרים על ההפעלה מחדש של ההשקה. |
AIGatewayRoute שמפנה אל InferencePool לא מתקבל |
ל-Envoy Gateway אין הרשאה לקרוא את inferencepools, או שחסרים ה-CRD של InferencePool |
צריך לאמת את ClusterRoleBinding envoy-gateway-inferencepool-reader ואת inferencepools.inference.networking.k8s.io CRD; לבדוק את יומני הבקר של forbidden. |
בקשה להשלמת צ'אט מחזירה את השגיאה 413 או שהחיבור מתאפס בהנחיות גדולות |
מגבלת ברירת המחדל של מאגר הנתונים הזמני של Envoy (32KiB) קטנה מדי למטענים ייעודיים (payloads) של 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