Imagine this: Your team’s meticulously developed .NET application works flawlessly on your machine but falls apart when deployed on the client’s server. Errors surface, libraries go missing, and dependencies misalign. Despite your best efforts, the app remains stubbornly functional only on your system. The culprit? Subtle environmental differences — OS versions, library dependencies, or config files — spawning major headaches and time-consuming troubleshooting.
Enter Docker, the solution to this all-too-common predicament.
Docker packages your application and all its dependencies into a standardized unit called a container. This container runs identically across any environment—your laptop, the client’s server, or the cloud.
In this blog, I’ll explain the importance of Docker and guide you through dockerizing .NET solutions and deploying them using Azure DevOps.
What is Docker?
Docker is a platform that revolutionizes how we develop, ship, and run applications. At its core, Docker uses containerization technology to package an application and all its dependencies together in a standardized unit called a container.
Think of a container as a lightweight, standalone, executable package that includes everything needed to run a piece of software, including the code, runtime, system tools, libraries, and settings. This container can run consistently on any system that supports Docker, whether it’s a developer’s laptop, a test server, or a production environment in the cloud.
Here are some of the key benefits of Docker:
- Consistency Across Environments: Inconsistency often hinders progress, with the infamous “it works on my machine” dilemma causing delays and impacting collaboration. Docker containers standardize the development environment, ensuring consistent software performance throughout the lifecycle, regardless of where it’s running. It packages applications and their dependencies, making it easy to move them across different environments.
- Isolation of Dependencies: It provides excellent isolation between applications and their dependencies. Each container operates independently, preventing conflicts with other containers on the same machine or server.
- Resource Efficiency: It uses fewer resources compared to traditional virtual machines. The containers share the same OS kernel, making them lightweight and reducing disk space and memory usage.
- Scalability: You can easily create multiple instances of a container and manage workloads efficiently using orchestration tools like Kubernetes or Docker Swarm.
Now that you’re familiar with Docker, let’s dive into the main part of the blog, beginning with the essential requirements for dockerizing.
Prerequisites for Dockerizing .NET Solutions
Here is the list the tools and services required for dockerizing .NET solutions:
- .NET SDK
- Docker Desktop
- Azure DevOps account
- Azure Container Registry (ACR)
- Azure App Service for Containers
With these essentials in hand, you’re ready to move to the action phase!
Steps for Dockerizing and Deploying .NET Solutions
Follow these steps to dockerize and deploy your .NET solutions, from initial setup to final deployment:
Step 1: Setting Up the .NET Solution
First, create a .NET solution which has multiple projects. To do so, follow these steps:
- Open Microsoft Visual Studio.
- Click on create new project.
- Search for ASP.NET core web API.
- Click on next and select .NET 8.0 LTS
- Check on Enable container support.
- Click on Create.
Fig: DotNet Project Folder Structure
Step 2: Adding a Dependency Project
Next, to add a dependency project, you need to follow these steps:
- To add a new project, right-click on the solution and choose Add > New Project. For this example, choose a Console project, or feel free to select any other project type that suits your needs.
- Then, right-click on the dependencies of your main project and select Add Project Reference. Choose the newly created project to establish the reference.
At this point, the dependency will be added, but what about the Dockerfile? Well, if you check it, you’ll notice that the new project isn’t referenced in the Dockerfile.
To fix this, we’ll recreate the Dockerfile by following these steps:
- Right-click on the Dockerfile and delete it.
- Then right-click on the DockerDemoProject, then select Add > Docker Support to regenerate the Dockerfile.
Once done, you’ll see the below given popup. Then click on OK.
Fig: Container Options
Post that, check the Docker file. It will look like this:
Fig: Built In Docker File
This indicates that the dependency project has been copied, reflecting an update to the Dockerfile to include the new dependencies that have been added.
Step 3: Building a Docker Image
- Open the DockerDemoProject in Windows PowerShell.
- Next, navigate to the directory where the Dockerfile is located by using the cd command, and then proceed to create the Docker image.
Use this command:
docker build -t "imageName" .
Note: Here, the dot (.) refers to the fact that the Dockerfile is in the same directory.
- Then, hit Enter to execute the command.
You might encounter this error once you run the command:
Fig: Error with Built In Docker File
I wanted to guide you through this, as the auto-generated Dockerfile may not function as intended.
Therefore, replace it with the following Dockerfile:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src # Copy the solution file and restore dependencies COPY . . RUN dotnet restore # Copy the entire project and build the solution COPY . . RUN dotnet build --no-restore # Publish the main project RUN dotnet publish "DockerDemoProject/DockerDemoProject.csproj" -c Release -o /app/publish # Final stage FROM mcr.microsoft.com/dotnet/aspnet:8.0 WORKDIR /app COPY --from=build /app/publish . # Set environment variables if needed # ENV ASPNETCORE_ENVIRONMENT=Production EXPOSE 8080 ENTRYPOINT ["dotnet", "DockerDemoProject.dll"]
Note: We need to place this Dockerfile inside the Solutions directory, where the .sln file is located as given below:
Fig: Directory to keep New Docker File
Step 4: Creating the Docker Image Again
Now, navigate back to the Solutions directory and run the following command to build the Docker image:
docker build -t dockerdemoproject .
Once this step is completed, the image generation should proceed smoothly and successfully. Then you can verify this by executing the command:
docker images
This command will display a list of available images, including the one just created. Something like this:
Fig: Docker Images Created
Step 5: Deploying the Docker Image
To deploy the Docker image, follow these 3 steps:
- Create an Azure App Service for Containers.
- Create an Azure Container Registry.
- Automate the build and deployment process using Azure DevOps CI/CD pipelines.
Step 6: Creating the App Service
- Ensure you select “Containers” as the publish option when creating the App Service.
- The remaining settings can be configured according to your requirements.
- Click on Create.
You can refer to the image below while creative the app service:
Fig: App Service for Containers Creation
Now let’s move to the second part of this blog where we explore Azure Container Registry (ACR) and learn how to automate the deployment process with Azure DevOps CI/CD (Continuous Integration and Continuous Deployment).
Optimize your digital transformation by following our expertly designed roadmap for a seamless Azure migration.
What is Azure Container Registry (ACR)?
Azure Container Registry (ACR) is used for managing and securely storing Docker container images and artifacts within Azure. It provides a scalable, private registry that integrates seamlessly with Azure services. Thus, making it ideal for managing container lifecycle activities such as – image building, storage, and deployment
To get started with the automation process, create an Azure Container Registry in the Azure portal.
Automating Deployment using Azure DevOps CI/CD
I’ve broken down the automation process for deploying .NET solutions into two parts: a) creating a build pipeline and b) releasing the pipeline.
Part A: Create a Build Pipeline
Follow these steps to create a new pipeline:
1. Navigate to Pipelines in the sidebar of your project.
2. Select New Pipeline, then choose Azure Git Repo (or any other repository where your code is stored) and select your repository. Then choose the Starter Pipeline to begin.
3. You will now see some boilerplate tasks in the pipeline configuration, like this:
Fig: Default YAML File
4. Click on “Show assistant” on the right side, search for Docker like this:
Fig: Adding Docker Task to DockerFile
5. Then select “Docker (build and push) and you will get this:
Fig: Docker Task Configuration
6. Refer to the image above to choose the correct Azure Container Registry (ACR) and specify the path to the Dockerfile after $(Build.SourcesDirectory).
This should generate a task that looks like this:
Fig: Build Pipeline YAML File
Note: This pipeline is triggered when the code is merged into the develop branch, with the vmImage configured to Ubuntu, as our Dockerfile is built on a Linux environment.
After automating the image-building process, you can proceed to push the image to the App Service for Containers.
Part B: Creating a Release Pipeline
To set up a release pipeline, follow these steps:
1. Navigate to Releases and click on Create new release pipeline at the top.
2. Select Azure App Service Deployment, click on Apply, and provide a stage name such as “Development.”
It should look like this:
Fig: Creating Release Pipeline
3. Next, click on Add an artifact, select Build from the options, and choose the relevant pipeline from the dropdown menu. Additionally, you can select the Azure Container Registry and provide – the necessary connection details, ACR, and repository name as needed.
4. Next, click on 1 job, 1 task and from the dropdown, select the appropriate Azure subscription, app type, and app service name.
5. Add the following configurations as shown in the image below, preferably selecting options from the dropdown:
Fig: Docker Release Pipeline Configuration
Optional: You can create a release without this step; however, if a port-related error occurs, click the Plus button and search for Azure App Service Settings. Select the necessary values from the dropdown, and further add WEBSITES_PORT as shown in the image.
Fig: Release Pipeline Configuration
6. To enable automatic triggering of the pipeline, click on the “lightning symbol at the top of the build artifact”. Then, simply enable releases to occur every time a new build is available, as illustrated in the image below:
Fig: AutoTrigger Configuration
Now once you push the code to the repository, the build process will initiate, triggering the release pipeline. As a result, your Docker image will be automatically deployed from code to the cloud.
By leveraging Docker, Azure DevOps, and Azure services, you will be able to streamline the process of building, packaging, and deploying .NET applications. This end-to-end automation ensures that your applications are consistently deployed in any environment. This minimizes manual intervention and enhances productivity.
Therefore, whether you’re managing multiple environments or scaling your application, Docker and Azure DevOps can make the journey from code to cloud both efficient and reliable.
Write to us with your thoughts about this blog and connect with us at Nitor Infotech.