Débloquer des solutions sur mesure : exploiter Apache APISIX et Node-RED en harmonie

March 6, 2024

Ecosystem

Contexte

De nombreux utilisateurs d'Apache APISIX ont besoin de personnalisations dans des environnements d'entreprise pour répondre à des exigences spécifiques dans certains scénarios, malgré les nombreuses fonctionnalités puissantes intégrées dans ses plugins.

Les utilisateurs choisissent souvent d'écrire des plugins en Lua et de les monter sur des instances d'APISIX pour les utiliser. Cependant, Lua a un public relativement limité. Bien qu'il soit facile à démarrer, le maîtriser n'est pas simple, et la mise en œuvre de logiques complexes de transformation de données en Lua peut devenir assez complexe. Actuellement, seule une partie des hooks est exposée par le Java Plugin Runner pour que les développeurs puissent les appeler, ce qui nécessite des modifications du code source du Java Plugin Runner pour les fonctionnalités qui ne sont pas directement supportées.

Le diagramme ci-dessous illustre trois modèles courants d'utilisation des plugins : les plugins Lua s'exécutent directement intégrés dans le cœur d'APISIX ; le Plugin Runner communique via RPC avec des Plugin Runners dans des langages comme Java, Golang, etc. ; et les plugins WASM sont convertis en bytecode et exécutés en interne dans le cœur d'APISIX.

plugins et plugin runners d'APISIX

Il a été observé à partir des retours des utilisateurs que le besoin de plugins personnalisés est souvent lié à la transformation de données telles que les paramètres de requête HTTP et l'appel d'API externes pour le traitement des données.

Pour répondre à cela, une nouvelle approche a été proposée pour améliorer les capacités d'Apache APISIX. Cette approche consiste à utiliser uniquement des plugins intégrés dans Apache APISIX pour configurer des capacités courantes comme l'authentification et la limitation de débit, tout en plaçant les nouvelles logiques personnalisées dans des services externes. Le service externe peut être soit un programme binaire, soit un autre service API, qui sera traité comme un upstream pour APISIX. Ce service externe gérera les requêtes et les réponses comme un middleware. Cette approche peut également être appliquée à d'autres passerelles API ou services de proxy.

Description du scénario

