CI with TeamCity and Docker – Part 2
This post is part of the series about Implementing a CI/CD pipeline. You can find an introduction and a full table of content here. This post is also the second part of the topic on how to containerize the CI server and have it generate Docker images. The first part can be found here.
In this post I am going to show how we can use what we have learned in the previous post to create and publish Docker images of ASP.NET core applications.
If you have Visual Studio 2015 installed then make sure you get ASP.NET Core 1.0 RC1 from here. If you are developing on a Mac or on Linux and want to use any other editor like Visual Studio Code or similar then you need to install ASP.NET core from the command line. Detailed instructions can be found here.
A simple ASP.NET Core Web API project
Using Visual Studio 2015
In VS 2015 create a new ASP.NET solution. Select the ASP.NET 5 template and select Web.API. Since we only want to target the .NET core library and not the full framework make sure you modify your project.json file and have your frameworks section looking like this
Let’s delete the default controller that the VS template creates for us and define our own. Delete the ValuesController.cs class and add a controller named ProjectsController. Add the following content to that file
If we want to have the Kestrel Web Server listen on any other port than 5000 (default) we can do so by adding a file called hosting.json to our project. The file should contain this content
Now run the project. A terminal will open and you should see this
As you will certainly have noticed, Kestrel is listening on port 5001 as requested. We can use the browser or a REST client like Postman to test the API. Navigate to the URI http://localhost:5001/api/projects and you should see this
Hurray, our API is working.
From the command line
Create a project folder and using an editor of choice add and edit the necessary files. You can find a working sample here. Once you have created the project execute the following command in a command shell (terminal)
Now run the application using this command
Note, if you get an error similar to this
then your currently set target framework does not match with the one you require. You can get the list of all possible setting using
which should show you something similar to
To change the active version to corecrl and x64 use this command
Building the Docker image
Now it is time to pack it as a Docker image. For this we need to first add a Dockerfile to the solution. The content of the file should look like this
As you can see this Docker image we are going to build inherits from a base image curated by Microsoft (line 1). The name of the image is awful but I am sure it will get better once ASP.NET Core is final. On line 3 we copy the project.json file into the image. Then on line 4 we make the project folder (/app) the working directory. Now we run the command to restore all nuget packages (line 5). Finally on line 6 we copy the whole content of our project folder into the image. It is very important that we separate the copying of the project.json file and the restoring of the nuget packages from the copying of the whole project directory content. Since Docker images are layered doing so will avoid that we have to restore the nuget packages on each build although only some code has changed and no additional nuget packages have been added to the project. The difference is a compile/build time in the area of minutes (if we have many nuget packages) versus a couple of seconds…
Note that the exact syntax of the restore command (dnu restore) will change with RC2. Microsoft has fundamentally changed the naming of their command line tools after RC1 was out the door. We then on line 8 expose port 5001 (remember, this is the port on which the Kestrel Web server is listening based on our configuration in the hosting.json file). Finally on line 9 we define what shall happen when a Docker container is created from this image. What basically is going to happen is that the following command is executed (in the working directory of the container)
dnx -p project.json web
Which executes the command called web defined in the project.json file.
Configuring TeamCity works exactly the same way as described in part one of this post. We really only need to adjust the name of the project and the name of the image to build from node-sample to web-api-sample.
In this second part of the post on running TeamCity in Docker and building and pushing docker images I have shown how one can Docker-ize a Web API based on ASP.NET core. The beauty of our pipeline that uses Docker is that no matter what framework or tools we use to implement our application or services we can treat them the same way from a DevOps perspective. In the end we are only building a Docker image and push it to the Docker hub. The content of the image doesn’t really matter to the CI pipeline.
In the 3rd part I will explain how to avoid to build nested Docker containers which can cause severe problems and side effects under certain circumstances and I will also explain how we can leverage Docker even further to run tests on the CI server.