One of the main features of .NET Core framework is the ability to run the applications on variety of environments such as Linux, MacOS etc. It includes a light weight server known as Kestrel which is used to run web apps targeting the .NET Core framework. But one of the main disadvantage of Kestrel is that it is primarily a development server and is not recommended for production environments. We always used IIS as server for hosting .NET Web app, but in the case of .NET Core since it's cross platform and we can't rely on IIS for platforms other than Windows. In the case of Linux, Nginx is one of the most popular web server out there and is very good at serving static content. But, for the example in this post we are going to make use of the reverse proxy(passing requests to other servers) feature in Nginx to serve our core web app from a docker container running on a Linux distro.
I had already written a post about the on how to get started with Nginx and I recommend you to go through the post here if you are working with Nginx for the first time. In this post, I am going to host a .NET Core web app in a Docker container using Nginx and Kestrel to serve the site on a Linux machine.
Before I get started with coding, here's a little advice for all you guys. It is always a recommended thing to follow a proper structure for your source files in your project. In this case, I have created two folders, one for putting all the files related to .NET and one for putting all those related to Nginx as shown in the screenshot below.
Step 1 : Create .NET Core App
Create a sample mvc core application under the dotnet folder. I created this one using the boiler plate template provided by yeoman which generated all the folders and files showed in the above image. If you run the application now using the dotnet run command, it will host the application in Kestrel and can be accessed by using the url http://localhost:5000. I have modified the sample page to show the name of the server using the below code.
Served By : @System.Environment.MachineName
Since I'm running it from the local machine, the page will show the name of my local box as the server name as shown in the image below.
Since we are going to host our application inside the Docker container, let's create a docker file which contains some instructions to Docker to set up the container. It basically tells which base image should be used, copies the files from the current folder to a folder called app inside the container and sets it's as the working directory. Then it executes two dotnet commands to restore the packages first and then executes the build. Once the build is completed it exposes the port #8018 for serving the app from the container. The ENTRYPOINT command is used execute the application when we start the container and in our case it will execute the run command to host the application.
Step 2 : Setting up Nginx
Before proceeding with this step, make sure that you set up Nginx properly in your machine. Let's create a configuration file inside the nginx folder and save it as default.conf. In this file we will specify configuration for redirecting the requests to Kestrel
In the file, we specified the port number and server name that will be used for bootstrapping the Nginx server and set up the location section to redirect all the requests to http://172.17.0.1:8018 where our Kestrel Server is listening for requests.
The Dockerfile is pretty straightforward, instructs to use nginx as the base image, removes the default config file and copies the default config file from the local folder to a folder in the container.
Step 3 : Building Images
Now we will build the images for both our dotnet application and nginx using the docker build command as shown below. The -t switch is used to apply a name to the newly created image and the dot(.) instructs docker to search for the default dockerfile in the current directory. If you have provided a different name for the dockerfile, then you need to explicitly specify that after the dot(.) in the docker build command
docker build -t <tag name> .
We can confirm the creation of the images by running the docker images command as shown in the image below
Step 4 : Running the Containers
The docker run command is used to create and run the container
docker run <container name>
In our case, I am using the following parameters along with the run command
i : interactive mode
t : disable psuedo terminal allocation
d : detached mode
p : port mapping
This command will create a container for our dotnet application and start the application inside the container and waits for requests in port 8018. We can confirm this by firing up the browser and accesing the localhost using port #8018. The resulting web page will show the image id of the container in the resulting web page in our application as shown below.
Let's fire up the nginx container to shield the application hosted in Kestrel from the outside world. By using the following command we will the container and start the nginx server listening to requests at port #8008.
You may recollect that while setting up the configuration for nginx we set up the options so that whatever requests we made for root website, the requests will be redirected to the Kestrel server listening in #8018. So if you to access the site hosted by nginx at #8008, you will get the ASP.NET Core MVC application hosted in the Kestrel server.
If you run the docker ps command now, it will list both our containers as shown below.
Full source code is available is here at GitHub