Nous avons une série de services qui fournissent des services de requête de données en amont (cet article utilise https://api-ninjas.com/api comme exemple). Par exemple, on peut récupérer les dernières informations météorologiques et des informations sur le pays (comme le PIB du pays, le nom de la capitale et l'unité monétaire) en fonction du nom de la ville.

Notre objectif principal est de fournir une interface de requête générique aux développeurs tout en étant capable de déterminer le contenu des données qu'ils souhaitent récupérer en fonction du nom de la ville et des paramètres de portée des données. De plus, pour protéger les services en amont contre les abus, nous devons ajouter un service d'authentification à l'interface des développeurs, permettant uniquement aux requêtes avec la bonne clé API d'être transmises au service en amont.

Analyse du problème

Avant d'introduire Node-Red, pour répondre aux exigences ci-dessus, les développeurs d'APISIX envisageraient d'utiliser des plugins Lua. Bien qu'Apache APISIX fournisse une documentation détaillée sur le développement de plugins, les développeurs métier doivent apprendre la syntaxe Lua et les techniques de réglage, comprendre les différents hooks de requête exposés par APISIX, et recharger continuellement les plugins pour validation tout en écrivant la logique pour l'extraction et la validation des paramètres. Après avoir terminé les tests, ils doivent également empaqueter les plugins Lua dans le programme APISIX ou les distribuer à toutes les instances d'APISIX pour le montage.

Les exigences de l'exemple que nous fournissons dans ce blog impliquent l'analyse de paramètres spécifiques à partir des requêtes des clients, puis la construction de requêtes pour récupérer des données à partir de différents services en amont. Cependant, nous avons passé beaucoup de temps à traiter des transactions en dehors de l'écriture métier. Par conséquent, pour une telle logique impliquant la conversion de paramètres, la conversion de format ou les appels externes, nous pouvons adopter une approche plus légère et plus intuitive, ce qui est précisément le problème que Node-Red peut résoudre.

Introduction à Node-Red

Node-RED est un outil de programmation par flux puissant et facile à utiliser, adapté à l'automatisation et au traitement des flux de données dans divers domaines. Son interface de programmation, sa riche bibliothèque de nœuds et son extensibilité flexible nous permettent de construire rapidement des flux complexes et de mettre en œuvre divers scénarios d'application. Voici quelques-uns des nœuds fournis par Node-RED :

  • Nœud HTTP_IN : Expose un point de terminaison pour l'invocation de services externes, que nous utiliserons comme un service en amont pour APISIX.

  • Nœud Function : Permet aux développeurs d'écrire des fonctions de code en JavaScript pour modifier, supprimer, etc., les entrées/sorties.

  • Nœud Switch : Permet aux développeurs de définir un ensemble de conditions pour entrer dans le nœud spécifié suivant lorsqu'une condition est remplie.

  • Nœud HTTP_Request : Peut définir l'URL, etc., pour envoyer des données à ce point de terminaison via Node-RED lors de l'exécution du flux de travail complet.

  • Nœud Change : Peut ajouter, modifier ou supprimer des valeurs spécifiées d'un objet spécifié.

  • Nœud HTTP_Response : Utilisé pour retourner les réponses aux clients.

En plus des nœuds listés ci-dessus, Node-RED fournit également de nombreux autres nœuds intégrés. Cet article montrera aux lecteurs comment mettre en œuvre les exigences ci-dessus via Node-RED.

Démonstration de l'exemple

Configuration de l'environnement

Nous déploierons les composants nécessaires via la conteneurisation, en utilisant un Droplet DigitalOcean comme ressource serveur.

$ doctl compute ssh-key list

ID          Name             FingerPrint
25621060    Zhiyuan Ju       2c:84:b7:d8:14:0a:a0:0f:ca:fe:ca:24:06:a4:fe:39

$ doctl compute droplet create \
    --image docker-20-04 \
    --size s-2vcpu-4gb-amd \
    --region sgp1 \
    --vpc-uuid 646cf2b8-03d8-4f48-b7c8-57cdee60ad27 \
    --ssh-keys 25621060 \
    apisix-nodered-docker-ubuntu-s-2vcpu-4gb-amd-sgp1-01
  
$ doctl compute droplet list

ID           Name                                                    Public IPv4       Private IPv4    Public IPv6    Memory    VCPUs    Disk    Region    Image                                   VPC UUID                                Status    Tags    Features                            Volumes
404094941    apisix-nodered-docker-ubuntu-s-2vcpu-4gb-amd-sgp1-01    143.198.192.64    10.104.0.3                     4096      2        80      sgp1      Ubuntu Docker 25.0.3 on Ubuntu 22.04    646cf2b8-03d8-4f48-b7c8-57cdee60ad27    active            droplet_agent,private_networking

Déploiement d'Apache APISIX

Nous utiliserons l'APISIX Quickstart pour démarrer une nouvelle instance d'APISIX. Pour la documentation spécifique, veuillez vous référer à https://docs.api7.ai/apisix/getting-started/.

$ curl -sL https://run.api7.ai/apisix/quickstart | sh

Déploiement de Node-RED

Node-RED propose plusieurs méthodes de déploiement, et nous le déploierons rapidement via Docker et l'intégrerons à l'environnement existant. Pour plus de détails sur le déploiement, veuillez vous référer à la documentation officielle : https://nodered.org/docs/getting-started/.

Lors du déploiement de Node-RED, assurez-vous que le conteneur est ajouté au réseau APISIX pour garantir qu'il peut communiquer avec APISIX et traiter les requêtes.

$ docker run -d -it -p 1880:1880 -v $PWD/configs/nodered/data:/data --network=apisix-quickstart-net --name mynodered -u Node-Red:dialout nodered/Node-Red

Configuration de Node-RED

  1. Pour traiter les requêtes entrant dans Node-RED depuis APISIX, Node-Red doit vérifier si les paramètres de la requête existent et sont valides. Si les paramètres sont manquants ou invalides, un message d'erreur sera retourné. S'ils sont valides, le nœud suivant sera exécuté. Dans ce scénario spécifique, nous n'autorisons que les requêtes de données pour deux villes, Stockholm (city=stockholm) et Berlin (city=berlin).

Utilisation de Node-Red pour vérifier les paramètres

  1. Une fois que la requête entre dans le nœud suivant, Node-RED doit déterminer le type de données demandées. Dans ce scénario, il y a trois types : les informations météorologiques (scope=weather), les informations sur le pays où se trouve la ville (scope=country), et le PIB du pays où se trouve la ville (scope=gdp).

Utilisation de Node-Red pour cibler le type de données

  1. Si les paramètres City et Scope sont valides, Node-RED déterminera à partir de quelle API récupérer les données en fonction de la valeur de scope. Après avoir défini l'URL, la méthode, la charge utile, la clé API, etc., le nœud de Node-RED accédera au point de terminaison correspondant pour récupérer les données lorsqu'il sera déclenché.

Choix de l'API pour obtenir des données selon le scope

  1. Lors de la récupération des données pour scope=gdp, Node-RED doit extraire la valeur du PIB du corps de la réponse de l'API externe. Cela peut être fait en utilisant le nœud Change pour l'extraction ou le nœud Function.

Analyse de la valeur du PIB à partir du corps de la réponse de l'API externe

  1. Enfin, Node-RED retournera les données traitées à APISIX, qui les transmettra au client. Le diagramme final de Node-RED est montré ci-dessous.

Diagramme de flux combinant APISIX et Node-Red

Création de routes APISIX

Pour rendre le service Node-Red accessible aux clients, nous devons utiliser APISIX comme un proxy inverse pour les points de terminaison exposés par Node-Red. Voici les étapes à suivre :

  1. Créez une route APISIX et définissez mynodered:1880 comme l'upstream pour cette route. Ainsi, toutes les requêtes envoyées à ce point de terminaison seront transférées au service Node-Red.

  2. Activez l'authentification par clé pour garantir que seules les requêtes portant une clé API valide peuvent passer l'authentification et accéder au service Node-Red.

En suivant les étapes ci-dessus, nous pouvons exposer de manière sécurisée le service Node-Red aux clients et nous assurer que seuls les utilisateurs autorisés peuvent y accéder.

$ curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
  "id": "proxy-global-data-endpoint",
  "uri": "/global-data",
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "mynodered:1880": 1
    }
  },
  "plugins": {
    "key-auth": {}
  }
}'

