Windows Containers: Portainer GUI
Windows Containers: Data

Windows Containers: Build

This post is a building block for working with containers on Windows. I have covered elsewhere installing the Containers feature with Docker, and running containers with the Docker command line. We can't do much that is useful without building our own images. Doing this tells us a lot about what we can and cannot do with containers on Windows.

Some preamble:

  1. A container is not persistent. It is an instance of an image. You can make changes inside a running container, for example installing or configuring an application, but unless you build a new container image with your changes, they will not be saved.
  2. A Windows container has no GUI. Any installation or configuration will be done at the command line.
  3. Therefore we should make our changes in a script, containing the instructions to build a new image.
  4. This script is a Dockerfile.

The command to build an image is: docker image build with a range of options, including the path to the Dockerfile.

You can also run: docker image commit to create a new image from a running container. This gives scope for configuring a container interactively before saving it as a new image. But, since the only interface to configure the container is the command line, and since the same commands can be performed in the Dockerfile, this has limited use.

Building an image in Docker is a similar idea to building an image for OS deployment. The Dockerfile is like the task sequence in MDT or SCCM, being a scripted set of tasks. The documentation is here: Dockerfile reference. An example is this one, from Microsoft, for IIS on Windows Server Core:

FROM microsoft/windowsservercore
RUN powershell -Command Add-WindowsFeature Web-Server
ADD ServiceMonitor.exe /ServiceMonitor.exe
ENTRYPOINT ["C:\\ServiceMonitor.exe", "w3svc"]

The basic structure of a Dockerfile is:

  • FROM to specify the image that the new image is developed from
  • ADD or COPY from source to destination to put new files into the image
  • RUN to execute commands to configure the image
  • CMD to specify a command to start the container with, if no other command is specified
  • EXPOSE to indicate what port or ports the application listens on
  • ENTRYPOINT to specify the services or executables that should run automatically when a container is created.

We can immediately see some implications:

  1. We don't have to build every part of the end image in one Dockerfile. We can chain images together. For example, we could build a generic web server FROM microsoft/iis, then build specific web sites with other components in new images based on that.
  2. Adding a single feature is easy, like: Add-WindowsFeature Web-Server. But configuring it with all the required options will be considerably more complicated: add website; application pool; server certificate etc.
  3. We may want to bundle sets of commands into separate scripts and run those instead of the individual commands.
  4. There is no RDP to the container, no remote management, no access to Event Logs: and arguably we don't need to manage the container in the same way. But we can add agents to the image, for example a Splunk agent.
  5. Static data can be included in the image, of course, but if we want dynamic data then we need to decide which folders it will be in, so we can mount external folders to these when we run the container.

It is rather like doing a scripted OS deployment without MDT. I would not be surprised if a GUI tool emerges soon to automate the build scripting.

You may find a number of Dockerfiles for Windows using the Deployment Image Servicing and Management (DISM) tool. There is a confusing choice of tools and no particular need to use DISM (or reason not to). DISM is typically used for offline servicing of Windows Imaging Format (WIM) images. For example it can be used to stream updates and packages into a WIM image by mounting it. But in the case of Docker images the changes are made by instantiating a temporary container for each RUN, and the DISM commands are executed online. This means we can use three different types of command to do the same thing:

  • Install-WindowsFeature from the ServerManager module in PowerShell
  • Enable-WindowsOptionalFeature from the DISM module in PowerShell
  • dism.exe /online /enable-feature from DISM.

Just to make life interesting and keep us busy, the commands to add a feature use different names for the same feature!