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/servercoreto install Python - Second stage uses the lighter
nanoserverbase image - Only necessary Python files are copied over, reducing bloat
- First stage uses
-
Security Best Practices:
- Switches to
ContainerUserafter PATH configuration - Uses
no-cache-dirwith pip to keep the image lean - Removes installation files after use
- Switches to
-
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:
- Connect to the Windows node:
kubectl node-shell akswpool000000 -- cmd - Check image size:
crictl images| findstr helloExample 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
nanoserveras 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.