$ curl -i "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT -d '
{
  "username": "tom",
  "plugins": {
    "key-auth": {
      "key": "secret-key"
    }
  }
}'

Validation des requêtes

Nous tenterons plusieurs scénarios séparément pour vérifier si APISIX et Node-Red se comportent comme prévu :

Scénario 1

  • Description du scénario : Accéder à l'API avec une clé incorrecte.

  • Résultat attendu : Comme la clé API fournie est incorrecte, la requête devrait être rejetée, et le message d'erreur correspondant devrait être retourné.

$ curl http://143.198.192.64:9080/global-data -H "apikey: invalid-key" -i

HTTP/1.1 401 Unauthorized
Date: Mon, 04 Mar 2024 07:47:24 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/3.8.0

{"message":"Invalid API key in request"}

Scénario 2

  • Description du scénario : Accéder à l'API avec la bonne clé, mais un champ City invalide.

  • Résultat attendu : Comme les paramètres de la requête ne répondent pas aux exigences, le message d'erreur correspondant devrait être retourné, indiquant que le champ City est invalide.

$ curl "http://143.198.192.64:9080/global-data?city=singapore&scope=country" -H "apikey: secret-key" -i

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 69
Connection: keep-alive
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
ETag: W/"45-IOhgB2XkDHi2Kt4PP42n1xa8Gys"
Date: Mon, 04 Mar 2024 07:48:02 GMT
Server: APISIX/3.8.0

{"errorCode":400,"message":"Allowed city Options: Stockholm, Berlin"}

Scénario 3

  • Description du scénario : Accéder à l'API avec la bonne clé, et des champs City et Scope valides pour récupérer les données du pays.

  • Résultat attendu : La requête devrait réussir, et les informations pertinentes sur le pays où se trouve la City devraient être retournées.

