# Configuração e Customização HAPROXY

# 2 Ways to Enable Sticky Sessions in HAProxy (Guide)

Link: [https://www.haproxy.com/blog/enable-sticky-sessions-in-haproxy](https://www.haproxy.com/blog/enable-sticky-sessions-in-haproxy)

<div class="flex justify-start" id="bkmrk-hypertext-transfer-p" style="text-align: justify;"><main class="post-content anchorise shrink xl:w-[780px] xl:min-w-[780px] xl:pl-default xl:mx-0 mx-auto overflow-x-hidden">HyperText Transfer Protocol (HTTP), the protocol that defines the language browsers use to communicate with web servers, is stateless, meaning that after you make a web request and a server sends back a response, no memory of that interaction remains. Websites need other ways to remember previous user interactions to make anything more sophisticated than a static web page work.

These days, Javascript frameworks like Vue.js, React.js, and others let developers create single-page applications (SPAs) that provide statefulness to the otherwise stateless web. Because they’re implemented as Javascript applications running in the user’s browser, they can keep track of what the user has done and render the app in a way that accounts for that shared history. However, the user’s browser

</main></div>Nick creates technical content for HAProxy Technologies ranging from documentation and blog posts to Wikipedia articles, GitHub READMEs and Stack Overflow answers. With a background in web development and DevOps, he has fun digging into product features and discovering the optimal path for a new blog tutorial.

<div class="shrink xl:w-[780px] xl:min-w-[780px] lg:px-default xl:mx-0 mx-auto" id="bkmrk-twitter%C2%A0linkedin%C2%A0git" style="text-align: justify;"><div class="max-w-[720px] mx-auto"><div class="pb-8 my-8 border-t-2 border-b-2"><div class="space-y-8"><div class="flex flex-row flex-wrap md:flex-nowrap"><div class="md:pl-default"><div>  
</div>[Twitter](https://twitter.com/NickMRamirez " on Twitter") [LinkedIn](https://www.linkedin.com/in/nick-ramirez-1b44624 " on LinkedIn") [GitHub](https://github.com/NickMRamirez " on GitHub")</div></div></div></div><div class="my-default">  
</div></div></div>## Related Posts

<div class="shrink xl:w-[780px] xl:min-w-[780px] lg:px-default xl:mx-0 mx-auto" id="bkmrk-september-14th%2C-2021"><div class="max-w-[720px] mx-auto"><div class="my-default" style="text-align: justify;">  
</div><div class="grid gap-6 md:grid-cols-2" style="text-align: justify;" x-intersect:enter="showSidebars = false" x-intersect:leave="showSidebars = true"><article class="flex flex-col mb-10 md:mb-0"><div class="">[![How to Enable Health Checks in HAProxy (Guide)](https://cdn.haproxy.com/img/containers/partner_integrations/haproxy_basics_-_health_checks.png/1e65a99af8752c8b986df775b5cde780/haproxy_basics_-_health_checks.webp)](https://www.haproxy.com/blog/how-to-enable-health-checks-in-haproxy)</div><div class="pt-2 px-3 flex flex-col justify-between flex-1"><div><time class="mb-1 text-xs " datetime="2021-09-14">September 14th, 2021</time></div></div>#### [How to Enable Health Checks in HAProxy (Guide)](https://www.haproxy.com/blog/how-to-enable-health-checks-in-haproxy)

HAProxy provides three types of health checks: active health checks, passive health checks and agent health checks. In this blog, we will learn about each one.

</article><article class="flex flex-col mb-10 md:mb-0"><div class="">[![2 Ways to Implement a Circuit Breaker in HAProxy (Guide)](https://cdn.haproxy.com/img/containers/partner_integrations/circuit-breaking-in-haproxy.png/837b6517d3385d8494d9b04e5537c20f/circuit-breaking-in-haproxy.webp)](https://www.haproxy.com/blog/circuit-breaking-haproxy)</div><div class="pt-2 px-3 flex flex-col justify-between flex-1"><div><time class="mb-1 text-xs " datetime="2021-01-05">January 5th, 2021</time></div></div>#### [2 Ways to Implement a Circuit Breaker in HAProxy (Guide)](https://www.haproxy.com/blog/circuit-breaking-haproxy)

In this blog post, you'll learn how to implement a circuit breaker with HAProxy in a simple and more complex way, that allows greater customization.

</article><article class="flex flex-col mb-10 md:mb-0"><div class="">[![Load Balancing, Affinity, Persistence & Sticky Sessions](https://cdn.haproxy.com/img/containers/partner_integrations/haproxy-blog.png/0f70f491cf1d494c1a33904b1fc4ae2d/haproxy-blog.webp)](https://www.haproxy.com/blog/load-balancing-affinity-persistence-sticky-sessions-what-you-need-to-know)</div><div class="pt-2 px-3 flex flex-col justify-between flex-1"><div><time class="mb-1 text-xs " datetime="2012-03-29">March 29th, 2012</time></div></div>#### [Load Balancing, Affinity, Persistence &amp; Sticky Sessions](https://www.haproxy.com/blog/load-balancing-affinity-persistence-sticky-sessions-what-you-need-to-know)

This blog post shows why and how to use a load balancer, the differences between Affinity, Persistence, and Sticky Sessions, and how to enable them in HAProxy.

</article><article class="flex flex-col mb-10 md:mb-0"><div class="" style="text-align: justify;">[![Preserve Source IP Address Despite Reverse Proxies (Guide)](https://cdn.haproxy.com/img/containers/partner_integrations/haproxy-blog.png/0f70f491cf1d494c1a33904b1fc4ae2d/haproxy-blog.webp)](https://www.haproxy.com/blog/preserve-source-ip-address-despite-reverse-proxies)</div><div class="pt-2 px-3 flex flex-col justify-between flex-1" style="text-align: justify;"><div><time class="mb-1 text-xs " datetime="2012-06-05">June 5th, 2012</time></div></div>#### [Preserve Source IP Address Despite Reverse Proxies (Guide)](https://www.haproxy.com/blog/preserve-source-ip-address-despite-reverse-proxies)

In this blog post, we aim to demonstrate how to preserve source IP addresses when using the reverse-proxy mode in HAProxy.

</article></div></div></div>

# HAPROXY com persistência de URL

Informações geradas pelo ChatGPT.

<article class="w-full text-token-text-primary" data-scroll-anchor="false" data-testid="conversation-turn-14" dir="auto" id="bkmrk-sim%2C-%C3%A9-poss%C3%ADvel-conf">Sim, é possível configurar o HAProxy para atender tanto **HTTP** (porta 80) quanto **HTTPS** (porta 443) ao mesmo tempo. Isso pode ser feito configurando dois **frontends separados** — um para HTTP e outro para HTTPS — e ambos encaminhando as requisições para o mesmo backend, de modo que o balanceamento de carga e a persistência por URL sejam aplicados de maneira uniforme para ambos os protocolos.

### Exemplo de Configuração para HTTP e HTTPS ao Mesmo Tempo

Aqui está um exemplo de configuração onde o HAProxy aceita tráfego HTTP e HTTPS e roteia as requisições para o mesmo backend, mantendo a persistência baseada na URL para ambos os protocolos:

<div class="text-base my-auto mx-auto py-5 px-6"><div class="mx-auto flex flex-1 text-base gap-4 md:gap-5 lg:gap-6 md:max-w-3xl group/turn-messages focus-visible:outline-none" tabindex="-1"><div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4"><div class="relative flex-col gap-1 md:gap-3"><div class="flex max-w-full flex-col flex-grow"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" data-message-author-role="assistant" data-message-id="caa12d07-b325-4f49-b98c-3d8df5313e04" data-message-model-slug="gpt-4o-mini" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]"><div class="markdown prose w-full break-words dark:prose-invert dark"><div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary dark:bg-token-main-surface-secondary select-none rounded-t-[5px]">haproxy</div><div class="sticky top-9"><div class="absolute bottom-0 right-0 flex h-9 items-center pr-2"><div class="flex items-center rounded bg-token-sidebar-surface-primary px-2 font-sans text-xs text-token-text-secondary dark:bg-token-main-surface-secondary"><span class="" data-state="closed"><button aria-label="Copiar" class="flex gap-1 items-center select-none px-4 py-1"><svg class="icon-xs" fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M7 5C7 3.34315 8.34315 2 10 2H19C20.6569 2 22 3.34315 22 5V14C22 15.6569 20.6569 17 19 17H17V19C17 20.6569 15.6569 22 14 22H5C3.34315 22 2 20.6569 2 19V10C2 8.34315 3.34315 7 5 7H7V5ZM9 7H14C15.6569 7 17 8.34315 17 10V15H19C19.5523 15 20 14.5523 20 14V5C20 4.44772 19.5523 4 19 4H10C9.44772 4 9 4.44772 9 5V7ZM5 9C4.44772 9 4 9.44772 4 10V19C4 19.5523 4.44772 20 5 20H14C14.5523 20 15 19.5523 15 19V10C15 9.44772 14.5523 9 14 9H5Z" fill="currentColor" fill-rule="evenodd"></path></svg>Copiar</button></span></div></div></div><div class="overflow-y-auto p-4" dir="ltr">`global`</div><div class="overflow-y-auto p-4" dir="ltr">`    log /dev/log local0`</div><div class="overflow-y-auto p-4" dir="ltr">`    log /dev/log local1 `</div><div class="overflow-y-auto p-4" dir="ltr">`    notice    maxconn 2000`</div><div class="overflow-y-auto p-4" dir="ltr">  
</div><div class="overflow-y-auto p-4" dir="ltr">`defaults`</div><div class="overflow-y-auto p-4" dir="ltr">`     log     global`</div><div class="overflow-y-auto p-4" dir="ltr">`     option  httplog`</div><div class="overflow-y-auto p-4" dir="ltr">`     option  dontlognull`</div><div class="overflow-y-auto p-4" dir="ltr">`     timeout connect 5000ms`</div><div class="overflow-y-auto p-4" dir="ltr">`     timeout client  50000ms`</div><div class="overflow-y-auto p-4" dir="ltr">`     timeout server  50000ms`</div><div class="overflow-y-auto p-4" dir="ltr">  
</div><div class="overflow-y-auto p-4" dir="ltr">`# Frontend HTTP (porta 80)`</div><div class="overflow-y-auto p-4" dir="ltr">`frontend http_front`</div><div class="overflow-y-auto p-4" dir="ltr"> `    bind *:80 `</div><div class="overflow-y-auto p-4" dir="ltr">`     acl is_http_request path_reg ^/.*  # Captura qualquer URL`</div><div class="overflow-y-auto p-4" dir="ltr">`     use_backend default_backend if is_http_request`</div><div class="overflow-y-auto p-4" dir="ltr">  
</div><div class="overflow-y-auto p-4" dir="ltr">`# Frontend HTTPS (porta 443)`</div><div class="overflow-y-auto p-4" dir="ltr">`frontend https_front`</div><div class="overflow-y-auto p-4" dir="ltr"> `bind *:443 ssl crt /etc/ssl/certs/meucertificado.pem  # Certificado SSL`</div><div class="overflow-y-auto p-4" dir="ltr"> `    acl is_https_request path_reg ^/.*  # Captura qualquer URL`</div><div class="overflow-y-auto p-4" dir="ltr"> `    use_backend default_backend if is_https_request`</div><div class="overflow-y-auto p-4" dir="ltr">  
</div><div class="overflow-y-auto p-4" dir="ltr">`# Backend`</div><div class="overflow-y-auto p-4" dir="ltr"> `backend default_backend`</div><div class="overflow-y-auto p-4" dir="ltr"> `balance roundrobin `</div><div class="overflow-y-auto p-4" dir="ltr">`   stick-table type string size 200k expire 30m store gpc0`</div><div class="overflow-y-auto p-4" dir="ltr"> `  stick on url_param(url)  # Persistência baseada na URL`</div><div class="overflow-y-auto p-4" dir="ltr"> `    cookie SERVERID insert indirect nocache`</div><div class="overflow-y-auto p-4" dir="ltr"> ` server server1 192.168.1.1:80 check  # Servidor HTTP`</div><div class="overflow-y-auto p-4" dir="ltr"> `    server server2 192.168.1.2:80 check  # Servidor HTTP`</div><div class="overflow-y-auto p-4" dir="ltr"> `    server server3 192.168.1.3:80 check  # Servidor HTTP `</div></div></div></div></div></div></div></div></div></div>### Explicação da Configuração:

<div class="text-base my-auto mx-auto py-5 px-6"><div class="mx-auto flex flex-1 text-base gap-4 md:gap-5 lg:gap-6 md:max-w-3xl group/turn-messages focus-visible:outline-none" tabindex="-1"><div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4"><div class="relative flex-col gap-1 md:gap-3"><div class="flex max-w-full flex-col flex-grow"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" data-message-author-role="assistant" data-message-id="caa12d07-b325-4f49-b98c-3d8df5313e04" data-message-model-slug="gpt-4o-mini" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]"><div class="markdown prose w-full break-words dark:prose-invert dark">1. **Frontend HTTP**:
    
    
    - **`bind *:80`**: O frontend escuta na porta 80 para tráfego HTTP.
    - **`acl is_http_request path_reg ^/.*`**: A ACL captura qualquer URL para redirecioná-la para o backend.
    - **`use_backend default_backend if is_http_request`**: Se a requisição for HTTP, ela é direcionada para o `default_backend`.
    -
2. **Frontend HTTPS**:
    
    
    - **`bind *:443 ssl crt /etc/ssl/certs/meucertificado.pem`**: Este frontend escuta na porta 443 para tráfego HTTPS e usa o certificado SSL fornecido no arquivo `/etc/ssl/certs/meucertificado.pem`.
    - **`acl is_https_request path_reg ^/.*`**: A ACL captura qualquer URL, assim como no frontend HTTP.
    - **`use_backend default_backend if is_https_request`**: Se a requisição for HTTPS, ela é direcionada para o `default_backend`.
    -
3. **Backend**:
    
    
    - O **backend** é compartilhado entre ambos os frontends HTTP e HTTPS, com **persistência baseada na URL** usando o `stick-table` para garantir que as requisições para a mesma URL sejam sempre roteadas para o **mesmo servidor**.
    - **`cookie SERVERID insert indirect nocache`**: Garante a persistência usando cookies.
    - Os servidores backend continuam na porta **80** (HTTP), porque o HAProxy está fazendo a **terminação SSL**.

</div></div></div></div></div></div></div></div>### Como Funciona:

<div class="text-base my-auto mx-auto py-5 px-6"><div class="mx-auto flex flex-1 text-base gap-4 md:gap-5 lg:gap-6 md:max-w-3xl group/turn-messages focus-visible:outline-none" tabindex="-1"><div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4"><div class="relative flex-col gap-1 md:gap-3"><div class="flex max-w-full flex-col flex-grow"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" data-message-author-role="assistant" data-message-id="caa12d07-b325-4f49-b98c-3d8df5313e04" data-message-model-slug="gpt-4o-mini" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]"><div class="markdown prose w-full break-words dark:prose-invert dark">1. **Requisição HTTP**:
    
    
    - O cliente acessa a URL usando `http://meusite.com/pagina1`.
    - O HAProxy recebe a requisição na porta **80** e a encaminha para um dos servidores backend (`server1`, `server2`, `server3`), com base no balanceamento de carga `roundrobin`.
    - O servidor backend responde, e o HAProxy registra a URL `/pagina1` e o servidor que a atendeu.
2. **Requisição HTTPS**:
    
    
    - O cliente acessa a URL usando `https://meusite.com/pagina1`.
    - O HAProxy recebe a requisição na porta **443**, faz a descriptografia SSL e encaminha para o mesmo backend (porta 80), utilizando o mesmo servidor que atendeu a primeira requisição HTTP para aquela URL.
    - O HAProxy verifica a tabela de stickiness e direciona a requisição para o **mesmo servidor** que atendeu a primeira requisição, mantendo a persistência.
3. **Persistência para Todos os Usuários**:
    
    
    - A persistência é mantida independentemente de ser uma requisição HTTP ou HTTPS, porque a persistência está baseada na URL (`stick on url_param(url)`), não em cookies ou IP do cliente.
    - Todos os usuários que acessarem a mesma URL (seja por HTTP ou HTTPS) serão direcionados para o **mesmo servidor backend** que atendeu a primeira requisição para aquela URL.

</div></div></div></div></div></div></div></div>### Certificado SSL:

O caminho `/etc/ssl/certs/meucertificado.pem` deve apontar para o **certificado SSL** e a **chave privada** do seu domínio. Se você estiver usando o Let's Encrypt ou outra autoridade certificadora (CA), basta colocar o arquivo `.pem` correto que contenha o **certificado público** e a **chave privada**.

Caso você precise gerar um certificado SSL autoassinado, pode usar o seguinte comando `openssl`:

<div class="text-base my-auto mx-auto py-5 px-6"><div class="mx-auto flex flex-1 text-base gap-4 md:gap-5 lg:gap-6 md:max-w-3xl group/turn-messages focus-visible:outline-none" tabindex="-1"><div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4"><div class="relative flex-col gap-1 md:gap-3"><div class="flex max-w-full flex-col flex-grow"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" data-message-author-role="assistant" data-message-id="caa12d07-b325-4f49-b98c-3d8df5313e04" data-message-model-slug="gpt-4o-mini" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]"><div class="markdown prose w-full break-words dark:prose-invert dark"><div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary dark:bg-token-main-surface-secondary select-none rounded-t-[5px]">bash</div><div class="sticky top-9"><div class="absolute bottom-0 right-0 flex h-9 items-center pr-2"><div class="flex items-center rounded bg-token-sidebar-surface-primary px-2 font-sans text-xs text-token-text-secondary dark:bg-token-main-surface-secondary"><span class="" data-state="closed"><button aria-label="Copiar" class="flex gap-1 items-center select-none px-4 py-1"><svg class="icon-xs" fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M7 5C7 3.34315 8.34315 2 10 2H19C20.6569 2 22 3.34315 22 5V14C22 15.6569 20.6569 17 19 17H17V19C17 20.6569 15.6569 22 14 22H5C3.34315 22 2 20.6569 2 19V10C2 8.34315 3.34315 7 5 7H7V5ZM9 7H14C15.6569 7 17 8.34315 17 10V15H19C19.5523 15 20 14.5523 20 14V5C20 4.44772 19.5523 4 19 4H10C9.44772 4 9 4.44772 9 5V7ZM5 9C4.44772 9 4 9.44772 4 10V19C4 19.5523 4.44772 20 5 20H14C14.5523 20 15 19.5523 15 19V10C15 9.44772 14.5523 9 14 9H5Z" fill="currentColor" fill-rule="evenodd"></path></svg>Copiar</button></span></div></div></div><div class="overflow-y-auto p-4" dir="ltr">`openssl req -x509 -newkey rsa:2048 -keyout /etc/ssl/private/meuchave.key -out /etc/ssl/certs/meucertificado.pem -days 365`</div></div></div></div></div></div></div></div></div></div>Esse comando gera um certificado **autoassinado**, o que é útil para testes ou ambientes internos, mas para produção, recomenda-se o uso de um certificado de uma autoridade certificadora (CA) confiável.

<div class="text-base my-auto mx-auto py-5 px-6"><div class="mx-auto flex flex-1 text-base gap-4 md:gap-5 lg:gap-6 md:max-w-3xl group/turn-messages focus-visible:outline-none" tabindex="-1"><div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4"><div class="relative flex-col gap-1 md:gap-3"><div class="flex max-w-full flex-col flex-grow"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" data-message-author-role="assistant" data-message-id="caa12d07-b325-4f49-b98c-3d8df5313e04" data-message-model-slug="gpt-4o-mini" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]"><div class="markdown prose w-full break-words dark:prose-invert dark">---

</div></div></div></div></div></div></div></div>### Conclusão:

<div class="text-base my-auto mx-auto py-5 px-6"><div class="mx-auto flex flex-1 text-base gap-4 md:gap-5 lg:gap-6 md:max-w-3xl group/turn-messages focus-visible:outline-none" tabindex="-1"><div class="group/conversation-turn relative flex w-full min-w-0 flex-col agent-turn @xs/thread:px-0 @sm/thread:px-1.5 @md/thread:px-4"><div class="relative flex-col gap-1 md:gap-3"><div class="flex max-w-full flex-col flex-grow"><div class="min-h-8 text-message relative flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" data-message-author-role="assistant" data-message-id="caa12d07-b325-4f49-b98c-3d8df5313e04" data-message-model-slug="gpt-4o-mini" dir="auto"><div class="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]"><div class="markdown prose w-full break-words dark:prose-invert dark">- **Sim, o HAProxy pode atender tanto HTTP quanto HTTPS ao mesmo tempo**, com a mesma configuração de persistência por URL para ambos os protocolos.
- A configuração utiliza dois **frontends** separados (um para HTTP e outro para HTTPS), mas ambos encaminham as requisições para o **mesmo backend**, garantindo que a persistência de URL seja aplicada de forma consistente, independentemente do protocolo.
- A **terminação SSL** acontece no HAProxy, o que significa que ele descriptografa as requisições HTTPS e as envia como HTTP para os servidores backend.

</div></div></div></div><div class="flex absolute left-0 right-0 flex justify-start">  
</div></div><div class="absolute"><div class="flex items-center justify-center"><svg class="icon-md" fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7.5 13.25C7.98703 13.25 8.45082 13.1505 8.87217 12.9708C8.46129 14.0478 7.62459 15.5792 6.35846 15.76C5.81173 15.8382 5.43183 16.3447 5.50993 16.8914C5.58804 17.4382 6.09457 17.8181 6.6413 17.7399C9.19413 17.3753 10.7256 14.4711 11.169 12.1909C11.4118 10.942 11.3856 9.58095 10.8491 8.44726C10.2424 7.16517 8.92256 6.24402 7.48508 6.25001C5.55895 6.25805 4 7.82196 4 9.74998C4 11.683 5.567 13.25 7.5 13.25Z" fill="currentColor"></path><path d="M16.18 13.25C16.667 13.25 17.1308 13.1505 17.5522 12.9708C17.1413 14.0478 16.3046 15.5792 15.0385 15.76C14.4917 15.8382 14.1118 16.3447 14.1899 16.8914C14.268 17.4382 14.7746 17.8181 15.3213 17.7399C17.8741 17.3753 19.4056 14.4711 19.849 12.1909C20.0918 10.942 20.0656 9.58095 19.5291 8.44726C18.9224 7.16517 17.6026 6.24402 16.1651 6.25001C14.2389 6.25805 12.68 7.82196 12.68 9.74998C12.68 11.683 14.247 13.25 16.18 13.25Z" fill="currentColor"></path></svg></div></div></div></div></div></article><article class="w-full text-token-text-primary" data-scroll-anchor="false" data-testid="conversation-turn-15" dir="auto" id="bkmrk-%C2%A0">Se você deseja configurar o HAProxy para rotear tráfego em **portas diferentes de 80 (HTTP)** e **443 (HTTPS)**, o procedimento é semelhante, mas você deve ajustar a configuração de **frontends** e **backends** para escutar nas portas específicas que deseja utilizar.

A lógica de balanceamento de carga e persistência por URL será a mesma, mas em vez de usar as portas padrão (80 e 443), você configurará o HAProxy para escutar nas portas que você escolher.

### Exemplo de Configuração para Portas Diferentes:

Vamos imaginar que você deseja usar as portas **8080** para HTTP e **8443** para HTTPS, em vez das portas padrão (80 e 443). A configuração seria:

</article><div id="bkmrk-global">global</div><div id="bkmrk-%C2%A0-%C2%A0-log-%2Fdev%2Flog-loc"> log /dev/log local0</div><div id="bkmrk-%C2%A0-%C2%A0-log-%2Fdev%2Flog-loc-1"> log /dev/log local1 notice</div><div id="bkmrk-%C2%A0-%C2%A0-maxconn-2000"> maxconn 2000</div><div id="bkmrk-">  
</div><div id="bkmrk-defaults">defaults</div><div id="bkmrk-%C2%A0-%C2%A0-log%C2%A0-%C2%A0-%C2%A0global"> log global</div><div id="bkmrk-%C2%A0-%C2%A0-option%C2%A0-httplog"> option httplog</div><div id="bkmrk-%C2%A0-%C2%A0-option%C2%A0-dontlogn"> option dontlognull</div><div id="bkmrk-%C2%A0-%C2%A0-timeout-connect-"> timeout connect 5000ms</div><div id="bkmrk-%C2%A0-%C2%A0-timeout-client%C2%A0-"> timeout client 50000ms</div><div id="bkmrk-%C2%A0-%C2%A0-timeout-server%C2%A0-"> timeout server 50000ms</div><div id="bkmrk--1">  
</div><div id="bkmrk-%23-frontend-para-http">\# Frontend para HTTP (porta 8080)</div><div id="bkmrk-frontend-http_front">frontend http_front</div><div id="bkmrk-%C2%A0-%C2%A0-bind-%2A%3A8080%C2%A0-%23-e"> bind *:8080 # Escutando na porta 8080</div><div id="bkmrk-%C2%A0-%C2%A0-acl-is_http_requ"> acl is_http_request path_reg ^/.* # Captura qualquer URL</div><div id="bkmrk-%C2%A0-%C2%A0-use_backend-defa"> use_backend default_backend if is_http_request</div><div id="bkmrk--2">  
</div><div id="bkmrk-%23-frontend-para-http-1">\# Frontend para HTTPS (porta 8443)</div><div id="bkmrk-frontend-https_front">frontend https_front</div><div id="bkmrk-%C2%A0-%C2%A0-bind-%2A%3A8443-ssl-"> bind *:8443 ssl crt /etc/ssl/certs/meucertificado.pem # Certificado SSL</div><div id="bkmrk-%C2%A0-%C2%A0-acl-is_https_req"> acl is_https_request path_reg ^/.* # Captura qualquer URL</div><div id="bkmrk-%C2%A0-%C2%A0-use_backend-defa-1"> use_backend default_backend if is_https_request</div><div id="bkmrk--3">  
</div><div id="bkmrk-%23-backend">\# Backend</div><div id="bkmrk-backend-default_back">backend default_backend</div><div id="bkmrk-%C2%A0-%C2%A0-balance-roundrob"> balance roundrobin</div><div id="bkmrk-%C2%A0-%C2%A0-stick-table-type"> stick-table type string size 200k expire 30m store gpc0</div><div id="bkmrk-%C2%A0-%C2%A0-stick-on-url_par"> stick on url_param(url) # Persistência baseada na URL</div><div id="bkmrk-%C2%A0-%C2%A0-cookie-serverid-"> cookie SERVERID insert indirect nocache</div><div id="bkmrk-%C2%A0-%C2%A0-server-server1-1"> server server1 192.168.1.1:80 check # Servidor HTTP</div><div id="bkmrk-%C2%A0-%C2%A0-server-server2-1"> server server2 192.168.1.2:80 check # Servidor HTTP</div><div id="bkmrk-%C2%A0-%C2%A0-server-server3-1"> server server3 192.168.1.3:80 check # Servidor HTTP</div><article class="w-full text-token-text-primary" data-scroll-anchor="false" data-testid="conversation-turn-15" dir="auto" id="bkmrk-explica%C3%A7%C3%A3o-das-modif"><div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary"><div class="overflow-y-auto p-4" dir="ltr"></div></div>### Explicação das Modificações:

1. **Frontend HTTP**:
    
    
    - **`bind *:8080`**: O HAProxy agora escuta na porta `8080` para tráfego HTTP, em vez de `80`.
    - **`acl is_http_request path_reg ^/.*`**: A regra ACL captura qualquer URL que for solicitada e a redireciona para o backend.
2. **Frontend HTTPS**:
    
    
    - **`bind *:8443 ssl crt /etc/ssl/certs/meucertificado.pem`**: O HAProxy escuta na porta `8443` para tráfego HTTPS e usa o certificado SSL localizado no caminho `/etc/ssl/certs/meucertificado.pem`. O certificado SSL deve ser válido, seja autoassinado ou de uma autoridade certificadora.
3. **Backend**:
    
    
    - A configuração do **backend** permanece a mesma, com balanceamento de carga baseado em `roundrobin`, persistência por URL usando `stick-table`, e a inserção do cookie `SERVERID` para garantir a persistência.
4. **Servidores Backend**:
    
    
    - **`server server1 192.168.1.1:80 check`**: Os servidores backend continuam sendo configurados na porta `80`, porque o HAProxy irá fazer a **terminação SSL** para o tráfego HTTPS e, em seguida, encaminhá-lo para os servidores backend na porta `80`.

### Fluxo de Funcionamento:

#### 1. **Requisição HTTP (porta 8080)**:

- O cliente acessa a URL `http://meusite.com:8080/pagina1`.
- O HAProxy recebe a requisição na porta `8080`, e com base no balanceamento de carga (roundrobin), encaminha a requisição para um dos servidores backend.
- O servidor que responder à primeira requisição para `/pagina1` será registrado no **stick-table** com a URL `/pagina1`, garantindo que futuras requisições para essa URL sejam roteadas para o **mesmo servidor**.

#### 2. **Requisição HTTPS (porta 8443)**:

- O cliente acessa a URL `https://meusite.com:8443/pagina1`.
- O HAProxy recebe a requisição na porta `8443`, faz a **terminação SSL** (descriptografa o tráfego HTTPS) e, em seguida, redireciona a requisição para os servidores backend na porta `80`.
- O HAProxy verifica na **stick-table** qual servidor foi associado à URL `/pagina1` e direciona a requisição para o **mesmo servidor** que atendeu a primeira requisição.

#### 3. **Persistência para Todos os Usuários**:

- Independentemente de ser uma requisição HTTP ou HTTPS, a persistência será mantida para todos os usuários com base na URL, garantindo que a URL `/pagina1` seja sempre tratada pelo **mesmo servidor**.

---

### Caso Você Queira Usar Portas Diferentes para Cada Backend:

Se você quiser que o tráfego seja roteado para diferentes portas nos servidores backend, você pode ajustar a configuração para cada servidor backend. Por exemplo, se você tiver servidores que escutam em portas diferentes, a configuração seria algo como:

<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary"><div class="overflow-y-auto p-4" dir="ltr">backend default_backend  
 balance roundrobin  
 stick-table type string size 200k expire 30m store gpc0  
 stick on url_param(url)  
 cookie SERVERID insert indirect nocache  
 server server1 192.168.1.1:8081 check # Servidor backend escutando na porta 8081  
 server server2 192.168.1.2:8082 check # Servidor backend escutando na porta 8082  
 server server3 192.168.1.3:8083 check # Servidor backend escutando na porta 8083  
</div></div>Essa configuração faria com que cada servidor backend estivesse escutando em portas diferentes, mas o HAProxy ainda aplicaria a persistência e o balanceamento de carga.

### Resumo:

- **Sim, é possível configurar o HAProxy para escutar em portas diferentes de 80 e 443** (por exemplo, 8080 para HTTP e 8443 para HTTPS).
- A persistência de URL e o balanceamento de carga continuam funcionando da mesma forma, **independentemente da porta** que está sendo utilizada.
- A configuração de **terminação SSL** no HAProxy permite que o tráfego HTTPS seja manipulado na porta `8443` e redirecionado para os servidores backend na porta `80` (ou qualquer porta configurada).

</article>

# Building a High Availability Cluster with HAProxy, Keepalived, and Docker: A Step-by-Step Guide

Link: [https://medium.com/@yahyasghiouri1998/building-a-high-availability-cluster-with-haproxy-keepalived-and-docker-a-step-by-step-guide-9325f4ac8aa7](https://medium.com/@yahyasghiouri1998/building-a-high-availability-cluster-with-haproxy-keepalived-and-docker-a-step-by-step-guide-9325f4ac8aa7)

<div class="ac cb" id="bkmrk-yahya-sghiouri-follo" style="text-align: justify;"><div class="ci bh gb gc gd ge"><div><div><div class="speechify-ignore ac cp"><div class="speechify-ignore bh m"><div class="ac hx hy hz ia ib ic id ie if ig ih"><div class="ac r ih"><div class="ac ii"><div><div aria-describedby="1" aria-hidden="false" aria-labelledby="1" class="bm"><div class="be" tabindex="-1"></div></div></div></div></div><div class="ac r iz"><div class="ac af"><span aria-hidden="true" class="m"><span class="bf b bg ab du">·</span></span><span data-testid="storyPublishDate">Aug 30, 2024</span></div></div></div><div class="ac cp jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr"><div class="i l x fi fj r"><div class="kh m"><div class="ac r ki kj"><div class="pw-multi-vote-icon fl kk kl km kn">[<div><div aria-describedby="3" aria-hidden="false" aria-labelledby="3" class="bm"><div class="be" tabindex="-1"><div class="ko ap kp kq kr ks an kt ku kv kn" role="presentation"><svg aria-label="clap" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M11.37.828 12 3.282l.63-2.454zM13.916 3.953l1.523-2.112-1.184-.39zM8.589 1.84l1.522 2.112-.337-2.501zM18.523 18.92c-.86.86-1.75 1.246-2.62 1.33a6 6 0 0 0 .407-.372c2.388-2.389 2.86-4.951 1.399-7.623l-.912-1.603-.79-1.672c-.26-.56-.194-.98.203-1.288a.7.7 0 0 1 .546-.132c.283.046.546.231.728.5l2.363 4.157c.976 1.624 1.141 4.237-1.324 6.702m-10.999-.438L3.37 14.328a.828.828 0 0 1 .585-1.408.83.83 0 0 1 .585.242l2.158 2.157a.365.365 0 0 0 .516-.516l-2.157-2.158-1.449-1.449a.826.826 0 0 1 1.167-1.17l3.438 3.44a.363.363 0 0 0 .516 0 .364.364 0 0 0 0-.516L5.293 9.513l-.97-.97a.826.826 0 0 1 0-1.166.84.84 0 0 1 1.167 0l.97.968 3.437 3.436a.36.36 0 0 0 .517 0 .366.366 0 0 0 0-.516L6.977 7.83a.82.82 0 0 1-.241-.584.82.82 0 0 1 .824-.826c.219 0 .43.087.584.242l5.787 5.787a.366.366 0 0 0 .587-.415l-1.117-2.363c-.26-.56-.194-.98.204-1.289a.7.7 0 0 1 .546-.132c.283.046.545.232.727.501l2.193 3.86c1.302 2.38.883 4.59-1.277 6.75-1.156 1.156-2.602 1.627-4.19 1.367-1.418-.236-2.866-1.033-4.079-2.246M10.75 5.971l2.12 2.12c-.41.502-.465 1.17-.128 1.89l.22.465-3.523-3.523a.8.8 0 0 1-.097-.368c0-.22.086-.428.241-.584a.847.847 0 0 1 1.167 0m7.355 1.705c-.31-.461-.746-.758-1.23-.837a1.44 1.44 0 0 0-1.11.275c-.312.24-.505.543-.59.881a1.74 1.74 0 0 0-.906-.465 1.47 1.47 0 0 0-.82.106l-2.182-2.182a1.56 1.56 0 0 0-2.2 0 1.54 1.54 0 0 0-.396.701 1.56 1.56 0 0 0-2.21-.01 1.55 1.55 0 0 0-.416.753c-.624-.624-1.649-.624-2.237-.037a1.557 1.557 0 0 0 0 2.2c-.239.1-.501.238-.715.453a1.56 1.56 0 0 0 0 2.2l.516.515a1.556 1.556 0 0 0-.753 2.615L7.01 19c1.32 1.319 2.909 2.189 4.475 2.449q.482.08.971.08c.85 0 1.653-.198 2.393-.579.231.033.46.054.686.054 1.266 0 2.457-.52 3.505-1.567 2.763-2.763 2.552-5.734 1.439-7.586z" fill-rule="evenodd"></path></svg></div></div></div></div>](https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fp%2F9325f4ac8aa7&operation=register&redirect=https%3A%2F%2Fmedium.com%2F%40yahyasghiouri1998%2Fbuilding-a-high-availability-cluster-with-haproxy-keepalived-and-docker-a-step-by-step-guide-9325f4ac8aa7&user=Yahya+sghiouri&userId=926c8b21d31f&source=---header_actions--9325f4ac8aa7---------------------clap_footer------------------)</div><div class="pw-multi-vote-count m kw kx ky kz la lb lc"><div><div aria-describedby="180" aria-hidden="false" aria-labelledby="180" class="bm"></div></div></div></div></div></div></div></div></div></div></div></div></div><div class="ml" id="bkmrk-high-availability" style="text-align: justify;"><div class="ac cb"><div class="ln mm lo mn lp mo cf mp cg mq ci bh"><figure class="mu mv mw mx my ml mz na paragraph-image"><div class="nb nc fl nd bh ne" role="button" tabindex="0"><div class="mr ms mt"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 1000px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 1100w, https://miro.medium.com/v2/resize:fit:2000/format:webp/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 2000w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 1000px" srcset="https://miro.medium.com/v2/resize:fit:640/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 640w, https://miro.medium.com/v2/resize:fit:720/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 720w, https://miro.medium.com/v2/resize:fit:750/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 750w, https://miro.medium.com/v2/resize:fit:786/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 786w, https://miro.medium.com/v2/resize:fit:828/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 828w, https://miro.medium.com/v2/resize:fit:1100/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 1100w, https://miro.medium.com/v2/resize:fit:2000/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg 2000w">![](https://miro.medium.com/v2/resize:fit:990/1*qGOZWP0mzHb2II-vrNx7Bg.jpeg)</source></source></picture></div></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">High availability</figcaption></figure></div></div></div><span class="m oj ok ol bo om on oo op oq fl">H</span>igh availability (HA) is essential for ensuring that web applications remain accessible, even in the face of hardware or software failures. An HA architecture distributes traffic across multiple servers, preventing any single point of failure from disrupting service. In this article, we’ll explore how to build a high availability cluster using HAProxy and Keepalived, two powerful tools for load balancing and failover.

To enhance flexibility and simplify deployment, we’ll leverage Docker to containerize HAProxy, Keepalived, and our web applications. Docker allows us to create a portable and easily manageable HA setup that can be deployed across different environments, whether on-premises or in the cloud.

Throughout this guide, we’ll walk you through the entire process — from setting up Docker networks and building Dockerfiles to configuring HAProxy and Keepalived for seamless failover. By the end, you’ll have a fully functional HA cluster that ensures your web applications are always available, all within a Dockerized environment.

# I-/ General Concepts

Before diving into the different configurations, it’s helpful to understand the core components of the architecture.

## I-1/HAproxy

**HAProxy**, as its name suggests, stands for High Availability Proxy. It is a robust and versatile tool designed to provide high availability and efficient load balancing for network traffic.

HAProxy is a widely used tool for distributing incoming requests across multiple backend servers to enhance both reliability and performance. It performs continuous health checks on these servers to ensure that traffic is routed only to those that are healthy and responsive.

The tool employs various algorithms, such as round-robin and least connections, to effectively balance the load. Supporting both TCP and HTTP traffic, HAProxy operates at Layer 4 (the transport layer) and Layer 7 (the application layer) of the OSI model.

There are two main components when configuring HAproxy , a frontend and a backend section.

<div class="ac cb" id="bkmrk-frontend-section%3A%C2%A0it" style="text-align: justify;"><div class="ci bh gb gc gd ge">- **Frontend section:** It is the entry point for incoming client requests. It defines how HAProxy listens for incoming traffic and how it should handle these requests, here we specify the address and port on which HAProxy should listen, as well as any rules or conditions for routing the traffic to the appropriate backend by inspecting the incoming packets.
- **Backend section:** it represents the servers that will handle the requests forwarded by the frontend. we define how HAProxy should route traffic to the backend servers and how it should manage these servers basically we specify also load balancing algorithms, and health checks. In summary we control how requests will be distributed among the servers and how we are going to handle server failures or maintenance.

<figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="nb nc fl nd bh ne" role="button" tabindex="0"><div class="mr ms qr"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*EiySIeb5Kzf6luig8UiIJw.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*EiySIeb5Kzf6luig8UiIJw.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*EiySIeb5Kzf6luig8UiIJw.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*EiySIeb5Kzf6luig8UiIJw.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*EiySIeb5Kzf6luig8UiIJw.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*EiySIeb5Kzf6luig8UiIJw.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*EiySIeb5Kzf6luig8UiIJw.png 1400w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/1*EiySIeb5Kzf6luig8UiIJw.png 640w, https://miro.medium.com/v2/resize:fit:720/1*EiySIeb5Kzf6luig8UiIJw.png 720w, https://miro.medium.com/v2/resize:fit:750/1*EiySIeb5Kzf6luig8UiIJw.png 750w, https://miro.medium.com/v2/resize:fit:786/1*EiySIeb5Kzf6luig8UiIJw.png 786w, https://miro.medium.com/v2/resize:fit:828/1*EiySIeb5Kzf6luig8UiIJw.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*EiySIeb5Kzf6luig8UiIJw.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*EiySIeb5Kzf6luig8UiIJw.png 1400w">![](https://miro.medium.com/v2/resize:fit:693/1*EiySIeb5Kzf6luig8UiIJw.png)</source></source></picture></div></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">HA proxy load balancer</figcaption></figure></div></div>## I-2/Keepalived &amp; VRRP

**VRRP (Virtual Router Redundancy Protocol)** is designed to create a virtual router that represents a group of physical routers, allowing them to work together to present a single virtual IP address (VIP) to the network. This VIP is used as the default gateway by clients.

In a VRRP setup, one router is elected as the master. The master router handles traffic directed to the VIP, while the other routers in the group act as backups and monitor the master router’s health. If the master router fails, one of the backup routers takes over as the new master, ensuring the continuity of service.

**Keepalived** is a widely used implementation of VRRP with additional features. It assigns a priority to each node in the group, and based on these priorities, it elects a new master if a failure occurs. Keepalived enhances VRRP with advanced health checks and failover capabilities, making it ideal for high-availability setups.

<div class="ac cb" id="bkmrk-virtual-ip-assignmen" style="text-align: justify;"><div class="ci bh gb gc gd ge"><figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="mr ms qx"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 363px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*NUi_6mEaRaIXi64siJ2yTQ.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*NUi_6mEaRaIXi64siJ2yTQ.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*NUi_6mEaRaIXi64siJ2yTQ.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*NUi_6mEaRaIXi64siJ2yTQ.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*NUi_6mEaRaIXi64siJ2yTQ.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NUi_6mEaRaIXi64siJ2yTQ.png 1100w, https://miro.medium.com/v2/resize:fit:726/format:webp/1*NUi_6mEaRaIXi64siJ2yTQ.png 726w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 363px" srcset="https://miro.medium.com/v2/resize:fit:640/1*NUi_6mEaRaIXi64siJ2yTQ.png 640w, https://miro.medium.com/v2/resize:fit:720/1*NUi_6mEaRaIXi64siJ2yTQ.png 720w, https://miro.medium.com/v2/resize:fit:750/1*NUi_6mEaRaIXi64siJ2yTQ.png 750w, https://miro.medium.com/v2/resize:fit:786/1*NUi_6mEaRaIXi64siJ2yTQ.png 786w, https://miro.medium.com/v2/resize:fit:828/1*NUi_6mEaRaIXi64siJ2yTQ.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*NUi_6mEaRaIXi64siJ2yTQ.png 1100w, https://miro.medium.com/v2/resize:fit:726/1*NUi_6mEaRaIXi64siJ2yTQ.png 726w">![](https://miro.medium.com/v2/resize:fit:359/1*NUi_6mEaRaIXi64siJ2yTQ.png)</source></source></picture></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Virtual IP assignment</figcaption></figure></div></div># II-/ Deployment architecture

After understanding the foundational concepts of HAProxy and Keepalived, it’s crucial to see how these components come together to form a high availability cluster.

<div class="ac cb" id="bkmrk-deployment-architect" style="text-align: justify;"><div class="ci bh gb gc gd ge"><figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="nb nc fl nd bh ne" role="button" tabindex="0"><div class="mr ms qy"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*nSEqrpGJR60jh6Wd9d6UQw.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*nSEqrpGJR60jh6Wd9d6UQw.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*nSEqrpGJR60jh6Wd9d6UQw.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*nSEqrpGJR60jh6Wd9d6UQw.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*nSEqrpGJR60jh6Wd9d6UQw.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*nSEqrpGJR60jh6Wd9d6UQw.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*nSEqrpGJR60jh6Wd9d6UQw.png 1400w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/1*nSEqrpGJR60jh6Wd9d6UQw.png 640w, https://miro.medium.com/v2/resize:fit:720/1*nSEqrpGJR60jh6Wd9d6UQw.png 720w, https://miro.medium.com/v2/resize:fit:750/1*nSEqrpGJR60jh6Wd9d6UQw.png 750w, https://miro.medium.com/v2/resize:fit:786/1*nSEqrpGJR60jh6Wd9d6UQw.png 786w, https://miro.medium.com/v2/resize:fit:828/1*nSEqrpGJR60jh6Wd9d6UQw.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*nSEqrpGJR60jh6Wd9d6UQw.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*nSEqrpGJR60jh6Wd9d6UQw.png 1400w">![Deployment Architecture](https://miro.medium.com/v2/resize:fit:693/1*nSEqrpGJR60jh6Wd9d6UQw.png)</source></source></picture></div></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Deployment architecture</figcaption></figure></div></div>The architecture I’ve implemented leverages Docker to create a resilient and scalable environment, ensuring continuous service availability. The visual representation above illustrates how traffic is routed through HAProxy instances and managed by Keepalived to provide redundancy and failover capabilities.

To interconnect all components, I set up a Docker bridge network, which ensures seamless communication between the HAProxy instances, Keepalived, and the backend servers. This network allows the HAProxy instances to effectively distribute incoming traffic across multiple backend servers while monitoring their health and performance.

In this setup, there is a primary HAProxy instance (the master) and a secondary instance (the backup) ready to take over if the master fails. Keepalived, installed on both machines, manages the virtual IP (VIP) that clients connect to. This VIP ensures that, even in the event of a failure, traffic is automatically redirected to the backup HAProxy instance, maintaining service availability without interruption.

The backend comprises three cloned instances of a server running a simple Flask application that serves static content. This setup is an example of a stateless application deployment, where each instance operates independently without relying on session persistence or shared state. In the case of stateful applications, additional architectural considerations would be necessary, such as implementing shared storage, session replication, sticky sessions, or database clustering to ensure consistency and availability. Following best practices in system design is crucial to address these challenges and optimize the architecture based on the application’s specific requirements and context.

# III-/Step by step guide

<div class="ac cb" id="bkmrk-the-first-thing-we%E2%80%99r" style="text-align: justify;"><div class="ci bh gb gc gd ge">- The first thing we’re going to do is create our stateless app a simple Python application that doesn’t store any session information. To get started, we need to create a virtual environment, so make sure you have Python installed on your machine.

<figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="mr ms qz"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 600px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*6rnOOSwuJMbZj5GIzMLPNA.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*6rnOOSwuJMbZj5GIzMLPNA.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*6rnOOSwuJMbZj5GIzMLPNA.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*6rnOOSwuJMbZj5GIzMLPNA.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*6rnOOSwuJMbZj5GIzMLPNA.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*6rnOOSwuJMbZj5GIzMLPNA.png 1100w, https://miro.medium.com/v2/resize:fit:1200/format:webp/1*6rnOOSwuJMbZj5GIzMLPNA.png 1200w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 600px" srcset="https://miro.medium.com/v2/resize:fit:640/1*6rnOOSwuJMbZj5GIzMLPNA.png 640w, https://miro.medium.com/v2/resize:fit:720/1*6rnOOSwuJMbZj5GIzMLPNA.png 720w, https://miro.medium.com/v2/resize:fit:750/1*6rnOOSwuJMbZj5GIzMLPNA.png 750w, https://miro.medium.com/v2/resize:fit:786/1*6rnOOSwuJMbZj5GIzMLPNA.png 786w, https://miro.medium.com/v2/resize:fit:828/1*6rnOOSwuJMbZj5GIzMLPNA.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*6rnOOSwuJMbZj5GIzMLPNA.png 1100w, https://miro.medium.com/v2/resize:fit:1200/1*6rnOOSwuJMbZj5GIzMLPNA.png 1200w">![](https://miro.medium.com/v2/resize:fit:594/1*6rnOOSwuJMbZj5GIzMLPNA.png)</source></source></picture></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Virtual environment creation</figcaption></figure>- Activate the virtual environment, and then install Flask.

<figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="mr ms ra"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 603px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*G1V6zoscdsOMLl6EO-mhQw.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*G1V6zoscdsOMLl6EO-mhQw.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*G1V6zoscdsOMLl6EO-mhQw.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*G1V6zoscdsOMLl6EO-mhQw.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*G1V6zoscdsOMLl6EO-mhQw.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*G1V6zoscdsOMLl6EO-mhQw.png 1100w, https://miro.medium.com/v2/resize:fit:1206/format:webp/1*G1V6zoscdsOMLl6EO-mhQw.png 1206w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 603px" srcset="https://miro.medium.com/v2/resize:fit:640/1*G1V6zoscdsOMLl6EO-mhQw.png 640w, https://miro.medium.com/v2/resize:fit:720/1*G1V6zoscdsOMLl6EO-mhQw.png 720w, https://miro.medium.com/v2/resize:fit:750/1*G1V6zoscdsOMLl6EO-mhQw.png 750w, https://miro.medium.com/v2/resize:fit:786/1*G1V6zoscdsOMLl6EO-mhQw.png 786w, https://miro.medium.com/v2/resize:fit:828/1*G1V6zoscdsOMLl6EO-mhQw.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*G1V6zoscdsOMLl6EO-mhQw.png 1100w, https://miro.medium.com/v2/resize:fit:1206/1*G1V6zoscdsOMLl6EO-mhQw.png 1206w">![](https://miro.medium.com/v2/resize:fit:597/1*G1V6zoscdsOMLl6EO-mhQw.png)</source></source></picture></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Installing Flask</figcaption></figure>- Now Flask is installed in our virtual environment, we are going to create a simple Flask App with `hello world!`content, for me I’ll use nano editor, you can use whatever editor you want for that purpose.

</div></div>```
nano app.py
```

<div class="ac cb" id="bkmrk-flask-webapp" style="text-align: justify;"><div class="ci bh gb gc gd ge"><figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="mr ms rn"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 568px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*WZlKOzXIcsWC8BSlwzMkkA.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*WZlKOzXIcsWC8BSlwzMkkA.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*WZlKOzXIcsWC8BSlwzMkkA.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*WZlKOzXIcsWC8BSlwzMkkA.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*WZlKOzXIcsWC8BSlwzMkkA.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*WZlKOzXIcsWC8BSlwzMkkA.png 1100w, https://miro.medium.com/v2/resize:fit:1136/format:webp/1*WZlKOzXIcsWC8BSlwzMkkA.png 1136w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 568px" srcset="https://miro.medium.com/v2/resize:fit:640/1*WZlKOzXIcsWC8BSlwzMkkA.png 640w, https://miro.medium.com/v2/resize:fit:720/1*WZlKOzXIcsWC8BSlwzMkkA.png 720w, https://miro.medium.com/v2/resize:fit:750/1*WZlKOzXIcsWC8BSlwzMkkA.png 750w, https://miro.medium.com/v2/resize:fit:786/1*WZlKOzXIcsWC8BSlwzMkkA.png 786w, https://miro.medium.com/v2/resize:fit:828/1*WZlKOzXIcsWC8BSlwzMkkA.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*WZlKOzXIcsWC8BSlwzMkkA.png 1100w, https://miro.medium.com/v2/resize:fit:1136/1*WZlKOzXIcsWC8BSlwzMkkA.png 1136w">![](https://miro.medium.com/v2/resize:fit:562/1*WZlKOzXIcsWC8BSlwzMkkA.png)</source></source></picture></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Flask webapp</figcaption></figure></div></div>Copy paste the following content or create your own:

```
from flask import Flask<br></br>app = Flask(__name__)<br></br><br></br>@app.route('/')<br></br>def hello_world():<br></br>    return 'Hello, World!'<br></br><br></br>if __name__ == '__main__':<br></br>    app.run(debug=False)
```

Flask apps run by default in port 5000, you can test the webapp by running: `python app.py`

<div class="ac cb" id="bkmrk-running-flask-app-ac" style="text-align: justify;"><div class="ci bh gb gc gd ge"><figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="nb nc fl nd bh ne" role="button" tabindex="0"><div class="mr ms ro"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*LZofDS6Y0H3OKhPDGWEtYg.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*LZofDS6Y0H3OKhPDGWEtYg.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*LZofDS6Y0H3OKhPDGWEtYg.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*LZofDS6Y0H3OKhPDGWEtYg.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*LZofDS6Y0H3OKhPDGWEtYg.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*LZofDS6Y0H3OKhPDGWEtYg.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*LZofDS6Y0H3OKhPDGWEtYg.png 1400w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/1*LZofDS6Y0H3OKhPDGWEtYg.png 640w, https://miro.medium.com/v2/resize:fit:720/1*LZofDS6Y0H3OKhPDGWEtYg.png 720w, https://miro.medium.com/v2/resize:fit:750/1*LZofDS6Y0H3OKhPDGWEtYg.png 750w, https://miro.medium.com/v2/resize:fit:786/1*LZofDS6Y0H3OKhPDGWEtYg.png 786w, https://miro.medium.com/v2/resize:fit:828/1*LZofDS6Y0H3OKhPDGWEtYg.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*LZofDS6Y0H3OKhPDGWEtYg.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*LZofDS6Y0H3OKhPDGWEtYg.png 1400w">![](https://miro.medium.com/v2/resize:fit:693/1*LZofDS6Y0H3OKhPDGWEtYg.png)</source></source></picture></div></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Running Flask app</figcaption></figure><figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="nb nc fl nd bh ne" role="button" tabindex="0"><div class="mr ms rp"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RydS1s3SacPyZndJsdEFQ.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*3RydS1s3SacPyZndJsdEFQ.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*3RydS1s3SacPyZndJsdEFQ.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*3RydS1s3SacPyZndJsdEFQ.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*3RydS1s3SacPyZndJsdEFQ.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*3RydS1s3SacPyZndJsdEFQ.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*3RydS1s3SacPyZndJsdEFQ.png 1400w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/1*3RydS1s3SacPyZndJsdEFQ.png 640w, https://miro.medium.com/v2/resize:fit:720/1*3RydS1s3SacPyZndJsdEFQ.png 720w, https://miro.medium.com/v2/resize:fit:750/1*3RydS1s3SacPyZndJsdEFQ.png 750w, https://miro.medium.com/v2/resize:fit:786/1*3RydS1s3SacPyZndJsdEFQ.png 786w, https://miro.medium.com/v2/resize:fit:828/1*3RydS1s3SacPyZndJsdEFQ.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*3RydS1s3SacPyZndJsdEFQ.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*3RydS1s3SacPyZndJsdEFQ.png 1400w">![](https://miro.medium.com/v2/resize:fit:693/1*3RydS1s3SacPyZndJsdEFQ.png)</source></source></picture></div></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Accessing web app on the browser</figcaption></figure></div></div>The app is up and running Now we should get the dependencies used in our app, we will save them on requirements.txt .

<div class="ac cb" id="bkmrk-saving-requirements-" style="text-align: justify;"><div class="ci bh gb gc gd ge"><figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="mr ms rq"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 580px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*18q0XhVH-PcLfuj2ZrUwgw.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*18q0XhVH-PcLfuj2ZrUwgw.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*18q0XhVH-PcLfuj2ZrUwgw.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*18q0XhVH-PcLfuj2ZrUwgw.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*18q0XhVH-PcLfuj2ZrUwgw.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*18q0XhVH-PcLfuj2ZrUwgw.png 1100w, https://miro.medium.com/v2/resize:fit:1160/format:webp/1*18q0XhVH-PcLfuj2ZrUwgw.png 1160w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 580px" srcset="https://miro.medium.com/v2/resize:fit:640/1*18q0XhVH-PcLfuj2ZrUwgw.png 640w, https://miro.medium.com/v2/resize:fit:720/1*18q0XhVH-PcLfuj2ZrUwgw.png 720w, https://miro.medium.com/v2/resize:fit:750/1*18q0XhVH-PcLfuj2ZrUwgw.png 750w, https://miro.medium.com/v2/resize:fit:786/1*18q0XhVH-PcLfuj2ZrUwgw.png 786w, https://miro.medium.com/v2/resize:fit:828/1*18q0XhVH-PcLfuj2ZrUwgw.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*18q0XhVH-PcLfuj2ZrUwgw.png 1100w, https://miro.medium.com/v2/resize:fit:1160/1*18q0XhVH-PcLfuj2ZrUwgw.png 1160w">![](https://miro.medium.com/v2/resize:fit:574/1*18q0XhVH-PcLfuj2ZrUwgw.png)</source></source></picture></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Saving requirements in a file</figcaption></figure></div></div>```
pip freeze > requirements.txt
```

\- We will need these requirements so we can create the Dockerfile of our webapp, then run the following command to create a Dockerfile of the webapp.

```
nano Dockerfile.webapp
```

copy and paste the following:

```
<br></br>FROM python:3.12.5<br></br>Expose 5000<br></br>ENV FLASK_app=app.py<br></br>WORKDIR /app<br></br>COPY ./app.py .<br></br>COPY ./requirements.txt .<br></br>RUN pip install -r requirements.txt<br></br>RUN pip install gunicorn<br></br>CMD gunicorn -w 4 -b :5000 app:app
```

Note that we added Gunicorn in our Dockerfile to run our Python web app because using development servers is not suitable for production environments.

Let’s create our first image , it’ll be saved locally.

```
docker build -f Dockerfile.webapp -t webapp_test .
```

After finalizing the Dockerfile for our web app, it’s time to create our Docker Compose YAML file, which will define our entire architecture. We’ll break it down step by step.

Here is the docker compose file.

```
version: '3.8'<br></br><br></br>services:<br></br>  haproxy1:<br></br>    build:<br></br>      context: .<br></br>      dockerfile: Dockerfile<br></br>    container_name: haproxy1<br></br>    networks:<br></br>      yahya_prive:<br></br>        ipv4_address: 10.0.0.150<br></br>    cap_add:<br></br>      - NET_ADMIN<br></br>    ports:<br></br>      - "8888:80"<br></br>      - "8404:8404"<br></br>    volumes:<br></br>      - C:\Users\John macmillan\Desktop\python_project\haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro<br></br>      - C:\Users\John macmillan\Desktop\python_project\keepalived_primary.conf:/etc/keepalived/keepalived.conf:ro<br></br>    depends_on:<br></br>      - web1<br></br>      - web2<br></br>      - web3<br></br>    entrypoint: ["/bin/sh", "-c", "keepalived -D -f /etc/keepalived/keepalived.conf && haproxy -f /usr/local/etc/haproxy/haproxy.cfg"]<br></br><br></br>  haproxy2:<br></br>    build:<br></br>      context: .<br></br>      dockerfile: Dockerfile<br></br>    container_name: haproxy2<br></br>    networks:<br></br>      yahya_prive:<br></br>        ipv4_address: 10.0.0.155<br></br>    cap_add:<br></br>      - NET_ADMIN<br></br>    ports:<br></br>      - "8800:80"<br></br>      - "8405:8404"<br></br>    volumes:<br></br>      - C:\Users\John macmillan\Desktop\python_project\haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro<br></br>      - C:\Users\John macmillan\Desktop\python_project\keepalived_bck.conf:/etc/keepalived/keepalived.conf:ro<br></br>    depends_on:<br></br>      - web1<br></br>      - web2<br></br>      - web3<br></br>    entrypoint: ["/bin/sh", "-c", "keepalived -D -f /etc/keepalived/keepalived.conf && haproxy -f /usr/local/etc/haproxy/haproxy.cfg"]<br></br><br></br>  web1:<br></br>    image: webapp_test<br></br>    container_name: web1<br></br>    networks:<br></br>      - yahya_prive<br></br><br></br>  web2:<br></br>    image: webapp_test<br></br>    container_name: web2<br></br>    networks:<br></br>      - yahya_prive<br></br><br></br>  web3:<br></br>    image: webapp_test<br></br>    container_name: web3<br></br>    networks:<br></br>      - yahya_prive<br></br><br></br>networks:<br></br>  yahya_prive:<br></br>   driver: bridge<br></br>   #specify the driver <br></br>   ipam:  <br></br>    config : <br></br>       - subnet: 10.0.0.0/24<br></br>         gateway: 10.0.0.1
```

let’s break this down:

First thing we did is to create a network bridge . This Network bridge is called `yahya_prive`then we specified the CIDR notation: `10.0.0.0/24`with the following gateway: `10.0.0.1`

it’ll be our private network in which all the containers will be assigned an ip address from the ip address range 10.0.0.0/24 .

```
networks:<br></br>  yahya_prive:<br></br>   driver: bridge<br></br>   #specify the driver <br></br>   ipam:  <br></br>    config : <br></br>       - subnet: 10.0.0.0/24<br></br>         gateway: 10.0.0.1
```

It’s advisable to use a custom network rather than the default network provided by Docker Compose. This approach enhances security and allows you to use domain names instead of IP addresses in configurations.

Then there is a section for our webapps, they’re called respectively: web1, web2 and web3, all three of them is now attached to `yahya_prive`network.

Each one was provided a name, and the base image was created previously, and it was saved locally which is `webapp_test`

```
web1:<br></br>    image: webapp_test<br></br>    container_name: web1<br></br>    networks:<br></br>      - yahya_prive<br></br><br></br>  web2:<br></br>    image: webapp_test<br></br>    container_name: web2<br></br>    networks:<br></br>      - yahya_prive<br></br><br></br>  web3:<br></br>    image: webapp_test<br></br>    container_name: web3<br></br>    networks:<br></br>      - yahya_prive
```

After this we’ve created the service for our HAProxy load balancer.

```
haproxy1:<br></br>    build:<br></br>      context: .<br></br>      dockerfile: Dockerfile<br></br>    container_name: haproxy1<br></br>    networks:<br></br>      yahya_prive:<br></br>        ipv4_address: 10.0.0.150<br></br>    cap_add:<br></br>      - NET_ADMIN<br></br>    ports:<br></br>      - "8888:80"<br></br>      - "8404:8404"<br></br>    volumes:<br></br>      - C:\Users\John macmillan\Desktop\python_project\haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro<br></br>      - C:\Users\John macmillan\Desktop\python_project\keepalived_primary.conf:/etc/keepalived/keepalived.conf:ro<br></br>    depends_on:<br></br>      - web1<br></br>      - web2<br></br>      - web3<br></br>    entrypoint: ["/bin/sh", "-c", "keepalived -D -f /etc/keepalived/keepalived.conf && haproxy -f /usr/local/etc/haproxy/haproxy.cfg"]
```

The Master HAProxy is called haproxy1, and it was assigned the ip address:

`10.0.0.150`from the private network yahya\_prive.

To enable Keepalived, which uses VRRP for failover, the container requires additional network capabilities. Therefore, we grant the container `NET_ADMIN` privileges, allowing it to manage network settings necessary for VRRP operations.

The necessary configuration files for HAProxy and Keepalived are mounted as read-only volumes from the host machine to ensure that both services are properly configured.

The `entrypoint` directive ensures that Keepalived starts in the background and monitors the HAProxy service, providing the high availability setup.

And obviously, there is the configuration `Dockerfile` that contains our Keepalived and HAProxy.

```
FROM ubuntu:22.04<br></br><br></br># Install Keepalived and HAProxy<br></br>RUN apt-get update && apt-get install -y \<br></br>    nano \<br></br> net-tools \<br></br>    keepalived \<br></br>    haproxy<br></br> <br></br>EXPOSE 80<br></br>EXPOSE 8444<br></br>
```

The configuration for both load balancers is exactly the same (`haproxy.cfg`)

```
global<br></br>  stats socket /var/run/api.sock user haproxy group haproxy mode 660 level admin expose-fd listeners<br></br>  log stdout format raw local0 info<br></br><br></br>defaults<br></br>  mode http<br></br>  timeout client 10s<br></br>  timeout connect 5s<br></br>  timeout server 10s<br></br>  timeout http-request 10s<br></br>  log global<br></br><br></br>frontend stats<br></br>  bind *:8404<br></br>  stats enable<br></br>  stats uri /<br></br>  stats refresh 10s<br></br><br></br>frontend myfrontend<br></br>  bind :80<br></br>  default_backend webservers<br></br><br></br>backend webservers<br></br>server s1 web1:5000 check<br></br>server s2 web2:5000 check<br></br>server s3 web3:5000 check
```

What we’ve done here is configure our load balancer to listen for requests on port 80. We’ve also defined our web servers in the backend webservers section, allowing us to route traffic to them effectively.

On the other hand, we had different configurations of Keepalived daemons for both nodes.

here is the configuration for the master node (`keepalived_primary.conf`):

```
vrrp_instance VI_1 {<br></br>    state MASTER<br></br>    interface eth0<br></br>    virtual_router_id 33<br></br>    priority 255<br></br>    advert_int 1<br></br>    unicast_src_ip 10.0.0.50<br></br>    <br></br>    authentication {<br></br>        auth_type PASS<br></br>        auth_pass letmein<br></br>    }<br></br>    <br></br>    virtual_ipaddress {<br></br>        10.0.0.50/24 dev eth0<br></br>    }  <br></br>}
```

This is a basic configuration where we’ve assigned a priority of 255, which is higher than the priority set on the backup machine. As a result, this machine will be assigned the virtual IP address first. We’ve defined the state as `MASTER` for this machine and specified `eth0` as the interface, as it’s connected to the private network through this interface.

The most critical part of this configuration is the `virtual_ipaddress` section, where we define the virtual IP address (`10.0.0.50/24`) that will be managed by the VRRP protocol. This IP address will be assigned to the master machine, ensuring that it handles traffic as long as it remains in the master state. The `authentication` section provides basic security by requiring a password for VRRP communications, adding an extra layer of protection to the setup.

The configuration for the backup node will be different a little bit (`keepalived_bck.conf`):

```
vrrp_instance VI_1 {<br></br>    state BACKUP<br></br>    interface eth0<br></br>    virtual_router_id 33<br></br>    priority 150<br></br>    advert_int 1<br></br>    unicast_src_ip 10.0.0.50<br></br>    <br></br>    authentication {<br></br>        auth_type PASS<br></br>        auth_pass letmein<br></br>    }<br></br>    <br></br>    virtual_ipaddress {<br></br>        10.0.0.50/24 dev eth0<br></br>    }  <br></br>}
```

We’ve set a priority of 150, which is lower than that of the master, and designated the state as `BACKUP`. Both the master and backup nodes share the same virtual IP address (VIP). In the event of the master node failing, the backup node will detect this through ARP checks and automatically take over the VIP, ensuring continued service availability.

Note that the Docker Compose file includes a `depends_on` section that specifies the order in which containers are started. In this case:

```
depends_on:<br></br>      - web1<br></br>      - web2<br></br>      - web3
```

This configuration ensures that the web applications (`web1`, `web2`, and `web3`) start before the load balancers are initialized. This order is crucial to ensure that the web servers are up and running before the load balancers begin routing traffic to them.

After completing the necessary configurations, you can run the entire setup with the following command:

```
docker-compose -p high_availability_cluster up -d
```

Now, your setup is up and running.

<div class="ac cb" id="bkmrk-accessing-webapp" style="text-align: justify;"><div class="ci bh gb gc gd ge"><figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="nb nc fl nd bh ne" role="button" tabindex="0"><div class="mr ms rr"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*aTZ2S3WfdfYJcsNjnjSpsg.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*aTZ2S3WfdfYJcsNjnjSpsg.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*aTZ2S3WfdfYJcsNjnjSpsg.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*aTZ2S3WfdfYJcsNjnjSpsg.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*aTZ2S3WfdfYJcsNjnjSpsg.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*aTZ2S3WfdfYJcsNjnjSpsg.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*aTZ2S3WfdfYJcsNjnjSpsg.png 1400w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/1*aTZ2S3WfdfYJcsNjnjSpsg.png 640w, https://miro.medium.com/v2/resize:fit:720/1*aTZ2S3WfdfYJcsNjnjSpsg.png 720w, https://miro.medium.com/v2/resize:fit:750/1*aTZ2S3WfdfYJcsNjnjSpsg.png 750w, https://miro.medium.com/v2/resize:fit:786/1*aTZ2S3WfdfYJcsNjnjSpsg.png 786w, https://miro.medium.com/v2/resize:fit:828/1*aTZ2S3WfdfYJcsNjnjSpsg.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*aTZ2S3WfdfYJcsNjnjSpsg.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*aTZ2S3WfdfYJcsNjnjSpsg.png 1400w">![Accessing webapp](https://miro.medium.com/v2/resize:fit:693/1*aTZ2S3WfdfYJcsNjnjSpsg.png)</source></source></picture></div></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Accessing webapp</figcaption></figure></div></div>The `haproxy1` container will receive the virtual IP address on its `eth0` interface. You can verify this by running the following command inside the container: `ip a`.

<div class="ac cb" id="bkmrk-virtual-ip-address-a" style="text-align: justify;"><div class="ci bh gb gc gd ge"><figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="nb nc fl nd bh ne" role="button" tabindex="0"><div class="mr ms rs"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*L_B3ZgQXT-ikW5_RaSxzyA.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*L_B3ZgQXT-ikW5_RaSxzyA.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*L_B3ZgQXT-ikW5_RaSxzyA.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*L_B3ZgQXT-ikW5_RaSxzyA.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*L_B3ZgQXT-ikW5_RaSxzyA.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*L_B3ZgQXT-ikW5_RaSxzyA.png 1100w, https://miro.medium.com/v2/resize:fit:1400/format:webp/1*L_B3ZgQXT-ikW5_RaSxzyA.png 1400w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px" srcset="https://miro.medium.com/v2/resize:fit:640/1*L_B3ZgQXT-ikW5_RaSxzyA.png 640w, https://miro.medium.com/v2/resize:fit:720/1*L_B3ZgQXT-ikW5_RaSxzyA.png 720w, https://miro.medium.com/v2/resize:fit:750/1*L_B3ZgQXT-ikW5_RaSxzyA.png 750w, https://miro.medium.com/v2/resize:fit:786/1*L_B3ZgQXT-ikW5_RaSxzyA.png 786w, https://miro.medium.com/v2/resize:fit:828/1*L_B3ZgQXT-ikW5_RaSxzyA.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*L_B3ZgQXT-ikW5_RaSxzyA.png 1100w, https://miro.medium.com/v2/resize:fit:1400/1*L_B3ZgQXT-ikW5_RaSxzyA.png 1400w">![](https://miro.medium.com/v2/resize:fit:693/1*L_B3ZgQXT-ikW5_RaSxzyA.png)</source></source></picture></div></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Virtual Ip address assigned</figcaption></figure></div></div>We can try sending an HTTP request using the `curl` command from a node inside the `yahya_prive` network. For example, we can use `web1` for this purpose.

<div class="ac cb" id="bkmrk-getting-web-content-" style="text-align: justify;"><div class="ci bh gb gc gd ge"><figure class="qs qt qu qv qw ml mr ms paragraph-image"><div class="mr ms rt"><picture><source sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 693px" srcset="https://miro.medium.com/v2/resize:fit:640/format:webp/1*IyOws179aa5Hu5lS8D_DOA.png 640w, https://miro.medium.com/v2/resize:fit:720/format:webp/1*IyOws179aa5Hu5lS8D_DOA.png 720w, https://miro.medium.com/v2/resize:fit:750/format:webp/1*IyOws179aa5Hu5lS8D_DOA.png 750w, https://miro.medium.com/v2/resize:fit:786/format:webp/1*IyOws179aa5Hu5lS8D_DOA.png 786w, https://miro.medium.com/v2/resize:fit:828/format:webp/1*IyOws179aa5Hu5lS8D_DOA.png 828w, https://miro.medium.com/v2/resize:fit:1100/format:webp/1*IyOws179aa5Hu5lS8D_DOA.png 1100w, https://miro.medium.com/v2/resize:fit:1386/format:webp/1*IyOws179aa5Hu5lS8D_DOA.png 1386w" type="image/webp"><source data-testid="og" sizes="(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 693px" srcset="https://miro.medium.com/v2/resize:fit:640/1*IyOws179aa5Hu5lS8D_DOA.png 640w, https://miro.medium.com/v2/resize:fit:720/1*IyOws179aa5Hu5lS8D_DOA.png 720w, https://miro.medium.com/v2/resize:fit:750/1*IyOws179aa5Hu5lS8D_DOA.png 750w, https://miro.medium.com/v2/resize:fit:786/1*IyOws179aa5Hu5lS8D_DOA.png 786w, https://miro.medium.com/v2/resize:fit:828/1*IyOws179aa5Hu5lS8D_DOA.png 828w, https://miro.medium.com/v2/resize:fit:1100/1*IyOws179aa5Hu5lS8D_DOA.png 1100w, https://miro.medium.com/v2/resize:fit:1386/1*IyOws179aa5Hu5lS8D_DOA.png 1386w">![](https://miro.medium.com/v2/resize:fit:686/1*IyOws179aa5Hu5lS8D_DOA.png)</source></source></picture></div><figcaption class="ng ff nh mr ms ni nj bf b bg ab du" data-selectable-paragraph="">Getting web content from using VIP</figcaption></figure></div></div># IV-/Conclusion

In conclusion, building a high availability cluster with HAProxy, Keepalived, and Docker is an effective way to ensure continuous service availability and reliability. Through this guide, we’ve explored the fundamental concepts of high availability, examined how HAProxy and Keepalived work together to manage traffic and failover, and demonstrated how to set up this architecture in a Docker environment. By following these steps, you can create a resilient infrastructure that can handle disruptions and maintain service continuity, making it an essential setup for any robust and scalable application deployment.

**Equipped with this understanding, you’ll now be able to enhance your network infrastructure and deploy more resilient, scalable applications with confidence.**

**We invite you to share your experiences and insights in the comments below. We’re eager to hear your feedback and thoughts. Happy networking!**

**References**

For more detailed information :

<div class="ac cb" id="bkmrk-networks-top-level-e"><div class="ci bh gb gc gd ge">1. [Networks top-level elements | Docker Docs](https://docs.docker.com/reference/compose-file/networks/)
2. [HAProxy version 2.9.9–41 — Starter Guide](https://docs.haproxy.org/2.9/intro.html)
3. [Understanding Virtual Router Redundancy Protocol (VRRP) | FS Community](https://community.fs.com/article/understanding-virtual-router-redundancy-protocol-vrrp.html)
4. [Setting up a Linux cluster with Keepalived: Basic configuration | Enable Sysadmin (redhat.com)](https://www.redhat.com/sysadmin/keepalived-basics)

</div></div>

# Configuração de URL Path no HAPROXY

Arquivo exemplo haproxy.cfg

<div id="bkmrk-global">global</div><div id="bkmrk-%C2%A0-%C2%A0-log-%2Fdev%2Flog-loc"> log /dev/log local0</div><div id="bkmrk-%23%C2%A0-%C2%A0-log-127.0.0.1-l">\# log 127.0.0.1 local0 info</div><div id="bkmrk-%C2%A0-%C2%A0-daemon"> daemon</div><div id="bkmrk-%C2%A0-%C2%A0-log-stdout-forma"> log stdout format raw local0</div><div id="bkmrk-%C2%A0-%C2%A0-maxconn-2048"> maxconn 2048</div><div id="bkmrk-%C2%A0-%C2%A0-user-haproxy"> user haproxy</div><div id="bkmrk-%C2%A0-%C2%A0-group-haproxy"> group haproxy</div><div id="bkmrk-">  
</div><div id="bkmrk-defaults">defaults</div><div id="bkmrk-%C2%A0-%C2%A0-log%C2%A0-%C2%A0-%C2%A0global"> log global</div><div id="bkmrk-%C2%A0-%C2%A0-mode%C2%A0-%C2%A0-http"> mode http</div><div id="bkmrk-%C2%A0-%C2%A0-option%C2%A0-httplog"> option httplog</div><div id="bkmrk-%C2%A0-%C2%A0-option%C2%A0-dontlogn"> option dontlognull</div><div id="bkmrk-%C2%A0-%C2%A0-timeout-connect-"> timeout connect 5000ms</div><div id="bkmrk-%C2%A0-%C2%A0-timeout-client%C2%A0-"> timeout client 50000ms</div><div id="bkmrk-%C2%A0-%C2%A0-timeout-server%C2%A0-"> timeout server 50000ms</div><div id="bkmrk-%C2%A0-%C2%A0-retries-3"> retries 3</div><div id="bkmrk--1">  
</div><div id="bkmrk-listen-stats">listen stats</div><div id="bkmrk-%C2%A0-%C2%A0-bind-%2A%3A1120"> bind *:1120</div><div id="bkmrk-%C2%A0-%C2%A0-mode-http"> mode http</div><div id="bkmrk-%C2%A0-%C2%A0-stats-enable"> stats enable</div><div id="bkmrk-%C2%A0-%C2%A0-stats-uri-%2Fstats"> stats uri /stats</div><div id="bkmrk-%C2%A0-%C2%A0-stats-refresh-10"> stats refresh 10s</div><div id="bkmrk-%C2%A0-%C2%A0-stats-auth-admin"> stats auth admin:senha123</div><div id="bkmrk--2">  
</div><div id="bkmrk--3">  
</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23">\######################################</div><div id="bkmrk-%23-frontend-1-%E2%80%93-porta">\# FRONTEND 1 – porta 2023</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-1">\######################################</div><div id="bkmrk-frontend-http_front_">frontend http_front_2023</div><div id="bkmrk-%C2%A0-%C2%A0-bind-%2A%3A2023"> bind *:2023</div><div id="bkmrk-%C2%A0-%C2%A0-default_backend-"> default_backend doc_backend_2023</div><div id="bkmrk--4">  
</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-2">\######################################</div><div id="bkmrk-%23-frontend-2-%E2%80%93-porta">\# FRONTEND 2 – porta 2024</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-3">\######################################</div><div id="bkmrk-frontend-http_front_-1">frontend http_front_2024</div><div id="bkmrk-%C2%A0-%C2%A0-bind-%2A%3A2024"> bind *:2024</div><div id="bkmrk-%C2%A0-%C2%A0-default_backend--1"> default_backend doc_backend_2024</div><div id="bkmrk--5">  
</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-4">\######################################</div><div id="bkmrk-%23-frontend-3-%E2%80%93-porta">\# FRONTEND 3 – porta 2025</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-5">\######################################</div><div id="bkmrk-frontend-http_front_-2">frontend http_front_2025</div><div id="bkmrk-%C2%A0-%C2%A0-bind-%2A%3A2025"> bind *:2025</div><div id="bkmrk-%C2%A0-%C2%A0-default_backend--2"> default_backend doc_backend_2025</div><div id="bkmrk--6">  
</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-6">\######################################</div><div id="bkmrk-%23-backend-1-%E2%80%93-para-f">\# BACKEND 1 – para frontend 2023</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-7">\######################################</div><div id="bkmrk-backend-doc_backend_">backend doc_backend_2023</div><div id="bkmrk-%C2%A0-%C2%A0-balance-roundrob"> balance roundrobin</div><div id="bkmrk-%C2%A0-%C2%A0-stick-table-type"> stick-table type string len 64 size 100k expire 1h store server_id</div><div id="bkmrk-%C2%A0-%C2%A0-stick-on-path%C2%A0-%C2%A0"> stick on path # registra somente o /teste</div><div id="bkmrk-%23%C2%A0-stick-on-concat%28r">\# stick on concat(req.hdr(host), path) - entrada para registrar url e /teste</div><div id="bkmrk-%23%C2%A0-stick-on-concat%28c">\# stick on concat(concat(req.hdr(host), path), concat("?", query)) - entrada para registrar url e /teste?normal (query)</div><div id="bkmrk--7">  
</div><div id="bkmrk-%C2%A0-%C2%A0-server-srv1_2023"> server srv1_2023 192.168.0.101:2023 check</div><div id="bkmrk-%C2%A0-%C2%A0-server-srv2_2023"> server srv2_2023 192.168.0.102:2023 check</div><div id="bkmrk-%C2%A0-%C2%A0-server-srv3_2023"> server srv3_2023 192.168.0.103:2023 check</div><div id="bkmrk--8">  
</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-8">\######################################</div><div id="bkmrk-%23-backend-2-%E2%80%93-para-f">\# BACKEND 2 – para frontend 2024</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-9">\######################################</div><div id="bkmrk-backend-doc_backend_-1">backend doc_backend_2024</div><div id="bkmrk-%C2%A0-%C2%A0-balance-roundrob-1"> balance roundrobin</div><div id="bkmrk-%C2%A0-%C2%A0-stick-table-type-1"> stick-table type string len 64 size 100k expire 1h store server_id</div><div id="bkmrk-%C2%A0-%C2%A0%C2%A0stick-on-path%C2%A0-%C2%A0"><div> stick on path # registra somente o /teste <div><div>\# stick on concat(req.hdr(host), path) - entrada para registrar url e /teste</div><div>\# stick on concat(concat(req.hdr(host), path), concat("?", query)) - entrada para registrar url e /teste?normal (query)</div></div><div></div></div><div>  
</div></div><div id="bkmrk-%C2%A0-%C2%A0-server-srv1_2024"> server srv1_2024 192.168.0.104:2024 check</div><div id="bkmrk-%C2%A0-%C2%A0-server-srv2_2024"> server srv2_2024 192.168.0.105:2024 check</div><div id="bkmrk-%C2%A0-%C2%A0-server-srv3_2024"> server srv3_2024 192.168.0.106:2024 check</div><div id="bkmrk--9">  
</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-10">\######################################</div><div id="bkmrk-%23-backend-3-%E2%80%93-para-f">\# BACKEND 3 – para frontend 2025</div><div id="bkmrk-%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23-11">\######################################</div><div id="bkmrk-backend-doc_backend_-2">backend doc_backend_2025</div><div id="bkmrk-%C2%A0-%C2%A0-balance-roundrob-2"> balance roundrobin</div><div id="bkmrk-%C2%A0-%C2%A0-stick-table-type-2"> stick-table type string len 64 size 100k expire 1h store server_id</div><div id="bkmrk-%C2%A0-%C2%A0-stick-on-path%C2%A0-%C2%A0-1"><div> stick on path # registra somente o /teste</div><div>\# stick on concat(req.hdr(host), path) - entrada para registrar url e /teste</div><div>\# stick on concat(concat(req.hdr(host), path), concat("?", query)) - entrada para registrar url e /teste?normal (query)</div></div><div id="bkmrk--10">  
</div><div id="bkmrk-%C2%A0-%C2%A0-server-srv1_2025"> server srv1_2025 192.168.0.107:2025 check</div><div id="bkmrk-%C2%A0-%C2%A0-server-srv2_2025"> server srv2_2025 192.168.0.108:2025 check</div><div id="bkmrk-%C2%A0-%C2%A0-server-srv3_2025"> server srv3_2025 192.168.0.109:2025 check</div>

# Configuração de logs no HAPROXY

ChatGPT:  
Ótimo! Vamos configurar o **HAProxy + rsyslog** dentro (ou fora) do Docker para gerar logs em **horário local**, salvos no arquivo `/var/log/haproxy.log`.

---

## 🧱 O que vamos fazer:

1. Configurar o HAProxy para enviar logs ao syslog (`/dev/log`)
2. Garantir que o rsyslog esteja ativo no host
3. Mapear o socket `/dev/log` para o container Docker (ou fazer logging fora)
4. Salvar os logs com horário local no host

---

## ✅ 1. Configuração do HAProxy (`haproxy.cfg`)

Altere a seção `global` do seu `haproxy.cfg` para:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-global-log-%2Fdev%2Flog-"><div class="overflow-y-auto p-4" dir="ltr">`global    log /dev/log local0 info    daemon    maxconn 2048    user haproxy    group haproxy`</div><div class="overflow-y-auto p-4" dir="ltr">  
`defaults    log global    mode http    option httplog    option dontlognull    timeout connect 5000ms    timeout client 50000ms    timeout server 50000ms`</div><div class="overflow-y-auto p-4" dir="ltr"></div></div>---

## ✅ 2. Configurar o rsyslog no **host Linux**

Verifique se o rsyslog está ativo:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-sudo-systemctl-statu"><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"></div></div><div class="overflow-y-auto p-4" dir="ltr">`sudo systemctl status rsyslog `</div></div>Se não estiver, ative:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-sudo-systemctl-enabl"><div class="overflow-y-auto p-4" dir="ltr">`sudo systemctl <span class="hljs-built_in">enable</span> --now rsyslog`</div></div>Crie o arquivo de log e a regra:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-sudo-touch-%2Fvar%2Flog%2F"><div class="overflow-y-auto p-4" dir="ltr">`sudo <span class="hljs-built_in">touch</span> /var/log/haproxy.logsudo <span class="hljs-built_in">chown</span> syslog:adm /var/log/haproxy.log`</div></div>Agora crie um arquivo de configuração para o rsyslog:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-sudo-nano-%2Fetc%2Frsysl"><div class="overflow-y-auto p-4" dir="ltr">`sudo nano /etc/rsyslog.d/49-haproxy.conf`</div></div>E adicione:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-local0.%2A-%2Fvar%2Flog%2Fha"><div class="overflow-y-auto p-4" dir="ltr">`local0.*    /var/log/haproxy.log`</div></div>Reinicie o rsyslog:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-sudo-systemctl-resta"><div class="overflow-y-auto p-4" dir="ltr">`sudo systemctl restart rsyslog`</div></div>---

## ✅ 3. Rodar o container HAProxy com acesso ao `/dev/log`

Você precisa montar o socket do syslog no container:

### 👉 Exemplo com `docker run`:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-bash-copiareditar-do"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary select-none rounded-t-2xl">bash</div><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"><button aria-label="Copiar" class="flex gap-1 items-center select-none py-1"><svg class="icon-xs" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path d="M12.668 10.667C12.668 9.95614 12.668 9.46258 12.6367 9.0791C12.6137 8.79732 12.5758 8.60761 12.5244 8.46387L12.4688 8.33399C12.3148 8.03193 12.0803 7.77885 11.793 7.60254L11.666 7.53125C11.508 7.45087 11.2963 7.39395 10.9209 7.36328C10.5374 7.33197 10.0439 7.33203 9.33301 7.33203H6.5C5.78896 7.33203 5.29563 7.33195 4.91211 7.36328C4.63016 7.38632 4.44065 7.42413 4.29688 7.47559L4.16699 7.53125C3.86488 7.68518 3.61186 7.9196 3.43555 8.20703L3.36524 8.33399C3.28478 8.49198 3.22795 8.70352 3.19727 9.0791C3.16595 9.46259 3.16504 9.95611 3.16504 10.667V13.5C3.16504 14.211 3.16593 14.7044 3.19727 15.0879C3.22797 15.4636 3.28473 15.675 3.36524 15.833L3.43555 15.959C3.61186 16.2466 3.86474 16.4807 4.16699 16.6348L4.29688 16.6914C4.44063 16.7428 4.63025 16.7797 4.91211 16.8027C5.29563 16.8341 5.78896 16.835 6.5 16.835H9.33301C10.0439 16.835 10.5374 16.8341 10.9209 16.8027C11.2965 16.772 11.508 16.7152 11.666 16.6348L11.793 16.5645C12.0804 16.3881 12.3148 16.1351 12.4688 15.833L12.5244 15.7031C12.5759 15.5594 12.6137 15.3698 12.6367 15.0879C12.6681 14.7044 12.668 14.211 12.668 13.5V10.667ZM13.998 12.665C14.4528 12.6634 14.8011 12.6602 15.0879 12.6367C15.4635 12.606 15.675 12.5492 15.833 12.4688L15.959 12.3975C16.2466 12.2211 16.4808 11.9682 16.6348 11.666L16.6914 11.5361C16.7428 11.3924 16.7797 11.2026 16.8027 10.9209C16.8341 10.5374 16.835 10.0439 16.835 9.33301V6.5C16.835 5.78896 16.8341 5.29563 16.8027 4.91211C16.7797 4.63025 16.7428 4.44063 16.6914 4.29688L16.6348 4.16699C16.4807 3.86474 16.2466 3.61186 15.959 3.43555L15.833 3.36524C15.675 3.28473 15.4636 3.22797 15.0879 3.19727C14.7044 3.16593 14.211 3.16504 13.5 3.16504H10.667C9.9561 3.16504 9.46259 3.16595 9.0791 3.19727C8.79739 3.22028 8.6076 3.2572 8.46387 3.30859L8.33399 3.36524C8.03176 3.51923 7.77886 3.75343 7.60254 4.04102L7.53125 4.16699C7.4508 4.32498 7.39397 4.53655 7.36328 4.91211C7.33985 5.19893 7.33562 5.54719 7.33399 6.00195H9.33301C10.022 6.00195 10.5791 6.00131 11.0293 6.03809C11.4873 6.07551 11.8937 6.15471 12.2705 6.34668L12.4883 6.46875C12.984 6.7728 13.3878 7.20854 13.6533 7.72949L13.7197 7.87207C13.8642 8.20859 13.9292 8.56974 13.9619 8.9707C13.9987 9.42092 13.998 9.97799 13.998 10.667V12.665ZM18.165 9.33301C18.165 10.022 18.1657 10.5791 18.1289 11.0293C18.0961 11.4302 18.0311 11.7914 17.8867 12.1279L17.8203 12.2705C17.5549 12.7914 17.1509 13.2272 16.6553 13.5313L16.4365 13.6533C16.0599 13.8452 15.6541 13.9245 15.1963 13.9619C14.8593 13.9895 14.4624 13.9935 13.9951 13.9951C13.9935 14.4624 13.9895 14.8593 13.9619 15.1963C13.9292 15.597 13.864 15.9576 13.7197 16.2939L13.6533 16.4365C13.3878 16.9576 12.9841 17.3941 12.4883 17.6982L12.2705 17.8203C11.8937 18.0123 11.4873 18.0915 11.0293 18.1289C10.5791 18.1657 10.022 18.165 9.33301 18.165H6.5C5.81091 18.165 5.25395 18.1657 4.80371 18.1289C4.40306 18.0962 4.04235 18.031 3.70606 17.8867L3.56348 17.8203C3.04244 17.5548 2.60585 17.151 2.30176 16.6553L2.17969 16.4365C1.98788 16.0599 1.90851 15.6541 1.87109 15.1963C1.83431 14.746 1.83496 14.1891 1.83496 13.5V10.667C1.83496 9.978 1.83432 9.42091 1.87109 8.9707C1.90851 8.5127 1.98772 8.10625 2.17969 7.72949L2.30176 7.51172C2.60586 7.0159 3.04236 6.6122 3.56348 6.34668L3.70606 6.28027C4.04237 6.136 4.40303 6.07083 4.80371 6.03809C5.14051 6.01057 5.53708 6.00551 6.00391 6.00391C6.00551 5.53708 6.01057 5.14051 6.03809 4.80371C6.0755 4.34588 6.15483 3.94012 6.34668 3.56348L6.46875 3.34473C6.77282 2.84912 7.20856 2.44514 7.72949 2.17969L7.87207 2.11328C8.20855 1.96886 8.56979 1.90385 8.9707 1.87109C9.42091 1.83432 9.978 1.83496 10.667 1.83496H13.5C14.1891 1.83496 14.746 1.83431 15.1963 1.87109C15.6541 1.90851 16.0599 1.98788 16.4365 2.17969L16.6553 2.30176C17.151 2.60585 17.5548 3.04244 17.8203 3.56348L17.8867 3.70606C18.031 4.04235 18.0962 4.40306 18.1289 4.80371C18.1657 5.25395 18.165 5.81091 18.165 6.5V9.33301Z"></path></svg>Copiar</button><span class="" data-state="closed"><button class="flex items-center gap-1 py-1 select-none"><svg class="icon-xs" fill="currentColor" height="20" viewbox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path d="M12.0303 4.11328C13.4406 2.70317 15.7275 2.70305 17.1377 4.11328C18.5474 5.52355 18.5476 7.81057 17.1377 9.2207L10.8457 15.5117C10.522 15.8354 10.2868 16.0723 10.0547 16.2627L9.82031 16.4395C9.61539 16.5794 9.39783 16.7003 9.1709 16.7998L8.94141 16.8916C8.75976 16.9582 8.57206 17.0072 8.35547 17.0518L7.59082 17.1865L5.19727 17.5859C5.05455 17.6097 4.90286 17.6358 4.77441 17.6455C4.67576 17.653 4.54196 17.6555 4.39648 17.6201L4.24707 17.5703C4.02415 17.4746 3.84119 17.3068 3.72559 17.0957L3.67969 17.0029C3.59322 16.8013 3.59553 16.6073 3.60547 16.4756C3.61519 16.3473 3.6403 16.1963 3.66406 16.0537L4.06348 13.6602C4.1638 13.0582 4.22517 12.6732 4.3584 12.3096L4.45117 12.0791C4.55073 11.8521 4.67152 11.6346 4.81152 11.4297L4.9873 11.1953C5.17772 10.9632 5.4146 10.728 5.73828 10.4043L12.0303 4.11328ZM6.67871 11.3447C6.32926 11.6942 6.14542 11.8803 6.01953 12.0332L5.90918 12.1797C5.81574 12.3165 5.73539 12.4618 5.66895 12.6133L5.60742 12.7666C5.52668 12.9869 5.48332 13.229 5.375 13.8789L4.97656 16.2725L4.97559 16.2744H4.97852L7.37207 15.875L8.08887 15.749C8.25765 15.7147 8.37336 15.6839 8.4834 15.6436L8.63672 15.5811C8.78817 15.5146 8.93356 15.4342 9.07031 15.3408L9.2168 15.2305C9.36965 15.1046 9.55583 14.9207 9.90527 14.5713L14.8926 9.58301L11.666 6.35742L6.67871 11.3447ZM16.1963 5.05371C15.3054 4.16304 13.8616 4.16305 12.9707 5.05371L12.6074 5.41602L15.833 8.64258L16.1963 8.2793C17.0869 7.38845 17.0869 5.94456 16.1963 5.05371Z"></path><path d="M4.58301 1.7832C4.72589 1.7832 4.84877 1.88437 4.87695 2.02441C4.99384 2.60873 5.22432 3.11642 5.58398 3.50391C5.94115 3.88854 6.44253 4.172 7.13281 4.28711C7.27713 4.3114 7.38267 4.43665 7.38281 4.58301C7.38281 4.7295 7.27723 4.8546 7.13281 4.87891C6.44249 4.99401 5.94116 5.27746 5.58398 5.66211C5.26908 6.00126 5.05404 6.43267 4.92676 6.92676L4.87695 7.1416C4.84891 7.28183 4.72601 7.38281 4.58301 7.38281C4.44013 7.38267 4.31709 7.28173 4.28906 7.1416C4.17212 6.55728 3.94179 6.04956 3.58203 5.66211C3.22483 5.27757 2.72347 4.99395 2.0332 4.87891C1.88897 4.85446 1.7832 4.72938 1.7832 4.58301C1.78335 4.43673 1.88902 4.3115 2.0332 4.28711C2.72366 4.17203 3.22481 3.88861 3.58203 3.50391C3.94186 3.11638 4.17214 2.60888 4.28906 2.02441L4.30371 1.97363C4.34801 1.86052 4.45804 1.78333 4.58301 1.7832Z"></path></svg>Editar</button></span></div></div></div><div class="overflow-y-auto p-4" dir="ltr">`docker run -d \  --name haproxy-logs \  -v $(<span class="hljs-built_in">pwd</span>)/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \  -v /dev/log:/dev/log \  -p 2025:2025 \  haproxy:2.9`</div></div>> ✅ O `-v /dev/log:/dev/log` é essencial — ele conecta o HAProxy ao rsyslog do host.

### 👉 Exemplo com `docker-compose.yml`:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-services%3A-haproxy%3A-i"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary select-none rounded-t-2xl">  
</div><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-attr">services:</span>  <span class="hljs-attr">haproxy:</span>    <span class="hljs-attr">image:</span> <span class="hljs-string">haproxy:2.9</span>    <span class="hljs-attr">volumes:</span>      <span class="hljs-bullet">  -</span> <span class="hljs-string">./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro</span>      <span class="hljs-bullet">  -</span> <span class="hljs-string">/dev/log:/dev/log</span>    <span class="hljs-attr">ports:</span>      <span class="hljs-bullet">  -</span> <span class="hljs-string">"2025:2025"<br></br></span>`</div></div>---

## ✅ 4. Verificar os logs

Após acessar HAProxy (ex: `curl http://localhost:2025/teste`), veja os logs com:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-tail--f-%2Fvar%2Flog%2Fhap"><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-built_in">tail</span> -f /var/log/haproxy.log`</div></div>O horário será o local do host, por exemplo:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-jul-18-10%3A08%3A27-hapr"><div class="overflow-y-auto p-4" dir="ltr">`<span class="hljs-selector-tag">Jul</span> <span class="hljs-number">18</span> <span class="hljs-number">10</span>:<span class="hljs-number">08</span>:<span class="hljs-number">27</span> <span class="hljs-selector-tag">haproxy-logs</span> <span class="hljs-selector-tag">haproxy</span><span class="hljs-selector-attr">[1]</span>: <span class="hljs-number">192.168</span><span class="hljs-selector-class">.0</span><span class="hljs-selector-class">.10</span>:<span class="hljs-number">42839</span> <span class="hljs-selector-attr">[18/Jul/2025:10:08:27.315]</span> <span class="hljs-selector-tag">http_front</span> <span class="hljs-selector-tag">doc_backend</span> ...`</div></div>---

## 🧪 Dica extra: usar hostname no log

Se quiser que apareça o nome do container no log, passe `-h haproxy-logs` no `docker run`, ou use no `docker-compose.yml`:

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk-hostname%3A-haproxy-lo"><div class="overflow-y-auto p-4" dir="ltr">`    <span class="hljs-attr">hostname:</span> <span class="hljs-string">haproxy-logs</span>`</div></div>---

## 🚀 Pronto!

Agora o HAProxy está:

- Gerando logs com `option httplog`
- Mandando logs para o rsyslog do host via `/dev/log`
- Gravando logs com horário **local** em `/var/log/haproxy.log`

# Reconfiguração automática do haproxy.cfg do HAPROXY

ChatGPT:

##### Perfeito! Aqui está um script cron completo e seguro para fazer o reload do haproxy.cfg em todos os containers haproxy do Swarm, sem reiniciar os containers — apenas recarregando o processo haproxy com kill -HUP.

<div id="bkmrk-">  
</div>✅ Script: reload-haproxy-containers.sh

<div id="bkmrk-bash">bash</div>##### \## INICIO SCRIPT ##

<div id="bkmrk-%23%21%2Fbin%2Fbash">\#!/bin/bash</div><div id="bkmrk-%23-nome-da-imagem-usa">\# Nome da imagem usada no serviço haproxy</div><div id="bkmrk-image_name%3D%22haproxyt">IMAGE_NAME="haproxytech/haproxy-debian:3.3"</div><div id="bkmrk-%23-comando-de-reload-">\# Comando de reload usado dentro do container</div><div id="bkmrk-reload_cmd%3D%22haproxy-">RELOAD_CMD="haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg &amp;&amp; kill -HUP 1"</div><div id="bkmrk-%23-busca-containers-q">\# Busca containers que usam a imagem especificada</div><div id="bkmrk-containers%3D%24%28docker-">CONTAINERS=$(docker ps --filter "ancestor=$IMAGE_NAME" --format "{{.ID}}")</div><div id="bkmrk--1">  
</div><div id="bkmrk-if-%5B--z-%22%24containers">if [ -z "$CONTAINERS" ]; then</div><div id="bkmrk-%C2%A0-%C2%A0-echo-%22%5Binfo%5D-nen"> echo "[INFO] Nenhum container HAProxy em execução com imagem '$IMAGE_NAME'"</div><div id="bkmrk-%C2%A0-%C2%A0-exit-0"> exit 0</div><div id="bkmrk-fi">fi</div><div id="bkmrk-echo-%22%5Binfo%5D-recarre">echo "[INFO] Recarregando configuração HAProxy em $(echo "$CONTAINERS" | wc -l) container(s)..."</div><div id="bkmrk-for-cid-in-%24containe">for cid in $CONTAINERS; do</div><div id="bkmrk-%C2%A0-%C2%A0-echo-%22-%E2%86%92-validan"> echo " → Validando e recarregando HAProxy no container $cid..."</div><div id="bkmrk-%C2%A0-%C2%A0-docker-exec-%22%24ci"> docker exec "$cid" sh -c "$RELOAD_CMD" &amp;&amp; \</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-echo-%22-%E2%9C%85-con"> echo " ✅ Container $cid recarregado com sucesso." || \</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-echo-%22-%E2%9D%8C-err"> echo " ❌ Erro ao recarregar o container $cid."</div><div id="bkmrk-done">done</div><div id="bkmrk--2"></div>##### \## FIM SCRIPT ##

<div id="bkmrk--3"></div>##### 🔧 Como usar

##### 1. Salve o script

<div id="bkmrk-sudo-mkdir--p-%2Fusr%2Fl">sudo mkdir -p /usr/local/bin</div><div id="bkmrk-sudo-nano-%2Fusr%2Flocal">sudo nano /usr/local/bin/reload-haproxy-containers.sh</div><div id="bkmrk-cole-o-conte%C3%BAdo%2C-sal">Cole o conteúdo, salve, e depois:</div><div id="bkmrk-sudo-chmod-%2Bx-%2Fusr%2Fl">sudo chmod +x /usr/local/bin/reload-haproxy-containers.sh</div><div id="bkmrk--4"></div><div id="bkmrk--5"></div>##### 2. Agende via crontab

<div id="bkmrk-edite-o-cron-para-o-">Edite o cron para o usuário que gerencia o Swarm (geralmente root):</div><div id="bkmrk-sudo-crontab--e">sudo crontab -e</div><div id="bkmrk-adicione-a-linha-par">Adicione a linha para executar de hora em hora:</div><div id="bkmrk-0-%2A-%2A-%2A-%2A-%2Fusr%2Flocal">0 * * * * /usr/local/bin/reload-haproxy-containers.sh &gt;&gt; /var/log/reload-haproxy.log 2&gt;&amp;1</div><div id="bkmrk--6"></div><div id="bkmrk--7"></div>##### 🛡️ Segurança adicional (opcional)

<div id="bkmrk-se-quiser-ser-ainda-">Se quiser ser ainda mais seletivo, você pode filtrar por nome do serviço ou label, ex:</div><div id="bkmrk-docker-ps---filter-%22">docker ps --filter "name=mystack_haproxy" --format "{{.ID}}"</div><div id="bkmrk-ou-com-label%3A">Ou com label:</div><div id="bkmrk-docker-ps---filter-%22-1">docker ps --filter "label=com.docker.stack.namespace=mystack" --format "{{.ID}}"</div><div id="bkmrk--8"></div><div id="bkmrk-%E2%9C%85-resultado">✅ Resultado</div><div id="bkmrk-com-isso%2C-seu-swarm%3A">Com isso, seu Swarm:</div><div id="bkmrk--9">  
</div><div id="bkmrk-%E2%9C%85-executar%C3%A1-reload-l">✅ Executará reload leve e seguro do haproxy.cfg de hora em hora</div><div id="bkmrk--10">  
</div><div id="bkmrk-%E2%9C%85-sem-precisar-redep">✅ Sem precisar redeploy, sem downtime</div><div id="bkmrk--11">  
</div><div id="bkmrk-%E2%9C%85-sincronizado-via-g">✅ Sincronizado via GlusterFS automaticamente</div><div id="bkmrk--12">  
</div><div id="bkmrk-%E2%9C%85-sem-scripts-dentro">✅ Sem scripts dentro dos containers</div>

# Reconfiguração automática do haproxy.cfg do HAPROXY - 2

chatGPT

<div id="bkmrk-perfeito%21-abaixo-est">Perfeito! Abaixo está um script que monitora alterações no arquivo haproxy.cfg via inotify e executa um reload automático nos containers HAProxy quando detectar mudanças — ideal para quem está usando GlusterFS com bind mount no Docker Swarm.</div><div id="bkmrk-">  
</div><div id="bkmrk-%E2%9C%85-objetivo">✅ Objetivo</div><div id="bkmrk-monitorar%3A">Monitorar:</div><div id="bkmrk--1">  
</div><div id="bkmrk-bash">bash</div><div id="bkmrk--2"></div><div id="bkmrk-%2Fopt%2Faplicativos%2Fhap">/opt/aplicativos/haproxy/haproxy.cfg</div><div id="bkmrk-quando-houver-qualqu">Quando houver qualquer modificação, o script:</div><div id="bkmrk--3">  
</div><div id="bkmrk-valida-a-nova-config">Valida a nova configuração com haproxy -c</div><div id="bkmrk--4">  
</div><div id="bkmrk-executa-kill--hup-1-">Executa kill -HUP 1 no processo do HAProxy dentro de cada container ativo</div><div id="bkmrk--5">  
</div><div id="bkmrk-%E2%9C%85-pr%C3%A9-requisitos">✅ Pré-requisitos</div><div id="bkmrk-instale-o-inotify-to">Instale o inotify-tools no node onde o script vai rodar:</div><div id="bkmrk-bash-1">bash</div><div id="bkmrk-copiar">Copiar</div><div id="bkmrk-editar">Editar</div><div id="bkmrk-sudo-apt-install-ino">sudo apt install inotify-tools</div><div id="bkmrk-%F0%9F%93%9C-script%3A-watch-hapr">📜 Script: watch-haproxy-reload.sh</div><div id="bkmrk-bash-2">bash</div><div id="bkmrk-copiar-1">Copiar</div><div id="bkmrk-editar-1">Editar</div><div id="bkmrk-%23%21%2Fbin%2Fbash">\#!/bin/bash</div><div id="bkmrk--6">  
</div><div id="bkmrk-watch_file%3D%22%2Fopt%2Fapl">WATCH_FILE="/opt/aplicativos/haproxy/haproxy.cfg"</div><div id="bkmrk-image%3D%22haproxytech%2Fh">IMAGE="haproxytech/haproxy-debian:3.3"</div><div id="bkmrk--7">  
</div><div id="bkmrk-echo-%22%5Binfo%5D-monitor">echo "[INFO] Monitorando $WATCH_FILE por alterações..."</div><div id="bkmrk--8">  
</div><div id="bkmrk-inotifywait--m--e-cl">inotifywait -m -e close_write "$WATCH_FILE" | while read -r path event file; do</div><div id="bkmrk-%C2%A0-%C2%A0-echo-%22%5Binfo%5D-det"> echo "[INFO] Detecção de alteração em $file — iniciando validação e reload..."</div><div id="bkmrk--9">  
</div><div id="bkmrk-%C2%A0-%C2%A0-containers%3D%24%28doc"> CONTAINERS=$(docker ps --filter "ancestor=$IMAGE" --format "{{.ID}}")</div><div id="bkmrk--10">  
</div><div id="bkmrk-%C2%A0-%C2%A0-if-%5B--z-%22%24contai"> if [ -z "$CONTAINERS" ]; then</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-echo-%22%5Bwarn%5D"> echo "[WARN] Nenhum container com imagem $IMAGE em execução."</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-continue"> continue</div><div id="bkmrk-%C2%A0-%C2%A0-fi"> fi</div><div id="bkmrk--11">  
</div><div id="bkmrk-%C2%A0-%C2%A0-for-cid-in-%24cont"> for cid in $CONTAINERS; do</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-echo-%22%E2%86%92-vali"> echo "→ Validando config dentro do container $cid..."</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-if-docker-ex"> if docker exec "$cid" haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg; then</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-%C2%A0-%C2%A0-echo-%22%E2%9C%93-"> echo "✓ Configuração válida. Recarregando HAProxy no container $cid..."</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-%C2%A0-%C2%A0-docker-e"> docker exec "$cid" kill -HUP 1</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-%C2%A0-%C2%A0-echo-%22%E2%9C%85-"> echo "✅ Reload concluído para $cid"</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-else"> else</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-%C2%A0-%C2%A0-echo-%22%E2%9D%8C-"> echo "❌ Configuração inválida. Nenhum reload feito."</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-fi"> fi</div><div id="bkmrk-%C2%A0-%C2%A0-done"> done</div><div id="bkmrk-done">done</div><div id="bkmrk-%E2%9C%85-como-usar">✅ Como usar</div><div id="bkmrk-1.-salve-o-script">1. Salve o script</div><div id="bkmrk-bash-3">bash</div><div id="bkmrk-copiar-2">Copiar</div><div id="bkmrk-editar-2">Editar</div><div id="bkmrk-sudo-nano-%2Fusr%2Flocal">sudo nano /usr/local/bin/watch-haproxy-reload.sh</div><div id="bkmrk-cole-o-conte%C3%BAdo-acim">Cole o conteúdo acima, salve e torne executável:</div><div id="bkmrk--12">  
</div><div id="bkmrk-bash-4">bash</div><div id="bkmrk-copiar-3">Copiar</div><div id="bkmrk-editar-3">Editar</div><div id="bkmrk-sudo-chmod-%2Bx-%2Fusr%2Fl">sudo chmod +x /usr/local/bin/watch-haproxy-reload.sh</div><div id="bkmrk-2.-execute-em-backgr">2. Execute em background (em 1 só node basta)</div><div id="bkmrk-bash-5">bash</div><div id="bkmrk-copiar-4">Copiar</div><div id="bkmrk-editar-4">Editar</div><div id="bkmrk-nohup-%2Fusr%2Flocal%2Fbin">nohup /usr/local/bin/watch-haproxy-reload.sh &gt;&gt; /var/log/haproxy-watch.log 2&gt;&amp;1 &amp;</div><div id="bkmrk-ou-adicione-no-%2Fetc%2F">Ou adicione no /etc/rc.local ou systemd para subir automaticamente no boot.</div><div id="bkmrk--13">  
</div><div id="bkmrk-%F0%9F%9B%A1%EF%B8%8F-seguran%C3%A7a-e-estab">🛡️ Segurança e estabilidade</div><div id="bkmrk-use-em-apenas-1-node">Use em apenas 1 node do Swarm para evitar reloads duplicados.</div><div id="bkmrk--14">  
</div><div id="bkmrk-o-reload-%C3%A9-suave-%28co">O reload é suave (com SIGHUP), não derruba conexões abertas.</div><div id="bkmrk--15">  
</div><div id="bkmrk-voc%C3%AA-pode-integrar-i">Você pode integrar isso com o script cron também, se quiser backup/rollback.</div><div id="bkmrk--16">  
</div><div id="bkmrk-%E2%9C%85-resumo-do-que-voc%C3%AA">✅ Resumo do que você tem agora:</div><div id="bkmrk-item-status">Item Status</div><div id="bkmrk-glusterfs-replicando">GlusterFS replicando haproxy.cfg ✅</div><div id="bkmrk-containers-docker-sw">Containers Docker Swarm usando bind mount ✅</div><div id="bkmrk-reload-autom%C3%A1tico-co">Reload automático com inotify ✅</div><div id="bkmrk-valida%C3%A7%C3%A3o-de-configu">Validação de configuração antes do reload ✅</div>