web-dev-qa-db-fra.com

Comment régler TCP pour les connexions haute fréquence entre deux nœuds

Je me gratte la tête depuis quelques jours, essayant de trouver une solution au problème suivant:

Dans notre centre de données, nous avons un F5 fonctionnant sur du matériel BigIP qui agit comme un point d'entrée unique pour les demandes HTTPS des machines clientes dans divers bureaux à travers le pays. F5 met fin à TLS puis transfère toutes les demandes à deux équilibreurs de charge Traefik, qui acheminent la distribution des demandes vers les différentes instances de service (les nœuds Traefik s'exécutent dans Docker sur Red Hat Enterprise, mais je pense que cela n'est pas pertinent pour mon problème). Du point de vue du débit, du processeur et de la mémoire, ces trois composants réseau sont plus que capables de gérer la quantité de demandes et de trafic avec une grande capacité à revendre.

Cependant, nous avons remarqué des retards fréquents de 1000 ms dans les requêtes HTTP (S) que les clients effectuent, en particulier pendant les périodes de charge élevée. Nous avons suivi le problème jusqu'à la cause racine suivante:

  • Pendant les périodes de forte charge, le "client" F5 initie de nouvelles TCP aux nœuds "serveur" Traefik à une fréquence élevée (éventuellement 100+ par seconde).
  • Ces connexions sont interrompues du côté "serveur" de Traefik lorsque les réponses HTTP ont été renvoyées.
  • Chaque connexion fermée reste dans un état TIME_WAIT pendant 60 secondes sur l'hôte Traefik.
  • Lorsque le F5 établit une nouvelle connexion, il choisit au hasard un port disponible dans sa plage de ports éphémères.
  • Parfois (souvent pendant une charge élevée), il y a déjà une connexion dans Traefik dans l'état TIME_WAIT avec le même port IP + source, combinaison IP + port de destination. Lorsque cela se produit, la pile TCP (?) Sur l'hôte Traefik ignore le premier paquet SYN. ​​Remarque: RFC 6056 appelle cela collision d'instance- ids.
  • Après 1000 ms, le mécanisme de délai de retransmission (RTO) entre en action sur le F5 et renvoie le paquet SYN. Cette fois, l'hôte Traefik accepte la connexion et termine la demande correctement.

De toute évidence, ces retards de 1000 ms sont absolument inacceptables. Nous avons donc envisagé les solutions suivantes jusqu'à présent:

  1. Réduisez le RTO dans F5 pour retransmettre plus rapidement, par ex. à 200ms.
  2. Réduisez net.ipv4.tcp_fin_timeout pour fermer abandonné  TEMPS D'ATTENTE connexions plus rapides.
    Mise à jour: Cela ne s'applique qu'aux connexions abandonnées par l'autre côté, lorsque aucune FIN n'est retournée. Cela n'a aucun effet sur les connexions dans l'état TIME_WAIT.
  3. Activer net.ipv4.tcp_tw_reuse: inutile pour les connexions entrantes.
  4. Activer net.ipv4.tcp_tw_recycle: AFAIK contre-indiqué si le client envoie des horodatages aléatoires TCP. Contradiction d'informations (y compris les preuves empiriques) si cette fonctionnalité a été supprimée de Linux ou non. En outre, généralement recommandé NON jouer avec.
  5. Ajoutez plus d'adresses IP sources et/ou faites écouter Traefik sur plusieurs ports pour augmenter le nombre de permutations dans les tuples IP/ports.

Je vais jeter # 1 parce que c'est juste un pansement. Des retards se produisent encore, un peu moins notables. Le n ° 3 n'aurait aucun effet de toute façon, le n ° 4 rendrait très probablement le système non fonctionnel. Cela laisse # 2 et # 5.

Mais d'après ce que j'ai appris après avoir lu des dizaines de messages et d'articles techniques, les deux réduiront finalement les risques de ces "collisions". Parce que, ce qui empêche finalement le côté émetteur, F5, de choisir (pseudo) aléatoirement une combinaison de port éphémère, IP source et port cible qui existe toujours dans l'état TIME_WAIT sur l'hôte Traefik cible, quelle que soit la longueur du paramètre fin_timeout (qui devrait rester dans la plage de plusieurs secondes de toute façon)? Nous ne ferions que réduire la possibilité de collisions, pas l'éliminer.

Après toutes mes recherches et à l'époque des applications web gigantesques, cela m'étonne vraiment que ce problème ne soit plus discuté sur le web (et les solutions disponibles). J'apprécierais vraiment vos pensées et vos idées pour savoir s'il existe une meilleure solution, plus systématique dans TCP terrain qui conduira à la survenue de collisions près de zéro. Je pense dans le sens d'un TCP configuration qui permettra à l'hôte Traefik d'accepter immédiatement une nouvelle connexion malgré une ancienne connexion en état TIME_WAIT. Mais pour l'instant, pas de chance pour le trouver.

