J'utilise actuellement Nginx comme répartiteur de charge afin d'équilibrer le trafic réseau entre 3 nœuds sur lesquels tourne une API NodeJS.
L'instance Nginx fonctionne sur le nœud 1 et chaque requête est adressée au nœud 1. J'ai un pic de requêtes d'environ 700k en 2 heures et nginx est configuré pour les basculer, de manière circulaire, entre node1, node2 et node3. Ici le conf.d/deva.conf
:
upstream deva_api {
server 10.8.0.30:5555 fail_timeout=5s max_fails=3;
server 10.8.0.40:5555 fail_timeout=5s max_fails=3;
server localhost:5555;
keepalive 300;
}
server {
listen 8000;
location /log_pages {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Connection "";
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Origin,X-Auth-Token';
add_header 'Access-Control-Allow-Credentials' 'true';
if ($request_method = OPTIONS ) {
return 200;
}
proxy_pass http://deva_api;
proxy_set_header Connection "Keep-Alive";
proxy_set_header Proxy-Connection "Keep-Alive";
auth_basic "Restricted"; #For Basic Auth
auth_basic_user_file /etc/nginx/.htpasswd; #For Basic Auth
}
}
et ici le nginx.conf
configuration :
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
worker_rlimit_nofile 65535;
events {
worker_connections 65535;
use epoll;
multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 120;
send_timeout 120;
types_hash_max_size 2048;
server_tokens off;
client_max_body_size 100m;
client_body_buffer_size 5m;
client_header_buffer_size 5m;
large_client_header_buffers 4 1m;
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
reset_timedout_connection on;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Le problème est qu'avec cette configuration, j'obtiens des centaines d'erreurs dans le fichier error.log, comme celles qui suivent :
upstream prematurely closed connection while reading response header from upstream
mais seulement sur les nœuds 2 et 3. J'ai déjà effectué les tests suivants :
- augmenter le nombre d'API concurrentes dans chaque nœud (en fait, j'utilise PM2 comme équilibreur intra-nœud)
- supprimer un nœud afin de faciliter le travail de nginx
- poids appliqués à nginx
Rien n'améliore le résultat. Dans ces tests, j'ai remarqué qu'il y avait des erreurs uniquement sur les 2 nœuds distants (node2 et node3), j'ai donc essayé de les supprimer de l'équation. Le résultat est que je n'ai plus d'erreurs comme celle-là mais j'ai commencé à avoir 2 erreurs différentes :
recv() failed (104: Connection reset by peer) while reading response header from upstream
et
writev() failed (32: Broken pipe) while sending request to upstream
Il semble que le problème soit dû à l'absence d'API sur node1, les API ne peuvent probablement pas répondre à tout le trafic entrant avant le timeout du client (c'était, c'est, mon hypothèse). Cela dit, j'ai augmenté le nombre d'API concurrentes sur node1 et le résultat était meilleur que les précédents, mais je continue à obtenir les 2 dernières erreurs et je ne peux plus augmenter le nombre d'API concurrentes sur node1.
La question est donc de savoir pourquoi je ne peux pas utiliser nginx comme équilibreur de charge avec tous mes nœuds ? Est-ce que je fais des erreurs dans la configuration de nginx ? Y a-t-il d'autres problèmes que je n'ai pas remarqués ?
EDIT : J'effectue des tests de réseau entre 3 nœuds. Les nœuds communiquent entre eux via Openvpn :
PING :
node1->node2
PING 10.8.0.40 (10.8.0.40) 56(84) bytes of data.
64 bytes from 10.8.0.40: icmp_seq=1 ttl=64 time=2.85 ms
64 bytes from 10.8.0.40: icmp_seq=2 ttl=64 time=1.85 ms
64 bytes from 10.8.0.40: icmp_seq=3 ttl=64 time=3.17 ms
64 bytes from 10.8.0.40: icmp_seq=4 ttl=64 time=3.21 ms
64 bytes from 10.8.0.40: icmp_seq=5 ttl=64 time=2.68 ms
node1->node2
PING 10.8.0.30 (10.8.0.30) 56(84) bytes of data.
64 bytes from 10.8.0.30: icmp_seq=1 ttl=64 time=2.16 ms
64 bytes from 10.8.0.30: icmp_seq=2 ttl=64 time=3.08 ms
64 bytes from 10.8.0.30: icmp_seq=3 ttl=64 time=10.9 ms
64 bytes from 10.8.0.30: icmp_seq=4 ttl=64 time=3.11 ms
64 bytes from 10.8.0.30: icmp_seq=5 ttl=64 time=3.25 ms
node2->node1
PING 10.8.0.12 (10.8.0.12) 56(84) bytes of data.
64 bytes from 10.8.0.12: icmp_seq=1 ttl=64 time=2.30 ms
64 bytes from 10.8.0.12: icmp_seq=2 ttl=64 time=8.30 ms
64 bytes from 10.8.0.12: icmp_seq=3 ttl=64 time=2.37 ms
64 bytes from 10.8.0.12: icmp_seq=4 ttl=64 time=2.42 ms
64 bytes from 10.8.0.12: icmp_seq=5 ttl=64 time=3.37 ms
node2->node3
PING 10.8.0.40 (10.8.0.40) 56(84) bytes of data.
64 bytes from 10.8.0.40: icmp_seq=1 ttl=64 time=2.86 ms
64 bytes from 10.8.0.40: icmp_seq=2 ttl=64 time=4.01 ms
64 bytes from 10.8.0.40: icmp_seq=3 ttl=64 time=5.37 ms
64 bytes from 10.8.0.40: icmp_seq=4 ttl=64 time=2.78 ms
64 bytes from 10.8.0.40: icmp_seq=5 ttl=64 time=2.87 ms
node3->node1
PING 10.8.0.12 (10.8.0.12) 56(84) bytes of data.
64 bytes from 10.8.0.12: icmp_seq=1 ttl=64 time=8.24 ms
64 bytes from 10.8.0.12: icmp_seq=2 ttl=64 time=2.72 ms
64 bytes from 10.8.0.12: icmp_seq=3 ttl=64 time=2.63 ms
64 bytes from 10.8.0.12: icmp_seq=4 ttl=64 time=2.91 ms
64 bytes from 10.8.0.12: icmp_seq=5 ttl=64 time=3.14 ms
node3->node2
PING 10.8.0.30 (10.8.0.30) 56(84) bytes of data.
64 bytes from 10.8.0.30: icmp_seq=1 ttl=64 time=2.73 ms
64 bytes from 10.8.0.30: icmp_seq=2 ttl=64 time=2.38 ms
64 bytes from 10.8.0.30: icmp_seq=3 ttl=64 time=3.22 ms
64 bytes from 10.8.0.30: icmp_seq=4 ttl=64 time=2.76 ms
64 bytes from 10.8.0.30: icmp_seq=5 ttl=64 time=2.97 ms
Vérification de la bande passante, via IPerf :
node1 -> node2
[ ID] Interval Transfer Bandwidth
[ 4] 0.0-10.0 sec 229 MBytes 192 Mbits/sec
node2->node1
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 182 MBytes 152 Mbits/sec
node3->node1
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 160 MBytes 134 Mbits/sec
node3->node2
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 260 MBytes 218 Mbits/sec
node2->node3
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 241 MBytes 202 Mbits/sec
node1->node3
[ ID] Interval Transfer Bandwidth
[ 4] 0.0-10.0 sec 187 MBytes 156 Mbits/sec
Il semble qu'il y ait un goulot d'étranglement dans le tunnel OpenVPN car le même test via eth
est d'environ 1Gbits. Cela dit, j'ai suivi ce guide community.openvpn.net mais je n'ai obtenu que le double de la bande passante mesurée auparavant.
J'aimerais conserver OpenVPN, y a-t-il d'autres réglages à faire pour augmenter la bande passante du réseau ou d'autres ajustements à la configuration de nginx pour qu'il fonctionne correctement ?