Migrate DotNet Core Docker image to Alpine based distribution

Background

Recently I had been using Docker and related technologies like Docker Swarm to play around with DotNet Core 2.0. I also used Azure Container Service (AKS) to scale multi-container app in  Azure. There was a recent announcement that early next year a Docker base image for .NetCore 2.1 will be available based on Alpine distribution. Currently these images are in preview and are available as nightly builds. I thought of giving it a try to upgrade my base image from stable microsoft/aspnetcore:2.0 image. This post is about the changes I had to do to upgrade to the experimental version. As usual the complete source code is available on GitHub repo.

Update Dockerfile to multi-stage build format

Before moving to the Alpine based Docker image, I was using aspnetcore 2.0 runtime optimized image. I used to publish the artifacts myself and then copy them over to the custom image. With recent release of Docker, there is support for multi-stage builds. This enables us to use a single Dockerfile to define various steps of our build, test and release stages of our application which is packaged using containers. Lets get started with these changes for the CoreMCV project.

Build source code using sdk image

We start with dotnet-nightly:2.1-sdk image. This image contains the sdk tools required for building and packaging the Dotnet Core 2.1 application. One difference you might have noticed is the AS keyword. We are naming this stage as the build-env to indicate that this is part of the build stage.

WORKDIR instruction remains the same. Next we copy the project file and the NuGet.config file. We need to override the default NuGet configuration. Otherwise we will be pointing to the stable version of NuGet instead of the 2.1 preview version. With these 2 files copied, we run the dotnet restore command to restore all the required NuGet packages required for running the application.

We also copy any static resources to the container. With all the files and dependencies available inside the container, we trigger the release with the help of dotnet publish command. Upon successful completion of publish command, releaseOutput directory should be created inside the container.

Package artifact using runtime image

The part related to containerizing the artifacts is exactly the same as earlier with just one exception. Earlier we used to copy the contents of release folder from the host machine to the container. Now we source the release output from the build-env image. The base image used here is dotnet-nightly:2.1-runtime-alpine image.

As we can see all the steps required for building and packaging the application into container are specified in the same file. With these changes we are good to build and test our code using the .Net Core 2.1 framework.

The Docker command to build the image is same as before docker build –t nileshgule/coremvc .

Error 1 : Mismatched .Net Core version reference

publish runtime error

The image does not build. We get an error saying that the compatible framework version is not found. The .NetCore version 2.0.0 is not found and the error also clearly states that 2.1.0-preview1-25919-02 is installed. To fix the error we update the TargetFramework property in CoreMVC.csproj file.

Along with the TargetFramework we also add the RuntimeFrameworkVersion to specify the exact version as shown below

<TargetFramework>netcoreapp2.1</TargetFramework>

<RuntimeFrameworkVersion>2.1.0-preview1-25919-02</RuntimeFrameworkVersion>

After modifying this change rerun the docker build command. The image is built successfully.

docker build success

We can try to run it with the command docker run –it –p80:80 nileshgule/coremvc

Error 2 : Default exposure to port 5000

docker run port error

Here we can see that although we are publishing port 80 on both the container and the host, for some reason the container tries to listen on port 5000. This seems to be the default port with the Alpine image. I do not wish to expose port 5000 but 80 as specified in the Dockerfile. In order to achieve this we need to update the Program.cs file.


We add UseUrls(“http://*:80”) statement to BuildWebHost method. This is to specify that the MVC website will listen to 80 port. Once again run the docker build command followed by docker run command. This time the image successfully listens on port 80. At this point we hit our next roadblock

Error 3 : Application Insight related setting

Application Insights error

We get an error suggesting that ApplicationInsights package is missing. This package is required for enabling cloud analytics. We do not need it at this point of time. Lets disable it. Get back to the CoreMVC.csproj file and add the following entry

<PropertyGroup>

<PublishWithAspNetCoreTargetManifest>false</PublishWithAspNetCoreTargetManifest>

</PropertyGroup>

Once again rebuild the image and run it. This time everything is fine and we can browse the site successfully by accessing the url http://localhost:80

Update WebAPI project

In order to update the WebAPI project we need to follow the same steps

  1. Create multistage docker build in Dockerfile.
  2. Update framework version in CoreWebAPI.csproj file
  3. Add PublishWithAspNetCoreTargetManifest property with false value
  4. Update Program.cs file and expose port 8080 as we will be exposing the webapi on 8080 port.

Build the image using command docker build –t nileshgule/corewebapi .

Run the image using docker run –it –p 8080:8080 nileshgule/corewebapi

We can see the output of running the MVC application accessing the WebApi

web app with api

You can see that I am using docker.for.mac.localhost special variable to access the webapi container. I had explained about this in my earlier post.

Conclusion

Except for minor problems, it was quite easy to upgrade for .Net Core 2.0 base image to 2.1 Alpine based image. The final runtime image is almost half the size of the previous image built with microsoft/dotnetcore:2.0 image. This is what I like the most about Docker and its ecosystem. It allows us to quickly test different technologies without messing up with our laptop. Tomorrow if I wish to revert back to stable version of .Net Core, I could do so by just changing the Docker files to earlier version. Same is the case if I want to try some experimental and preview features of Core 2.1 framework, I don’t  need to install anything on my base machine.

One feature which I like about the stable version of the aspnetcore:2.0 base image is that we do not need to explicitly expose the ports using UseUrls method. Once we expose the ports in the Dockerfile they are mapped automatically to the underlying ports. Since the Alpine image is in preview, I expect the full version to address this issue when it is release officially.

If you have not yet started using Docker I would really recommend that you start doing so. For sure you will fall in love with this wonderful technology. If you have already started using it, I would be very happy to hear about your experience.

Until next time Happy Programming.

Share:
spacer

No comments:

Post a comment