Overview
--------
- Nginx can provide
load balancing to a group of servers
- Advantages of load
balancing
    a. optimize resource utilization
    b. maximize throughput
    c. reduce latency
    d. fault tolerance
Proxying Traffic to a group of servers
--------------------------------------
- setup is similar to
`Secure TCP to Upstream` KB
| 
example config | 
http { 
    upstream backend { 
        # `server` directive here is
  different from 
        # the one used in virtual servers 
        server backend1.example.com; 
        # member 2 
        server backend2.example.com; 
        # backup 
        server 192.0.0.1 backup; 
    } 
    server { 
        location / { 
            # call the upstream via name 
            proxy_pass http://backend; 
        } 
    } 
} | 
| 
actual testing | 
#
  version 
nginx version:
  nginx/1.10.2 
built by gcc 4.8.5
  20150623 (Red Hat 4.8.5-4) (GCC) 
built with OpenSSL
  1.0.1e-fips 11 Feb 2013 
#
  proxy server config 
[/etc/nginx/conf.d]_$
  cat virt1.conf 
upstream backend { 
  server backend1.home.net; 
  server backend2.home.net; 
 } 
server { 
  server_name virt1.home.net; 
  location / { 
    proxy_pass http://backend; 
  } 
} 
[/etc/nginx/conf.d]_$ 
# backend1 config 
[/etc/nginx/conf.d]_$
  cat backend1.conf 
server { 
  listen 192.168.1.11:80; 
  server_name backend1.home.net; 
  root /data; 
  location / { 
    autoindex on; 
  } 
} 
[/etc/nginx/conf.d]_$ # backend2 config 
[/etc/nginx/conf.d]_$
  cat backend2.conf 
server { 
  listen 192.168.1.12:80; 
  server_name backend2.home.net; 
  root /data; 
  location / { 
    autoindex on; 
  } 
} 
[/etc/nginx/conf.d]_$ | 
Load Balancing Methods
----------------------
| 
Round-Robin 
 - requests are distributed evenly across
  servers 
 - server weights are taken into
  consideration 
 - default method (no directive for enabling
  it) | 
upstream backend { 
   server backend1.example.com; 
   server backend2.example.com; 
} | 
| 
least_conn 
 - request is sent to server w/ least number
  of 
   active connections 
 - server weights are taken into
  consideration | 
upstream backend { 
    least_conn; 
    server backend1.example.com; 
    server backend2.example.com; 
} | 
| 
ip_hash 
 - destination of request is determine from
  client's 
   ip (first 3 octets of IPV4 or whole IPV6) 
 - guarantees that requests from same IP gets
  into 
   same server unless it is not available 
 - if server needs to be removed temporarily,
  it 
   can be mark as `down` to preserve current
  hashing 
   of client IPs (requests to that server
  will go 
   to the next in the group) | 
upstream backend { 
    ip_hash; 
    server backend1.example.com; 
    server backend2.example.com; 
} 
in case 1 server is
  down for maintenance: 
upstream backend { 
    server backend1.example.com; 
    server backend2.example.com; 
    server backend3.example.com down; 
} | 
| 
hash 
 - generic hash method 
 - destination server is determined by
  user-defined 
   key (text/variable/source IP:PORT) 
 - `consistent` option enables
  "ketama" hash load 
   balancing 
 - requests will be evenly distributed across
  based 
   on user-defined hash value 
 - if a server is added/removed, only few
  keys will 
   be remapped w/c minimizes cache misses | 
upstream backend { 
    hash $request_uri consistent; 
    server backend1.example.com; 
    server backend2.example.com; 
} | 
| 
least_time 
 - available on Nginx PLUS only 
 - selects server w/ lowest average latency
  and 
   least number of connections 
 - lowest average latency is computed based
  on: 
    a. header - time to receive 1st byte from
  server 
    b. last_byte - time to receive full
  response 
                   from server | 
upstream backend { 
    least_time header; 
    server backend1.example.com; 
    server backend2.example.com; 
} | 
Server Weights
--------------
- by default, Nginx
distributes requests among servers according to their 
  weights using round-robin method
