Some fairly recent announcements have indicated some interesting potential when it comes to using Docker in the Microsoft .NET space. First off, with ASP.NET Core, it is now much easier to host an ASP.NET HTTP-enabled app (either API or MVC site) within a Docker container [ref: Hanselman, 5/27/2015]. In addition, Docker Tools for Visual Studio 2015 now makes it very easy to develop in the all familiar and cozy Visual Studio environment, but also leverage a Docker host running on Windows via Oracle VirtualBox.
Some of the features that the Visual Studio extension bring us are:
- Simplified creation of a ASP.NET image that is built using the .NET core Docker image
- Simplified deployment of your code to the local Docker host
- Simplified management of the underlying container (stop, start, live updates)
- And even, debug Docker container within Visual Studio!
Some of the features that Docker bring us are:
- Application isolation through the use of containers; Umm… can anyone say, Micro-Services isolation?
- Simplified release
- Extreme virtualization
- Via Docker clusters, we can also achieve some pretty advanced clustering and scaling goals
- Cloud-enabled; Almost all of the hosting providers have a container hosting solution. Now your ASP.NET site can run within one of those cloud hosted container services
Some of the features that ASP.NET bring us are (sorry but this will probably be subjective):
- C# is a well developed language. It definitely spawned from a combination of Java and C++ syntax, but it quickly became a language that stood on its own ground, with features today that neither C++ or Java have (TPL, async / await, LINQ)
- .NET is a mature and robust
- .NET and ASP.NET alike are actively maintained by Microsoft, but also open-source (the best of both worlds)
- Wide adoption means better documentation and a wider range of Microsoft and third-party add-ons
- Did I mention Visual Studio already? Visual Studio and .NET go hand-in-hand when it comes to benefits. Everyone has the option to fire up a terminal and compile C# code while digging through the command line argument documentation, but Visual Studio makes every step a cake walk
For this blog post, I aim to provide a step-by-step guide to get up and running with Docker and Docker Tools for Visual Studio. Assuming you already have Visual Studio 2015 installed and don’t run into any hiccups during the installation process, which I do cover several of in the appendix, you can easily get through the steps in this post in under an hour. At which point, you will have a full-fledged .NET Core Docker development box.
Throughout the post, I’ll point out some areas where you might have trouble when you are starting things up for the first time. As you follow along, be sure to note the details as things may not work as intended if you miss steps.
In addition to providing a step-by-step guide, I hope that current Docker users that are new to Visual Studio might be able to skim through the content and see the benefits that the Visual Studio IDE yields developers. This extension is just a small part of the massive benefits encompassed within this amazing IDE. Granted, I have been out of the Java world for many years now, but throughout my experience, I have never found an IDE that matches the power Visual Studio provides.
On the “Docker Tools for Visual Studio 2015 – Preview” site, it advises that the following are required in order to install the extension. I will walk through these installations in just a bit, but listing here for reference.
- Microsoft Visual Studio 2015 Update 2. The following SKUs are supported:
- Enterprise 2015
- Professional 2015
- Community 2015
- Microsoft .NET Core 1.0.0 RC2 VS 2015 Tooling Preview 1
- Install Docker Toolbox or request access to the Docker For Windows beta
To run Docker containers locally, you’ll need a local docker client. You can use the released Docker Toolbox which requires Hyper-V to be disabled, or you can alternatively use the Docker for Windows Beta which uses Hyper-V, and requires Windows 10.
If using Docker Toolbox, you’ll need to Configure the Docker client
Your Visual Studio Version
At this point, I am assuming that you have some flavor of Visual Studio 2015 installed. If you do not, then you will need to install the bits, which I will not cover in this post.
First and foremost, you will need to ensure you have Visual Studio 2015 Update 2. To determine this, navigate within Visual Studio to Help à About Microsoft Visual Studio. You should see information that reflects a version similar to this:
If you see that anything less than Update 2 installed, then you will need to navigate to here and follow the instructions in order to apply Update 2 to your environment.
Once you confirm your environment to be the same as the image above, please continue on to the next section.
Install .NET Core 1.0.0 RC2
At the time I write this post, .NET Core 1.0.0 RC2 is the latest and greatest tooling preview for .NET Core. As of the time you are reading this, it is likely that there is a newer RC available, or even better yet, the toolkit might be officially released. You should be fine with grabbing the latest version of the tooling assuming there were no major changes.
In the event that .NET Core 1.0.0 RC2 is still the latest and greatest, you can grab the installer from here.
Once downloaded, run through the installer and then continue to the next step.
NOTE: Not sure that this will be the case you, but the installer took a bit of time to complete in my case. If that’s the case for you as well, just wait a couple of minutes for it to finish. You shouldn’t need to cancel or intervene manually… Patience young padawan.
Download Docker Toolbox
Now that we have Visual Studio with Update 2 and the .NET Core tooling installed, we’re ready to install Docker on our Windows machine. Head over to the Docker website and follow the instructions in order to download the Windows version of the tools (the download option is about halfway down the page).
Once download, kick open the installer and walk through the steps, selecting all of the default options as you go along.
NOTE: While installing, you will be prompted with some security prompts for the Oracle drivers. These are fine and should not be of concern. With the current version of Docker Tools on Windows, everything runs through Oracle VirtualBox. The security prompts are required for the virtual networks and virtual buses that get provisioned throughout the install.
Verify Docker is Working
After the install complete, you should be all setup with Docker on your machine. To confirm this, navigate from the start menu to the “Docker Quickstart Terminal”.
After starting the Quickstart Terminal shell command, the console window should reflect several steps executing as it creates you a Docker host VM:
… and eventually, you should see the Docker whale ASCII art and be presented with a prompt.
IMPORTANT: I have seen on occasion that virtualization needs to be turned on in your BIOS for you to be able to run VirtualBox. The BIOS settings will vary machine, but I attempt to cover this to some degree in the appendix of this post. If this is the case, you will probably see an error message instead that is along the following lines: “… computer doesn’t have VT-X/AMD-v enabled.”
If you do in fact see the prompt, run the following command just to ensure that things are working:
You should see some information that reflects the VM that was just created with an IP of 192.168.1.100.
There are a number of special Docker commands that you can run at this prompt. We will come back to this later. For now, just minimize the console window.
Install Docker Tools for Visual Studio 2015 – Preview
We’re just about done getting everything installed. The last piece is to install the Visual Studio Docker Tools extension. For starters, go grab the installer from the website. There is a lot of useful information about the tools on that site and I recommend coming back to this page and browsing through the documentation. But for now, just click the purple “Download” button to grab the installer.
Once downloaded, kick off the installer and run through the wizard.
Create a WebAPI that Runs in Docker
Now that we have all of the pre-requisites installed, we’re ready to check out some of the features provided by the Docker Tools for Visual Studio extension.
Let’s fire up Visual Studio 2015 and create a new project that targets the ASP.NET Core platform. Provide some details
IMPORTANT: For now, please use a folder in your C:\Users\<username> path for the location and a use project name that does not contain any spaces or periods.
NOTE: I have found that using fully qualified namespaces in your Docker project name can cause some issues. See this post for more information.
NOTE: The compiler will complain if you use a path other than your user path. Furthermore, I believe this will most likely will not work due to how the debugger ties in with the DNX engine in order to enable live code updates and debugging. There is a way to add a shared path to the VM in Oracle VirtualBox, but I haven’t had luck with doing so yet and will not be covering that in this post.
On the following page, choose “Web API” for the template and disable the “Host in the cloud” option. This isn’t necessary, but will result in extra fluff that we don’t need for the sake of what we’re doing.
Add Docker Support to the Project
Thus far, we’ve seen some of the features of the .NET Core tooling, but nothing as of yet is the Docker toolkit. Let’s change that.
Right click on the project we just created, hover over Add, and then select the “Docker Support” option.
Now let’s take a look at the solution explorer to see what we just did.
A couple of interesting files show up here now. These files are utilized when you run the build on the project in order to provide the necessary glue to wire things up in your Docker host. DockerTask.ps1 is the main entry point for all of this.
So how does the script get wired up? Let’s take a look at the properties for the project.
From here, we can see that the Launch target for the newly created Docker profile is PowerShell. PowerShell is then fed several parameters which ultimately lead to the execution of the DockerTask.ps1 file.
-ExecutionPolicy RemoteSigned .\Docker\DockerTask.ps1 -Run -Environment $(Configuration) -Machine '$(DockerMachineName)'
You may have even noticed already that we now have an option in the top menu for starting the debugger with the Docker option. Just take note this for now. We’ll come back and debug in a few after we’ve taken a moment to look at some other interesting things.
Build the Project
At this point, we should be ready to build the application. Right click on the solution from the Solution Explorer and choose the option to Build.
You should begin to see a lot of information being generated to the Build Output window. Eventually, the build should succeed. If it doesn’t you may want to double check that you created the project in the correct User directory and with an appropriate project name. Beyond that, the Appendix of this post contains a few tips for getting past a couple of the build errors that you may encounter.
If you do see output, the tail end should reflect something similar to the following:
1> Building hellodocker 1> Step 1 : FROM microsoft/dotnet:1.0.0-rc2-core 1> 1.0.0-rc2-core: Pulling from microsoft/dotnet 1> Digest: sha256:8a09be0b287e886097e79b70d0a8e1b06609ad50cea13c0c768032d2e48dcfbe 1> Status: Downloaded newer image for microsoft/dotnet:1.0.0-rc2-core 1> ---> 9b5ddd7a5b97 1> Step 2 : COPY /clrdbg /clrdbg 1> ---> a6a66d1d362b 1> Removing intermediate container 7ba5f8553727 1> Step 3 : RUN chmod 700 -R /clrdbg 1> ---> Running in 4afdf84713f5 1> ---> 099970b957b1 1> Removing intermediate container 4afdf84713f5 1> Step 4 : COPY /app /app 1> ---> 444d95737a55 1> Removing intermediate container 4a950e3276b5 1> Step 5 : WORKDIR /app 1> ---> Running in 05ae3f0a4506 1> ---> 122a2f033ed2 1> Removing intermediate container 05ae3f0a4506 1> Step 6 : ENV ASPNETCORE_SERVER.URLS http://*:80 1> ---> Running in 5001aff497c5 1> ---> 5ce1fa895fd7 1> Removing intermediate container 5001aff497c5 1> Step 7 : EXPOSE 80 1> ---> Running in b2d53c0114fa 1> ---> c2b80f925ddf 1> Removing intermediate container b2d53c0114fa 1> Step 8 : ENTRYPOINT /bin/bash -c if [ "$REMOTE_DEBUGGING" -eq 0 ]; then dotnet HelloDocker.dll; else sleep infinity; fi 1> ---> Running in 763d3470f798 1> ---> ff3221a60e68 1> Removing intermediate container 763d3470f798 1> Successfully built ff3221a60e68
This is where the toolkit really begins to shine. A lot of the implementation is hidden away in the PowerShell script, but what we can see here is actual output from the “docker build” command.
I won’t go into the details of the docker build command, but in a previous post, I show more of the guts of the Docker command line that is being hidden away by the PowerShell script. In that post, I show you how to create an image from scratch using the command line that is based on the mono image. That particular post was prior to .NET core.
The short of it is that the PowerShell task kicks off a command known as “docker compose”. The compose configuration file in our solution instructs compose on several aspects of how it should build our application suite.
Furthermore, our solution also contains two Dockerfile.<environment> files. These files instruct the docker build command on what it should be building and from what source image it should derive from (“microsoft/dotnet:1.0.0-rc2-core”).
I’ve explained some of this in the other post I mentioned, but the beauty of the tools is that they simplify and abstract all of this away. It is definitely still worthwhile for you to understand what is happening behind the scenes if you plan to use Docker, but for now, let’s continue along our path towards debugging our Docker container.
But… Before we do that, let’s take one more quick stop in the Docker terminal. If you closed this, do not worry. It can quickly be opened again from the “Docker Quickstart Terminal” within your start menu. Once in the terminal run the following command:
And then run this command as well:
Your output should reflect something similar to the following:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE hellodocker_hellodocker 2016-05-23_02-41-17 ff3221a60e68 31 seconds ago 414.1 MB hellodocker_hellodocker latest ff3221a60e68 31 seconds ago 414.1 MB microsoft/dotnet 1.0.0-rc2-core 9b5ddd7a5b97 3 days ago 242.3 MB $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
This is proof that our build successfully created a Docker image for us. Not only did it create the image, but you can see from the output that it successfully fetched the “microsoft/dotnet” image which our image is based upon. It even applied some tags for us so we know when the image was created.
Also note that no containers exist… just yet. To speak in MS Deploy terms, if you think of the image as the compiled application deployment package, then the container can be thought of as an instance of that package that has been deployed to IIS and is ready to serve requests. This is obviously a simplified explanation, but the key take away is that the container (runnable instance of our application) doesn’t exist yet. Let’s fix that. On to Debugging our Container!
Debugging .NET Core within a Docker Container
Now on to the cool part, right? And yes, you heard me right, with Docker Tools for Visual Studio 2015, you can now debug a .NET Core application that is running within a Docker container. This is probably my favorite feature of the extension. To be upfront, the mechanics behind the scenes is something I’m still wrapping my head around, but that topic goes beyond the scope of this post.
Let’s place a debug point in our ValuesController.cs file on line 16.
Now, hit F5 to fire up the debugger, or alternatively, click the green debug (play) button at the top.
You will see a couple of prompts pop… Firstly:
At the point you see this, our container has actually been created. In the background, the extension is executing the “docker run” command on our host. This prompt should shortly be followed by:
This part is a bit tricky. The script has a wait command in the background that loops until it gets a response from the root of the running application. However, if our WebAPI does not have anything responding at the root, this piece may spin indefinitely. The idea behind it though is that once it gets a response, it will fire the browser open to the root of the application for you. If this box doesn’t go away after 30 seconds or so, go ahead and click cancel and then manually open your browser.
With your browser now open, go ahead and navigate to the ValuesController endpoint where we have our debug breakpoint: http://192.168.99.100/api/values.
You can see it successfully is debugging our application, which is now running in a container. Take a moment to pause and think about that… Pretty cool right? Now hit F5 to continue the debugger past our breakpoint and you should see our content show up in your browser:
Just to prove there’s no smoke and mirrors here, let’s jump back over to the Docker terminal and see what has changed. Send through the “docker ps” command in order to query all active containers running within Docker.
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1f74b0dbd6f8 hellodocker_hellodocker "/bin/bash -c 'if [ \"" 3 minutes ago Up 4 minutes 0.0.0.0:80->80/tcp hellodocker_hellodocker_1
And there you have it. Our container is running within our Docker host and we can even set break points in order to debug our code.
I hope you enjoyed this post. In a follow up post, I’m going to cover where we go from here. In the subsequent post, which will probably land sometime next week, I will show you how to take the Docker image that we just created and deploy it to Azure Container Services.
NOTE: If you ran into any issues while following along, make sure to skim the Appendix below to see if it has a solution listed for your problem.
If you enjoyed this post, I would greatly appreciate you sharing this post using the social icons at the bottom. In addition, I moderate the comments every other day or so and will make sure to include your comments as soon as I can and will respond to any questions you have.
Stay tuned for the upcoming post on how to deploy to Azure Container Service.
Other Useful Content
If you’re interested in learning more about Docker Tools for Visual Studio, you should check out the following video and documentation links:
- Docker Tools for Visual Studio 2015 – Preview: https://visualstudiogallery.msdn.microsoft.com/0f5b2caa-ea00-41c8-b8a2-058c7da0b3e4
- Debugging apps in a local Docker container: https://azure.microsoft.com/en-us/documentation/articles/vs-azure-tools-docker-edit-and-refresh/
- Deploy an ASP.NET container to a remote Docker host: https://azure.microsoft.com/en-us/documentation/articles/vs-azure-tools-docker-hosting-web-apps-in-docker/
- Steve Lasker on Docker Tools for Visual Studio: https://channel9.msdn.com/Blogs/Seth-Juarez/Steve-Lasker-on-Docker-Tools-for-Visual-Studio
Appendix A – Errors
As I experimented with Docker Tools for Visual Studio 2015, I did run into a couple of hickups, which were mostly due to my environment setup. Use this section to troubleshoot any potential errors you saw. If this doesn’t solve your problem, please send me the resolution in your comments below so that I can update this post.
Appendix B – Virtualization Error from Docker Quickstart Terminal
When I first setup the Docker Toolkit on Windows, I ran into the following error:
Error with pre-create check: “This computer doesn’t have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory”
Looks like something went wrong in step ´Checking if machine default exists´… Press any key to continue…
Possible VMWare Fusion Solution
The first time I ran into this, I was trying to set things up in a VMWare Fusion VM on my Mac. I did not fully troubleshoot this issue and get it working, but according to this post, it should be possible to tweak your settings in order to make this work.
I do have VirtualBox inside of VMware Fusion working on a VM on my laptop, you have to toggle a setting in the Preferences menu for the VM to get nested virtualization to work though.
I opted to go a different route as I started thinking about it and realized that this solution would result in a container virtualization platform (Docker) running inside a VM that is hosted in VirtualBox, which is running on a VM that is hosted in VMWare fusion on my Mac. Someone commented on one of the threads that this all seems very much like the movie, “Inception” and I couldn’t agree more. I worried I might run into other issues with this setup, so I decided to go another route.
Enable Virtualization in BIOS
If you are not trying to set things up in a VM, then you most likely need to enable virtualization in your BIOS. You should run a google search for your computer make / model or for your specific motherboard (if you know it) along the terms, “VT-X” and/or “AMD-v”. This should yield you the steps to get this working hopefully.
In my case, I had to reboot my computer and mash F12 prior to the boot sequence in order to get into the BIOS. From there, the option was under
- “Advanced” Tab à “CPU Setup” option (drill-down)
- “CPU Setup” Screen à “Advanced” tab à “Intel (R) Virtualization Technology” toggle
I set this toggle to enabled, and then navigated backwards until I was eventually able to save and exit.
Appendix C – PowerShell Build Errors
Most of the errors you encounter will originate when you build the project and will be in the form of:
This by itself is not super useful, and you’ll need to browse to the Output section to see more details.
Couldn’t Connect to Daemon Error
This error is pretty common and comes in the form of:
1> publish: Published to C:\<snipped>\ HelloAzureContainer\bin\Docker\Debug\app 1> Published 1/1 projects successfully 1> Building helloazurecontainer 1> Couldn't connect to Docker daemon. You might need to install Docker: 1> 1> https://docs.docker.com/engine/installation/ 1> Build : Failed to build the image
This could mean a number of things, but most likely it means that your Docker daemon is not even running. If you reboot your computer, Docker will not automatically start and you will need to intervene.
The easiest method is to open the Docker Quickstart Terminal, which will start up your existing “default” Docker machine.
If you run into issues with this, you may need to troubleshoot further. In one case, my Docker host was really foobar (probably by my own doing) and I eventually just removed it and allowed the Quickstart Terminal to create it again from scratch. This is probably a last resort and understand that if you do so, you will lose all images and containers that you created. Therefore, proceed at your own discretion.
In order to reset the Docker machine, run the following from the Docker Terminal:
docker-machine rm default
This will delete the Docker machine that is named “default”. The easiest way to create the machine again is to close all existing console windows and then open the Docker Quickstart Terminal again.