HAProxy

info

HAProxy is an alternative to NGINX.  Unlike NGINX it is simply a proxy handler, it cannot be used to host static web pages.

When used from within docker, be aware that browsers such as chrome will cache references to the <ip>:<port> location.  If the location is invalid, the browser seems to hold onto to the cached data.  As an example, we initially set up the frontend section to bind to wrong host (the docker address) on port 8080. 

When we ran the proxy via docker we mapped 8090 out to 8080, this was the wrong way round, it should have been 8080 out to 8090.  When we tried to access <docker ip>:8080 or <docker ip>:8090 from Chrome, they both failed for obvious reasons.

We then corrected the forntend section to bind on all interfaces at port 8080 "*:8080".  We ran docker, exporting docker port 8080 to host port 8090, then tried to access the proxy at <docker ip>:8090 and it still failed.  We then tried to access the proxy at <docker ip>:8090 from another browser - Opera, and it also failed.

We brought the docker container down and re-run (not restart) it, tried again from chrome, it still failed, but from Opera, it now worked.

Finally we changed the docker port mapping to host 8095 and container to 8080, access worked from both Chrome and Opera.

All access has failed from MS Edge???

Working with Docker

  1. Create the haproxy.cfg file, use the example script below as a template
  2. Create a shared folder within the Windows //Users/<username>/<somefolder>/<some shared folder>/, this will be used in the mount option of docker run


haproxy.cfg
# Simple configuration for an HTTP proxy listening on port 80 on all
    # interfaces and forwarding requests to a single backend "servers" with a
    # single server "server1" listening on 127.0.0.1:8000
    global
        daemon
        maxconn 256

    defaults
        mode http
        timeout connect 	5000ms
        retries                 3
		timeout http-request    10s
		timeout queue           1m
		timeout connect         10s
		timeout client          1m
		timeout server          1m
        timeout http-keep-alive 10s

    frontend http-in
        bind *:8080
        default_backend servers

    backend servers
    	mode http
    	balance roundrobin
        server server1 192.168.99.100:8081 maxconn 32
        server server2 192.168.99.100:8082 maxconn 32

To make life easy mount a shared folder to /usr/local/etc/haproxy.  Let's look at this in more detail with the command we will use the docker run haproxy

docker run -p 8095:8080 -dit --name haproxy-instance -v //c/Users/Selvyn/demo/vm_share/haproxy:/usr/local/etc/haproxy haproxy

Breakdown of command

  • -p 8095:8080 - expose the container port 8080 onto the host port of 8095 (examine the haproxy.cfg and look for the bind command)
  • --name haproxy-intance - we register this container instance with docker as haproxy-instance, useful for docker rm, stop, and docker start commands
  • -v //c/Users/Selvyn/demo/vm_share/haproxy - host folder that we will mapped to a mounted folder within the container
  • -v :/usr/local/etc/haproxy - the mounted folder within the container

Using mounted volumes (folders/files) in docker makes it a lot easier to pass data between the host and the container environment.  Any changes in the host environemt to a folder and all its contents, or a file are immediately reflected in the container environemt.  Conversley any changes in the container environemt to a folder and all its contents, or a file are immediately reflected in the host environment.

Adding Sticky Sessions to the server's behaviour

ONe of my main reasons for moving to HAProxy was to enable HTTP session management at as little cost as possible.  The following is a description of how to enable this in HAProxy.

If you want web sessions to have persistent connections to the same server, you can use a balance algorithm such as 

hdr, rdp-cookie, source, uri, orurl_param.

If your implementation requires the use of the 

leastconn, roundrobin, or static-rr algorithm,

you can implement session persistence by using server-dependent cookies.

To enable session persistence for all pages on a web server, use the cookie directive to define the name of the cookie to be inserted and add the cookie option and server name to the server lines, for example:

 	cookie WEBSVR insert
    server websvr1 192.168.1.71:80 weight 1 maxconn 512 cookie 1 check
    server websvr2 192.168.1.72:80 weight 1 maxconn 512 cookie 2 check

HAProxy includes an additional Set-Cookie: header that identifies the web server in its response to the client, for example: Set-Cookie: WEBSVR=N; path=page_path. If a client subsequently specifies the WEBSVR cookie in a request, HAProxy forwards the request to the web server whose server cookie value matches the value of WEBSVR.

The following example demonstrates how an inserted cookie ensures session persistence:

Shell scripting example

$ while true; do curl http://10.0.0.10; sleep 1; done
This is HTTP server websvr1 (192.168.1.71).
This is HTTP server websvr2 (192.168.1.72).
This is HTTP server websvr1 (192.168.1.71).
^C
$ curl http://10.0.0.10 -D /dev/stdout
HTTP/1.1 200 OK
Date: ...
Server: Apache/2.4.6 ()
Last-Modified: ...
ETag: "26-5125afd089491"
Accept-Ranges: bytes
Content-Length: 38
Content-Type: text/html; charset=UTF-8
Set-Cookie: WEBSVR=2; path=/

This is HTTP server svr2 (192.168.1.72).
$ while true; do curl http://10.0.0.10 --cookie "WEBSVR=2;"; sleep 1; done
This is HTTP server websvr2 (192.168.1.72).
This is HTTP server websvr2 (192.168.1.72).
This is HTTP server websvr2 (192.168.1.72).
^C


To enable persistence selectively on a web server, use thecookiedirective to specify that HAProxy should expect the specified cookie, usually a session ID cookie or other existing cookie, to be prefixed with theserver cookievalue and a~delimiter, for example:

	cookie SESSIONID prefix
    server websvr1 192.168.1.71:80 weight 1 maxconn 512 cookie 1 check
    server websvr2 192.168.1.72:80 weight 1 maxconn 512 cookie 2 check


If the value of SESSIONID is prefixed with a server cookie value, for example: Set-Cookie: SESSIONID=N~Session_ID;, HAProxy strips the prefix and delimiter from the SESSIONID cookie before forwarding the request to the web server whose server cookie value matches the prefix.

The following example demonstrates how using a prefixed cookie enables session persistence:

Shell scripting example

$ while true; do curl http://10.0.0.10 --cookie "SESSIONID=1~1234;"; sleep 1; done 
This is HTTP server websvr1 (192.168.1.71).
This is HTTP server websvr1 (192.168.1.71).
This is HTTP server websvr1 (192.168.1.71).
^C


A real web application would usually set the session ID on the server side, in which case the first HAProxy response would include the prefixed cookie in the Set-Cookie: header.