Migration de Nginx vers Envoy Proxy

Bonjour, Habr! J'attire votre attention sur la traduction de l'article: Migration de Nginx vers Envoy Proxy .


Envoy est un serveur proxy distribué hautes performances (écrit en C ++) conçu pour les services et applications individuels, c'est aussi un bus de communication et un «plan de données universel» conçu pour les grandes architectures de «service mesh» de microservices. Lors de sa création, les solutions aux problèmes survenus lors du développement de serveurs tels que NGINX, HAProxy, les équilibreurs de charge matérielle et les équilibreurs de charge cloud ont été prises en compte. Envoy fonctionne avec toutes les applications et résume le réseau, offrant des fonctions communes quelle que soit la plate-forme. Lorsque tout le trafic de bureau dans l'infrastructure passe par la grille Envoy, il devient facile de visualiser les zones à problème avec une observabilité cohérente, le réglage des performances globales et l'ajout de fonctions de base à un endroit spécifique.


Les possibilités


  • Architecture hors processus: l'envoyé est un serveur autonome haute performance qui consomme une petite quantité de RAM. Il fonctionne en conjonction avec n'importe quel langage ou framework d'application.
  • Prise en charge de http / 2 et grpc: envoyé a une prise en charge de première classe pour http / 2 et grpc pour les connexions entrantes et sortantes. Il s'agit d'un proxy transparent de http / 1.1 à http / 2.
  • Équilibrage de charge avancé: l'envoyé prend en charge les fonctionnalités avancées d'équilibrage de charge, y compris les tentatives automatiques, le circuit ouvert, la limite de vitesse globale, les demandes d'observation, l'équilibrage de charge de zone locale, etc.
  • API de gestion de la configuration: envoyé fournit une API robuste pour gérer dynamiquement sa configuration.
  • Observabilité: observabilité profonde du trafic L7, prise en charge intégrée du traçage distribué et observabilité de mongodb, dynamodb et de nombreuses autres applications.

Étape 1 - Exemple de configuration NGINX


Ce script utilise un fichier nginx.conf spécialement créé, basé sur un exemple complet du wiki NGINX . Vous pouvez afficher la configuration dans l'éditeur en ouvrant nginx.conf


Configuration de nginx source


