Bonnes pratiques pour les plug-ins

Cette page fournit des conseils pour écrire des plug-ins d'extensions de service qui sont corrects, performants et bien isolés. L'exactitude est essentielle, car les plug-ins s'exécutent dans un bac à sable de moteur contraint avec une surface d'API limitée. Les performances sont essentielles, car les plug-ins s'exécutent lors des requêtes des utilisateurs finaux, avec une petite quantité de ressources. L'isolation est fournie au niveau du projet.

Premiers pas

Commencer à partir des exemples

Pour commencer, vous pouvez parcourir nos exemples de code de plug-in. Voici des exemples de modèles de plug-in courants, tels que l'analyse de chemin d'accès ou de requête, la réécriture d'en-tête, la journalisation personnalisée et l'authentification personnalisée.

Utiliser les API compatibles

Les plug-ins doivent être compilés par rapport à l'interface binaire (ABI) Proxy-Wasm. Les extensions de service sont compatibles avec un sous-ensemble de l'ABI Proxy-Wasm, qui inclut les mutations d'en-tête et de corps HTTP, les réponses locales, la journalisation personnalisée et la configuration des plug-ins. Proxy-Wasm est également compatible avec un petit sous-ensemble de WASI preview 1, y compris stdout et stderr pour la journalisation, clock_time_get et random_get.

Les extensions de service ne sont pas compatibles avec les minuteurs, les métriques personnalisées, les données partagées, les files d'attente partagées ni les appels réseau sortants. Les extensions de service ne sont pas compatibles avec les valeurs de retour de rappel de plug-in qui tentent de suspendre le traitement des requêtes. Elles les ignorent.

Exécuter des tests fonctionnels et des benchmarks

Pour évaluer l'exactitude et les performances, nous fournissons un outil de test de plug-in local qui peut exécuter, tester et évaluer votre plug-in. Vous appelez cet outil dans un conteneur Docker, en transmettant un binaire de plug-in et un proto de texte des entrées et des attentes. Consultez la documentation du testeur local et l'exemple d'entrée de test.

Exactitude

Ne vous fiez pas aux horloges

Pour des raisons de sécurité, l'heure est définie lors de la création du contexte (pour un plug-in ou une requête) et reste figée pendant les appels de plug-in. Cela signifie que les plug-ins WebAssembly ne doivent pas se mettre en veille. En effet, la mise en veille expire, car le temps ne s'écoule pas. Cela signifie également que les plug-ins ne peuvent pas mesurer leur propre temps d'exécution, bien que ces informations soient disponibles dans Cloud Monitoring.

Traiter les noms d'en-têtes comme non sensibles à la casse

Selon la sémantique HTTP, les noms de champs d'en-tête HTTP ne doivent pas être sensibles à la casse. La casse des en-têtes écrits par un plug-in peut être modifiée avant d'être envoyée aux clients ou aux backends.

Performances

Éviter les plantages de plug-ins

Lorsqu'un plug-in plante, la requête déclenchante reçoit une erreur. Si cela se produit fréquemment, les redémarrages de plug-in sont limités, ce qui entraîne des pics d'erreurs qui affectent plusieurs utilisateurs.

Pour éviter les plantages de plug-in, essayez ce qui suit :

  • Évitez toute forme d'affirmation. Configurez plutôt la journalisation des erreurs ou les réponses locales aux utilisateurs.
  • En Rust, évitez d'utiliser des méthodes, telles que unwrap, qui peuvent entraîner des paniques. Vous pouvez également configurer le plug-in pour qu'il gère les plantages à l'aide de panic::set_hook.
  • Testez votre plug-in à l'aide de l'outil de test de plug-in fourni par Google et assurez-vous que les entrées mal formées ou vides sont gérées sans que le plug-in plante.

Compiler pour la vitesse d'exécution

Compilez votre code WebAssembly pour obtenir la meilleure vitesse d'exécution en utilisant l'option de compilation -O3 (pour C++) ou opt-level=3 (pour Rust).

Précompiler les expressions régulières

Évitez les opérations gourmandes en ressources de calcul sur le chemin de chaque requête (dans les gestionnaires HTTP). Effectuez plutôt tout travail non spécifique à une requête lors de la configuration du plug-in et transmettez tout état précalculé dans chaque contexte de requête HTTP (également appelé contexte de flux).

En particulier, précompilez toutes les expressions régulières lors de la configuration du plug-in. Notre exemple de code d'expression régulière montre comment procéder.

Éviter de copier des données dans des contextes HTTP

Lorsque vous partagez des données entre le contexte racine et les contextes HTTP, évitez les copies ou les appels de clonage coûteux. Partagez plutôt des pointeurs ou des références. En C++, cela peut être fait avec std::shared_ptr ou des pointeurs et références bruts, car le contexte racine survit à tout contexte HTTP. En Rust, les valeurs peuvent être partagées à l'aide de std::rc::Rc. En Go, les valeurs peuvent être partagées en stockant des pointeurs vers les valeurs, qui sont supprimées par le récupérateur de mémoire.

Sécurité

Isoler les charges de travail par projet

Sur l'infrastructure de Google, les plug-ins appartenant au même projet Google Cloud peuvent s'exécuter dans le même bac à sable sécurisé. Cela signifie que le runtime WebAssembly est la seule barrière de sécurité entre les plug-ins d'un même projet.

Utilisez des projets Google Cloud distincts pour les charges de travail qui nécessitent une séparation de sécurité. Cette pratique permet également de séparer les ressources et les autorisations.

Restreindre les secrets sur Media CDN

Par conception, Media CDN s'exécute sur un parc de matériel exploité en dehors du contrôle physique de Google. Lorsque vous écrivez des plug-ins liés à la sécurité pour Media CDN, utilisez des noms d'hôte ou des sous-domaines dédiés, et configurez les plug-ins avec des clés de signature qui sont renouvelées fréquemment.