Building Minimal Windows Containers for Python Apps: A DevOps Guide

Posted by

As DevOps engineers, we’re always looking for ways to optimize our container images. While Linux containers are common, Windows containers present unique challenges. Today, I’ll show you how to create a minimal Windows container for a Python application that comes in at under 200MB.

The Multi-Stage Build Magic

Here’s the Dockerfile that makes it happen:

 


FROM mcr.microsoft.com/windows/servercore:ltsc2022-amd64 AS installer
RUN powershell.exe -Command \
$ErrorActionPreference = 'Stop'; \
wget https://www.python.org/ftp/python/3.10.11/python-3.10.11-amd64.exe -OutFile c:\python3-install.exe ; \
Start-Process c:\python3-install.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1 DefaultAllUsersTargetDir=C:\Python310' -Wait ; \
Remove-Item c:\python3-install.exe -Force
RUN python.exe -m pip install --upgrade pip
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022-amd64
USER ContainerAdministrator
RUN setx /M PATH "%PATH%;C:\Python310\Scripts\;C:\Python310"
USER ContainerUser
COPY --from=installer ["/Python310", "/Python310"]
WORKDIR /app
COPY code/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY code/* ./
CMD ["python", "./hello.py"]

Why This Approach Works

  • Multi-Stage Build: We use a two-stage build process:
    • First stage uses windows/servercore to install Python
    • Second stage uses the lighter nanoserver base image
    • Only necessary Python files are copied over, reducing bloat
  • Security Best Practices:
    • Switches to ContainerUser after PATH configuration
    • Uses no-cache-dir with pip to keep the image lean
    • Removes installation files after use
  • Cross-Platform Building:
    az acr build --platform windows/amd64 --image hello --registry $ACR_NAME --file Dockerfile .

    This command is brilliant because:

    • Works from any development machine (including Mac)
    • Handles platform-specific builds in Azure Container Registry
    • Eliminates the need for a Windows development environment

Verifying Image Size

After deploying to your Windows node pool in Kubernetes, you can check the image size:

  1. Connect to the Windows node:
    kubectl node-shell akswpool000000 -- cmd
  2. Check image size:
    crictl images| findstr hello

    Example output:

    hello latest 91bdb3e0bcc04 165MB

At 165MB, this image is remarkably small for a Windows container with Python. For comparison, a basic Windows Server Core image alone is typically over 3GB!

Pro Tips

  • Always use nanoserver as your final base image when possible
  • Remove unnecessary files during the build process
  • Use multi-stage builds to separate installation from runtime
  • Leverage Azure Container Registry’s cross-platform build capabilities

Conclusion

By using multi-stage builds and the Windows Nano Server base image, we’ve created a production-ready Python container that’s surprisingly small. This approach gives us the best of both worlds: Windows compatibility when we need it, without the traditional size penalties associated with Windows containers.

Remember, in containerization, size matters. Smaller containers mean faster deployments, less storage costs, and better resource utilization. This approach helps achieve that while maintaining all the functionality we need.