Running .NET apps on Docker
This blog post covers running simple .NET apps in Docker lightweight containers using Mono. I run Docker in a Vagrant/VirtualBox VM on Windows. This works great and is fast. Installation instructions are available on the Docker site.
Building the base image
First order of business is to create a Docker image that has Mono installed. We will use this as the base image for containers that actually run apps. To get the most recent Mono version (3.2.6 at the time of writing) I use packages created by Timotheus Pokorra installed on a Ubuntu 12.04 LTS Docker image. Here’s the Dockerfile for that:
FROM ubuntu:12.04 MAINTAINER friism RUN apt-get -y -q install wget RUN wget -q http://download.opensuse.org/repositories/home:tpokorra:mono/xUbuntu_12.04/Release.key -O- | apt-key add - RUN apt-get remove -y --auto-remove wget RUN sh -c "echo 'deb http://download.opensuse.org/repositories/home:/tpokorra:/mono/xUbuntu_12.04/ /' >> /etc/apt/sources.list.d/mono-opt.list" RUN apt-get -q update RUN apt-get -y -q install mono-opt
Here’s what’s going on:
- Install wget
- Add repository key to apt-get
- remove wget
- Add openSUSE repository to sources list
- Install Mono from there
At first I did all of the above in one command since these steps represent the single logical step of installing Mono and it seems like they should be just one commit. Nobody will be interested in the commit after wget was installed, for example. I ended up splitting it up into separate RUN
commands since that’s what other people seem to do.
With that Dockerfile, we can build an image:
$ docker build -t friism/mono .
We can then run a container using the generated image and check our Mono installation:
vagrant@precise64:~/mono$ docker run -i -t friism/mono bash root@0bdca65e6e8e:/# /opt/mono/bin/mono --version Mono JIT compiler version 3.2.6 (tarball Sat Jan 18 16:48:05 UTC 2014)
Note that Mono is installed in /opt
and that it works!
Running a console app
First, we’ll deploy a very simple console app:
using System; namespace HelloWorld { public class Program { static void Main(string[] args) { Console.WriteLine("Hello World"); } } }
For the purpose of this example, we’ll pre-build apps using the VS command prompt and msbuild
and then add the output to the container:
msbuild /property:OutDir=C:\tmp\helloworld HelloWorld.sln
Alternatively, we could have invoked xbuild
or gmcs
from within the container.
The Dockerfile for the container to run the app is extremely simple:
FROM friism/mono MAINTAINER friism ADD app/ . CMD /opt/mono/bin/mono `ls *.exe | head -1`
Note that it relies on the friism/mono
image created above. It also expects the compiled app to be in the /app
folder, so:
$ ls app HelloWorld.exe
The CMD
will simply use mono to run the first executable found in the build output. Let’s build and run it:
$ docker build -t friism/helloworld-container . ... $ docker run friism/helloworld-container Hello World
It worked!
Web app
Running a self-hosting OWIN web app is only slightly more work. For this example I used the sample code from the OWIN/Katana-on-Heroku post.
$ ls app/ HelloWorldWeb.exe Microsoft.Owin.Diagnostics.dll Microsoft.Owin.dll Microsoft.Owin.Host.HttpListener.dll Microsoft.Owin.Hosting.dll Owin.dll
The Dockerfile for this exposes port 5000 from the container, and CMD
is used to start the web app and specify port 5000 to listen on:
FROM friism/mono MAINTAINER friism ADD app/ . EXPOSE 5000 CMD ["/opt/mono/bin/mono", "HelloWorldWeb.exe", "5000"]
We start the container and map port 5000 to port 80 on the machine running Docker:
$ docker run -p 80:5000 -t friism/mono-hello-world-web
And with that we can visit the OWIN sample site on http://localhost/
.
If you’re a .NET developer this post will hopefully have helped you place Docker in the context of your everyday work. If you’re interested in more ideas on how to use Docker to deploy apps, check out this Automated deployment with Docker – lessons learnt post.