HAProxy
1. Overview
HAProxy is a reliable and high-performance load balancer that can operate on the TCP and HTTP networking layers it's a very popular free and open-source software load balancer that powers many distributed systems web applications and websites and is considered to be almost a standard for load balancers in the industry. It is actually very easy to set up but despite its simplicity and has a lot of very advanced features and capabilities which are so important for production systems. Naturally, it officially supports only Linux.
2. Configuration
HAProxy's logic comes from a config file (haproxy.cfg)
- Predefined location: /usr/local/etc/haproxy/haproxy.cfg
- Command line: haproxy -f haproxy.cfg
2.1 Sections
2.1.1 global section
Parameters for the entire load balancing process (OS-specific)
2.1.2 Proxies section
Parameters for proxying incoming traffic to our backend cluster
- defaults: optional default subsection where we can set default parameters for all of our networking proxies
- frontend: Describes the listening sockets for all the incoming requests from the client and the logic on how to handle
- backend: describes a set of servers that participate in our back-end cluster
- listen: optional frontend + backend
3. Load Balancing
global
maxconn 512
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend servers
backend servers
balance roundrobin
server server1 127.0.0.1:8000 weight 2
server server2 127.0.0.1:8001 weight 2
server server3 127.0.0.1:8002 weight 1
3. High Availability
global
maxconn 512
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend servers
backend servers
balance roundrobin
option httpchk GET /status
http-check expect string "Server is alive"
server server1 127.0.0.1:8000 check inter 1s
server server2 127.0.0.1:8001 check inter 1s
server server3 127.0.0.1:8002 check inter 1s
4. Monitoring
global
maxconn 512
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend servers
backend servers
balance roundrobin
option httpchk GET /status
http-check expect string "Server is alive"
server server1 127.0.0.1:8000 check inter 1s
server server2 127.0.0.1:8001 check inter 1s
server server3 127.0.0.1:8002 check inter 1s
listen stats_page
bind *:83
stats enable
stats uri /
5. Advanced Routing (ACLs)
global
maxconn 512
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
acl even path_end -i /even
acl odd path_end -i /odd
use_backend even_application_nodes if even
use_backend odd_application_nodes if odd
backend odd_application_nodes
balance roundrobin
option httpchk GET /status
http-check expect string "Server is alive"
server server1 127.0.0.1:8000 check
server server3 127.0.0.1:8002 check
backend even_application_ndoes
balance roundrobin
option httpchk GET /status
http-check expect string "Server is alive"
server server2 127.0.0.1:8001 check
listen stats_page
bind *:83
stats enable
stats uri /
6. Layer 4 and 7 Load Balancing
global
maxconn 512
defaults
mode tcp
timeout connect 10s
timeout client 50s
timeout server 50s
frontend http-in
bind *:80
default_backend application_nodes
backend application_nodes
balance roundrobin
option httpchk GET /status
http-check expect string "Server is alive"
server server1 127.0.0.1:8000 check
server server1 127.0.0.1:8001 check
server server3 127.0.0.1:8002 check
listen stats_page
bind *:83
stats enable
stats uri /
After switching to TCP, We open the browser and keep sending your requests to localhost suddenly all our requests are going to the same server instead of each request being sent to a different one. The reason for this behavior is any time we refresh the browser we actually send a new HTTP GET request to an HAProxy. However, all those requests are still sent on the same TCP connection. Since our load balancer now does not understand HTTP as far as its concerned all the TCP packets belong to the same stream and hence will be sent to the same server. To break the TCP connection and open a new one for each request we need to close the web browser entirely and once we open a new browser instance and send a new request to localhost and new TCP connection will be established. Now, this new connection will be routed to a new back-end server based on the round-robin policy.
7. Running HAProxy with Docker
The great part about docker for us is for platforms like Windows that do not have the same resource isolation features like Linux docker will run a single Linux virtual machine behind the scenes for us so we can run the same commands in the same containers on any platform.
7.1 Example
7.1.1 Docker Image for Sample Project
FROM maven:3.6.1-jdk-11 AS MAVEN_TOOL_CHAIN_CONTAINER
RUN mkdir src
COPY src /tmp/src
COPY ./pom.xml /tmp/
WORKDIR /tmp/
RUN mvn package
RUN ls -la /tmp
FROM openjdk:11
COPY --from=MAVEN_TOOL_CHAIN_CONTAINER /tmp/target/webapp-1.0-SNAPSHOT-jar-with-dependencies.jar /tmp/
WORKDIR /tmp/
ENTRYPOINT ["java","-jar", "webapp-1.0-SNAPSHOT-jar-with-dependencies.jar"]
CMD ["80", "Server Name"]
7.1.2 HAProxy Image
FROM haproxy:1.7
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
ENTRYPOINT ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg"]
7.1.3 HAProxy Configuration
global
maxconn 500
defaults
mode http
timeout connect 10s
timeout client 50s
timeout server 50s
frontend http-in
bind *:80
default_backend application_nodes
backend application_nodes
balance roundrobin
option httpchk GET /status
http-check expect string "Server is alive"
server server01 app1:9001 check inter 1s
server server02 app2:9002 check inter 2s
server server03 app3:9003 check inter 2s
listen stats
bind *:83
stats enable
stats uri /
8. Reference
https://en.wikipedia.org/wiki/HAProxy
https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#7.1