Cette page explique comment personnaliser le déploiement du GKE Inference Gateway.
Cette page s'adresse aux spécialistes de la mise en réseau chargés de gérer l'infrastructure GKE et aux administrateurs de plate-forme qui gèrent les charges de travail d'IA.
Pour gérer et optimiser les charges de travail d'inférence, vous devez configurer les fonctionnalités avancées de la passerelle d'inférence GKE.
Comprendre et configurer les fonctionnalités avancées suivantes:
- Pour utiliser l'intégration de Model Armor, configurez les vérifications de sécurité et de sûreté de l'IA.
- Pour afficher les métriques et les tableaux de bord du GKE Inference Gateway et du serveur de modèle, et pour activer la journalisation des accès HTTP pour obtenir des informations détaillées sur les requêtes et les réponses, configurez l'observabilité.
- Pour mettre à l'échelle automatiquement vos déploiements de passerelle d'inférence GKE, configurez l'autoscaling.
Configurer les vérifications de sécurité de l'IA
GKE Inference Gateway s'intègre à Model Armor pour effectuer des vérifications de sécurité sur les requêtes et les réponses des applications qui utilisent de grands modèles de langage (LLM). Cette intégration fournit une couche supplémentaire de renforcement de la sécurité au niveau de l'infrastructure, qui complète les mesures de sécurité au niveau de l'application. Cela permet d'appliquer des règles centralisées à l'ensemble du trafic LLM.
Le schéma suivant illustre l'intégration de Model Armor avec la passerelle d'inférence GKE sur un cluster GKE:

