Personnaliser la configuration de GKE Inference Gateway


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:

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:

Intégration de Google Cloud Model Armor à un cluster GKE
Figure: Intégration de Model Armor à un cluster GKE

Pour configurer les vérifications de sécurité de l'IA, procédez comme suit:

  1. Assurez-vous de remplir les conditions préalables suivantes:

    1. Activez le service Model Armor dans votre projet Google Cloud .
    2. Créez les modèles Model Armor à l'aide de la console Model Armor, de la Google Cloud CLI ou de l'API.
  2. Assurez-vous d'avoir déjà créé un modèle Model Armor nommé my-model-armor-template-name-id.

  3. Pour configurer GCPTrafficExtension, procédez comme suit:

    1. 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ètres extensionPolicy et de nettoyage de l'invite ou de la réponse.
    2. 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 et Queue 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:

  1. Dans la console Google Cloud , accédez à la page Surveillance.

    Accéder à Monitoring

  2. Dans le volet de navigation, sélectionnez Tableaux de bord.

  3. Dans la section Intégrations, sélectionnez GMP.

  4. Sur la page Modèles de tableaux de bord Cloud Monitoring, recherchez "Balise".

  5. 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:

  1. Collectez les métriques de votre serveur de modèle.
  2. Dans la console Google Cloud , accédez à la page Surveillance.

    Accéder à Monitoring

  3. Dans le volet de navigation, sélectionnez Tableaux de bord.

  4. Sous Intégrations, sélectionnez GMP. Les tableaux de bord d'intégration correspondants s'affichent.

    Vue des tableaux de bord d'intégration
    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:

  1. 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"
                  }
                }
              }
            }
          ]
        }
      }
    
  2. 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
    
  3. Ouvrez la page Surveillance dans la console Google Cloud .

    Accéder à Monitoring

  4. Dans le menu de navigation, sélectionnez Tableaux de bord.

  5. 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.

  6. 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.

  1. 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 votre InferencePool est déployé.
    • INFERENCE_POOL_NAME : le nom du InferencePool.
  2. 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:

  1. 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é.

  2. Déployez l'adaptateur de métriques personnalisées Stackdriver pour accorder à l'AHP un accès aux métriques:

    1. 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
      
    2. Appliquez l'exemple de fichier manifeste à votre cluster:

      kubectl apply -f adapter_new_resource_model.yaml
      
  3. 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.

  4. 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 du InferencePool.
    • INFERENCE_POOL_NAMESPACE: espace de noms du InferencePool.
    • 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 le InferencePool. 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 champ MAX_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 valeur KV-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