$ curl "http://143.198.192.64:9080/global-data?city=stockholm&scope=country" -H "apikey: secret-key" -i

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 947
Connection: keep-alive
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
ETag: W/"3b3-XDlm9OHfuUrWH+g42q8L1F2uu/o"
Date: Mon, 04 Mar 2024 07:48:26 GMT
Server: APISIX/3.8.0

[{"gdp":556086,"sex_ratio":100.4,"surface_area":438574,"life_expectancy_male":80.8,"unemployment":6.7,"imports":158710,"homicide_rate":1.1,"currency":{"code":"SEK","name":"Swedish Krona"},"iso2":"SE","employment_services":80.7,"employment_industry":17.7,"urban_population_growth":1.1,"secondary_school_enrollment_female":157.9,"employment_agriculture":1.6,"capital":"Stockholm","co2_emissions":37.6,"forested_area":68.9,"tourists":7440,"exports":160538,"life_expectancy_female":84.4,"post_secondary_enrollment_female":82.1,"post_secondary_enrollment_male":52.7,"primary_school_enrollment_female":127.4,"infant_mortality":2,"gdp_growth":2.2,"threatened_species":98,"population":10099,"urban_population":87.7,"secondary_school_enrollment_male":148.1,"name":"Sweden","pop_growth":0.7,"region":"Northern Europe","pop_density":24.6,"internet_users":92.1,"gdp_per_capita":55766.8,"fertility":1.8,"refugees":310.4,"primary_school_enrollment_male":125.8}]

Scénario 4

  • Description du scénario : Accéder à l'API avec la bonne clé, et des champs City et Scope valides pour récupérer les données de GDP.

  • Résultat attendu : La requête devrait réussir, et les données de GDP pour le pays où se trouve la City devraient être retournées.

$ curl "http://143.198.192.64:9080/global-data?city=stockholm&scope=gdp" -H "apikey: secret-key" -i

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 6
Connection: keep-alive
Access-Control-Allow-Origin: *
ETag: W/"6-j8I5kokycgWjCeKC1c2UfJW7AQY"
Date: Mon, 04 Mar 2024 07:48:48 GMT
Server: APISIX/3.8.0

556086

En vérifiant ces quatre scénarios, nous pouvons confirmer que APISIX et Node-Red fonctionnent comme prévu et peuvent correctement gérer divers types de requêtes.

Résumé

Nous proposons une nouvelle approche pour résoudre plus intelligemment le problème du développement de capacités personnalisées, démontrée par un exemple détaillé.

  1. Routage des requêtes API et vérification d'identité : Tout d'abord, en utilisant la fonctionnalité de routage d'Apache APISIX et les plugins d'authentification, APISIX transfère les requêtes des clients au service Node-Red lorsque les informations d'identification fournies sont valides.

  2. Traitement et transformation des requêtes : Dans Node-Red, nous créons un flux pour traiter les requêtes API entrantes. En utilisant le nœud HTTP input pour recevoir les requêtes d'APISIX, nous analysons et validons les paramètres de la requête pour nous assurer qu'ils répondent aux exigences métier.

  3. Traitement de la logique métier : Une fois qu'une requête valide est reçue, nous pouvons exécuter la logique métier dans Node-Red. Par exemple, envoyer des requêtes à différentes API métier en fonction des paramètres pour récupérer des données et extraire les champs requis de la réponse. Après avoir terminé ces opérations, les données finales sont retournées à APISIX.

  4. Gestion des erreurs et journalisation : Pendant le traitement, si des erreurs ou des exceptions surviennent, nous pouvons ajouter des nœuds de gestion des erreurs dans Node-Red pour capturer et gérer les situations exceptionnelles. De plus, nous pouvons utiliser des nœuds de journalisation pour enregistrer les informations clés pendant le traitement pour un dépannage et une analyse ultérieurs, ce qui n'est pas démontré dans cet exemple.

En combinant APISIX et Node-Red, nous pouvons mettre en œuvre visuellement un processus complet de traitement des requêtes, y compris le routage des requêtes, le traitement des données, la logique métier, etc., sans avoir besoin d'écrire du code ou des plugins complexes. Cette solution flexible et personnalisable peut nous aider à construire et ajuster plus rapidement les fonctionnalités du système, améliorer l'efficacité du développement, réduire les coûts de développement et assurer la stabilité et l'évolutivité du système.

Tags: