CI with TeamCity and Docker – Part 2

Introduction

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.

Prerequisites

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

"frameworks": {
    "dnxcore50": { }
},

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

using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc;

namespace WebApplication1.Controllers
{
    [Route("api/[controller]")]
    public class ProjectsController : Controller
    {
        private readonly Project[] _projects = new Project[]
        {
            new Project {id = 1, name = "Pears"},
            new Project {id = 2, name = "Apples"},
            new Project {id = 3, name = "Oranges"},
        };

        [HttpGet]
        public IEnumerable<Project> Get()
        {
            return _projects;
        }

        [HttpGet("{id}")]
        public Project Get(int id)
        {
            return _projects.SingleOrDefault(x => x.id == id);
        }
    }

    public class Project
    {
        public int id { get; set; }
        public string name { get; set; }
    }
}

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

{
  "server.urls":  "http://0.0.0.0:5001"
}

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)

dnu restore

Now run the application using this command

dnx web

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

dnvm list

which should show you something similar to

To change the active version to corecrl and x64 use this command

dnvm use 1.0.0-rc1-update1 -r coreclr -arch x64

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

FROM microsoft/aspnet:1.0.0-rc1-update1-coreclr

COPY project.json /app/
WORKDIR /app
RUN ["dnu", "restore"]
COPY . /app

EXPOSE 5001
ENTRYPOINT ["dnx", "-p", "project.json", "web"]

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

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.

Summary

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.

About Gabriel Schenker

Gabriel N. Schenker started his career as a physicist. Following his passion and interest in stars and the universe he chose to write his Ph.D. thesis in astrophysics. Soon after this he dedicated all his time to his second passion, writing and architecting software. Gabriel has since been working for over 25 years as a consultant, software architect, trainer, and mentor mainly on the .NET platform. He is currently working as senior software architect at Alien Vault in Austin, Texas. Gabriel is passionate about software development and tries to make the life of developers easier by providing guidelines and frameworks to reduce friction in the software development process. Gabriel is married and father of four children and during his spare time likes hiking in the mountains, cooking and reading.
This entry was posted in ASP.NET vNext, CI/CD, containers, docker, How To, introduction. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

Comments are closed.