Containerizing .NET applications with Docker delivers substantial benefits in terms of portability, reproducibility, and scalability across Kubernetes or Docker-powered infrastructure. Continue reading →
Docker containers offer an isolated and portable environment, enabling developers to seamlessly deploy their .NET applications across various environments, from development machines to testing servers and production infrastructure. This versatility has made them a cornerstone in facilitating .NET development services. In this blog post, we’ll guide you through the complete process of constructing and executing a sample .NET Core console application within a Docker container.
The first step is to create a basic .NET Core console application that we will later containerize. Open up your terminal or command prompt and run the following commands:
dotnet new console -o SampleApp
cd SampleApp
This will generate a new .NET Core console app project in a directory called SampleApp. Open up the generated SampleApp.csproj file and update the TargetFramework property to target .NET Core 3.1:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
Next, open up the Program.cs file and add a simple “Hello World” message:
using System;
namespace SampleApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Now you have a basic .NET Core console app that prints “Hello World” to the console when run.
The next step is to create a Dockerfile that contains the instructions for building a Docker image for this application. Create an empty file called Dockerfile in the SampleApp directory.
The first line of the Dockerfile specifies which base image our Docker image will be built on top of. For .NET Core apps, the official Microsoft Docker images provide the .NET Core runtime and are a good choice as a base image:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
The build stage will copy the source code into the container, restore dependencies, compile the code, and publish the output:
WORKDIR /app
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o out
After building, we use a second Docker image as a runtime image for the published app files. This keeps the final image lightweight:
FROM mcr.microsoft.com/dotnet/core/runtime:3.1
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "SampleApp.dll"]
The full Dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
WORKDIR /app
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/core/runtime:3.1
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "SampleApp.dll"]
Now that we have the Dockerfile ready, we can build the Docker image. Navigate to the folder containing the Dockerfile and run:
docker build -t sampleapp .
This will build a Docker image called “sampleapp” using the Dockerfile in the current directory. Docker will layer the image, restoring dependencies, publishing, and ultimately producing a Docker image containing our .NET application.
To run the Docker container from the newly built image, run:
docker run -it sampleapp
This will start a new container from the sampleapp image, launching the application’s entrypoint which runs dotnet SampleApp.dll. You should see the “Hello World!” output in your terminal.
To run the container in detached mode in the background:
docker run -d --name sampleappcontainer sampleapp
Now the container will run in the background. You can stop, start, or inspect the container using the container name:
docker stop sampleappcontainer
docker start sampleappcontainer
docker logs sampleappcontainer
You have now successfully built and run a .NET Core app inside a Docker container. With Docker, your application is packaged into a standard, portable unit that can be deployed anywhere Docker runs without dependencies on the underlying infrastructure.
Often applications require configuration values to be set at runtime. With Docker, these values can be passed into the container as environment variables.
Open Program.cs and modify the main method to read an environment variable:
static void Main(string[] args)
{
var connectionString = Environment.GetEnvironmentVariable("ConnectionString");
Console.WriteLine($"Connection string is {connectionString}");
}
When running the container, pass the value using -e:
docker run -e ConnectionString="server=localhost;database=app;user=sa;password=Passw0rd" sampleappcontainer
Now the app reads the connection string from the environment.
You can also define default configuration values in appsettings.json and override them at runtime. For example:
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}
Reference the settings file in Program.cs:
static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var logLevel = configuration["Logging:LogLevel:Default"];
// log using logLevel
}
Then override LogLevel when running the container:
docker run -e Logging__LogLevel__Default="Debug" sampleappcontainer
Now the app reads the connection string from the environment.
You can also define default configuration values in appsettings.json and override them at runtime. For example:
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
Reference the settings file in Program.cs:
static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var logLevel = configuration["Logging:LogLevel:Default"];
// log using logLevel
}
Then override LogLevel when running the container:
docker run -e Logging__LogLevel__Default="Debug" sampleappcontainer
These examples demonstrate how Docker enables flexible configuration of applications at runtime through environment variables.
For multi-container applications, Docker Compose allows defining and running multiple application containers and their dependencies in a single configuration file.
Create a docker-compose.yml:
version: '3'
services:
app:
image: sampleapp
ports:
- "8080:80"
database:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=app
ports:
- "3306:3306"
Now instead of launching individual containers, use Docker Compose:
docker-compose up
This will start both the app and database containers together in the background. The database is only accessible to the app container internally on its container network.
Docker Compose is very useful for testing multi-container setups locally before deployment to production environments. Entire development environments with full application stacks including dependencies can be reproduced consistently.
When deploying to production, you should consider container security and optimizations for performance and size. Here are some best practices:
For production deployments to a Kubernetes cluster:
Containerizing .NET applications with Docker delivers substantial benefits in terms of portability, reproducibility, and scalability across Kubernetes or Docker-powered infrastructure. This approach, coupled with DevOps best practices, unleashes the complete potential for automated and resilient application deployments, making it an enticing proposition for businesses looking to hire .NET developers proficient in leveraging Docker for efficient application deployment strategies.
As climate change becomes a pressing issue, sustainability has taken center stage in the beverage…
Errors on credit reports aren’t uncommon, and if left uncorrected, they can create significant financial…
In today's world of education, teachers often feel pressure to create interesting and thorough course…
The traffic laws can be blurry, especially when there are several infractions happening at the…
By incorporating gift cards into your business strategy, you open up flexible options for appreciation,…
Integration of QR codes into CRM and data management tools enhances access, real-time synchronization, insightful…