- default weight is 1
| 
example config from | 
upstream backend { 
    # This member has the highest weight of 5
  meaning 5 out of 6 
    # requests will go this server. 
    server backend1.example.com weight=5; 
    # This member has a default weight of 1. 
    server backend2.example.com; 
    # this server doesn't receive request
  unless any of the servers 
    # above goes down (weight=1) 
    server 192.0.0.1 backup; 
} | 
Server Slow Start
-----------------
- prevents a recently
recovered server from overwhelming by connections w/c may
  timeout and cause the server to be marked as
failed again
- this allows to
gradually recover its weight from zero to nominal value
- this is done using
`slow_start` directive
- note: if there is
only 1 server in a group, the following directives will be
  ignored
    a. max_fails
    b. fail_timeout
    c. slow_start
| 
config | 
upstream backend { 
    server backend1.example.com
  slow_start=30s; 
    server backend2.example.com; 
    server 192.0.0.1 backup; 
} | 
Enabling Session Persistence
----------------------------
- Nginx plus can
identify user sessions and route requests to same upstream
  server
- Nginx plus supports
3 session persistence methods using `sticky` directive:
    1. sticky cookie
         - simplest session persistence method
         - adds session cookie to the first
response from upstream
         - identifies server w/c has sent the
response
         - when client issues next request, it
will contain the cookie and Nginx
           will route it to the same upstream
server
    2. sticky route
         - when client sends 1st request, a
route is assigned to that client
         - all subsequent requests will be
compared to `route` option
         - route information is taken from
cookie/URI
    3. cookie learn
         - NGINX Plus finds session identifiers
by inspecting requests and
           responses
         - NGINX Plus learns w/c upstream
server corresponds to w/c session
           identifier
         - session identifiers are passed to
HTTP cookie
         - if a request contains a session
identifier already "learned", NGINX
           Plus forwards the request to the
corresponding server
         - doesn't require keeping cookies on
client side
         - all info is kept on memory-side in
shared memory zone
         - most sophisticated way of storing
persistence method
Sample configs:
| 
sticky cookie | 
upstream backend { 
    server backend1.example.com; 
    server backend2.example.com; 
    # srv_id 
  -> cookie name 
    # expires -> time cookie will expire
  on client browser 
    # domain 
  -> domain for w/c cookie is set (optional) 
    # path   
  -> path for w/c cookie is set (optional) 
    sticky cookie srv_id expires=1h
  domain=.example.com path=/; 
} | 
| 
sticky route | 
upstream backend { 
    server backend1.example.com route=a; 
    server backend2.example.com route=b; 
    sticky route $route_cookie $route_uri; 
} | 
| 
cookie learn | 
upstream backend { 
   # upstream servers creates session by
  setting "EXAMPLECOOKIE" in the response < I don't understand 
   server backend1.example.com; 
   server backend2.example.com; 
   # create -> indicates how a new session
  is created (required) 
   # lookup -> indicates how to search for
  existing sessions (required) 
   # zone  
  -> shared memory zone where all cookies are kept 
   sticky learn  
       create=$upstream_cookie_examplecookie 
       lookup=$cookie_examplecookie 
       zone=client_sessions:1m 
       timeout=1h; 
   # sequence: 
   #  
  1. new session is created from cookie "EXAMPLECOOKIE" sent
  by upstream server 
   #  
  2. existing sessions are searched in cookie "EXAMPLECOOKIE" 
} | 
Limiting number of connections
------------------------------
- requires NGINX Plus
- number of
connections can be limited using `max_conns` parameter
- if `max_conns` has
been reached, further connections can be placed in queue if
  `queue` directive is specified
- if `queue` has been
filled up or upstream server cannot be selected during the
  timeout period specified in `timeout`, client
will receive an error
- NOTE: `max_conns`
will be ignored if idle keepalive is set on worker processes
- number of
connections can exceed `max_conns` in a configuration where the
  memory is shared w/ multiple worker processes
| 
sample config | 
upstream backend { 
    server backend1.example.com  max_conns=3; 
    server backend2.example.com; 
    queue 100 timeout=70; 
} | 
Passive Health Monitoring
-------------------------
- when Nginx consider
a server unavailable, it stops sending requests to that
  server until its consider to be active again
- `server` options
w/c consider a server unavailable:
    a. fail_timeout (default: 10 seconds)
    b. max_fails (default: 1 attempt)
