Nginx High volume traffic load balancing

22,158

Solution 1

Here are some good references:

http://dak1n1.com/blog/12-nginx-performance-tuning

Server fault: https://serverfault.com/questions/221292/tips-for-maximizing-nginx-requests-sec

A very well documented config from the dak1n1 link:

# This number should be, at maximum, the number of CPU cores on your system. 
# (since nginx doesn't benefit from more than one worker per CPU.)
worker_processes 24;

# Number of file descriptors used for Nginx. This is set in the OS with 'ulimit -n 200000'
# or using /etc/security/limits.conf
worker_rlimit_nofile 200000;


# only log critical errors
error_log /var/log/nginx/error.log crit


# Determines how many clients will be served by each worker process.
# (Max clients = worker_connections * worker_processes)
# "Max clients" is also limited by the number of socket connections available on the system (~64k)
worker_connections 4000;


# essential for linux, optmized to serve many clients with each thread
use epoll;


# Accept as many connections as possible, after nginx gets notification about a new connection.
# May flood worker_connections, if that option is set too low.
multi_accept on;


# Caches information about open FDs, freqently accessed files.
# Changing this setting, in my environment, brought performance up from 560k req/sec, to 904k req/sec.
# I recommend using some varient of these options, though not the specific values listed below.
open_file_cache max=200000 inactive=20s; 
open_file_cache_valid 30s; 
open_file_cache_min_uses 2;
open_file_cache_errors on;


# Buffer log writes to speed up IO, or disable them altogether
#access_log /var/log/nginx/access.log main buffer=16k;
access_log off;


# Sendfile copies data between one FD and other from within the kernel. 
# More efficient than read() + write(), since the requires transferring data to and from the user space.
sendfile on; 


# Tcp_nopush causes nginx to attempt to send its HTTP response head in one packet, 
# instead of using partial frames. This is useful for prepending headers before calling sendfile, 
# or for throughput optimization.
tcp_nopush on;


# don't buffer data-sends (disable Nagle algorithm). Good for sending frequent small bursts of data in real time.
tcp_nodelay on; 


# Timeout for keep-alive connections. Server will close connections after this time.
keepalive_timeout 30;


# Number of requests a client can make over the keep-alive connection. This is set high for testing.
keepalive_requests 100000;


# allow the server to close the connection after a client stops responding. Frees up socket-associated memory.
reset_timedout_connection on;


# send the client a "request timed out" if the body is not loaded by this time. Default 60.
client_body_timeout 10;


# If the client stops reading data, free up the stale client connection after this much time. Default 60.
send_timeout 2;


# Compression. Reduces the amount of data that needs to be transferred over the network
gzip on;
gzip_min_length 10240;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
gzip_disable "MSIE [1-6]\.";

Also more info on linux system tuning for sysctl.conf:

# Increase system IP port limits to allow for more connections

net.ipv4.ip_local_port_range = 2000 65000


net.ipv4.tcp_window_scaling = 1


# number of packets to keep in backlog before the kernel starts dropping them 
net.ipv4.tcp_max_syn_backlog = 3240000


# increase socket listen backlog
net.core.somaxconn = 3240000
net.ipv4.tcp_max_tw_buckets = 1440000


# Increase TCP buffer sizes
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_congestion_control = cubic

Solution 2

I found that using the least connected algorithm was problematic. I switched to

hash $remote_addr consistent;

and found the service much quicker.

Solution 3

nginx should definately be able to handle more then 1000 req/s (I get about 2800 req/s in nginx when playing around on my cheap laptop with jmeter using one and a halve of the 2 cores)

You're using epoll which is the optimal option on current linux kernel as I understand it.

You've turned acces_log off, so you'r disc IO shouldn't be a bottleneck either (note: you could also set the access_log to buffered mode with a large buffer where it only writes after each x kb, which avoids the disk io being constantly hammered, but keeps the logs for analysis)

My understanding is that in order to maximize nginx performance you normally set the number of worker_processes equal to the number of core/cpu's, and then up the number of worker_connections to allow more concurrent connections (along with the number of open files os limit). Yet in the data you posted above you have a quadcore cpu with 10 worker processes with 10k connections allowed each. Consequently on the nginx side I'd try something like:

worker_processes 4;
worker_rlimit_nofile 999999;
events {
  worker_connections 32768;
  use epoll;
  multi_accept on;
}

On the kernel side I'd tune tcp read and write buffers differently, you want a small minimum, small default and large max.

You've upped the ephemeral port range already.

I'd up the number open files limit more, as you'll have lots of open sockets.

Which gives the following lines to add/change in your /etc/sysctl.conf

net.ipv4.tcp_rmem = 4096 4096 25165824                                
net.ipv4.tcp_wmem = 4096 4096 25165824
fs.file-max=999999

Hope that helps.

Share:
22,158
naih
Author by

naih

Updated on July 09, 2022

