Docker Compose with a multi part ASP.NET Application

aka Using Docker Compose with Telligent Community

I've seen a lot about Docker and Docker Compose over the last few months, but not had a chance to use it. With the push from Microsoft, and the ability to run windows based containers I decided it was time to give it a go!

I wanted to try something complicated enough that would allow me to have a good look at the concepts. But also something simple enough that I spent my time learning Docker. So I started with something I've setup many times before - Telligent Community.

Telligent Community is a platform for building online communities. It uses .NET, and consists of several components that work together. This makes it like many other applications out there today. This post will focus on Docker learnings rather than being Telligent specific. So should be applicable to anybody looking at a similar application.

If you want to run Telligent Community using Docker Compose, or would like to see my final files. You can find them on Github

The Telligent platform consists of the following components:

  • Website - An ASP.NET Web Forms application
  • SQL Server - Microsoft SQL Server with a specified schema and initial data
  • Job Service - Windows Service that processes background jobs on schedules
  • Socket Message Bus Service - Windows Service for handling SignalR messages (only required when using many web servers)
  • Search - Uses SOLR, a Java-based search application running on Tomcat server.

I started off installing Docker for Windows. At the time of writing Windows containers only work with the beta version (1.13.0-rc4-beta34). First step was running through some of the examples available online. Then starting to build up the docker files and the docker compose file. During this process, I ran into a few things that took a little time to get my head around. Most of the rest of this post discusses these (you can jump to the conclusion if you want to skip these!)

Switching to Windows containers

You can't run Windows based containers alongside Linux-based containers. Before running them you have to "Switch to Windows containers". This is mentioned in the Docker documentation. But if you haven't done this the error message can be a little hard to understand:

ERROR: Service 'search' failed to build: unknown blob

Or if you have the networks section defined (see below), you'll get this instead:

ERROR: Network nat declared as external, but could not be found. Please create the network manually using `docker network create nat` and try again.

Windows container images are big

The base Windows container images are pretty big. And if you have a sluggish internet connection (as I do), getting them on your machine can be quite painful. I would recommend downloading the base images you intend to use as soon as possible. Once these are on your machine everything is fine. The layered way containers are built means once you have the common images, retrieving others based on them is a lot quicker.

Error with Docker Compose if network not specified

Most examples of docker-compose.yml files I could find did not have a networks section defined. My first attempts didn't either. But this appears to be required when using Windows containers, without it, you receive the following error:

Creating network "telligentdocker_default" with the default driver
ERROR: {"message":"HNS failed with error : The parameter is incorrect. "}

This is fixed by adding the following snippet to the end of your docker-compose.yml file.

Docker Compose aliases don't work

In your docker-compose.yml, you can usually specify that one service depends on another. Then be able to reference the dependent service by name, for example with the following file:

You should be able to reference the db container by from the web container using the hostname db. This does not currently work with Windows containers. The workaround I used was to assign a static IP address to the db container that I could then use where needed. This can be done by updating the docker-compose.yml to be:

The IP should be in the range 172.16.0.0 - 172.31.255.255.

SQL Server sa password

When using the microsoft/mssql-server-windows-express image you must specify a password for the sa account. This is done as an environment variable in the compose file. The password must meet the MSSQL sa password requirements . If it doesn't you won't see an error message, and will be unable to login to SQL.

Spaces in folder names

When referencing folders in docker files, spaces are treated as separators. I couldn't find a way of successfully escaping them, so something like this will not work:

My workaround was to rename the folder. In my example, the folder structure was defined elsewhere. So I've created a Powershell script that will rename the folder before starting.

Windows Services

When creating a Docker image that runs a Windows Service there are two things to watch out for.

Firstly, the service should be set to Automatic Startup. This can be done with the following line of Powershell in your docker file:

Secondly, you will need to have a process that keeps running when the container starts up. This can be done using the ServiceMonitor tool Microsoft use in their IIS docker image. This takes a service name and will run as long as the service is running. This is currently a standalone exe, but Microsoft is planning on working on this as a standard approach to this problem.

