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.
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.
Thanks people if you reached till here. You guys are awesome. Keep learning, keep building ππππππ
Top comments (1)
Your analysis of the docker file is so helpful. Thank You