Are you looking for a fast and easy way to create and run .NET applications using Docker containers without writing any Dockerfile? If so, you will be glad to know that Microsoft has introduced a new feature of the .NET SDK 7.0.200 that makes it possible to create and publish OCI container images directly from your project file. We have seen how “.NET 7 SDK built-in container support and Ubuntu Chiseled“ can be used together. It lets us create small and secure containers for our .NET applications easily. We went from a first docker image of 216MB down to 48.3 MB. That is more than a 77% reduction in size. .NET SDK 7.0.200 bring some new capabilities. We will explore some in this post.
Capability now bundled into the .NET SDK 7.0.200
Previously you had to add a reference to the nuget Microsoft.NET.Build.Containers
. With the .NET SDK 7.0.200, we do not require it anymore. The .NET SDK bundle this new capability.
The only thing needed is the following property in your csproj
project file:
1 | <EnableSdkContainerSupport>true</EnableSdkContainerSupport> |
The feature leverages dotnet publish
. The command-line tool allows you to build and publish your .NET applications as Docker images with one command. With dotnet publish, you can:
- Create a Docker image for your .NET application without writing any Dockerfile
- Publish your Docker image to a container registry of your choice with one command
- Specify variables such as the base image, image tag, and registry name in your project file
It is interesting to realize that you don’t need docker installed on your machine to publish to a remote container registry. The .NET SDK can build the image and then push it to the remote registry. It is interesting for CI/CD pipelines or even local development.
Create a Docker image
To create a Docker image for your .NET application, you need to run the following command:
1 | ❯ dotnet publish --os linux --arch x64 -p:PublishProfile=DefaultContainer -c Release |
Publishing your Docker image
By default, dotnet publish
publishes your Docker image to the local Docker daemon.
To publish your Docker image to a container registry, you need again to add a new MSBuild property to your .csproj
file:
1 | <ContainerRegistry>myregistry.azurecr.io</ContainerRegistry> |
I tend to prefer another approach than extending the .csproj
. It lets you use the same command line to publish to a local Docker daemon or to a container registry. For that, I use publish profiles. Those are files found in the /Properties/PublishProfiles folder. You can set publish-related properties like ContainerRegistry
or EnableSdkContainerSupport
by referring to a .pubxml file in that folder.
Sample publish profile
Here is a minimal publish profile. You can use it to publish to the local container registry. We will extend it with the new properties.
1 | <Project> |
With this approach, you can define multiple publish profiles and use them to publish to different container registries. You might want a publish profile for GitHub Package registry for your feature branch builds and push to Azure Container Registry only the release built images. Or, you might want to push images trimmed and chiseled, and other not.
Publishing to local container registry using local publish profile
Run dotnet publish
specifying the name of the publish profile, here local, for the -p:PublishProfile
parameter.
1 | ❯ dotnet publish --os linux --arch x64 -p:PublishProfile=local -c Release |
.NET SDK has been tested against the following registries: Azure Container Registry, GitLab Container Registry, Google Cloud Artifact Registry, Quay.io, AWS Elastic Container Registry, GitHub Package Registry, and Docker Hub.
Currently, the .NET SDK 7.0.200 has an issue to upload the image to GitHub Container registry. The issue is tracked on GitHub issue #292
You would need to authenticate to the container registry before publishing. Docker has established a pattern via the docker login command. It is a way of interacting with a Docker config file containing rules for authenticating with specific registries. It should ensure that this package works seamlessly with any registry you can docker pull
from and docker push
.
You can read more about this part in the Authenticating to container registries documentation.
Specifying a base image
By default, the SDK will infer the best base image to use. It can use values of properties of your project like TargetFramework
or verify if the application is self-contained or an ASP.NET Core application. You can read about this here.
Nevertheless, you can override the default base image by adding the ContainerBaseImage
property to your .csproj
or .pubxml
file:
1 | <Project> |
We saw how to use lighter base images in the previous post “.NET 7 SDK built-in container support and Ubuntu Chiseled”.
Specifying the container port
We are using an ASP.NET Core web application in this example, so we need to specify the port on which the mapping will be done with the host machine. In our case, we are using tcp
port 80
, but you can also use udp
.
1 | <Project> |
And running the docker image, we can browse to http://localhost:8080/
1 | ❯ docker run --rm -it -p 8080:80 laurentkempe/my-awesome-webapp |
Define the image name
You can achieve this by adding the following property to your .csproj
or .pubxml
file:
1 | <Project> |
We define the image name in the publish profile so that we can have multiple names.
Tagging your image
You can achieve this by adding the following property to your .csproj
or .pubxml
file:
1 | <PropertyGroup> |
This property also can be used to push multiple tags - simply use a semicolon-delimited set of tags, e.g. 1.2.3-alpha2;latest
.
This part is a part that we want to automate. For example, we can use MinVer tool for minimalistic versioning using Git tags.
After adding the reference to MinVer to your project, run the publish command again
1 | ❯ dotnet publish --os linux --arch x64 -p:PublishProfile=local -c Release |
We can see now that the image is built with a tag 0.0.0-alpha.0.2
. Nice 😁.
When you want to release a version, you need to create a Git tag. The image will be tagged with that version automatically.
1 | ❯ git tag 1.0.0 |
Now that the image is built with the tag 1.0.0
.
1 | ❯ docker images |
It is an efficient and easy way. Nevertheless, I am always balanced about tagging to produce an artifact. I think tagging should happen as the last step of your release process. Your release process might be something like this: provide a release candidate artifact to your QA team, they do the testing, and finally, approve it. Then, you would need to tag and produce yet another artifact. Then, would you ask them to test it again?
Conclusion
In this post, we have seen how to build a container image using the .NET SDK without creating a Dockerfile. We also looked at how to configure the image name and tag. Then how to publish the image to a remote registry using publish profiles.
There are more ways to configure your container image, which we might explore in a future post. For example, you can use the ContainerLabel
property to add a metadata label to the container.
I hope you enjoyed this post and learned something new that you want to try out. If you have any questions or comments, please leave them below. Thanks for reading!