It appears that for this to work the service name cannot contain spaces. This is something to consider when installing the service.

Cannot start service: error during CreateContainer

Once or twice when running docker-compose up I received the following error:

ERROR: for search  Cannot start service search: {"message":"container 4e329ba0ca8a547b5c8400dc340e2f501e2b93fe6ecea14b074caa01481af4d encountered an error during CreateContainer: failure in a Windows system call: The handle is invalid. (06)"}

Restarting the Docker service seemed to fix this.

Unable to mix Linux and Windows containers

Telligent uses SOLR for search. When I was first trying to set up this I found the official SOLR image on docker hub and tried to add this into my Docker compose file. Unfortunately, the official image runs on a Linux container. You can't currently run Windows and Linux containers together in the same Docker compose file. If you try you'll get this error:

ERROR: image operating system "linux" cannot be used on this platform

This meant I had to build my own docker file that installs SOLR on Windows.

Setting Environment Variables

As most things in my Docker files were using Powershell, when I needed to set environment variables my first instinct was to use Powershell. This didn't work, instead, I had to use the ENV command in my Docker file. 

Escaping of file paths

The escaping of file paths in Docker files for Windows containers seems inconsistent. In some cases, the slashes in paths needed to be escaped, and in others, this wasn't required. For example, look at the snippet bellow from this docker file:

ENV, ADD, and ENTRYPOINT needed the paths to be escaped, whereas RUN didn't. This isn't a big deal but meant having to run a few things a couple of times. Then trying to work out why things weren't quite working or where I thought they were.

Use volumes with docker compose

Volumes with Docker and Docker Compose look like a great feature. They allow you to map a folder on the host machine to the docker container. This way changes you make on the host will be available in the container. Without this, you have to rebuild the image and restart the container for every change. If you're using the Docker compose workflow for development this would get tedious. Unfortunately, I haven't been able to get volumes to work with Windows containers yet. I keep getting "Invalid volume specification" errors. I'll keep trying with this and update this post when I work it out - if you know what I'm doing wrong let me know! 

Conclusion

Docker for Windows seems to be a promising technology. Windows based containers still feel like a work in progress. From what I can tell Linux containers on Windows are much more fully featured.

Having said that it is still pretty powerful and I will be aiming to keep trying it out as it develops. This could be a really powerful making life a lot easier, but for now, I would be nervous relying on this.

If you have any suggestions of what I've done wrong, what could be better or any other comments, let me know! Using the comments below, in the GitHub issues, or submit a PR to show me how it should be done!

The container image used is by Sam MacCutchan. It is available on Flickr, under a creative commons license.

Ardour Digital can help you get the most out of Telligent Community. Providing development, training, and advice.

To find out more email hello@ardourdigital.co.uk or call +44 (0)177 3254 150.

  • Post
  • Alex Crome
    Alex Crome

    Nice, I was actually playing with the same thing over the weekend after attending NDC last week.

    * Prefer Copy over Add - Add has some odd behaviour if you use urls rather than paths which can catch you out.

    * To copy folders with spaces in, use the json syntax.

    COPY ["source with spaces", "destination with spaces"]

    * I'd try and avoid windows services completely if you can.  IIS you have to do this as there's no supported way to run IIS as a console app, but for solr, jobs and socketbus just run the executable directly.

    * Escaping of file paths - the easiest way to fix this is to change the escape character to not conflict with the windows path seperator.  When using powershell, backtick is a good escape character because it makes it easy to copy multi line run scripts directly into powershell for testing

    # escape=` (backtick)

    * Mixing linux & windows contained is apparently being blocked by some bugs in Microsoft's networking stack

    (PRs incoming, once I fix my local docker isntance)

  • Thanks Alex. How was NDC?

    Hadn't thought about running the services as standalone EXEs, that will simplify them nicely.

    Will take a look at the difference between Copy and Add, think in one case made the change deliberately but can't remember why. The JSON syntax for copy looks really handy too! Will go through and update when I get a chance, also looking at getting the 10 preview running (got everything apart from the new SOLR going so far).

    Look forward to some PRs :p