Getting Started with DockerFile for Asp.Net Core
Tags: devops, aspnetcore, dotnet, programming
Published: 2021-06-09 10:43:25

So over the past few days I have been working on a .Net Core API which was using SQL Server via the Entity Framework Core. Now since I am using M1 Mac the only way to achieve this was by running the SQL Server on docker.

The implementation was straight forward but running the entire app on docker was bit tricky and it took some time to figure it out. So once I was successful, I thought why not jot down my understanding of the docker file created by Visual Studio and how I completed this task

## Pre-requisites

* Visual Studio IDE or Visual Studio Code along with Asp.Net Core framework
* Docker Desktop up and running on the system
* Dot Net API Solution, the one I am using has a dependency on SQL Server (https://github.com/rajat-srivas/GallaSoft.RetailerOnboarding)

## Requirement

* We need to run the application entirely via docker 
* SQL Server container should be up and running before the .Net container as it is required for seeding the initial database using the Entity Framework migrations

## Creating the Docker File

* So I believe that you already have a project ready on which we are going to follow this step
* We will use the default dockerfile generated via Visual Studio for our implementation
* To add Docker Support: Right click on the Project >> Add >> Docker Support
* This will create three files in the solution, Dockerfile, docker-compose.yml and dockerignore
* We are going to focus on the first two files and understand it content

### Dockerfile

```
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["GallaSoft.RetailerOnboardingAPI/GallaSoft.RetailerOnboardingAPI.csproj", "GallaSoft.RetailerOnboardingAPI/"]
RUN dotnet restore "GallaSoft.RetailerOnboardingAPI/GallaSoft.RetailerOnboardingAPI.csproj"
COPY . .
WORKDIR "/src/GallaSoft.RetailerOnboardingAPI"
RUN dotnet build "GallaSoft.RetailerOnboardingAPI.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "GallaSoft.RetailerOnboardingAPI.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "GallaSoft.RetailerOnboardingAPI.dll"]
```

let's break down these 4 stages here and try to get some more detailed info about each and every one of them

```
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
```

* The **FROM** command is used to specify the base image which is the Asp.Net 5.0 runtime environment required to run dot net based applications and name it is as base here
* We then use the **WORKDIR** command to change the working directory for subsequent instructions in the Dockerfile
* We also expose the ports 80 and 443

```
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["GallaSoft.RetailerOnboardingAPI/GallaSoft.RetailerOnboardingAPI.csproj", "GallaSoft.RetailerOnboardingAPI/"]
RUN dotnet restore "GallaSoft.RetailerOnboardingAPI/GallaSoft.RetailerOnboardingAPI.csproj"
COPY . .
WORKDIR "/src/GallaSoft.RetailerOnboardingAPI"
RUN dotnet build "GallaSoft.RetailerOnboardingAPI.csproj" -c Release -o /app/build
```

* Now in this stage we are actually building our project and just before that we run the **restore** command to make sure all dependencies are resolved
* We are also changing the base image and working directory of our container here
* The base image is now the dotnet sdk named as **build** which is required to build and restore our project 
* Working directory is changed as we want to keep the size of eventual container as small as possible and we dont need the sdk as we will require only the published file in the image
* **COPY** command is used to copy the content from source to destination. Here being from the project directory to the WORKDIR
* Then we run the **dotnet restore** command on the project, copy everything to the WORKDIR
* We then build the project in Release mode and specify the output directory

```
FROM build AS publish
RUN dotnet publish "GallaSoft.RetailerOnboardingAPI.csproj" -c Release -o /app/publish
```

* Now we have the build files of our project and next step is to use the build base image to publish our project in the output directory specified

```
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "GallaSoft.RetailerOnboardingAPI.dll"]
```

* In the final stage we again go back to the base image of dotnet runtime as we dont need the build sdk anymore
* We only copy the entire content of the publish folder to our image
* Finally, we just specify the **ENTRYPOINT** which will be used to run the app when the container is ran using this dockerfile

Now since we have gone through the Dockerfile which was created, we move on to the docker-compose.yml file which is actually used to build this project using the dockerfile

We will have to create this on our own and let's discuss how we have done this for our usecase

### Docker Compose

If your project doesn't have any dependency that needs to be locally present, then the docker-compose file will only have instruction to create and run a container from the dockerfile. 

But since here we need to have the SQL Server running before the dotnet project is ran, we specify and create this dependency

```
version: '1.4'

services:

  db:
   image: "mcr.microsoft.com/azure-sql-edge"
   container_name: sql-server-db
   ports:
      - 1433:1433
   environment:
      - SA_PASSWORD=MyPass@word
      - MSSQL_PID=Developer
      - ACCEPT_EULA='Y'
    

  gallasoft.retaileronboardingapi:
    image: ${DOCKER_REGISTRY-}gallasoft
    ports:
       - 49838:443
    build:
      context: .
      dockerfile: GallaSoft.RetailerOnboardingAPI/Dockerfile
    depends_on:
      - db

```

For our API to run, we need to two services to be running on the docker container, the db which refers to the SQL Server instance and the API project itself

*Note*: I am using the Azure SQL Edge version of SQL and not the SQL Server since at the time implementation this was the best way to run an instance of sql server on M1 powered MacBooks.

#### db Service

* The properties mentioned here are similar to the one used while we run the container using the **docker run** command
* We specify the image, container name, ports and the password which will be used to the access the server


#### api Service

* This step is to use the Dockerfile and create and run image 
* The important thing here is the **depends_on** tag which specifies that this service depends on the db service and hence, it must be up and running for seeding data before we run it.

So, that is all the overhead which is required to containerise our dot net core apps.

![So Simple Gif](
https://media.giphy.com/media/dQkcf8GANR0ps57oBH/giphy.gif)

Finally, we can now either run the docker compose command from command prompt/terminal or Run the project from Visual Studio directly.

## Conclusion

We should have the API project running in the browser and the two docker containers running as well. 

![Docker Asp.Net Core Running Api](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tzi3s2q2dvddaanglofj.png)

![Docker Container running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0wplfrj96f6clg7bv5x8.png)

Thanks people if you reached till here. You guys are awesome. Keep learning, keep building 🚀🚀🚀🚀🚀🚀