| 
sample config | 
upstream backend
  {                 
    server backend1.example.com; 
    # server is
  mark unavailable for 30 seconds if response is not receive after 3 attempts 
    server backend2.example.com max_fails=3
  fail_timeout=30s; 
    # server is mark unavailable for 10
  seconds if response is not receive after 2 attempts 
    server
  backend3.example.com max_fails=2; 
} | 
Active Health Monitoring
------------------------
- a more
sohpisticated way of monitoring compared to "Passive Health
Monitoring"
- periodically sends
special requests to servers and checks their response
- to activate:
include `health_check` within the `location` block that passes
  request to the group
| 
sample config | 
http { 
    upstream backend { 
        # shared memory zone among worker
  processes 
        #  
  - stores configuration of server group 
        #  
  - enables workers to use same set of counters to keep track 
        #    
  of responses from servers in the group 
        #  
  - a counter includes: 
        #       a. current number of connections to
  each server in the group 
        #  
  -   b. number of failed attempts
  to pass request to a server 
        zone backend 64k; 
        server backend1.example.com; 
        server backend2.example.com; 
        server backend3.example.com; 
        server backend4.example.com; 
    } 
    server { 
        location / { 
  # - by default, Nginx Plus sends `/`
  requests to each member of group every 5 seconds 
  # - if an error/timeout occured (proxied
  server respond other than status code 2XX/3XX), 
  #  
  health check failed for that server 
  # - a server that fails a health check is
  unhealty and Nginx Plus stops sending requests 
  #  
  to it until it passes a healtch check 
            proxy_pass http://backend; 
            health_check; 
        } 
    } 
} | 
| 
default behaviour
  can be 
overwritten using
  some parameters 
in `health_check`
  directive | 
location / { 
    proxy_pass http://backend; 
    health_check interval=10 fails=3
  passes=2; 
    # interval - interval between checks in
  seconds 
    # fails   
  - # of failed health checks to consider a server unhealthy 
    # passes  
  - # of successful health checks to consider a server healthy 
} | 
| 
setting a specific
  URI to check 
every health check | 
location / { 
    proxy_pass http://backend; 
    health_check uri=/some/path; 
    # every health check will append the uri
  to the IP/hostname of 
    # each server in the group # e.g 
} | 
| 
specifying a custom
  condition 
that will satisfy a
  healthy 
check using `match`
  directive | 
http { 
    ... 
    match server_ok { 
        status 200-399;  # responses
  must be any number between 200 and 399 
        body !~ "maintenance
  mode";  #
  body must not contain any kind of "maintenance mode" string (regex) 
    } 
    server { 
        ... 
        location / { 
            proxy_pass http://backend; 
            health_check match=server_ok; 
        } 
    } 
} | 
| 
another examples
  using `match` | 
#
  example 1 
match welcome { 
    status 200; 
    header Content-Type = text/html; 
    body ~ "Welcome to nginx!"; 
} # example 2 
match not_redirect
  { 
    status ! 301-303 307; 
    header ! Refresh; 
} | 
Sharing Data with Multiple Worker Processes
-------------------------------------------
- if `upstream`
directive doesn't include `zone` directive:
    a. each worker process keeps its own copy
of server group config
    b. each maintain its own set of related
counters
    c. server group configuration isn't
changeable
- if `upstream`
directive does include `zone` directive:
    a. server group config is placed in memory
area shared among all workers
    b. workers utilize same set of counters
    c. dynamically configurable
- `zone` directive is
mandatory for:
    a. health checks
    b. on-the-fly configurations
| 
example scenario if
  server configuration is not shared | 
For example, if the
  configuration of a group is not shared, each worker process maintains its 
own counter for
  failed attempts to pass a request to a server (see the max_fails parameter). 
In this case, each
  request gets to only one worker process. When the worker process that is 
selected to process
  a request fails to transmit the request to a server, other worker 
processes don’t
  know anything about it. While some worker process can consider a server 
unavailable, others
  may still send requests to this server. For a server to be definitively 
considered
  unavailable, max_fails multiplied by the number of workers processes of
  failed 
attempts should
  happen within the timeframe set by fail_timeout. On the other hand, the 
zone directive
  guarantees the expected behavior. | 
| 
some notes on
  `least_conn` directive | 
The least_conn load
  balancing method might not work as expected without the zone directive, 
at least on small
  loads. This method of tcp and http load balancing passes a request to the 
server with the
  least number of active connections. Again, if the configuration of the group 
