Final part of a series covering some of the fundamentals of ASP.NET Core, Docker and Azure. This part is fairly dependent on what we covered in part 1 and part 2. However if you want to skip ahead you can use my pre-created ASP.NET Core demo app on Github; https://github.com/benc-uk/dotnet-demoapp which you can clone and use to skip what was covered in part 1.
However we’re going to need a running machine with Docker engine on so skipping part 2 isn’t really an option

Publishing Your Application

As you’d expect .NET Core provides a means to publish your application so it can be run without your source code. For console apps this will be some form of executable like a .exe, and for ASP.NET web applications this will be a .dll and bundle of required libaries & static content (e.g. css, images, js etc).
I Hang on? Did I just say .dll? I thought we were going to run this on Docker on a Linux host? DLLs are a Windows thing, right? Yeah but don’t worry, .NET Core uses the dll files regardless of the target OS, even on Linux, it seems like a dumb choice of file extension to me but there we go.

To do this we run dotnet publish as follows:

Note & Caution
You may get an error at this point, depending on how you created your initial .NET Core project. If you did a plain old dotnet new and followed all the steps in part 1 to webify it to ASP.NET then hopefully you will be fine.
If you created your project with dotnet new –type web then you will hit a problem. You will probably get an error:
No executable found matching command “npm” or No executable found matching command “bower”
It is unlikely (unless you develop Node.js applications) that you have npm or bower on your system and in your path, so the publish will undoubtedly fail.

In summary (this is really a topic for a whole other blog post), the bundled web template that comes with the .NET Core command line tools sucks and is out of date. 

If you have this error, there are many ways to fix it, but rather than get sidetracked further, lets either ingore it or do a quick and dirty fix:

•  Skip ahead to the Docker build stage, we don’t actually need to get the dotnet publish working locally at this point
OR
•  Open your ‘project.json’ and delete the whole ‘prepublish’ line (about line 105 which will contain references to npm and/or bower) from the ‘scripts’ section of the file, and save.

OK let’s move on….

This should publish your app into the ‘pubout‘ sub-directory. Inside the ‘pubout‘ directory will be a .dll called the same as your current project working directory, if you are following on from part one this is likely to be “webapp.dll” if you cloned my git project, it will be “demo-webapp.dll”. If we like we can now copy the contents of the ‘pubout‘ directory to any machine that has .NET Core installed and run it with

Which will start the app running under the built-in .NET Core Kestrel webserver, just like we did with dotnet run in part 1. That’s all well and good, but we want to do something different…

Intro to building Docker images

OK what about Docker and containers? We want to get our app into what Docker calls an image, from the image you can start a running container like we did in part 2. When we did docker run nginx, Docker knew to pull down the standard nginx image from the online repository (called Dockerhub). However we’ll be creating our own image all neatly packaged up with .NET Core and our application. The best part – it’ll be totally standalone so anyone can run it without faffing about installing .NET Core.

This is all done with Dockerfiles and the docker build command. For me Dockerfiles are where I really had that first “ah-ha” moment with Docker and started to see what all the fuss was about, let’s take a look at a Dockerfile:

This is a totally pointless example that simply runs a shell script, but it introduces many of the core elements of a Dockerfile. Key things we can do:

  • FROM – Use a base image as our starting point, all our changes are applied on top of that base. In this case we use the basic ubuntu image with the tag “latest” which will be pulled down from Dockerhub for us.
  • MAINTAINER – Just documentation and info
  • WORKDIR – Sets the working directory “inside”, for the image build process, directory will be created for you. Note. this is not a directory on the machine where you run docker build
  • RUN – You can run commands as part of the build process, to prep the image and do other things, installing software packages is a common example. The apt-get update in this case serves no purpose but it proves a point.
  • ADD or COPY – Inject your own files into the image on top of the base. So this will copy the hello.sh file from the “outside” where I’m running docker build into the image. The file must exist, and the operation will honor the WORKDIR so in this case our file ends up in /stuff/script/
  • ENTRYPOINT – This is what gets run when a container is run from this image, so it’s pretty much the most important part. Here we just call bash to exec our script.

This is just the tip of the iceberg. Rather than digress into a full blog post on Dockerfiles and docker build, I suggest you take a look at the main Dockerfile reference docs

Building our ASP.NET Core app Docker image

