Ever since .NET Core was released I've always wanted to do something with Docker. In my daily work I haven't had the chance yet, since the Kentico CMS we work with relies on .NET Framework and there are no major benefits of running that in Docker.
Luckily, a new version of Kentico, now Kentico Xperience 13, came with .NET Core support! 🎉 In this post I'll show you how to setup Docker with Kentico Xperience 13 and what the benefits of using Docker are.
Docker is a platform for developing, deploying and running applications. Docker seperates your application from it's underlying infrastructure so you don't have to configure the hosting environment. That's a huge plus for me, since most developers don't really like to do that anyway. It relies on virtualization technology like Hyper-V, but has many optimizations which make it faster and better than most virtualization technologies out there.
Docker's architecture consists of a few major elements.
An image is basically an instruction on how to create a container. Most of the times, you will use another image to build your own image. A good example of this is the .NET Core SDK. When creating a Docker image of your .NET Core application, you will reference the SDK image in order to build your application. This way you can even add a SQL Server to your image.
A container is an instance of the image that can be run on a Docker host. Containers are isolated from the host system; by default they have their own network and can't be reached externally. They also have their own file storage.
After an image is created it needs to be pushed to a registry. The registry can be a public registry, like Dockerhub, or a private registry from your company.
The Docker host can pull images from a registry and runs it in a Docker container. A Docker host can be either Windows or Linux, which allows you to run a .NET Core application on Linux.
We're hosting the major part of our websites in Azure App Services, mostly in the Premium V2 tier. When we compare the prices a of Windows and Linux App Service the differences are huge. A P2V2 App Service costs around €246 a month for Windows and €142 a month for Linux; that's a 57% decrease 😱. See the App Service pricing for all prices.
Apart from the reduce in costs, which is of course great for your customers, there's another benefit of Linux applications. Developers like me love it when their application is fast and try to constantly improve the performance. What if I told you that Linux gives you a huge performance boost? Roberto Prevato wrote a post about Linux vs Windows performance in Azure App Services. The results? Linux could handle about twice the amount of requests per second than Windows with about half the response time. I knew Linux was faster than Windows, but this fast? Wow! 🚀
Alright. We've covered the basics of the Docker architecture, but how does all of this tie together? Let's find out.
- First, we need to create an image for our application.
- Then we need to publish this image to a registry.
- On a Docker host, we need to pull this image from the registry and run it in a container.
That's it! It sounds complicated since there are so many parts involved, but luckily Visual Studio makes it very easy to do on your local machine. 😎
Let's get to work 🔨 and get Kentixo Xperience 13 running in Docker!
I'm assuming you have Visual Studio 2019 and SQL Server installed on your machine. So go ahead and do that if you haven't! 🔗
So we have everything installed now! Open the Visual Studio solution for the .NET Core site (DancingGoatCore.sln) and run it. If all goes well, you should see the Dancing🕺 Goat sample site:
Now that we have the Dancing Goat website running, we can install Docker Desktop. This is used as a local image registry and host we can run Docker containers on.
Okay, we've got Docker running. The next step will be to create a Docker image for the application. Images can be assembled using a Dockerfile, which is a set of commands that Docker uses to build the image.
Luckily, the folks at Microsoft made it very easy for us to create a Docker image from within Visual Studio 🙌.
Right-click the DancingGoatCore project, click on Add and click on Docker support:
Choose Windows as operating system (OS) and press OK. A Dockerfile will now be created in the root of the project. This Dockerfile consists of four steps:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base WORKDIR /app EXPOSE 80
Sets the runtime for the Docker container. In this case, the .NET Core runtime. It pulls a Docker image from Microsoft to do this. By default, the network of a Docker container is not externally accessible. Since this is a web application, port 80 is exposed and made accessible.
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build WORKDIR /src COPY ["DancingGoatCore/DancingGoatCore.csproj", "DancingGoatCore/"] RUN dotnet restore "DancingGoatCore/DancingGoatCore.csproj" COPY . . WORKDIR "/src/DancingGoatCore" RUN dotnet build "DancingGoatCore.csproj" -c Release -o /app/build
Builds the application using the .NET Core SDK image. It uses the usual dotnet build command to do this.
FROM build AS publish RUN dotnet publish "DancingGoatCore.csproj" -c Release -o /app/publish
Runs the dotnet publish command using the .NET Core SDK.
FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "DancingGoat.dll"]
Creates an entrypoint for the image, so Docker knows how to run it.
Now it's time to build and run the image in a container 🤩! Docker is a command-line tool, so normally we'd have to invoke two commands: one to build and publish the image, and one to run an image. Visual Studio conveniently helps us with that and added a launch configuration:
Select it and run the application!
Uh-oh... a time-out 😅 and even an exception in Visual Studio:
CMS.DataEngine.ApplicationInitException: 'A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - No connection could be made because the target machine actively refused it.)'
Looks like the application can't reach the database 💾
Remember when I explained earlier that a Docker container is isolated from the host, even it's network? We did expose port 80 in the Dockerfile for the web server. But.. the container can't reach the SQL Server that is running on your local machine, which causes the exception.
In order to do that, we need to expose the right port on the container which is used by SQL Server, which is 1433. Go back to the Dockerfile and add this line below the EXPOSE 80 like:
Save the Dockerfile and run the application again. It should work this time, won't it? 🤔
Nope, again a time-out... When I've tried running Kentico Xperience in Docker for the first time, I've really struggled with this and didn't know what caused it. The ports on the Docker image are fine, SQL Server is running, what's wrong?
Turns out that SQL Server blocks all incoming external traffic from port 1433 by default. Time to fix that!
First, make sure to modify the CMSConnectionString in the appsettings.json file, so the Data source is the local IP adress of your machine, with an explicit port after it:
Next, you need to allow communication through port 1433 in Windows Defender. Create an inbound rule to allow TCP traffic through port 1433.
Finally, we need to tweak some SQL Server settings. Press CTRL+R and execute compmgmt.msc. In the menu on the left, go to this particular entry:
Right-click on TCP/IP in the middle and set Enabled to Yes. In the same window, select the IP Addresses tab and look for the IPAll group and set the TCP port to 1433:
Press OK and give your machine a quick reboot 🔌.
After your machine has been rebooted, launch the application again and after a short wait you should be greeted with the Dancing Goat website once again 💪🏼
Ever had the problem that a new team-member couldn't get your application running, even if he followed a setup guide? With Docker, a big part of this setup is taken care of by the image. From the used .NET Core SDK, to exposed ports, filesystem configuration and environment variables. All he has to do is install Docker Desktop, check-out the Git repository containing the application and the Docker file and build it. Oh, and maybe configure some SQL Server settings. 😇 But really, that's it! No more struggles with things like .NET Core versions, IIS settings or filesystem permissions 👍
You've learned what Docker is and how to set it up in a Kentico Xperience 13 website. I hope you're as excited as I am about Kentico Xperience 13 having .NET Core support, since it opens up more possibilities, like using Docker.
Using Docker has many advantages over traditional development, but these are the reasons that have the biggest impact on your team or customer:
✔️ Running Docker containers in a Linux environment in Azure reduces cost by more than 50% 💰📉
✔️ Linux App Services in Azure are twice as fast and can handle double the requests per second 📈
✔️ New team members are setup much faster
Thank you for your attention and have fun experimenting with Docker! 📦