Exercise - Build a container image for your .NET microservice
In this exercise, you create a microservice endpoint and containerize it by using the .NET SDK and Docker.
Note
You can complete this exercise in an instance of GitHub Codespaces that has Docker and the .NET SDK preinstalled. When you use these tools and techniques in your own development environment, make sure that you have these prerequisites installed.
Open the development environment
You can choose to use a GitHub codespace that hosts the exercise, or complete the exercise locally in Visual Studio Code.
To use a codespace, create a preconfigured GitHub Codespace with this Codespace creation link.
GitHub takes several minutes to create and configure the codespace. When the process completes, you see the code files for the exercise. The code used for the remainder of this module is in the /dotnet-docker directory.
To use Visual Studio Code, clone the https://github.com/MicrosoftDocs/mslearn-dotnet-cloudnative repository to your local machine. Then:
- Install any system requirements to run Dev Container in Visual Studio Code.
- Make sure Docker is running.
- In a new Visual Studio Code window open the folder of the cloned repository
- Press Ctrl+Shift+P to open the command palette.
- Search: >Dev Containers: Rebuild and Reopen in Container
- Select eShopLite - dotnet-docker from the drop-down. Visual Studio Code creates your development container locally.
Use .NET publish to create the Products back-end image
The latest .NET 8 release improves support for containerization. You can use the dotnet publish
command to create a Docker image for your microservices. The command creates a rootless container image that runs services under an app
account. Running rootless containers is great for security and performance. The command knows how to pick the best base image by checking the settings in the project file.
To create the images for all the eShopLite services, go to the TERMINAL tab and run this command:
cd ./dotnet-docker dotnet publish /p:PublishProfile=DefaultContainer
You see output like the following messages:
DataEntities -> /workspaces/mslearn-dotnet-cloudnative/dotnet-docker/DataEntities/bin/Release/net8.0/publish/ Products -> /workspaces/mslearn-dotnet-cloudnative/dotnet-docker/Products/bin/Release/net8.0/Products.dll Products -> /workspaces/mslearn-dotnet-cloudnative/dotnet-docker/Products/bin/Release/net8.0/publish/ Store -> /workspaces/mslearn-dotnet-cloudnative/dotnet-docker/Store/bin/Release/net8.0/Store.dll Store -> /workspaces/mslearn-dotnet-cloudnative/dotnet-docker/Store/bin/Release/net8.0/publish/ Building image 'store' with tags 'latest' on top of base image 'mcr.microsoft.com/dotnet/aspnet:8.0'. Building image 'products' with tags 'latest' on top of base image 'mcr.microsoft.com/dotnet/aspnet:8.0'. Pushed image 'store:latest' to local registry via 'docker'. Pushed image 'products:latest' to local registry via 'docker'.
The command reads the solution file, determined it contains three projects, built them, and created images for the store and products projects. The images are named after the projects and published into the local docker registry.
Check the images are available in docker:
docker images
You see output like the following messages:
REPOSITORY TAG IMAGE ID CREATED SIZE products latest 63614e340088 About a minute ago 293MB store latest e9458c3abdb1 About a minute ago 218MB
Use a Dockerfile to create the Products back-end image
If you want more control of how the images are built, you can use a Dockerfile to create an image for the Products web service.
In the EXPLORER pane, create a file named Dockerfile in ./dotnet-docker/Products. The file is empty.
Enter the following code:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /DataEntities COPY "DataEntities/DataEntities.csproj" . RUN dotnet restore COPY "DataEntities" . RUN dotnet publish -c release -o /app
After completing the following steps, this code will set up the DataEntities library in the Products docker image:
- Pull the
mcr.microsoft.com/dotnet/sdk:8.0
image and name the imagebuild
. - Set the working directory within the image to
/DataEntities
. - Copy the file named DataEntities.csproj found locally to the
/DataEntities
directory that you created. - Call
dotnet restore
on the project. - Copy everything in the local DataEntities directory to the image.
- Call
dotnet publish
on the project.
- Pull the
Directly below the last line, enter this code:
WORKDIR /src COPY Products/Products.csproj . RUN dotnet restore COPY Products . RUN dotnet publish -c release -o /app
This code does the following steps sequentially when invoked:
- Set the working directory within the image to
/src
. - Copy the file named Products.csproj found locally to the
/src
directory that you created. - Call
dotnet restore
on the project. - Copy everything in the local Products directory to the image.
- Call
dotnet publish
on the project.
- Set the working directory within the image to
Directly below the last line, enter this code:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 WORKDIR /app EXPOSE 80 EXPOSE 443 COPY --from=build /app . ENTRYPOINT ["dotnet", "Products.dll"]
This code does the following steps sequentially when invoked:
- Pull the
mcr.microsoft.com/dotnet/aspnet:8.0
image. - Set the working directory within the image to
/app
. - Expose port 80 and 443.
- Copy everything from the app directory of the build image you created into the app directory of this image.
- Set the entry point of this image to
dotnet
and passProducts.dll
as an argument.
- Pull the
Create the Docker image
Having completed the Dockerfile, the next step is to use it to create a Docker image:
To create the image for the Products back-end service, go to the TERMINAL tab and run this command:
cd ./dotnet-docker docker build -t productsbackend:latest -f Products/Dockerfile .
This runs the commands in Dockerfile in the current directory and applies the tag productsbackend:latest to the resulting image.
After much output, the image will be built. Entering
docker images
shows you a list of all images in your codespace including productsbackend. The other image is the one for the codespace itself.You see output like the following messages:
REPOSITORY TAG IMAGE ID CREATED SIZE products latest 63614e340088 10 minutes ago 293MB store latest e9458c3abdb1 10 minutes ago 218MB productsbackend latest 190783f7e06f About a minute ago 293MB
Think about the difference between using dotnet publish
and having to manually create the Dockerfiles for each microservice in your apps.
Run the container and test the service
Now you can use the image to run and host the Products service.
To create and run a container from the new products image and expose the service on port 32001, run this command:
docker run -it --rm -p 32001:8080 products
Or if you'd like to run the image you created using the Dockerfile, run:
docker run -it --rm -p 32001:8080 productsbackend
To test the service, switch to the PORTS tab then, to the right of the local address for the Back End port, select the globe icon. The browser opens a new tab at that address.
To query some products, append the address with /api/product and then press Enter. You should see some product information listed in JSON format.
[ { "id": 1, "name": "Solar Powered Flashlight", "description": "A fantastic product for outdoor enthusiasts", "price": 19.99, "imageUrl": "product1.png" }, { "id": 2, "name": "Hiking Poles", "description": "Ideal for camping and hiking trips", "price": 24.99, "imageUrl": "product2.png" }, { "id": 3, "name": "Outdoor Rain Jacket", "description": "This product will keep you warm and dry in all weathers", "price": 49.99, "imageUrl": "product3.png" }, ... ]