carloscastilla - Fotolia

Tip

Optimize Docker images on Windows for better system performance

Multiple layers in a Windows Docker image can cause resource contention. Optimization requires you to limit the number of layers that you use and minimize each layer's size.

Optimization is an important part of creating Docker images on Windows, and it's a good idea to reduce the number and size of layers whenever possible.

On the surface, optimization might seem somewhat unimportant given the small size and low overhead of most containers. Although containers generally aren't overly resource-intensive, container hosts often run large numbers of containers simultaneously. As you run more containers, the number of relatively small inefficiencies increases. As such, it makes sense to optimize your Docker images to ensure improved performance and better use of hardware resources.

Reduce the number of layers

Docker images contain a minimum of two layers: the base OS layer and a layer containing image instructions. Each time you use the Run command, you add another layer to the image. For example, a Dockerfile containing three Run instructions results in four separate layers: one layer for the base image and one layer for each of the three Run instructions.

There are valid reasons for creating additional layers, but it's important to avoid creating layers haphazardly to better mitigate reduced system performance. If you want to know how many layers make up an image, simply enter the Docker History command, followed by the image name.

One way to reduce the number of image layers is to partner multiple instructions with each layer. Normally, the words PowerShell and command follow a Run command. A PowerShell command is then added. For example, if you want to install Internet Information Services (IIS), you can use the following command:

Run PowerShell -command Install-WindowsFeature -Name Web-Server

You can use subsequent Run commands to install additional Windows components or to configure components you previously installed. For example, if you want to install ASP.NET to the IIS server, you can use the command below:

Run PowerShell -command Add-WindowsFeature Web-asp-net

Although this approach does work, you'll end up with a three-layered image. One layer is the base OS layer, and then there's a layer for IIS and a layer for ASP.NET. A better option would be to combine IIS and ASP.NET into a single layer. This reduces the image size and improves performance.

Each time you use the Run command, you add another layer to the image.

The trick to combining multiple PowerShell commands into a single Run statement is to separate each command with a semicolon and add a backslash to the end of each line of code. The semicolon tells Windows that it has reached the end of one PowerShell command and the beginning of the next. The backslash indicates that Windows should perform a line wrap, which treats whatever instructions that appear on the next line as a continuation of the current line of code. Here is how to combine the previous instructions into a single layer:

Run PowerShell -command Install-WindowsFeature -Name Web-Server ; \
Add-WindowsFeature Web-asp-net

The first command ends with a semicolon, indicating that there are additional PowerShell commands to process. The backslash tells Windows to read the next command from the next line. Structuring the commands this way reduces the number of layers from three to two because all of the PowerShell commands are running as a part of a single Run command. Incidentally, you can use this technique to link as many PowerShell commands together as you need.

Remove unnecessary files

Another technique you can use to optimize Docker images is to remove anything you don't need. For example, it's relatively common for a Run command to download PHP from Windows.php.net. The PHP download consists of a zip file. To extract the zip file's contents, use the Expand-Archive cmdlet. Then, you can use the Remove-Item cmdlet to remove the zip file from the image to reduce its size.

If you're going to use the Remove-Item cmdlet, specify the Remove-Item cmdlet in the same Run command you use to download the zip file. Otherwise, you'll end up with one layer containing the zip file and another layer in which the zip file has been removed. Here is what the actual code might look like:

Run PowerShell -command wget -Uri http://windows.php.net/downloads/releases/php-5.5.33-Win32-VC11-x86.zip -OutFile c:\php.zip ; \
Expand-Archive -Path c:\php.zip -DestinationPath c:\php ; \
Remove-Item c:\php.zip

If you want to make the code a bit easier to read, you can use a separate line for the Run PowerShell command portion. You simply need to place a backslash at the end of the text; a semicolon isn't required:

Run PowerShell -command \
wget -Uri http://windows.php.net/downloads/releases/php-5.5.33-Win32-VC11-x86.zip -OutFile c:\php.zip ; \
Expand-Archive -Path c:\php.zip -DestinationPath c:\php ; \
Remove-Item c:\php.zip

Next Steps

Combine PowerShell and Docker to simplify testing across OSes

Dig Deeper on Containers and virtualization