is not shared, each
  worker process uses its own counter for the number of connections. And 
if one worker
  process passes by a request to a server, the other worker process can also 
pass a request to
  the same server. However, you can increase the number of requests to 
reduce this effect.
  On high loads requests are distributed among worker processes evenly, 
and the least_conn
  load balancing method works as expected. | 
| 
setting the size of
  zone - there are no exact settings - it all depends on usage patterns - each feature such as: 
     `sticky` 
     `cookie` 
     `route` 
     `learn` 
   will affect the zone size. | 
For example, the
  256 Kb zone with the sticky_route session persistence method and a single 
health check can
  hold up to: 
128 servers (adding
  a single peer by specifying IP:port); 
88 servers (adding
  a single peer by specifying hostname:port, hostname resolves to single IP); 
12 servers (adding
  multiple peers by specifying hostname:port, hostname resolves to many IPs). | 
Configuring Load Balancing using DNS
------------------------------------
- Nginx Plus can
monitor changes in server IP and apply this w/o restart
- this is done via
`resolver` directive
- by default, Nginx
resolves DNS records based on TTL
    > this can be overwritten using `valid`
parameter
- if hostname
resolves to multiple IPs, the IPs will be store on the configuration and will
be load balanced
| 
sample config | 
http { 
    resolver 10.0.0.1 valid=300s ipv6=off; 
    resolver_timeout 10s; 
    server { 
        location / { 
            proxy_pass http://backend; 
        } 
    } 
    upstream backend { 
        zone backend 32k; 
        least_conn; 
        ... 
        server backend1.example.com resolve; 
        server backend2.example.com resolve; 
    } 
} | 
Load Balancing of Microsoft Exchange Servers
--------------------------------------------
- in release 7 and
later, Nginx Plus can proxy traffic to Exchange servers and
  load balanced it
| 
sample config | 
http { 
    ... 
    upstream exchange { 
        zone exchange 64k; 
        ntlm; 
        server exchange1.example.com; 
        server exchange2.example.com; 
    } 
    server { 
        listen              443 ssl; 
        ssl_certificate     /etc/nginx/ssl/company.com.crt; 
        ssl_certificate_key
  /etc/nginx/ssl/company.com.key; 
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2; 
        location / { 
            proxy_http_version 1.1; 
            proxy_set_header   Connection ""; 
        } 
    } | 
On-Fly Configuration
--------------------
- In Nginx Plus, it
is possible to modify load balancing configuration
    > view all servers
    > view a server in a group
    > modify parameter for a particular
server
    > add server(s)
    > remove server(s)
- stores
configuration in shared memory
- changes are
discarded every time Nginx config file is reloaded
| 
setup | 
http { 
    ... 
    #
  Configuration of the server group 
    upstream appservers { 
        zone appservers 64k; 
        server appserv1.example.com      weight=5; 
        server appserv2.example.com:8080
  fail_timeout=5s; 
        server reserve1.example.com:8080
  backup; 
        server reserve2.example.com:8080
  backup; 
    } 
    server { 
        # Location that proxies requests to the
  group 
        location / { 
            proxy_pass http://appservers; 
            health_check; 
        } 
        # This
  allows on-the-fly configuration via API interface 
        location /upstream_conf { 
            upstream_conf; 
            allow 127.0.0.1; 
            deny  all; 
        } 
    } 
} | 
Configuring Persistence of On-the-Fly Configuration
---------------------------------------------------
- changes is stored
on a special file to avoid being discarded every config reload
| 
setup | 
http { 
    ... 
    upstream appservers { 
        zone appservers 64k; 
        # This file is modified by
  configuration commands from `upstream_conf` 
        # API interface. You should avoid
  modifying this file directly. 
        state /var/lib/nginx/state/appservers.conf; 
        # All these servers should be moved
  to the file using the upstream_conf API: 
        # server appserv1.example.com      weight=5; 
        # server appserv2.example.com:8080
  fail_timeout=5s; 
        # server reserve1.example.com:8080
  backup; 
        # server reserve2.example.com:8080
  backup; 
    } 
} | 
APIs used to configure Upstream servers on the fly
--------------------------------------------------
| 
view all backup
  servers | |
| 
adds new server in
  the group | |
| 
remove server from
  a group | |
| 
modifies parameter
  for a particular server | 
 
No comments:
Post a Comment