En ce qui concerne les sites Web les plus fréquentés sur Internet, NGINX et NGINX Plus dominent le marché. En fait, NGINX alimente plus d'un million de sites parmi les plus fréquentés au monde que n'importe quel autre serveur Web. Sa capacité à gérer plus d’un million de connexions simultanées sur un seul serveur a favorisé son adoption par des sites et applications « hyperscale » tels qu’Airbnb, Netflix et Uber.
Bien que NGINX Plus soit plus communément connu comme un serveur Web, un proxy inverse HTTP et un équilibreur de charge, il s'agit également d'un contrôleur de distribution d'applications (ADC) complet avec prise en charge des applications TCP et UDP. Son architecture pilotée par événements et tous les autres attributs qui ont fait son succès dans les cas d’utilisation HTTP s’appliquent également à l’Internet des objets (IoT).
Dans cet article, nous montrons comment NGINX Plus peut être utilisé pour équilibrer la charge du trafic MQTT . MQTT a été initialement publié en 1999 pour la communication avec les champs pétrolifères éloignés. Il a été mis à jour pour les cas d'utilisation IoT en 2013 et est depuis devenu le protocole de choix pour de nombreux déploiements IoT. Les déploiements IoT de production avec des millions d'appareils exigent des performances élevées et des fonctionnalités avancées de la part d'un équilibreur de charge. Dans cette série de billets de blog en deux parties, nous aborderons les cas d'utilisation avancés suivants.
Pour explorer les fonctionnalités de NGINX Plus, nous utiliserons un environnement de test simple qui représente les composants clés d'un environnement IoT avec un cluster de courtiers MQTT. Les courtiers MQTT dans cet environnement sont des instances HiveMQ exécutées dans des conteneurs Docker.
NGINX Plus agit comme un proxy inverse et un équilibreur de charge pour le courtier MQTT, écoutant sur le port MQTT par défaut de 1883. Cela fournit une interface simple et cohérente au client, tandis que les nœuds MQTT back-end peuvent être mis à l'échelle (et même mis hors ligne) sans affecter le client de quelque manière que ce soit. Nous utilisons l’ outil de ligne de commande Mosquitto comme client, qui représente les appareils IoT dans l’environnement de test.
Tous les cas d’utilisation de cet article et de la partie 2 utilisent cet environnement de test, et toutes les configurations s’appliquent directement à l’architecture illustrée dans la figure. Pour obtenir des instructions complètes sur la création de l’environnement de test, voir l’annexe 1 .
L’une des principales fonctions d’un équilibreur de charge est de fournir une haute disponibilité à l’application afin que les serveurs principaux puissent être ajoutés, supprimés ou mis hors ligne sans affecter le client. Pour que cela soit possible de manière fiable, il est nécessaire d'effectuer des contrôles de santé qui sondent de manière proactive la disponibilité de chacun des serveurs principaux. Grâce à des contrôles de santé actifs, NGINX Plus peut supprimer les serveurs défaillants du groupe d’équilibrage de charge avant que les demandes réelles des clients ne les atteignent.
L’utilité d’un contrôle de santé dépend de la précision avec laquelle il simule le trafic réel des applications et analyse la réponse. De simples contrôles de vivacité du serveur, tels qu'un ping, ne garantissent pas que le service backend est en cours d'exécution. Les contrôles d’ouverture de port TCP ne garantissent pas que l’application elle-même est saine. Ici, nous configurons l'équilibrage de charge de base pour l'environnement de test avec un contrôle de santé qui garantit que chaque serveur back-end est capable d'accepter de nouvelles connexions MQTT.
Nous apportons des modifications à deux fichiers de configuration.
Dans le fichier nginx.conf principal, nous incluons le bloc de flux
suivant et la directive d'inclusion
pour que NGINX Plus lise la configuration pour l'équilibrage de charge TCP à partir d'un ou plusieurs fichiers dans le sous-répertoire stream_conf.d , qui se trouve dans le même répertoire que nginx.conf . Nous faisons cela au lieu d'inclure la configuration réelle dans nginx.conf .
Ensuite, dans le même répertoire que nginx.conf, nous créons le répertoire stream_conf.d pour contenir nos fichiers de configuration TCP et UDP. Notez que nous n’utilisons pas le répertoire conf.d préexistant car par défaut, il est réservé au contexte de configuration http
et donc l’ajout d’une configuration de flux
à cet endroit échouera.
Dans stream_mqtt_healthcheck.conf, nous définissons d’abord le format du journal d’accès pour le trafic MQTT (lignes 1–2). Ceci est délibérément similaire au format de journal commun HTTP afin que les journaux résultants puissent être importés dans des outils d’analyse de journaux.
Ensuite, nous définissons le groupe en amont
appelé hive_mq (lignes 4 à 9) qui contient trois serveurs MQTT. Dans notre environnement de test, ils sont chacun accessibles sur localhost avec un numéro de port unique. La directive de zone
définit une quantité de mémoire partagée entre tous les processus de travail NGINX Plus pour maintenir l’état d’équilibrage de charge et les informations d’intégrité.
Le bloc de correspondance
(lignes 11 à 15) définit le contrôle de santé utilisé pour tester la disponibilité des serveurs MQTT. La directive send
est une représentation hexadécimale d'un paquet MQTT CONNECT
complet avec un identifiant client (ClientId) de nginx
health
check
. Ceci est envoyé à chacun des serveurs définis dans le groupe en amont chaque fois que le contrôle d'intégrité se déclenche. La directive expect
correspondante décrit la réponse que le serveur doit renvoyer pour que NGINX Plus le considère comme sain. Ici, la chaîne hexadécimale de 4 octets20
02
00
00
est un paquet MQTT CONNACK
complet. La réception de ce paquet démontre que le serveur MQTT est capable de recevoir de nouvelles connexions client.
Le bloc serveur
(lignes 17 à 25) configure la manière dont NGINX Plus traite les clients. NGINX Plus écoute sur le port MQTT par défaut, 1883, et transmet tout le trafic au groupe en amont hive_mq (ligne 19). La directive health_check
spécifie que les contrôles de santé sont effectués sur le groupe en amont (à la fréquence par défaut de cinq secondes) et que le contrôle défini par le bloc de correspondance
mqtt_conn est utilisé.
Pour tester que cette configuration de base fonctionne, nous pouvons utiliser le client Mosquitto pour publier certaines données de test dans notre environnement de test.
$ mosquitto_pub -d -h mqtt.example.com -t "topic/test" -m "test123" -i "thing001" Le client thing001 envoie CONNECT Le client thing001 a reçu CONNACK Le client thing001 envoie PUBLISH (d0, q0, r0, m1, 'topic/test', ... (7 octets)) Client thing001 envoie DISCONNECT $ tail --lines=1 /var/log/nginx/mqtt_access.log 192.168.91.1 [23/Mar/2017:11:41:56 +0000] TCP 200 23 4 127.0.0.1:18831
La ligne du journal d’accès montre que NGINX Plus a reçu un total de 23 octets et que 4 octets ont été envoyés au client (le paquet CONNACK
). Nous pouvons également voir que le nœud MQTT node1 a été choisi (port 18831). Comme le montrent les lignes suivantes du journal d’accès, lorsque nous répétons le test, l’algorithme d’équilibrage de charge Round Robin par défaut sélectionne tour à tour node1 , node2 et node3 .
$ tail --lines=4 /var/log/nginx/mqtt_access.log 192.168.91.1 [23/mars/2017:11:41:56 +0000] TCP 200 23 4 127.0.0.1:18831 192.168.91.1 [23/mars/2017:11:42:26 +0000] TCP 200 23 4 127.0.0.1:18832 192.168.91.1 [23/mars/2017:11:42:27 +0000] TCP 200 23 4 127.0.0.1:18833 192.168.91.1 [23/03/2017:11:42:28 +0000] TCP 200 23 4 127.0.0.1:18831
[ Éditeur – Le cas d’utilisation suivant n’est qu’un parmi tant d’autres pour le module JavaScript NGINX. Pour une liste complète, voir Cas d'utilisation du module JavaScript NGINX .]
Le code de cette section est mis à jour comme suit pour refléter les modifications apportées à l'implémentation de NGINX JavaScript depuis la publication initiale du blog :
s
) refactorisé pour le module Stream, qui a été introduit dans NGINX JavaScript 0.2.4 .js_import
, qui remplace la directive js_include
dans NGINX Plus R23 et versions ultérieures. Pour plus d’informations, consultez la documentation de référence du module JavaScript NGINX – la section Exemple de configuration affiche la syntaxe correcte pour les fichiers de configuration NGINX et JavaScript.L'équilibrage de charge Round Robin est un mécanisme efficace pour répartir les connexions client sur un groupe de serveurs. Cependant, il existe plusieurs raisons pour lesquelles ce n’est pas idéal pour les connexions MQTT.
Les serveurs MQTT s’attendent souvent à une connexion de longue durée entre le client et le serveur et une grande quantité d’états de session peuvent être créés sur le serveur. Malheureusement, la nature des appareils IoT et des réseaux IP qu’ils utilisent signifie que les connexions sont interrompues, obligeant certains clients à se reconnecter fréquemment. NGINX Plus peut utiliser son algorithme d’équilibrage de charge Hash pour sélectionner un serveur MQTT en fonction de l’adresse IP du client. L'ajout simple du hachage
$remote_addr;
au bloc en amont permet la persistance de la session de sorte que chaque fois qu'une nouvelle connexion arrive d'une adresse IP client donnée, le même serveur MQTT est sélectionné.
Mais nous ne pouvons pas compter sur les appareils IoT pour se reconnecter à partir de la même adresse IP, surtout s’ils utilisent des réseaux cellulaires (par exemple, GSM ou LTE). Pour garantir que le même client se reconnecte au même serveur MQTT, nous devons utiliser l’identifiant client MQTT comme clé de l’algorithme de hachage.
Le MQTT ClientId est un élément obligatoire du paquet CONNECT
initial, ce qui signifie qu'il est disponible pour NGINX Plus avant que le paquet ne soit envoyé par proxy au serveur en amont. Nous pouvons utiliser NGINX JavaScript pour analyser le paquet CONNECT
et extraire le ClientId en tant que variable qui peut ensuite être utilisée par la directive hash
pour implémenter la persistance de session spécifique à MQTT.
NGINX JavaScript est le langage de configuration programmatique « natif NGINX ». Il s'agit d'une implémentation JavaScript unique pour NGINX et NGINX Plus, conçue spécifiquement pour les cas d'utilisation côté serveur et le traitement par requête. Il présente trois caractéristiques clés qui le rendent adapté à une implémentation de la persistance de session pour MQTT :
CONNECT
nécessite moins de 20 lignes de code.Pour obtenir des instructions sur l’activation de NGINX JavaScript, voir l’annexe 2 .
La configuration NGINX Plus pour ce cas d'utilisation reste relativement simple. La configuration suivante est une version modifiée de l'exemple dans Équilibrage de charge avec contrôles d'intégrité actifs , avec les contrôles d'intégrité supprimés pour plus de concision.
Nous commençons par spécifier l'emplacement du code JavaScript NGINX avec la directive js_import
. La directive js_set
indique à NGINX Plus d'appeler la fonction setClientId
lorsqu'elle doit évaluer la variable $mqtt_client_id
. Nous ajoutons plus de détails au journal d’accès en ajoutant cette variable au format du journal mqtt sur la ligne 5.
Nous activons la persistance de session sur la ligne 12 avec la directive hash
spécifiant $mqtt_client_id
comme clé. Notez que nous utilisons le paramètre cohérent
afin que si un serveur en amont tombe en panne, sa part du trafic soit répartie uniformément sur les serveurs restants sans affecter les sessions déjà établies sur ces serveurs. Le hachage cohérent est abordé plus en détail dans notre article de blog sur le partitionnement d’un cache Web – les principes et les avantages s’appliquent également ici.
La directive js_preread
(ligne 18) spécifie la fonction JavaScript NGINX qui est exécutée lors de la phase de prélecture du traitement de la requête. La phase de prélecture est déclenchée pour chaque paquet (dans les deux sens) et se produit avant le proxy afin que la valeur de $mqtt_client_id
soit disponible lorsqu'elle est nécessaire dans le bloc en amont
.
Nous définissons le JavaScript pour extraire le MQTT ClientId dans le fichier mqtt.js , qui est chargé par la directive js_import
dans le fichier de configuration NGINX Plus ( stream_mqtt_session_persistence.conf ).
La fonction principale, getClientId()
, est déclarée à la ligne 4. Il est passé l'objet appelé s
, qui représente la session TCP en cours. L'objet de session possède de nombreuses propriétés , dont plusieurs sont utilisées dans cette fonction.
Les lignes 5 à 9 garantissent que le paquet actuel est le premier à être reçu du client. Les messages clients ultérieurs et les réponses du serveur sont ignorés afin qu'une fois la connexion établie, il n'y ait pas de surcharge supplémentaire sur le flux de trafic.
Les lignes 10 à 24 examinent l’en-tête MQTT pour garantir que le paquet est de type CONNECT
et pour déterminer où commence la charge utile MQTT.
Les lignes 27 à 32 extraient le ClientId de la charge utile, en stockant la valeur dans la variable globale JavaScript client_id_str
. Cette variable est ensuite exportée vers la configuration NGINX avec la fonction setClientId
(lignes 43 à 45).
Nous pouvons maintenant utiliser à nouveau le client Mosquitto pour tester la persistance de la session en envoyant une série de requêtes de publication MQTT avec trois valeurs ClientId différentes (l'option -i
).
$ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "foo" $ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "bar" $ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "baz" $ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "foo" $ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "foo" $ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "foo" $ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "bar" $ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "bar" $ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "baz" $ mosquitto_pub -h mqtt.example.com -t "sujet/test" -m "test123" -i "baz"
L'examen du journal d'accès montre que ClientId foo se connecte toujours à node1 (port 18831), ClientId bar se connecte toujours à node2 (port 18832) et ClientId baz se connecte toujours à node3 (port 18833).
$ tail /var/log/nginx/mqtt_access.log 192.168.91.1 [23/mars/2017:12:24:24 +0000] TCP 200 23 4 127.0.0.1:18831 foo 192.168.91.1 [23/mars/2017:12:24:28 +0000] TCP 200 23 4 127.0.0.1:18832 bar 192.168.91.1 [23/mars/2017:12:24:32 +0000] TCP 200 23 4 127.0.0.1:18833 baz 192.168.91.1 [23/03/2017:12:24:35 +0000] TCP 200 23 4 127.0.0.1:18831 foo 192.168.91.1 [23/03/2017:12:24:37 +0000] TCP 200 23 4 127.0.0.1:18831 foo 192.168.91.1 [23/03/2017:12:24:38 +0000] TCP 200 23 4 127.0.0.1:18831 foo 192.168.91.1 [23/03/2017:12:24:42 +0000] TCP 200 23 4 127.0.0.1:18832 bar 192.168.91.1 [23/mars/2017:12:24:44 +0000] TCP 200 23 4 127.0.0.1:18832 bar 192.168.91.1 [23/mars/2017:12:24:47 +0000] TCP 200 23 4 127.0.0.1:18833 baz 192.168.91.1 [23/mars/2017:12:24:48 +0000] TCP 200 23 4 127.0.0.1:18833 baz
Notez que nous bénéficions également de l’avantage que le ClientId MQTT apparaît dans les lignes du journal d’accès, que nous utilisions ou non la persistance de session ou tout autre algorithme d’équilibrage de charge.
Dans ce premier article d'une série en deux parties, nous avons démontré comment NGINX Plus utilise des contrôles de santé actifs pour améliorer la disponibilité et la fiabilité des applications IoT, et comment NGINX JavaScript peut étendre NGINX Plus en fournissant une capacité d'équilibrage de charge de couche 7 comme la persistance de session pour le trafic TCP. Dans la deuxième partie , nous explorons comment NGINX Plus peut rendre vos applications IoT plus sécurisées en déchargeant la terminaison TLS et en authentifiant les clients.
En combinaison avec NGINX JavaScript ou seul, les hautes performances et l'efficacité inhérentes à NGINX Plus en font un équilibreur de charge logiciel idéal pour votre infrastructure IoT.
Pour essayer NGINX JavaScript avec NGINX Plus, démarrez votre essai gratuit de 30 jours ou contactez-nous pour discuter de vos cas d'utilisation .
Annexes
Nous avons installé l’environnement de test sur une machine virtuelle afin qu’il soit isolé et reproductible. Cependant, il n’y a aucune raison pour que vous ne puissiez pas l’installer sur un serveur physique « bare metal ».
Consultez les instructions dans le Guide d'administration de NGINX Plus .
N'importe quel serveur MQTT peut être utilisé, mais cet environnement de test est basé sur HiveMQ ( télécharger ici ). Dans cet exemple, nous installons HiveMQ sur un seul hôte à l’aide de conteneurs Docker pour chaque nœud. Les instructions suivantes sont adaptées de Déploiement de HiveMQ avec Docker .
Créez un Dockerfile pour HiveMQ dans le même répertoire que hivemq.zip .
En travaillant dans le répertoire qui contient hivemq.zip et le Dockerfile, créez l'image Docker.
$ docker build -t hivemq:dernier .
Créez trois nœuds HiveMQ, chacun exposé sur un port différent.
$ docker run -p 18831:1883 -d --name node1 hivemq:dernière version ff2c012c595a $ docker run -p 18832:1883 -d --name node2 hivemq:dernière version 47992b1f4910 $ docker run -p 18833:1883 -d --name node3 hivemq:dernière version 17303b900b64
Vérifiez que les trois nœuds HiveMQ sont en cours d’exécution. (Dans l'exemple de sortie suivant, les colonnes COMMAND
, CREATED
et STATUS
ont été omises pour faciliter la lecture.)
$ docker ps ID DU CONTENEUR IMAGE ... NOMS DES PORTS 17303b900b64 hivemq:latest ... 0.0.0.0:18833->1883/tcp node3 47992b1f4910 hivemq:dernière version ... 0.0.0.0:18832->1883/tcp node2 ff2c012c595a hivemq:dernière version ... 0.0.0.0:18831->1883/nœud tcp1
Le client de ligne de commande Mosquitto peut être téléchargé à partir du site Web du projet . Les utilisateurs Mac avec Homebrew installé peuvent exécuter la commande suivante.
$ brew installer moustique
Testez le client Mosquitto et l’installation de HiveMQ en envoyant un simple message de publication à l’une des images Docker.
$ mosquitto_pub -d -h mqtt.example.com -t "topic/test" -m "test123" -i "thing001" -p 18831 Le client thing001 envoie CONNECT Le client thing001 a reçu CONNACK Le client thing001 envoie PUBLISH (d0, q0, r0, m1, 'topic/test', ... (7 octets)) Le client thing001 envoie DISCONNECT
[nom de ngx_snippet = 'njs-enable-instructions']
« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."