An exploration of Docker in Windows

 Docker's great. Running applications without cluttering up your machine with random installs is beneficial for a number of reasons.


However, for years its power has been in the Linux space. Images are smaller, and everything just works. Docker for Windows? Everything does not "just work" all the time, at least not in my experience, and each roadblock seems to point to a toolkit that obviously has been playing second fiddle to its Linux counterpart.

When researching this, I ran into all sorts of interesting things, including someone that set up a monolithic container running Windows Server Core, IIS, and a SQL database. My first thought on seeing that was "why not just use a VM?" There has to be a better way. I set out to see if I could put together a practical containerized application using Docker for Windows.

My wheelhouse is .NET web development, so that seems like a logical place to begin. To start, I clean my system of all development apps, frameworks, and tools so I can start from scratch.

IIS is not installed on my host machine, at least not anymore.

Tools

Stripping everything turns out to be a questionable idea. Since I'm developing in .NET I need the framework and build tools at a minimum - and if I don't want to go insane I need an IDE.
  • Visual Studio 2017 
  • ASP.NET 4.7 
  • Visual Studio Code (I could really do everything in VS2017, but VSCode is lightweight and excellent for quick changes in almost any language) 

IIS and ASP.NET

Microsoft already provides a microsoft/iis image; how handy! The run command Microsoft provides seems to work well enough. To make this solution more robust and reusable, I use a Dockerfile.

FROM microsoft/iis

EXPOSE 80


Easy enough. Time to give it a shot in Powershell:

docker build -t my-iis .
docker run -d -p 80:80 --name running-iis my-iis


Time to hit it in a web browser using http://localhost -



Awesome, IIS runs! Now I need my OWN content up and running as a site.

I can do that by tweaking the Dockerfile a bit and adding a new website. The new version looks like this:

FROM microsoft/iis

RUN mkdir C:\webroot

RUN powershell -NoProfile -Command Import-Module IISAdministration; \
New-IISSite -Name "Website" -PhysicalPath C:\webroot -BindingInformation "*:80:" -Force

EXPOSE 80


After ensuring that works without error, I add a simple HTML file to my ecosystem. In the host directory next to the Dockerfile, I create a content folder and add an index.html file:

<html>
<body>
<h1>Hello, world!</h1>
<p>Now is the time for all good men to come to the aid of their country.</p>
</body>
</html>


Next step is getting that index file into my container - which I do by adding it in the Dockerfile right above the EXPOSE command:

ADD content/ /webroot

Time to try it again! First, I delete my container and image from the initial attempt, then run the steps again.



So far, so good. Personally, I don't like having to run two (or more) commands to bring something up - especially cryptic ones. I can simplify the spin-up process and make adding other items a bit more straightforward by using docker-compose.

In the same directory, I create a file called docker-compose.yml and add the following:

version: '3.6'
services:
    web:
        build: .
        ports:
            - "80:80"
volumes:
  - "./content:c:\\webroot"


Since this adds a volume (and the "content" folder), I can remove the mkdir and ADD statements from the Dockerfile at the same time. A quick run of the docker compose version tells me everything still works, so I can move on.

I just gave myself the ability to start and stop the container by using docker-compose up and docker-compose down.

So far, I have a simple HTML page running in a Docker container - but what about .NET? That's next.

In Visual Studio, I scaffold a new .NET 4.7.1 MVC application and set my publish target to the content folder used in my container volume. To ensure I get my app, and not the index file, the index file is renamed to index.html.old.

Another test!



Uh-oh. Something broke.

Then I realize I forgot to install ASP.NET into my container! Fortunately I can fix that via Powershell:

docker exec -it <container id> powershell

That gives me a new Powershell prompt inside the context of the container. Time to install .NET:

Install-WindowsFeature NET-Framework-45-ASPNET
Install-WindowsFeature Web-Asp-Net45
Restart-WebAppPool DefaultAppPool


Hit the site again on my host, and voila!



My site is running from the container - as if it were on my local machine.

Now I have to fix my Dockerfile so the site starts up properly every time without needing to do that again. My final Dockerfile looks like this:

FROM microsoft/iis

RUN powershell -NoProfile -Command Import-Module IISAdministration; \
New-IISSite -Name "Website" -PhysicalPath C:\webroot -BindingInformation "*:80:" -Force; \
Install-WindowsFeature NET-Framework-45-ASPNET ; \
Install-WindowsFeature Web-Asp-Net45

EXPOSE 80

One final test. I delete all my containers and images, go to my root folder in Powershell, and run docker-compose up -d to start a detached version of the container.

It works!

Popular Posts