Pour configurer les vérifications de sécurité de l'IA, procédez comme suit:
Assurez-vous de remplir les conditions préalables suivantes:
- Activez le service Model Armor dans votre projet Google Cloud .
- Créez les modèles Model Armor à l'aide de la console Model Armor, de la Google Cloud CLI ou de l'API.
Assurez-vous d'avoir déjà créé un modèle Model Armor nommé
my-model-armor-template-name-id
.Pour configurer
GCPTrafficExtension
, procédez comme suit:Enregistrez l'exemple de fichier manifeste suivant sous le nom
gcp-traffic-extension.yaml
:kind: GCPTrafficExtension apiVersion: networking.gke.io/v1 metadata: name: my-model-armor-extension spec: targetRefs: - group: "gateway.networking.k8s.io" kind: Gateway name: GATEWAY_NAME extensionChains: - name: my-model-armor-chain1 matchCondition: celExpressions: - celMatcher: request.path.startsWith("/") extensions: - name: my-model-armor-service supportedEvents: - RequestHeaders timeout: 1s googleAPIServiceName: "modelarmor.us-central1.rep.googleapis.com" metadata: 'extensionPolicy': MODEL_ARMOR_TEMPLATE_NAME 'sanitizeUserPrompt': 'true' 'sanitizeUserResponse': 'true'
Remplacez les éléments suivants :
GATEWAY_NAME
: nom de la passerelle.MODEL_ARMOR_TEMPLATE_NAME
: nom de votre modèle d'armure de modèle.
Le fichier
gcp-traffic-extension.yaml
inclut les paramètres suivants:targetRefs
: spécifie la passerelle à laquelle s'applique cette extension.extensionChains
: définit une chaîne d'extensions à appliquer au trafic.matchCondition
: définit les conditions dans lesquelles les extensions sont appliquées.extensions
: définit les extensions à appliquer.supportedEvents
: spécifie les événements au cours desquels l'extension est appelée.timeout
: spécifie le délai avant expiration de l'extension.googleAPIServiceName
: spécifie le nom du service pour l'extension.metadata
: spécifie les métadonnées de l'extension, y compris les paramètresextensionPolicy
et de nettoyage de l'invite ou de la réponse.
Appliquez l'exemple de fichier manifeste à votre cluster:
kubectl apply -f `gcp-traffic-extension.yaml`
Une fois que vous avez configuré les vérifications de sécurité de l'IA et les avez intégrées à votre passerelle, Model Armor filtre automatiquement les requêtes et les réponses en fonction des règles définies.
Configurer l'observabilité
La passerelle d'inférence GKE fournit des insights sur l'état, les performances et le comportement de vos charges de travail d'inférence. Cela vous permet d'identifier et de résoudre les problèmes, d'optimiser l'utilisation des ressources et de garantir la fiabilité de vos applications.
Google Cloud fournit les tableaux de bord Cloud Monitoring suivants qui offrent une observabilité de l'inférence pour GKE Inference Gateway:
- Tableau de bord du GKE Inference Gateway : fournit des métriques de référence pour le traitement LLM, telles que le débit des requêtes et des jetons, la latence, les erreurs et l'utilisation du cache pour le
InferencePool
. Pour obtenir la liste complète des métriques disponibles pour le GKE Inference Gateway, consultez la section Métriques exposées. - Tableau de bord du serveur de modèles: fournit un tableau de bord pour les signaux clés du serveur de modèles. Vous pouvez ainsi surveiller la charge et les performances des serveurs de modèles, tels que
KVCache Utilization
etQueue length
. Vous pouvez ainsi surveiller la charge et les performances des serveurs de modèles. - Tableau de bord de l'équilibreur de charge: affiche les métriques de l'équilibreur de charge, telles que le nombre de requêtes par seconde, la latence de traitement des requêtes de bout en bout et les codes d'état de la requête-réponse. Ces métriques vous aident à comprendre les performances de la diffusion des requêtes de bout en bout et à identifier les erreurs.
- Métriques du gestionnaire GPU de centre de données (DCGM): fournit des métriques des GPU NVIDIA, telles que les performances et l'utilisation des GPU NVIDIA. Vous pouvez configurer les métriques du gestionnaire GPU de centre de données (DCGM) NVIDIA dans Cloud Monitoring. Pour en savoir plus, consultez la section Collecter et afficher les métriques DCGM.
Afficher le tableau de bord de la passerelle d'inférence GKE
Pour afficher le tableau de bord de la passerelle d'inférence GKE, procédez comme suit:
Dans la console Google Cloud , accédez à la page Surveillance.
Dans le volet de navigation, sélectionnez Tableaux de bord.
Dans la section Intégrations, sélectionnez GMP.
Sur la page Modèles de tableaux de bord Cloud Monitoring, recherchez "Balise".
Affichez le tableau de bord GKE Inference Gateway.
Vous pouvez également suivre les instructions du tableau de bord de surveillance.
Configurer le tableau de bord d'observabilité du serveur de modèles
Pour collecter des signaux d'or de chaque serveur de modèle et comprendre ce qui contribue aux performances du GKE Inference Gateway, vous pouvez configurer la surveillance automatique de vos serveurs de modèle. Cela inclut les serveurs de modèles tels que les suivants:
Pour afficher les tableaux de bord d'intégration, procédez comme suit:
- Collectez les métriques de votre serveur de modèle.
Dans la console Google Cloud , accédez à la page Surveillance.
Dans le volet de navigation, sélectionnez Tableaux de bord.
Sous Intégrations, sélectionnez GMP. Les tableaux de bord d'intégration correspondants s'affichent.
Figure : Tableaux de bord d'intégration
Pour en savoir plus, consultez Personnaliser la surveillance des applications.
Configurer le tableau de bord d'observabilité de l'équilibreur de charge
Pour utiliser l'équilibreur de charge d'application avec GKE Inference Gateway, importez le tableau de bord en procédant comme suit:
Pour créer le tableau de bord de l'équilibreur de charge, créez le fichier suivant et enregistrez-le au format
dashboard.json
:{ "displayName": "GKE Inference Gateway (Load Balancer) Prometheus Overview", "dashboardFilters": [ { "filterType": "RESOURCE_LABEL", "labelKey": "cluster", "templateVariable": "", "valueType": "STRING" }, { "filterType": "RESOURCE_LABEL", "labelKey": "location", "templateVariable": "", "valueType": "STRING" }, { "filterType": "RESOURCE_LABEL", "labelKey": "namespace", "templateVariable": "", "valueType": "STRING" }, { "filterType": "RESOURCE_LABEL", "labelKey": "forwarding_rule_name", "templateVariable": "", "valueType": "STRING" } ], "labels": {}, "mosaicLayout": { "columns": 48, "tiles": [ { "height": 8, "width": 48, "widget": { "title": "", "id": "", "text": { "content": "### Inferece Gateway Metrics\n\nPlease refer to the [official documentation](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/site-src/guides/metrics.md) for more details of underlying metrics used in the dashboard.\n\n\n### External Application Load Balancer Metrics\n\nPlease refer to the [pubic page](/load-balancing/docs/metrics) for complete list of External Application Load Balancer metrics.\n\n### Model Server Metrics\n\nYou can redirect to the detail dashboard for model servers under the integration tab", "format": "MARKDOWN", "style": { "backgroundColor": "#FFFFFF", "fontSize": "FS_EXTRA_LARGE", "horizontalAlignment": "H_LEFT", "padding": "P_EXTRA_SMALL", "pointerLocation": "POINTER_LOCATION_UNSPECIFIED", "textColor": "#212121", "verticalAlignment": "V_TOP" } } } }, { "yPos": 8, "height": 4, "width": 48, "widget": { "title": "External Application Load Balancer", "id": "", "sectionHeader": { "dividerBelow": false, "subtitle": "" } } }, { "yPos": 12, "height": 15, "width": 24, "widget": { "title": "E2E Request Latency p99 (by code)", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.99, sum by(le, response_code) (rate(loadbalancing_googleapis_com:https_external_regional_total_latencies_bucket{monitored_resource=\"http_external_regional_lb_rule\",forwarding_rule_name=~\".*inference-gateway.*\"}[1m])))", "unitOverride": "ms" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 12, "height": 43, "width": 48, "widget": { "title": "Regional", "collapsibleGroup": { "collapsed": false }, "id": "" } }, { "yPos": 12, "xPos": 24, "height": 15, "width": 24, "widget": { "title": "E2E Request Latency p95 (by code)", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.95, sum by(le, response_code) (rate(loadbalancing_googleapis_com:https_external_regional_total_latencies_bucket{monitored_resource=\"http_external_regional_lb_rule\",forwarding_rule_name=~\".*inference-gateway.*\"}[1m])))", "unitOverride": "ms" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 27, "height": 15, "width": 24, "widget": { "title": "E2E Request Latency p90 (by code)", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.90, sum by(le, response_code) (rate(loadbalancing_googleapis_com:https_external_regional_total_latencies_bucket{monitored_resource=\"http_external_regional_lb_rule\",forwarding_rule_name=~\".*inference-gateway.*\"}[1m])))", "unitOverride": "ms" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 27, "xPos": 24, "height": 15, "width": 24, "widget": { "title": "E2E Request Latency p50 (by code)", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.50, sum by(le, response_code) (rate(loadbalancing_googleapis_com:https_external_regional_total_latencies_bucket{monitored_resource=\"http_external_regional_lb_rule\",forwarding_rule_name=~\".*inference-gateway.*\"}[1m])))", "unitOverride": "ms" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 42, "height": 13, "width": 48, "widget": { "title": "Request /s (by code)", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "sum by (response_code)(rate(loadbalancing_googleapis_com:https_external_regional_request_count{monitored_resource=\"http_external_regional_lb_rule\", forwarding_rule_name=~\".*inference-gateway.*\"}[1m]))", "unitOverride": "" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 55, "height": 4, "width": 48, "widget": { "title": "Inference Optimized Gateway", "id": "", "sectionHeader": { "dividerBelow": false, "subtitle": "" } } }, { "yPos": 59, "height": 17, "width": 48, "widget": { "title": "Request Latency", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "p95", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.95, sum by(le) (rate(inference_model_request_duration_seconds_bucket{}[${__interval}])))", "unitOverride": "s" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p90", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.9, sum by(le) (rate(inference_model_request_duration_seconds_bucket{}[${__interval}])))", "unitOverride": "s" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p50", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.5, sum by(le) (rate(inference_model_request_duration_seconds_bucket{}[${__interval}])))", "unitOverride": "s" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 59, "height": 65, "width": 48, "widget": { "title": "Inference Model", "collapsibleGroup": { "collapsed": false }, "id": "" } }, { "yPos": 76, "height": 16, "width": 24, "widget": { "title": "Request / s", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "sum by(model_name, target_model_name) (rate(inference_model_request_total{}[${__interval}]))", "unitOverride": "" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 76, "xPos": 24, "height": 16, "width": 24, "widget": { "title": "Request Error / s", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "sum by (error_code,model_name,target_model_name) (rate(inference_model_request_error_total[${__interval}]))", "unitOverride": "" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 92, "height": 16, "width": 24, "widget": { "title": "Request Size", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "p95", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.95, sum by(le) (rate(inference_model_request_sizes_bucket{}[${__interval}])))", "unitOverride": "By" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p90", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.9, sum by(le) (rate(inference_model_request_sizes_bucket{}[${__interval}])))", "unitOverride": "By" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p50", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.5, sum by(le) (rate(inference_model_request_sizes_bucket{}[${__interval}])))", "unitOverride": "By" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 92, "xPos": 24, "height": 16, "width": 24, "widget": { "title": "Response Size", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "p95", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.95, sum by(le) (rate(inference_model_response_sizes_bucket{}[${__interval}])))", "unitOverride": "By" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p90", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.9, sum by(le) (rate(inference_model_response_sizes_bucket{}[${__interval}])))", "unitOverride": "By" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p50", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.5, sum by(le) (rate(inference_model_response_sizes_bucket{}[${__interval}])))", "unitOverride": "By" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 108, "height": 16, "width": 24, "widget": { "title": "Input Token Count", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "p95", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.95, sum by(le) (rate(inference_model_input_tokens_bucket{}[${__interval}])))", "unitOverride": "" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p90", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.9, sum by(le) (rate(inference_model_input_tokens_bucket{}[${__interval}])))", "unitOverride": "" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p50", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.5, sum by(le) (rate(inference_model_input_tokens_bucket{}[${__interval}])))", "unitOverride": "" } } ], "thresholds": [] } } }, { "yPos": 108, "xPos": 24, "height": 16, "width": 24, "widget": { "title": "Output Token Count", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "p95", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.95, sum by(le) (rate(inference_model_output_tokens_bucket{}[${__interval}])))", "unitOverride": "" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p90", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.9, sum by(le) (rate(inference_model_output_tokens_bucket{}[${__interval}])))", "unitOverride": "" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p50", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.5, sum by(le) (rate(inference_model_output_tokens_bucket{}[${__interval}])))", "unitOverride": "" } } ], "thresholds": [] } } }, { "yPos": 124, "height": 16, "width": 24, "widget": { "title": "Average KV Cache Utilization", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "sum by (name)(avg_over_time(inference_pool_average_kv_cache_utilization[${__interval}]))*100", "unitOverride": "%" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 124, "height": 16, "width": 48, "widget": { "title": "Inference Pool", "collapsibleGroup": { "collapsed": false }, "id": "" } }, { "yPos": 124, "xPos": 24, "height": 16, "width": 24, "widget": { "title": "Average Queue Size", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "sum by (name) (avg_over_time(inference_pool_average_queue_size[${__interval}]))", "unitOverride": "" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 140, "height": 4, "width": 48, "widget": { "title": "Model Server", "id": "", "sectionHeader": { "dividerBelow": true, "subtitle": "The following charts will only be populated if model server is exporting metrics." } } }, { "yPos": 144, "height": 32, "width": 48, "widget": { "title": "vLLM", "collapsibleGroup": { "collapsed": false }, "id": "" } }, { "yPos": 144, "xPos": 1, "height": 16, "width": 24, "widget": { "title": "Token Throughput", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "Prompt Tokens/Sec", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "sum by(model_name) (rate(vllm:prompt_tokens_total[${__interval}]))", "unitOverride": "" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "Generation Tokens/Sec", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "sum by(model_name) (rate(vllm:generation_tokens_total[${__interval}]))", "unitOverride": "" } } ], "thresholds": [] } } }, { "yPos": 144, "xPos": 25, "height": 16, "width": 23, "widget": { "title": "Request Latency", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "p95", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.95, sum by(le) (rate(vllm:e2e_request_latency_seconds_bucket[${__interval}])))", "unitOverride": "s" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p90", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.9, sum by(le) (rate(vllm:e2e_request_latency_seconds_bucket[${__interval}])))", "unitOverride": "s" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p50", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.5, sum by(le) (rate(vllm:e2e_request_latency_seconds_bucket[${__interval}])))", "unitOverride": "s" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 160, "xPos": 1, "height": 16, "width": 24, "widget": { "title": "Time Per Output Token Latency", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "p95", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.95, sum by(le) (rate(vllm:time_per_output_token_seconds_bucket[${__interval}])))", "unitOverride": "s" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p90", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.9, sum by(le) (rate(vllm:time_per_output_token_seconds_bucket[${__interval}])))", "unitOverride": "s" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p50", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.5, sum by(le) (rate(vllm:time_per_output_token_seconds_bucket[${__interval}])))", "unitOverride": "s" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } }, { "yPos": 160, "xPos": 25, "height": 16, "width": 23, "widget": { "title": "Time To First Token Latency", "id": "", "xyChart": { "chartOptions": { "displayHorizontal": false, "mode": "COLOR", "showLegend": false }, "dataSets": [ { "breakdowns": [], "dimensions": [], "legendTemplate": "p95", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.95, sum by(le) (rate(vllm:time_to_first_token_seconds_bucket[${__interval}])))", "unitOverride": "s" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p90", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.9, sum by(le) (rate(vllm:time_to_first_token_seconds_bucket[${__interval}])))", "unitOverride": "s" } }, { "breakdowns": [], "dimensions": [], "legendTemplate": "p50", "measures": [], "plotType": "LINE", "targetAxis": "Y1", "timeSeriesQuery": { "outputFullDuration": false, "prometheusQuery": "histogram_quantile(0.5, sum by(le) (rate(vllm:time_to_first_token_seconds_bucket[${__interval}])))", "unitOverride": "s" } } ], "thresholds": [], "yAxis": { "label": "", "scale": "LINEAR" } } } } ] } }
Pour installer le tableau de bord dans Google Cloud Armor, exécutez la commande suivante:
gcloud monitoring dashboards create --project $PROJECT_ID --config-from-file=dashboard.json
Ouvrez la page Surveillance dans la console Google Cloud .
Dans le menu de navigation, sélectionnez Tableaux de bord.
Sélectionnez le tableau de bord Vue d'ensemble Prometheus de la passerelle optimisée pour l'inférence (avec L7LB) dans la liste des tableaux de bord personnalisés.
La section Équilibreur de charge d'application externe affiche les métriques d'équilibrage de charge suivantes:
- Latence de requête de bout en bout p99 (par code): indique le 99e centile de la latence de requête de bout en bout pour les requêtes diffusées par l'équilibreur de charge, agrégées par code d'état renvoyé.
- Requêtes (par code): indique le nombre de requêtes diffusées par l'équilibreur de charge, agrégées par code d'état renvoyé.
Configurer la journalisation pour GKE Inference Gateway
Configurer la journalisation pour le GKE Inference Gateway fournit des informations détaillées sur les requêtes et les réponses, ce qui est utile pour le dépannage, l'audit et l'analyse des performances. Les journaux d'accès HTTP enregistrent chaque requête et réponse, y compris les en-têtes, les codes d'état et les codes temporels. Ce niveau de détail peut vous aider à identifier les problèmes, à détecter les erreurs et à comprendre le comportement de vos charges de travail d'inférence.
Pour configurer la journalisation pour le GKE Inference Gateway, activez la journalisation des accès HTTP pour chacun de vos objets InferencePool
.
Enregistrez l'exemple de fichier manifeste suivant sous le nom
logging-backend-policy.yaml
:apiVersion: networking.gke.io/v1 kind: GCPBackendPolicy metadata: name: logging-backend-policy namespace: NAMESPACE_NAME spec: default: logging: enabled: true sampleRate: 500000 targetRef: group: inference.networking.x-k8s.io kind: InferencePool name: INFERENCE_POOL_NAME
Remplacez les éléments suivants :
NAMESPACE_NAME
: nom de l'espace de noms dans lequel votreInferencePool
est déployé.INFERENCE_POOL_NAME
: le nom duInferencePool
.
Appliquez l'exemple de fichier manifeste à votre cluster:
kubectl apply -f logging-backend-policy.yaml
Une fois ce fichier manifeste appliqué, GKE Inference Gateway active les journaux d'accès HTTP pour le InferencePool
spécifié. Vous pouvez afficher ces journaux dans Cloud Logging. Les journaux incluent des informations détaillées sur chaque requête et réponse, telles que l'URL de la requête, les en-têtes, le code d'état de la réponse et la latence.
Configurer l'autoscaling
L'autoscaling ajuste l'allocation des ressources en fonction des variations de charge, tout en maintenant les performances et l'efficacité des ressources en ajoutant ou en supprimant de manière dynamique des pods en fonction de la demande. Pour GKE Inference Gateway, cela implique l'autoscaling horizontal des pods dans chaque InferencePool
. L'Autoscaler horizontal de pods (AHP) de GKE effectue un autoscaling des pods en fonction des métriques du serveur de modèle, telles que KVCache Utilization
. Cela garantit que le service d'inférence gère différents volumes de requêtes et de charges de travail tout en gérant efficacement l'utilisation des ressources.
Pour configurer les instances InferencePool
afin qu'elles s'autoscalent en fonction des métriques produites par GKE Inference Gateway, procédez comme suit:
Déployez un objet
PodMonitoring
dans le cluster pour collecter les métriques produites par la passerelle d'inférence GKE. Pour en savoir plus, consultez la section Configurer l'observabilité.Déployez l'adaptateur de métriques personnalisées Stackdriver pour accorder à l'AHP un accès aux métriques:
Enregistrez l'exemple de fichier manifeste suivant sous le nom
adapter_new_resource_model.yaml
:apiVersion: v1 kind: Namespace metadata: name: custom-metrics --- apiVersion: v1 kind: ServiceAccount metadata: name: custom-metrics-stackdriver-adapter namespace: custom-metrics --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: custom-metrics:system:auth-delegator roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: custom-metrics-stackdriver-adapter namespace: custom-metrics --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: custom-metrics-auth-reader namespace: kube-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: extension-apiserver-authentication-reader subjects: - kind: ServiceAccount name: custom-metrics-stackdriver-adapter namespace: custom-metrics --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: custom-metrics-resource-reader namespace: custom-metrics rules: - apiGroups: - "" resources: - pods - nodes - nodes/stats verbs: - get - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: custom-metrics-resource-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: custom-metrics-resource-reader subjects: - kind: ServiceAccount name: custom-metrics-stackdriver-adapter namespace: custom-metrics --- apiVersion: apps/v1 kind: Deployment metadata: run: custom-metrics-stackdriver-adapter k8s-app: custom-metrics-stackdriver-adapter spec: replicas: 1 selector: matchLabels: run: custom-metrics-stackdriver-adapter k8s-app: custom-metrics-stackdriver-adapter template: metadata: labels: run: custom-metrics-stackdriver-adapter k8s-app: custom-metrics-stackdriver-adapter kubernetes.io/cluster-service: "true" spec: serviceAccountName: custom-metrics-stackdriver-adapter containers: - image: gcr.io/gke-release/custom-metrics-stackdriver-adapter:v0.15.2-gke.1 imagePullPolicy: Always name: pod-custom-metrics-stackdriver-adapter command: - /adapter - --use-new-resource-model=true - --fallback-for-container-metrics=true resources: limits: cpu: 250m memory: 200Mi requests: cpu: 250m memory: 200Mi --- apiVersion: v1 kind: Service metadata: labels: run: custom-metrics-stackdriver-adapter k8s-app: custom-metrics-stackdriver-adapter kubernetes.io/cluster-service: 'true' kubernetes.io/name: Adapter name: custom-metrics-stackdriver-adapter namespace: custom-metrics spec: ports: - port: 443 protocol: TCP targetPort: 443 selector: run: custom-metrics-stackdriver-adapter k8s-app: custom-metrics-stackdriver-adapter type: ClusterIP --- apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: name: v1beta1.custom.metrics.k8s.io spec: insecureSkipTLSVerify: true group: custom.metrics.k8s.io groupPriorityMinimum: 100 versionPriority: 100 service: name: custom-metrics-stackdriver-adapter namespace: custom-metrics version: v1beta1 --- apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: name: v1beta2.custom.metrics.k8s.io spec: insecureSkipTLSVerify: true group: custom.metrics.k8s.io groupPriorityMinimum: 100 versionPriority: 200 service: name: custom-metrics-stackdriver-adapter namespace: custom-metrics version: v1beta2 --- apiVersion: apiregistration.k8s.io/v1 kind: APIService metadata: name: v1beta1.external.metrics.k8s.io spec: insecureSkipTLSVerify: true group: external.metrics.k8s.io groupPriorityMinimum: 100 versionPriority: 100 service: name: custom-metrics-stackdriver-adapter namespace: custom-metrics version: v1beta1 --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: external-metrics-reader rules: - apiGroups: - "external.metrics.k8s.io" resources: - "*" verbs: - list - get - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: external-metrics-reader roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: external-metrics-reader subjects: - kind: ServiceAccount name: horizontal-pod-autoscaler namespace: kube-system
Appliquez l'exemple de fichier manifeste à votre cluster:
kubectl apply -f adapter_new_resource_model.yaml
Pour autoriser l'adaptateur à lire les métriques du projet, exécutez la commande suivante:
$ PROJECT_ID=PROJECT_ID $ PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format="value(projectNumber)") $ gcloud projects add-iam-policy-binding projects/PROJECT_ID \ --role roles/monitoring.viewer \ --member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/$PROJECT_ID.svc.id.goog/subject/ns/custom-metrics/sa/custom-metrics-stackdriver-adapter
Remplacez
PROJECT_ID
par l'ID de votre Google Cloud projet.Pour chaque
InferencePool
, déployez un AHP semblable à celui-ci:apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: INFERENCE_POOL_NAME namespace: INFERENCE_POOL_NAMESPACE spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: INFERENCE_POOL_NAME minReplicas: MIN_REPLICAS maxReplicas: MAX_REPLICAS metrics: - type: External external: metric: name: prometheus.googleapis.com|inference_pool_average_kv_cache_utilization|gauge selector: matchLabels: metric.labels.name: INFERENCE_POOL_NAME resource.labels.cluster: CLUSTER_NAME resource.labels.namespace: INFERENCE_POOL_NAMESPACE target: type: AverageValue averageValue: TARGET_VALUE
Remplacez les éléments suivants :
INFERENCE_POOL_NAME
: le nom duInferencePool
.INFERENCE_POOL_NAMESPACE
: espace de noms duInferencePool
.CLUSTER_NAME
: nom du cluster.MIN_REPLICAS
: disponibilité minimale de l'InferencePool
(capacité de référence). AHP maintient ce nombre de réplicas lorsque l'utilisation est inférieure au seuil cible AHP. Les charges de travail hautement disponibles doivent définir cette valeur sur une valeur supérieure à1
pour assurer la disponibilité continue en cas de perturbation du pod.MAX_REPLICAS
: valeur qui limite le nombre d'accélérateurs à attribuer aux charges de travail hébergées dans leInferencePool
. L'AHP n'augmentera pas le nombre de réplicas au-delà de cette valeur. En période de pointe du trafic, surveillez le nombre de réplicas pour vous assurer que la valeur du champMAX_REPLICAS
offre suffisamment d'espace pour que la charge de travail puisse être mise à l'échelle afin de maintenir les caractéristiques de performances de la charge de travail choisie.TARGET_VALUE
: valeur représentant l'KV-Cache Utilization
cible choisie par serveur de modèle. Il s'agit d'un nombre compris entre 0 et 100, qui dépend fortement des caractéristiques du serveur de modèle, du modèle, de l'accélérateur et du trafic entrant. Vous pouvez déterminer cette valeur cible de manière expérimentale à l'aide de tests de charge et en traçant un graphique de débit par rapport à la latence. Sélectionnez une combinaison de débit et de latence dans le graphique, puis utilisez la valeurKV-Cache Utilization
correspondante comme cible AHP. Vous devez ajuster et surveiller de près cette valeur pour obtenir les résultats de prix-performances que vous avez choisis. Vous pouvez utiliser les recommandations d'inférence GKE pour déterminer automatiquement cette valeur.
Étape suivante
- En savoir plus sur le gateway d'inférence GKE
- Découvrez comment déployer GKE Inference Gateway.
- Découvrez les opérations de déploiement de la passerelle d'inférence GKE.
- Découvrez le service avec GKE Inference Gateway.