-->

09/05/2021

Containerizing ASP.Net Core Application on a Linux Host

In this article we will see how to deploy a ASP.Net Core 3.1 Webapplication to a container hosted on a Linux Host.
Beofore we jump into implementation, we need to look at every component and know what it does.


1. Host: This can be a Linux or Windows server. Considering Linux servers have used from long time for contanerization, i picked it. But Windows is very close choice considering all the capabilities it acquired in last 2 years.

2. Docker: It is the engine we use to host our containers. It will package the application with its dependecies and run them in isolated containers on the host.

3 Reverse Proxy Server: This will be sitting behind the firewall and redirects the user requests to appropriate apps/containers. This will provide extra layer of abstraction and reduces the public exposure of the containers. 

4. Kestrel Web Server: When you install a .Net Core SDK, it will internally create a webserver to act as a backend server for the .Net Application. Proxy server will be sending the client calls to Kestrel Web server and responds back with the output from .Net App.

5. App: .Net Core Application we deploy to be containerized. 
Lets begin the implemntation.

Step1: Provisioning the Host
Its recommended that you start using Azure Key Vault for storing the Admin credentials, SSH keys and
Certficate Private keys. So i have my Linux Admin password in Key valut.
I am creating a Linux server using below CLI commands.
$psw=az keyvault secret show --vault-name AdminCreds  -n MyLinuxAdminPSW --query value

az vm create -g LinContainerRG -n LinuxHost --image UbuntuLTS --admin-username linuxadminuser --admin-password $psw
Once the Host is created, go to the virtual machine in zure portal, go to Networking and open Port 80 for TCP connections.

Step 2: Install Docker on the Host
For this we need Putty to connect to our linux host. Download it from this link, and install.
Go to Azure portal and copy the Public IP of the host. Open a new session connection to the host using Putty. You need Public IP of the host for this. 
Once you try to open the session, it will prompt you for credentials, use the admin credentials for now.

Run below commands one by one to install Docker engine on your host.
sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
sudo apt update
sudo apt install docker-ce
Now we got docker installed on our host. 

Step 3: Setup Reverse Proxy Server
A proxy server is a machine with its own IP, which makes web requests on behalf of other machines/users. These can provide badwidth, privacy and additional layer of sceurity.

There are two types of proxy servers, below picture speaks volumnes about it.

Now you know whay we are using Reverse Proxy server infront of our containers.
You can use IIS Express, Apache or NGINX as a proxy server. 
We will setup the proxy server on Host, by executing below commands. 
sudo docker pull nginx:1.17.0
sudo docker images
sudo apt-get install nginx
sudo apt update
At this point if you try to broswe for http://PublicIP of your LinuxHost, this is what you see.

Step 4: Create .Net Core Application
We are now creating a basic Asp.Net Core webapplication. Go to Visual Studio 2019 and create a "ASP.Net Core Web Application". Framework as .Net Core 3.1
I call it "DemoCoreApp".
There are 2 small changes you need to do in code.

1. Go to Startup.cs and replace this line "app.UseHttpsRedirection();" with below code.
app.UseForwardedHeaders(new ForwardedHeadersOptions
	{
		ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
	});
As our core app will not be receiing call in ragulat client server fasion (http), it has to use Forward headers to identify protocal and IP details of the client. The above lines of code will take care of it.

2. Go to solution and add a file with name "Dockerfile" without any extention. Add below code to it.
FROM mcr.microsoft.com/dotnet/core/sdk:3.1
WORKDIR /app
COPY  . .
ENV ASPNETCORE_URLS http://*:5000
EXPOSE 5000
ENTRYPOINT ["dotnet", "DemoCoreApp.dll"]
I will explain every line of this code in following step.  
Right click on the Docketfile and go to properties, chnage Copy to Output Directory to "Copy always".


Now build the project and Publish to a Folder, the published content will be in "\bin\Release\netcoreapp3.1/Publish" folder. Check that Dockerfile is copied to Publish folder.

Step 5: Transfer the Published content o Host
Download "WinSCP" from this link. Install it and open WinSCP to transfer publish folder to Host.
Connect the Host using the PublicIP address and the user credentials.
Left side shows the local files and right side shows the Azure host's file system.
Navigate to "\bin\Release\netcoreapp3.1/" on left side and make sure you opened "/home/Username" directory on your host.
Now Drag and drop the publish folder.


Now go back to your Putty session, and run bwlow code.
cd /etc/nginx/sites-available
sudo chmod 667 default
This will grant you edit access to dafult NGINX routing file.
Remeber we taked about reverse proxy where the proxy server routes the calls to containers based on certain conditions. Now we are going to write those conditions in that default file.

Open WinSCP tool and now navigate to /etc/nginx/sites-available/ and edit the default file.
Paste below content replacing the line "try_files $uri $uri/ =404;"
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
This will take care of forwarding our calls to the .Net Core container.
Save and close it.

Now go back to Putty session and run below command.
sudo nginx -s reload
In some cases you might get an error while reloading NGINX, saying 
nginx: [error] open() "/run/nginx.pid" failed (2: No such file or directory)

There are many fixes on google for this which suites for PROD environment, where you cannot afford to restart. But here i can. So lets go to Azure portal and restart the Linux Host.

Now connect to Host using Putty and run above command.

Step 6: Setting up container for .Netcore App
As i promised earlier, lets have a look at the commands in docker file which we embedded in app.

Now go to Putty session and run below commands one after another.
cd publish
sudo docker build -t democoreapp .
sudo docker run -d -p 5000:5000 democoreapp
Above command will execute the content from Dockerfile in publish folder and create a container to host .Net Core App on port 5000 in kestrel webserver.

Now on your dev machine, if you browse for http://PublicIP of your Linux Host, this is what you see.


Now, how do we know that app is running from container and not from Host itself?
Run below command to see list of containers running.
sudo docker ps

This will show all the containers running, and we can stop the container by running below command.
sudo docker stop XXX
XXX being the first 3 digits of CONTAINER ID, highlighted in above screenshot.
Now the container is stopped , and try accessing the app now.
Now we know that App is hosted in its own container on a Linux Host.
Lets start it back, 
sudo docker run -d -p 5000:5000 democoreapp
Its back online.


We have sucesfully deployed .Net Core App in a container on a Linux Host

No comments:

Post a Comment