DEV Community

Cover image for ASP.NET Core - Load Balancing com Nginx e Docker
Danilo Silva
Danilo Silva

Posted on

ASP.NET Core - Load Balancing com Nginx e Docker

O load balancing, ou balanceamento de carga é a prática que permite que o tráfego de uma aplicação seja distribuído entre diferentes servidores ou recursos computacionais para evitar sobrecargas e garantir um desempenho eficiente.

Imagine um site popular que recebe muitas visitas em uma determinada funcionalidade. Tanto backend como frontend dessa funcionalidade poderiam se tornar muito demorados.

Em vez de deixar um único servidor lidar com todas as solicitações, o load balancing distribui essas solicitações entre vários servidores. Isso ajuda a evitar que um servidor fique sobrecarregado, garantindo que a aplicação continue funcionando de maneira rápida e eficiente, mesmo durante períodos de alto tráfego.

Aqui, veremos como utilizar o Nginx para balancear a carga de uma API com 03 instâncias iniciadas. Cada instância irá rodar em um servidor, no nosso caso, um container docker, e as solicitações para essa API serão divididas entre as diferentes instâncias.

Image description

API

Portanto, para verificar se as mesmas chamadas estão sendo realizadas em diversas máquinas, vamos criar uma controller de exemplo que mostre essa determinada funcionalidade.

using Microsoft.AspNetCore.Mvc;

namespace FirstAPI.Controllers;

[ApiController]
[Route("[controller]")]
public class StatusController : ControllerBase
{
    private readonly ILogger<StatusController> _logger;

    public StatusController(ILogger<StatusController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IActionResult Get()
    {
        _logger.LogInformation("Request on Status Controller");
        var MachineName = System.Environment.MachineName;
        return Ok(new { Status = "online", MachineName });
    }
}
Enter fullscreen mode Exit fullscreen mode

Criamos aqui uma API chamada FirstAPI no ASP.NET Core. A rota GET /status mostrará uma mensagem fixa de online junto do nome da máquina na qual a aplicação está rodando. Obtemos essa informação através da propriedade de ambiente System.Environment.MachineName.

Para que a nossa API também seja executada em uma porta parametrizada pela variável de ambiente, devemos forçar essa URL no arquivo Program.cs ou Startup.cs com a seguinte instrução:

var port = builder.Configuration["PORT"];
builder.WebHost.UseUrls($"http://*:{port}");

var app = builder.Build();
Enter fullscreen mode Exit fullscreen mode

Isso garantirá que a aplicação suba pela variável de ambiente PORT.

Dockerfile

Para que essa API possa ser executada em um container, devemos construir um dockerfile para a mesma. Sabendo que o nome da aplicação é FirstAPI e que estamos utilizando o .NET Framework 7.0, o dockerfile se dará da seguinte forma:

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS publish-environment
WORKDIR /app

COPY . ./
RUN dotnet restore
RUN dotnet publish -c Release -o published

FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /app
COPY --from=publish-environment /app/published .
ENTRYPOINT ["dotnet", "FirstAPI.dll"]

Enter fullscreen mode Exit fullscreen mode

Nginx

O Nginx é um servidor web de código aberto, utilizado para servir conteúdo na internet, sendo um dos servidores web mais leve dentre os existentes. Além de suas capacidades como servidor web, o Nginx também pode agir como proxy reverso, balanceador de carga e servidor de cache.

Utilizaremos o Nginx como balanceador de carga através do simples arquivo de configuração abaixo:

upstream api {
    server first-api:3000;    
}

server {
    listen 80;

    include /etc/nginx/mime.types;

    location / {
        proxy_pass http://api/;
    }
}
Enter fullscreen mode Exit fullscreen mode

Nessa configuração, primeramente criamos um upstream chamado api. Aqui temos como server, o endereço chamado first-api:3000. Nosso intuito será criar o container da api utilizando um docker compose de forma que o nome do serviço, e consequentemente, o endereço criado no DNS da rede criada pelo docker compose seja first-api.

Caso aqui você tivesse outros servers com diferentes nomes e portas, mas que fizessem parte da mesma funcionalidade, você poderia apenas adicionar linhas nesse bloco. Por exemplo:

upstream api {
    server first-api:3000;    
    server second-api:3001; 
}
Enter fullscreen mode Exit fullscreen mode

Nesse bloco, o endereço second-api:3001 também seria uma das opções no load balancing.

Depois, temos o bloco server que definirá o servidor. Esse servidor será criado na porta 80 e o redirecionamento será para um proxy apontado para o upstream api, portanto para o endereço http://api/.

Docker compose

Agora podemos criar um docker compose com dois serviços. O primeiro serviço será a nossa API com o nome first-api.

version: '3'
services:
  first-api:
    build: ./APIs/FirstAPI
    environment:
      - PORT=3000
    deploy:
      replicas: 3
    networks:
      - load-balancing

networks:
  load-balancing:
Enter fullscreen mode Exit fullscreen mode

Nesse serviço, injetamos a variável de ambiente PORT com o valor 3000. Criamos uma rede interna chamada load-balancing para o nome do DNS funcionar dentro do docker. Também criamos a chamada de replicas igual a 3 para subir 03 containers com a mesma imagem de nossa API.

Para o serviço do Nginx, seguiremos com a seguinte configuração no mesmo docker compose:

  nginx:
    image: nginx:latest
    container_name: reverse_proxy
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    ports:
      - "3000:80"
    networks:
      - load-balancing
    depends_on:
      - first-api
Enter fullscreen mode Exit fullscreen mode

Nesse serviço, temos 04 configurações importantes. Primeiramente, o volume que fará com que o arquivo nginx.conf que criamos a pouco seja o arquivo de configuração default dentro do container do nginx.

Fazemos um bind de portas para que a porta 80 do nginx seja refletida como a porta 3000, originalmente a porta do serviço que devemos replicar.

Devemos também manter a mesma rede load-balancing e dizer que esse serviço depende do serviço first-api.

Com esse docker-compose.yml, podemos subir o mesmo pelo comando:

docker compose up -d --build
Enter fullscreen mode Exit fullscreen mode

E esperamos o seguinte resultado:

Image description

Uso da API

A partir desse container, o load balancing do Nginx irá distribuir as solicitações dentre as 03 instâncias da WebAPI.

Image description

Para facilitar os testes, você pode clonar o Repositório completo.

Danilo Silva

Desenvolvedor de software experiente em boas práticas, clean code e no desenvolvimento de software embarcado e de integração com hardwares de controle e telecomunicação.

Linkedin
Github
Twitter
E-mail

Top comments (0)