Comments

  • naih
    naih almost 2 years

    For the last 3 weeks we have been testing Nginx as load balance. Currently, we're not succeeding to handle more than 1000 req/sec and 18K active connections. When we get to the above numbers, Nginx starts to hang, and returns timeout codes. The only way to get a response is to reduce the number of connection dramatically.

    I must note that my servers can and does handle this amount of traffic on a daily basis and we currently use a simple round rubin DNS balancing.

    We are using a dedicated server with the following HW:

    • INTEL XEON E5620 CPU
    • 16GB RAM
    • 2T SATA HDD
    • 1Gb/s connection
    • OS: CentOS 5.8

    We need to load balance 7 back servers running Tomcat6 and handling more than 2000 req/sec on peek times, handling HTTP and HTTPS requests.

    While running Nginx's cpu consumption is around 15% and used RAM is about 100MB.

    My questions are:

    1. Has any one tried to load balance this kind of traffic using nginx?
    2. Do you think nginx can handle such traffic?
    3. Do you have any idea what can cause the hanging?
    4. Am I missing something on my configurations?

    Below are my configuration files:

    nginx.conf:

    user  nginx;
    worker_processes 10;
    
    worker_rlimit_nofile 200000;
    
    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;
    
    
    events {
        worker_connections  10000;
        use epoll;
        multi_accept on;
    }
    
    
    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;
    
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
    
        #access_log  /var/log/nginx/access.log  main;
        access_log off;
    
        sendfile        on;
        tcp_nopush     on;
    
        keepalive_timeout  65;
        reset_timedout_connection on;
    
        gzip  on;
        gzip_comp_level 1;
        include /etc/nginx/conf.d/*.conf;
    } 
    

    servers.conf:

    #Set the upstream (servers to load balance)
    #HTTP stream
    upstream adsbar {
      least_conn;
      server xx.xx.xx.34 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.36 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.37 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.39 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.40 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.42 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.43 max_fails=2 fail_timeout=15s;
    }      
    
    #HTTPS stream
    upstream adsbar-ssl {
      least_conn;
      server xx.xx.xx.34:443 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.36:443 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.37:443 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.39:443 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.40:443 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.42:443 max_fails=2 fail_timeout=15s;
      server xx.xx.xx.43:443 max_fails=2 fail_timeout=15s;
    }
    
    #HTTP
    server {
      listen xxx.xxx.xxx.xxx:8080;
      server_name www.mycompany.com;
      location / {
          proxy_set_header Host $host;
          # So the original HTTP Host header is preserved
          proxy_set_header X-Real-IP $remote_addr;
          # The IP address of the client (which might be a proxy itself)
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_pass http://adsbar;
      }
    }
    
    #HTTPS
    server {
      listen xxx.xxx.xxx.xxx:8443;
      server_name www.mycompany.com;
      ssl on;
      ssl_certificate /etc/pki/tls/certs/mycompany.crt;
      # Path to an SSL certificate;
      ssl_certificate_key /etc/pki/tls/private/mycompany.key;
      # Path to the key for the SSL certificate;
      location / {
          proxy_set_header Host $host;
          # So the original HTTP Host header is preserved
          proxy_set_header X-Real-IP $remote_addr;
          # The IP address of the client (which might be a proxy itself)
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_pass https://adsbar-ssl;
      }
    }
    
    server {
        listen xxx.xxx.xxx.xxx:61709;
        location /nginx_status {
            stub_status on;
            access_log off;
            allow 127.0.0.1;
            deny all;
        }
    } 
    

    sysctl.conf:

    # Kernel sysctl configuration file for Red Hat Linux
    #
    # For binary values, 
    
    0 is disabled, 1 is enabled.  See sysctl(8) and
    # sysctl.conf(5) for more details.
    
    # Controls IP packet forwarding
    net.ipv4.ip_forward = 0
    
    # Controls source route verification
    net.ipv4.conf.default.rp_filter = 1
    
    # Do not accept source routing
    net.ipv4.conf.default.accept_source_route = 0
    
    # Controls the System Request debugging functionality of the kernel
    kernel.sysrq = 1
    
    # Controls whether core dumps will append the PID to the core filename
    # Useful for debugging multi-threaded applications
    kernel.core_uses_pid = 1
    
    # Controls the use of TCP syncookies
    net.ipv4.tcp_syncookies = 1
    
    # Controls the maximum size of a message, in bytes
    kernel.msgmnb = 65536
    
    # Controls the default maxmimum size of a mesage queue
    kernel.msgmax = 65536
    
    # Controls the maximum shared segment size, in bytes
    kernel.shmmax = 68719476736
    
    # Controls the maximum number of shared memory segments, in pages
    kernel.shmall = 4294967296
    
    fs.file-max = 120000
    net.ipv4.ip_conntrack_max = 131072
    net.ipv4.tcp_max_syn_backlog = 8196
    net.ipv4.tcp_fin_timeout = 25
    net.ipv4.tcp_keepalive_time = 3600
    net.ipv4.ip_local_port_range = 1024 65000
    net.ipv4.tcp_rmem = 4096 25165824 25165824
    net.core.rmem_max = 25165824
    net.core.rmem_default = 25165824
    net.ipv4.tcp_wmem = 4096 65536 25165824
    net.core.wmem_max = 25165824
    net.core.wmem_default = 65536
    net.core.optmem_max = 25165824
    net.core.netdev_max_backlog = 2500
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_tw_reuse = 1
    

    Any help, guidance, ideas will be highly appreciated.

  • Joseph Persie III
    Joseph Persie III over 8 years
    Are you making these changes to both the load balancer server and the backend server or one or the other?
  • chrislovecnm
    chrislovecnm over 8 years
    More details please. Every nginx server get these adjustments, if at a high volume.
  • Joseph Persie III
    Joseph Persie III about 8 years
    I assume if I had database load balancing servers (pgpool, not an nginx server) it too should acquire the settings considering the database connection would be utilized for every single request. In contrast the connection betwen pgpool and postgres would not assume these settings because there is a persistent connection established between pgpool and postgres thus not a new tcp connection establsihed for every database request. Does this sound correct?
  • chrislovecnm
    chrislovecnm about 8 years
    @JosephPersie unsure since this is about nginx, not pgpool. Also I have never used pgpool.