Você está pronto para levar suas aplicações .NET para o próximo nível? Nesta postagem, vamos explorar como configurar um proxy reverso usando Docker, Nginx e o .NET 8. Essa configuração poderosa não apenas otimiza o desempenho de suas aplicações, mas também oferece maior segurança e escalabilidade.
Antes de começar vamos entender o que é um proxy reverso, você sabe o que é um proxy?
Um proxy é um intermediário entre o usuário e o destino final na comunicação pela internet. Ele atua como um servidor que recebe solicitações de um cliente e as encaminha para o destino pretendido.
Existem vários tipos de proxies, cada um com suas próprias funcionalidades e finalidades:
Proxy de Encaminhamento (Forward Proxy): Neste tipo de proxy, o cliente envia suas solicitações para o proxy, que por sua vez as encaminha para o destino. É comumente usado para contornar restrições de rede ou acessar conteúdo bloqueado.
Proxy Reverso (Reverse Proxy): Ao contrário do forward proxy, o reverse proxy recebe solicitações de clientes em nome de um ou mais servidores de destino. Ele pode ser usado para equilibrar a carga entre servidores, ocultar a infraestrutura de servidores de destino ou fornecer cache e segurança adicional.
Proxy Transparente: Este tipo de proxy não requer configuração no cliente. Ele intercepta todas as solicitações de rede sem que o cliente tenha conhecimento disso. Geralmente usado em firewalls para aplicar políticas de segurança.
Proxy de Nível de Aplicativo: Este tipo de proxy opera no nível de aplicativo e é específico para um protocolo, como HTTP ou FTP. Ele pode modificar os dados transmitidos entre o cliente e o servidor de destino.
Os proxies desempenham um papel crucial na otimização de rede, segurança e gerenciamento de tráfego na internet. Eles são amplamente utilizados em ambientes corporativos, provedores de serviços de internet e aplicativos que exigem alto desempenho e segurança.
Proxy Reverso
Um Proxy Reverso é um tipo de servidor proxy que opera na frente de um ou mais servidores de destino. Ele recebe solicitações de clientes em nome desses servidores e, em seguida, encaminha essas solicitações para o servidor apropriado. O servidor de destino, por sua vez, envia a resposta de volta ao proxy reverso, que a encaminha de volta ao cliente.
Aqui está um exemplo para ilustrar o funcionamento de um proxy reverso:
Imagine que você tenha três servidores web em execução: Servidor A, Servidor B e Servidor C. Cada servidor executa uma parte diferente do seu site ou aplicação. O Proxy Reverso está configurado para lidar com todas as solicitações dos clientes.
1 - Um cliente faz uma solicitação para acessar o seu site.
2 - A solicitação do cliente chega ao Proxy Reverso.
3 - O Proxy Reverso, baseando-se em regras de roteamento definidas, encaminha a solicitação para o servidor apropriado. Por exemplo, se a solicitação for para uma página específica do seu site que está sendo servida pelo Servidor B, o Proxy Reverso encaminhará a solicitação para o Servidor B.
4 - O Servidor B processa a solicitação e envia a resposta de volta ao Proxy Reverso.
5 - O Proxy Reverso recebe a resposta do Servidor B e a envia de volta ao cliente.
Este é um cenário básico, mas as configurações do Proxy Reverso podem ser muito mais complexas. Ele pode ser configurado para balanceamento de carga entre os servidores, cache de conteúdo estático, SSL e filtragem de conteúdo, entre outras funcionalidades.
Em resumo, o Proxy Reverso atua como um intermediário entre os clientes e os servidores de destino, ajudando a melhorar o desempenho, a segurança e a confiabilidade de um sistema distribuído na web.
Minimal API
Para o exemplo, vamos usar uma simples API padrão do .NET. Como o mais importante será a configuração do Nginx e a criação do docker-compose, vou adotar a API criada por padrão.
Para criar uma API, execute o comando:
dotnet new webapi -n WeatherForecastApi
Após a execução do comando, será gerado o projeto WebAPI. No arquivo Program.cs, haverá um código inicial semelhante ao seguinte:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Entre no diretório do projeto e no terminal rode os comandos:
dotnet build
dotnet run
Se tudo estiver certo e nada errado, ao acessar a url http://localhost:5037/swagger/index.html será redirecionado para o swagger. Obs.: No meu caso a aplicação subiu na porta 5037, verifique o seu e mude caso necessário.
Dockerfile
Vamos criar a image da API que acabamos de desenvolver. Para isso, crie um arquivo Dockerfile no projeto com o seguinte conteúdo:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /App
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0
RUN apt-get update && apt-get install -y wget
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "WeatherForecastApi.dll"]
Vamos testar a criação da nossa image e do container. Para criar a imagem, execute o seguinte comando:
docker build -t wandersonalves/weather-forecast-api .
Para criar o container, execute:
docker run --rm -it -p 5069:8080 --name api wandersonalves/weather-forecast-api
Se estiver correto vamos conseguir acessar o Swagger via http://localhost:8080/swagger/index.html
Configurar Nginx
Para podemos fazer que o Nginx seja proxy reverso da nossa API, vamos ter que criar o arquivo nginx.conf. A configuração para esse exemplo será bem simples, porém podemos ter configurações mais complexa.
events {}
http {
server {
listen 80;
location / {
proxy_pass http://api:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
O trecho de código no qual realizamos o redirecionamento ao acessar o Nginx é a linha proxy_pass http://api:8080;. É nesta linha que seremos redirecionados para a nossa API, que está rodando na porta 8080. Uma observação bastante importante é a forma como acessamos nossa API, não é via IP direto e sim pelo nome. Quem fica responsável pela resolução de nome para IP é o Docker. Para isso, o Nginx e a API precisam estar na mesma rede.
Docker-compose
Agora vamos juntar tudo que fizemos até agora para podemos acessar nossa API através do proxy reverso Nginx. Nosso arquivo vai ficar igual o trecho abaixo:
version: "3"
services:
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
networks:
- mynet
depends_on:
api:
condition: service_healthy
api:
build:
context: .
container_name: api
tty: true
ports:
- "8080:8080"
networks:
- mynet
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:8080/healthz || exit 1
interval: 30s
retries: 5
start_period: 20s
timeout: 10s
networks:
mynet:
driver: bridge
O devemos saber:
- Nossa rede é do tipo bridge e o nome dela será mynet, e a API e Nginx estão na mesma rede;
- Como estão na mesma rede podemos usar o nome ao invés do IP, quem fica responsável pela resolução de nome para IP é o docker;
- Para evitar que o container Nginx suba antes da nossa API, implementamos um healthcheck. Além disso, o Nginx tem uma dependência da nossa API usando depends_on. Para saber se nossa API subiu sem nenhum problema vamos criar uma chamada de API Health Checks, para isso adicione as linhas no arquivo Program.cs:
builder.Services.AddHealthChecks();
app.MapHealthChecks("/healthz");
Agora vamos subir nossa infra e ver se nosso proxy reverso vai funcionar, para isso execute o comando:
docker-compose up -d --build
Após subir os dois container, poderemos acessar nossa API na porta 80 do Nginx http://localhost/swagger/index.html
Nossos dois contêineres, a API e o Nginx, estão em execução, e o Nginx está atuando como um servidor de proxy reverso para a API.
Conclusão
A combinação de Docker, Nginx e o .NET 8 oferece uma solução poderosa para otimizar o desempenho, a segurança e a escalabilidade de suas aplicações. Configurando um proxy reverso, podemos direcionar o tráfego de entrada de maneira eficiente para nossas aplicações .NET, garantindo uma experiência de usuário confiável e responsiva.
Ao seguir os passos delineados nesta postagem, você aprendeu como preparar seu ambiente, criar uma aplicação .NET, configurar o Dockerfile e o Nginx como um proxy reverso, e executar o ambiente usando o Docker Compose. Essa abordagem simplifica o processo de implantação e gerenciamento de suas aplicações, permitindo que você se concentre no desenvolvimento de recursos essenciais.
Sinta-se à vontade para explorar e personalizar ainda mais essa configuração de acordo com suas necessidades específicas. E lembre-se, se surgirem dúvidas ou questões adicionais, estou aqui para ajudar!
O código completo pode ser acessado: Github
Referências
Create a minimal API with ASP.NET Core
Criando um proxy de encaminhamento usando o roteamento de solicitação de aplicativo
Top comments (1)
Essa observação aqui me salvou! Muito obrigado.