user www www; pid /var/run/nginx.pid; worker_processes 2; events { worker_connections 2000; } http { gzip on; gzip_min_length 1100; gzip_buffers 4 8k; gzip_types text/plain; log_format main '$remote_addr - $remote_user [$time_local] ' '"$request" $status $bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$gzip_ratio"'; log_format download '$remote_addr - $remote_user [$time_local] ' '"$request" $status $bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$http_range" "$sent_http_content_range"'; upstream targetCluster { 172.18.0.3:80; 172.18.0.4:80; } server { listen 8080; server_name one.example.com www.one.example.com; access_log /var/log/nginx.access_log main; error_log /var/log/nginx.error_log info; location / { proxy_pass http://targetCluster/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } } 

Les configurations NGINX comportent généralement trois éléments clés:


  1. Configuration du serveur NGINX, de la structure des journaux et de la fonctionnalité Gzip. Ceci est déterminé globalement dans tous les cas.
  2. Configuration de NGINX pour accepter les demandes d'hôte one.example.com sur le port 8080.
  3. Définition de votre emplacement de destination, comment gérer le trafic pour différentes parties de l'URL.

Toutes les configurations ne seront pas appliquées à Envoy Proxy et vous n'avez pas besoin de configurer certains paramètres. Envoy Proxy dispose de quatre types de clés qui prennent en charge l'infrastructure sous-jacente offerte par NGINX. Le noyau est:


  • Auditeurs: ils déterminent comment Envoy Proxy accepte les demandes entrantes. Envoy Proxy ne prend actuellement en charge que les écouteurs basés sur TCP. Une fois la connexion établie, elle est transférée vers un ensemble de filtres pour traitement.
  • Filtres: ils font partie d'une architecture en pipeline qui peut traiter les données entrantes et sortantes. Cette fonctionnalité comprend des filtres, tels que Gzip, qui compresse les données avant de les envoyer au client.
  • Routeurs: ils redirigent le trafic vers la destination souhaitée, définie comme un cluster.
  • Clusters: ils définissent le point de terminaison pour les paramètres de trafic et de configuration.

Nous utiliserons ces quatre composants pour créer la configuration du proxy Envoy pour correspondre à la configuration NGINX spécifique. Le but d'Envoy est de travailler avec l'API et la configuration dynamique. Dans ce cas, la configuration de base utilisera des paramètres statiques codés en dur de NGINX.


Étape 2 - Configurer NGINX


La première partie de nginx.conf définit certains des composants NGINX internes qui doivent être configurés.


Connexions des travailleurs


La configuration ci-dessous détermine le nombre de processus de travail et de connexions. Cela indique comment NGINX évoluera pour répondre à la demande.


 worker_processes 2; events { worker_connections 2000; } 

Envoy Proxy gère les workflows et les connexions différemment.


Envoy crée un flux de travail pour chaque thread matériel du système. Chaque thread de travail exécute une boucle d'événements non bloquants qui est responsable de


  1. À l'écoute de chaque auditeur
  2. Accepter de nouvelles connexions
  3. Création d'un ensemble de filtres pour une connexion
  4. Gestion de toutes les opérations d'E / S pendant la durée de vie d'une connexion.

Tous les autres traitements de connexion sont entièrement traités dans le flux de travail, y compris tout comportement de transfert.


Pour chaque flux de travail dans Envoy, il existe une connexion dans le pool. Ainsi, les pools de connexions HTTP / 2 établissent une seule connexion pour chaque hôte externe à la fois; s'il y a quatre threads de travail, il y aura quatre connexions HTTP / 2 pour chaque hôte externe dans un état stable. En stockant tout dans un flux de travail, presque tout le code peut être écrit sans verrouillage, comme s'il s'agissait d'un seul thread. Si plus de workflows que nécessaire sont alloués, cela peut entraîner une utilisation non rationnelle de la mémoire, la création d'un grand nombre de connexions inactives et une diminution du nombre de connexions renvoyées au pool.


Pour plus d'informations, visitez le blog Envoy Proxy .


Configuration HTTP


Le bloc de configuration NGINX suivant définit les paramètres HTTP, tels que:


  • Quels types MIME sont pris en charge
  • Délais d'expiration par défaut
  • Configuration de Gzip

Vous pouvez configurer ces aspects à l'aide de filtres dans Envoy Proxy, dont nous discuterons plus tard.


Étape 3 - Configuration du serveur


Dans le bloc de configuration HTTP, la configuration NGINX vous demande d'écouter sur le port 8080 et de répondre aux demandes entrantes pour les domaines one.example.com et www.one.example.com .


  server { listen 8080; server_name one.example.com www.one.example.com; 

À l'intérieur d'Envoy, les auditeurs le contrôlent.


Auditeurs envoyés


L'aspect le plus important pour commencer avec Envoy Proxy est l'identification des auditeurs. Vous devez créer un fichier de configuration qui décrit comment vous souhaitez exécuter une instance Envoy.


L'extrait ci-dessous créera un nouvel écouteur et l'associera au port 8080. La configuration indique à Envoy Proxy les ports auxquels il doit être lié pour les demandes entrantes.


Envoy Proxy utilise la notation YAML pour sa configuration. Pour vous familiariser avec cette notation, consultez le lien ici.


 Copy to Editorstatic_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 8080 } 

Il n'est pas nécessaire de définir server_name , car les filtres Envoy Proxy peuvent gérer cela.


Étape 4 - Configuration de l'emplacement


Lorsqu'une demande arrive à NGINX, le bloc d'emplacement détermine comment traiter et où diriger le trafic. Dans le fragment suivant, tout le trafic vers le site est transmis à un cluster en amont (note du traducteur: en amont est généralement un serveur d'applications) nommé targetCluster . Le cluster en amont définit les nœuds qui doivent traiter la demande. Nous en discuterons à l'étape suivante.


 location / { proxy_pass http://targetCluster/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } 

Chez Envoy, Filters fait cela.


Filtres Envoy


Pour une configuration statique, les filtres déterminent comment gérer les demandes entrantes. Dans ce cas, nous avons défini des filtres qui correspondent aux noms de serveur à l'étape précédente. Lorsque des demandes entrantes qui correspondent à des domaines et à des itinéraires spécifiques arrivent, le trafic est acheminé vers le cluster. C'est l'équivalent de la configuration amont NGINX.


 Copy to Editor filter_chains: - filters: - name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: backend domains: - "one.example.com" - "www.one.example.com" routes: - match: prefix: "/" route: cluster: targetCluster http_filters: - name: envoy.router 

Le nom envoyé.http_connection_manager est un filtre intégré dans Envoy Proxy. D'autres filtres incluent Redis , Mongo , TCP . Vous pouvez trouver la liste complète dans la documentation .


Pour plus d'informations sur les autres stratégies d'équilibrage de charge, consultez la documentation d'Envoy .


Étape 5 - Configuration proxy et amont


Dans NGINX, la configuration en amont définit l'ensemble des serveurs cibles qui géreront le trafic. Dans ce cas, deux grappes ont été attribuées.


  upstream targetCluster { 172.18.0.3:80; 172.18.0.4:80; } 

Dans Envoy, il est géré par cluster.


Clusters d'envois


L'équivalent de l'amont est défini comme des clusters. Dans ce cas, les hôtes qui serviront le trafic ont été identifiés. Une méthode d'accès aux hôtes, telle qu'un délai d'attente, est définie comme une configuration de cluster. Cela vous permet de contrôler plus précisément la granularité d'aspects tels que la latence et l'équilibrage de charge.


 Copy to Editor clusters: - name: targetCluster connect_timeout: 0.25s type: STRICT_DNS dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN hosts: [ { socket_address: { address: 172.18.0.3, port_value: 80 }}, { socket_address: { address: 172.18.0.4, port_value: 80 }} ] 

Lors de l'utilisation de la découverte du service STRICT_DNS, Envoy résoudra de manière continue et asynchrone les cibles DNS spécifiées. Chaque adresse IP retournée à la suite de DNS sera considérée comme un hôte explicite dans le cluster en amont. Cela signifie que si la demande renvoie deux adresses IP, Envoy supposera qu'il y a deux hôtes dans le cluster et que les deux doivent être à charge équilibrée. Si l'hôte est supprimé du résultat, Envoy suppose qu'il n'existe plus et sélectionnera le trafic de tous les pools de connexions existants.


Pour plus d'informations, consultez la documentation du proxy Envoy .


Étape 6 - Accès au journal et erreurs


La configuration finale est l'enregistrement. Au lieu de transférer les journaux d'erreurs sur le disque, Envoy Proxy utilise une approche basée sur le cloud. Tous les journaux d'application sont affichés dans stdout et stderr .


Lorsque les utilisateurs font une demande, les journaux d'accès sont facultatifs et sont désactivés par défaut. Pour activer les journaux d'accès pour les requêtes HTTP, activez la configuration access_log pour le gestionnaire de connexions HTTP. Le chemin peut être soit un périphérique, tel que stdout , soit un fichier sur disque, selon vos besoins.


La configuration suivante redirigera tous les journaux d'accès vers stdout (note du traducteur - stdout est nécessaire pour utiliser l'envoyé dans docker. Si vous utilisez sans docker, remplacez / dev / stdout par le chemin d'accès au fichier journal normal). Copiez l'extrait dans la section de configuration du gestionnaire de connexions:


 Copy to Clipboardaccess_log: - name: envoy.file_access_log config: path: "/dev/stdout" 

Les résultats devraient ressembler à ceci:


  - name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http access_log: - name: envoy.file_access_log config: path: "/dev/stdout" route_config: 

Par défaut, Envoy a une chaîne de format qui inclut les détails de la demande HTTP:


 [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n 

Le résultat de cette chaîne de format:


 [2018-11-23T04:51:00.281Z] "GET / HTTP/1.1" 200 - 0 58 4 1 "-" "curl/7.47.0" "f21ebd42-6770-4aa5-88d4-e56118165a7d" "one.example.com" "172.18.0.4:80" 

Le contenu de la sortie peut être personnalisé en définissant le champ de format. Par exemple:


 access_log: - name: envoy.file_access_log config: path: "/dev/stdout" format: "[%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n" 

La chaîne de journal peut également être sortie au format JSON en définissant le champ json_format . Par exemple:


 access_log: - name: envoy.file_access_log config: path: "/dev/stdout" json_format: {"protocol": "%PROTOCOL%", "duration": "%DURATION%", "request_method": "%REQ(:METHOD)%"} 

Pour plus d'informations sur les techniques d'enregistrement des envoyés, visitez


https://www.envoyproxy.io/docs/envoy/latest/configuration/access_log#config-access-log-format-dictionaries


La journalisation n'est pas le seul moyen de se faire une idée de travailler avec Envoy Proxy. Il dispose de fonctionnalités avancées intégrées pour le suivi et les mesures. Vous pouvez en savoir plus dans la documentation de suivi ou via le script de suivi interactif .


Étape 7 - Lancement


Vous avez maintenant transféré la configuration de NGINX vers Envoy Proxy. La dernière étape consiste à exécuter une instance d'Envoy Proxy pour la tester.


Exécuter à partir de l'utilisateur


En haut de la configuration NGINX, l' utilisateur de ligne www www; indique que NGINX a été lancé en tant qu'utilisateur disposant de faibles privilèges pour améliorer la sécurité.


Envoy Proxy adopte une approche basée sur le cloud pour gérer à qui appartient le processus. Lorsque nous exécutons Envoy Proxy via le conteneur, nous pouvons spécifier un utilisateur avec un niveau de privilège bas.


Lancer Envoy Proxy


La commande ci-dessous lancera Envoy Proxy via le conteneur Docker sur l'hôte. Cette commande permet à Envoy d'écouter les demandes entrantes via le port 80. Cependant, comme indiqué dans la configuration de l'écouteur, Envoy Proxy écoute le trafic entrant via le port 8080. Cela permet au processus de s'exécuter en tant qu'utilisateur avec des privilèges faibles.


 docker run --name proxy1 -p 80:8080 --user 1000:1000 -v /root/envoy.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy 

Test


Avec les proxys en cours d'exécution, les tests peuvent désormais être effectués et traités. La commande cURL suivante émet une demande avec l'en-tête d'hôte défini dans la configuration du proxy.


 curl -H "Host: one.example.com" localhost -i 

Une requête HTTP entraînera l'erreur 503 . Cela est dû au fait que les connexions en amont ne fonctionnent pas et qu'elles ne sont pas disponibles. Par conséquent, Envoy Proxy n'a aucune destination cible disponible pour la demande. La commande suivante lancera une série de services HTTP qui correspondent à la configuration définie pour Envoy.


 docker run -d katacoda/docker-http-server; docker run -d katacoda/docker-http-server; 

Avec les services disponibles, Envoy peut réussir le proxy du trafic vers sa destination.


 curl -H "Host: one.example.com" localhost -i 

Vous devriez voir une réponse indiquant quel conteneur Docker a traité la demande. Dans les journaux Envoy Proxy, vous devriez également voir la chaîne d'accès affichée.


En-têtes de réponse HTTP supplémentaires


Vous verrez des en-têtes HTTP supplémentaires dans les en-têtes de réponse de la demande réelle. L'en-tête affiche le temps que l'hôte en amont a passé à traiter la demande. Il est exprimé en millisecondes. Ceci est utile si le client souhaite déterminer la durée de service par rapport à la latence du réseau.


 x-envoy-upstream-service-time: 0 server: envoy 

Configuration finale


 static_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 8080 } filter_chains: - filters: - name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: backend domains: - "one.example.com" - "www.one.example.com" routes: - match: prefix: "/" route: cluster: targetCluster http_filters: - name: envoy.router clusters: - name: targetCluster connect_timeout: 0.25s type: STRICT_DNS dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN hosts: [ { socket_address: { address: 172.18.0.3, port_value: 80 }}, { socket_address: { address: 172.18.0.4, port_value: 80 }} ] admin: access_log_path: /tmp/admin_access.log address: socket_address: { address: 0.0.0.0, port_value: 9090 } 

Informations supplémentaires du traducteur


Les instructions d'installation d'Envoy Proxy sont disponibles à l' adresse https://www.getenvoy.io/


Par défaut dans rpm, il n'y a pas de configuration de service systemd.


Ajoutez la configuration du service systemd /etc/systemd/system/envoy.service:


 [Unit] Description=Envoy Proxy Documentation=https://www.envoyproxy.io/ After=network-online.target Requires=envoy-auth-server.service Wants=nginx.service [Service] User=root Restart=on-failure ExecStart=/usr/bin/envoy --config-path /etc/envoy/config.yaml [Install] WantedBy=multi-user.target 

Vous devez créer le répertoire / etc / envoyé / et y mettre la configuration config.yaml.


Par mandataire envoyé, il y a une discussion par télégramme: https://t.me/envoyproxy_ru


Envoy Proxy ne prend pas en charge la distribution de contenu statique. Alors, qui peut voter pour la fonctionnalité: https://github.com/envoyproxy/envoy/issues/378

Source: https://habr.com/ru/post/fr469455/


All Articles