Introduction
First of all and before we go straight to deployment and demo let’s understand what is a reverse proxy .
A reverse proxy intercepts all incoming requests and direct them to the appropriate server ,it can play the role of Load Balancer ” a Traffic Cop ” that will distribute the clients requests across group of servers in order to maximizes speed and capacity and making sure that no server is overloaded (in case a server is down the load balancer will redirect the traffic to the other servers ) ,more than that a reverse proxy will protect the identity of the other servers and act as a defense againsts security attacks .In this sense the reverse proxy will make sure that multiple servers can be accessed only from a single URL .
Prerequisites
- Visual studio or visual studio code
- A command mine /Terminal
- Docker installed on your system
Part 1- Without Docker Compose
in this first part we will create our dotnet application that we will use now and later too , also nginx .
and we will go straight to our Index.cshtml , and edit it to be like this :
@page @model IndexModel @{ ViewData["Title"] = "Home page"; var hostname = ViewData["host"]; } <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> <p> <strong>Hostname :</strong> <h3> <code>@hostname</code></h3> </p> </div>
Also we will edit the Index.cshtml.cs
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using System.Net; namespace hostnameapp.Pages { public class IndexModel : PageModel { private readonly ILogger<IndexModel> _logger; [BindProperty] public string? hostname { get; set; } public IndexModel(ILogger<IndexModel> logger) { _logger = logger; } public void OnGet() { String hostName = Dns.GetHostName(); Console.WriteLine(hostName); ViewData["host"] = hostName; } } }
now if we run the application using IIS express we will have this view :
As you can see the goal here is to show the hostname , nothing special .
now we will dockeriz application by creating a dockerfile with this content (full source code of the solution will be shared )
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS base WORKDIR /app ENV ASPNETCORE_URLS http://+:5000 # EXPOSE 80 EXPOSE 5000 FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build WORKDIR /src COPY ["hostnameapp/hostnameapp.csproj", "hostnameapp/"] RUN dotnet restore "hostnameapp/hostnameapp.csproj" COPY . . WORKDIR "/src/hostnameapp" RUN dotnet build "hostnameapp.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "hostnameapp.csproj" -c Release -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "hostnameapp.dll"]
now we will create the nginx application but we have some config for the app :
we will create a file : nginx.conf
worker_processes 4; events { worker_connections 1024; } http { sendfile on; limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s; # upstream app_servers { # #server app:127.0.0.1:5000; # # server http://backend:5000; # } server { listen 80; location / { proxy_pass http://backend:5000; proxy_redirect off; 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-Host $server_name; } } }
Also we are going to docke the nginx proxy app
FROM nginx:alpine RUN rm -r /etc/nginx/nginx.conf COPY nginx.conf /etc/nginx/nginx.conf
now we can build both apps and run them to see what will happen .
let’s start with our dotnet application we need to build and run our solution :
docker build -t backend . docker run -it --rm -p 5000:5000 --name backend backend
Now if you go to your browser and visit : http://localhost:5000/ you will have this result :
And now as you can see our hostname have the id of the container .
now it’s time to run the nginx proxy that will be the entry to our application or let’s say the way we will expose our application .
docker build -t frontend . docker run -it --rm -p 8082:80 --name frontend frontend
if we run those commandes the project will build but will not run and we will finish by having an error saying that host not found , the host is the backend that we defined in the config file with port 5000 , and that’s because the two containers are not in the same network !
now let’s make some changes :
Let’s create a network :
docker network create demonetwork-001; #to inspect that network docker network inspect demonetwork-001;
now we need to attach our containers to the network that we have created :
docker network connect demonetwork-001 backend; #this will fail because the container did exit beceause of error docker network connect demonetwork-001 frontend; --------------instead we will use this ------------------------- docker run -it --rm -p 5000:5000 --network=demonetwork-001 --name backend backend docker run -it --rm -p 8082:80 --network=demonetwork-001 --name frontend frontend
and like that we will have our result while visiting our entry point on port 8082 :
Part 2- With Docker Compose
now all we need is to create a docker compose file without editing our solutions : docker-compose.yml
version: "3.9" services: app: build: ./hostnameapp expose: - "5000" proxy: #image: nginx:alpine build: ./nginx volumes: - ./nginx.conf:/etc/nginx/nginx.conf depends_on: - app links: - app ports: - "8082:80"
now in our file we define the two apps and we say that the proxy app depends on the first app let see the result :
and this is our goal , we want the backend not to be exposed to public and we want our frontend exposed as you can see on port 8082 and we can not acces the backend , now if we modify the config file and add different backends with different port the load balancer will play it’s role to manage the traffic between the apps .
Now if we have the same app on different port and we edit the config file like this ofr example :
worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server app:5000 weight=4; server app2:5010 weight=2; } server { listen 80; location / { proxy_pass http://app_servers; proxy_redirect off; 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-Host $server_name; } } }
the result will show that we balance between the two apps
that’s it for this demo , in next post we will discuss how we will do this on Azure Kubernetes Service (AKS) .
source code : https://bit.ly/3EDJVus
Comments 6