What is HAProxy ?
Tags: loadbalancer, haproxy, docker, node
Published: 2022-11-06 16:04:39


## Target Audience
  
I've aimed this article at people who want to learn about HAProxy, Docker, Containers, Node.js.

## Learning Objectives

After completing this article, you will know how to do the following:

-   Create a simple node.js application and docker image for it.
-   Create and setup HAProxy configuration and docker image for it
-   Setup `docker-compose.yml` configuration.
-   Use docker compose to start the entire application.
-   Make API calls to test the HAProxy.


## What is HAProxy ?

### Wiki Definition

HAProxy is **a free and open source software that provides a high availability load balancer and reverse proxy for TCP and HTTP-based applications that spreads requests across multiple servers**.It is written in C and has a reputation for being fast and efficient (in terms of processor and memory usage). [src](https://en.wikipedia.org/wiki/HAProxy)


## Configuration

### Frontend

When HAProxy is used as a reverse proxy in front of  backend servers, a `frontend` section defines the IP addresses and ports that clients can connect to. You may add as many `frontend` sections as needed to expose various websites or applications to the internet. [src](https://www.haproxy.com/documentation/hapee/latest/configuration/config-sections/frontend/)


```bash
# haproxy.cfg file

frontend myapp
	mode http # proxy mode --> http(layer 7), tcp(layer 4)
	bind *:80 # listen all ip adresses with port number 80
	default_backend myserver # default pool of backend servers
...

```

	The "mode" of the "backend" section that receives traffic from a "frontend" section should be the same. The configuration won't work if there's a mismatch.


### Backend

HAProxy frontend sections accept incoming connections that can then be forwarded to a pool of servers. The `backend` section is where those pools of servers that will service requests are defined.[src](https://www.haproxy.com/documentation/hapee/latest/configuration/config-sections/backend/)

```bash

# haproxy.cfg file

#backend section
backend myserver
	mode http # mode has to be same as frontend
	server s1 localhost:11
	server s2 localhost:22
	server s3 localhost:33
	

```

### Health Checking

HAProxy keeps only healthy servers in the load-balancing rotation. It checks the status of each server by using one of the health checking modes described in this section.


You can see the example configuration for health check below:

```bash
backend myserver
	mode http
	option httpchk # enable http health check 
	
	server s1 localhost:80 check
	server s2 localhost:81 check
	server s3 localhost:82 check
```

### Timeout

	If you’re unsure as to what it means, it’s simple: when hitting a timeout, your first reaction should **not** be to increase the timeout. You should investigate why something is taking so long that you hit a timeout somewhere and **fix the root cause**

You can check out the basic HAProxy timeouts in the table below:

| name            | explanation                                                                   |
| --------------- | ----------------------------------------------------------------------------- |
| timeout client  | Set the maximum inactivity time on the client side.                           |
| timeout connect | Set the maximum time to wait for a connection attempt to a server to succeed. |
| timeout server  | Set the maximum inactivity time on the server side.                           | 


### Configuration file for this demo

We will use this configuration for demo.

```bash
defaults
	mode http
	timeout client 10s
	timeout server 10s

frontend f
	bind *:80
	default_backend servers

backend b
	server s1 127.0.0.1:1111
	server s2 127.0.0.1:2222
	server s3 127.0.0.1:2222

```


## Creating simple HTTP server

We will create a simple HTTP web sever using node.js and **http** library.

Take the following steps to create node.js application:
1- create app folder
2- create index.js file and copy & paste the following code
- `index.js` file:
```js
const http = require("http");

const defaultPort = 1111;
const portNumber = process.env.PORT || defaultPort;

const httpServer = http.createServer((req, res) => {
  res.statusCode = 200;
  res.end("Hi, listening on port: " + portNumber);
});

httpServer.listen(portNumber, "0.0.0.0", () => {
  console.log(httpServer.address());
});
```

3- Create `package.json`

You can create `package.json` file using the following command

```bash
npm init -y # yes to skip every question
```
4- install the `http` library

You can install a node package using `npm install` command

```bash
npm i http
```

## Dockerfile for HAProxy and Node.js App

We will use docker to run HAProxy and Node.js servers, and we will test our application using simple HTTP requests. 


### Dockerfile for Node App

You can copy & paste the following snippet to create a docker image for node.js application.

```Dockerfile
FROM node:13
WORKDIR /home/node/app
COPY app /home/node/app
RUN npm install
CMD npm run app
```

To create a docker image, use the following command:

```bash
docker build -t nodeapp .
```

### Dockerfile for HAProxy

```Dockerfile
FROM haproxy
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
CMD ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg"]
```


To create a docker image, use the following command:

```bash
docker build -t haproxy .
```

### Test Docker images

You can test docker images using `docker run` command.

```bash
docker run -p 8080:8080 nodeapp
```
This will run our node.js application on port 8080. You can use `curl` request to test the application.

```bash
curl -v localhost:8080
```

## Docker compose

_Compose_ is a tool for defining and running multi-container _Docker_ applications. With _Compose_, you use a YAML file to configure your application's services.
[src](https://docs.docker.com/compose/)

```Dockerfile
#docker-compose.yml
version: "3"
services:
  app1:
    image: app
    environment:
      - PORT=1111
    ports:
      - "1111:1111"
  app2:
    image: app
    environment:
      - PORT=2222
    ports:
      - "2222:2222"
  app3:
    image: app
    environment:
      - PORT=3333
    ports:
      - "3333:3333"
  haproxy:
    image: haproxy
    ports:
      - "80:80"
    volumes:
      - ./haproxy:/usr/local/etc/haproxy
```

Type the following command to start entire application(node.js and haproxy server):

```bash
docker compose up
```

This will run the entire application, and now you can test the application making simple HTTP requests. HAProxy by default use round-robin algorithm to handle which servers will be selected when requests come.

Example logs in docker:
```
haproxy_test-app1-1     | { address: '0.0.0.0', family: 'IPv4', port: 1111 }
haproxy_test-app3-1     | { address: '0.0.0.0', family: 'IPv4', port: 3333 }
haproxy_test-app2-1     | { address: '0.0.0.0', family: 'IPv4', port: 2222 }
```

As you can see, we can see that our three node.js applications started, and we can make request.


You can see the codes here:

https://github.com/eminvergil/haproxy-test