Microsoft supply base Docker images with .NET Core for us to easily layer our application on top of. These are hosted as you’d expect over on Dockerhub, and there’s several variants to be aware of, which caught me out:

There’s also tags we can supply to specify the version we we’ll stick to using using :latest
If you’re really interested in what these images contain you can poke about with the source Dockerfiles over on Github dotnet/dotnet-docker and aspnet/aspnet-docker 

We have two approaches from here, one where we carry out the dotnet publish separate from the Docker image build, then copy the published contents into the Docker image (using COPY in our Dockerfile), an example Dockerfile doing this would be

Note. This Dockerfile introduces a new term, EXPOSE, which doesn’t do anything really, but serves as a hint as to what ports your app will be listening on.

Another slightly better approach I think is to run the .NET publish and build as part of the build of the docker image. This approach is cleaner from a CI/CD workflow perspective and the one we’ll take from here. Note, we are using the microsoft/aspnetcore-build base image, this image includes some dependent tools like npm and bower so we don’t get any issues with the publish step like we did right at the start of this post. Which is another nice thing about Docker, you don’t need to clutter up your build servers with lots of tools and maintain them. The Dockerfile to do the full build & publish, looks like:

Create a file called Dockerfile (with no extension and uppercase D, it can not have any other name. Look I don’t make the rules) in the root of your ASP.NET Core project (i.e. where ‘project.json’ is) and copy/paste the above into it.

  • Update 18th Nov
    .NET Core v1.1.0 has just been released, and using the :latest tag may cause problems with your image as you will likely created your code from the ‘dotnet new’ template which still spits out v1.0.1 based project. A simple fix is to change the tag in your Dockerfile to – FROM microsoft/aspnetcore-build:1.0.1

Before we run the build we need to be connected to our remote Docker host, if you haven’t already follow the steps from part 2 where we use docker-machine to point our local system at a remote running Docker host. A good test is doing a quick docker ps if you don’t get an error, you’re good to go

To carry out the build we’d go to where our Dockerfile resides, i.e. the root of our project and run the docker build command. I was actually quite surprised to see this command work with a remote machine, after all – the code and other files we are injecting into the image reside locally, but there’s some magic where all the gubbins we need is uploaded to the remote machine and the build done there. Very clever. So the command is:

Note we don’t specify the name or path of the Dockerfile, just the directory where it resides (in this case ‘.’). The -t part effectively names our image, the full naming convention is {repository}/{name}:{tag}, but you don’t really need to worry about all that until you want to push your image to a registry like Dockerhub.

That might take a while the first time, but don’t worry Docker is pretty smart and caches filesystem layers in images. This means subsequent builds will be much quicker and only bring in deltas, which is another reason Docker is so popular for CI/CD. You might also see some red warnings, don’t worry

A quick docker images command will verify things, and should look much like this:

Running our app and conclusion

We’re nearly at the end of our journey, let’s run our app as a container. We’ll run it detached and expose port 5000 out of the container so we can connect to it. Hopefully you remembered to tweak Program.cs and add the .UseUrls("http://*:5000") as discussed in part 1. OK go…

You’ll get back a long string which is the container id, don’t worry about that now. If all has gone well you can connect to the container via the public IP of your docker host. Obviously you’re running your docker host in Azure (where else?!) so you can get the public IP of the VM from the Azure portal. While you are there, you will also need to edit the network security group and add a rule to allow port 5000 through the firewall, how to do this was covered in part 2

Open your browser to http://{docker-host-publicip}:5000 and all going well you should see your app.

2016-11-12_180124

Not super exciting is it? Well I agree it’s not terribly visually pleasing, but we should take a moment recap what we’ve achieved:

  • Created a portable .NET Core web application in about 30 lines of code
  • Deployed a Docker host running in Azure from a single command
  • Connected to our Docker instance to remotely control & manage it
  • Built a custom Docker image from our application, bundled with the .NET Core runtime
  • Deployed and run our little web app inside a container, on a host in the cloud

These posts have turned out a little longer than I envisioned, but we’ve covered a lot of ground. Never the less we’ve barely scratched the surface of what we can achieve with Docker .NET Core and Azure.

Running your ASP.NET Core app in Docker
Tagged on:         

Leave a Reply

Your email address will not be published. Required fields are marked *