Pensées et points aléatoires:

  • À ce stade, il n'est pas possible de modifier nos diverses applications internes pour utiliser des connexions HTTP (S) plus longues afin de réduire le nombre de demandes/connexions par seconde.
  • L'architecture réseau de F5 et Traefik n'est pas à discuter, ne peut pas être modifiée.
  • J'ai récemment étudié la sélection de port éphémère sur les clients Windows. Cet algorithme semble être séquentiel et non aléatoire. Maximise le temps de réutilisation du port et réduit la sécurité.
  • Lors des tests de charge sur un système autrement inactif, nous avons généré ~ 100 requêtes/connexions HTTP par seconde. Les premières collisions se sont déjà produites après quelques secondes (disons avant 2000 demandes au total), même si le F5 est configuré pour utiliser plus de 60 000 ports éphémères. Je suppose que cela est dû à la nature pseudo-aléatoire de l'algorithme de sélection de port, qui semble faire assez mal pour éviter les collisions d'ID d'instance.
  • Le fait que l'hôte Traefik accepte la TCP sur la retransmission de paquets SYN est probablement une caractéristique de la TCP. RFC6056 parle de assassinat de TIME_WAIT, ce qui pourrait être lié à cela.

Update: Per The Star Experiment , le net.ipv4. Le paramètre tcp_fin_timeout n'affecte PAS l'état TIME_WAIT, uniquement l'état FIN_WAIT_2. Et par Samir Jafferali , sur les systèmes Linux (y compris notre Red Hat Linux), la période TIME_WAIT est codée en dur dans le code source et ne peut pas être configurée. Sur BSD selon la source, il est configurable mais je ne l'ai pas vérifié.

8
Christoph

Dans notre centre de données, nous avons un F5 fonctionnant sur du matériel BigIP qui agit comme nique point d'entrée pour les requêtes HTTPS à partir des ordinateurs clients dans nos différents bureaux à travers le pays .

Si ce point unique (front-end) reste unique lorsqu'il transmet les connexions au back-end, pourquoi vous posez-vous des questions sur le hoquet? Surtout si l'intensité des connexions est "peut-être 100+ par seconde".

Votre configuration consiste à presser un ensemble avec une cardinalité plus élevée dans un autre avec une cardinalité nettement inférieure.

en fin de compte seulement réduire les risques de ces "collisions"

Ceci est mis dans la base du fonctionnement des réseaux à commutation de paquets. Disons qu'au niveau Ethernet, il y a aussi des collisions. L'aléatoire est inévitable et TCP/IP s'en occupe. Le protocole IP lui-même n'a pas été construit en pensant aux réseaux locaux (mais fonctionne toujours très bien là aussi).

Donc oui "Ajouter plus d'adresses IP source et/ou faire écouter Traefik sur plusieurs ports" est une façon assez raisonnable de suivre.

4
poige

Bien que je pense également que l'ajout d'autres adresses IP est la voie la plus simple, avez-vous envisagé d'explorer la réutilisation des connexions TCP entre le F5 et les nœuds Traefik au lieu d'en créer une nouvelle par demande externe?

Je ne sais pas comment F5 prend en charge cela, mais c'est peut-être aussi simple que de passer à http2 entre le F5 et les nœuds Traefik. Voir https://developers.google.com/web/fundamentals/performance/http2#one_connection_per_Origin

3
Pedro Perez

Il s'avère que était une solution très simple à ce problème après tout, que nous avons découvert après avoir travaillé avec le fournisseur Traefik pendant un certain temps. Il s'avère également que le fait que nous exécutons Traefik dans Docker le fait importe. Le problème et la solution sont très spécifiques à notre configuration, mais je veux toujours le documenter ici au cas où d'autres rencontreraient la même chose. Néanmoins, cela n'induit pas pas les autres recommandations, plus générales, car les collisions d'ID d'instance sont un vrai problème.

En bref: toutes les instances de Traefik sont configurées en tant que conteneurs contraints par l'hôte (c'est-à-dire liés à des hôtes spécifiques) exécutés dans un cluster Docker Swarm. Les instances de Traefik doivent exposer un port au niveau de l'hôte afin qu'elles deviennent accessibles à partir du F5, qui n'est évidemment pas un participant Docker Swarm. Ces ports exposés avaient été configurés en mode d'entrée , ce qui était non seulement inutile (pas besoin d'acheminer le trafic via le réseau d'entrée Docker Swarm) mais était également le cause pour les paquets SYN abandonnés/ignorés. Une fois que nous avons commuté le mode de port sur Host , les retards ont disparu.

Avant:

  ports:
  - target: 8080
    published: 8080
    protocol: tcp
    mode: ingress

Après:

  ports:
  - target: 8080
    published: 8080
    protocol: tcp
    mode: Host
2
Christoph