'Multi-Image' Docker Image
Problem
I was on the lookout for a solution to a problem in Docker that needed to do the following:
Create a Docker Image for a bunch of compiled ASP.NET
.dll
files and try reducing the container footprint
Sounds simple, right? But it turned out to be a bit more messy than I imagined. To top it off, I have NEVER even programmed in .NET so I was feeling like a visually impaired person made to navigate his way through a dark room with furniture scattered all over!
Thousand Leagues under the Container Sea!
Keeping the whole Maritime theme alive with Docker (and Kubernetes), I jumped into the sea of Docker Hub with millions of containers and found out that Microsoft hosts all the .NET related container images as .NET by Microsoft registry.
Gentle reminder that I have never even thought of .NET before in my life, so just trying to figure out whether I need an one or more of the following images:
- SDK
- ASP.NET Core Runtime
- .NET Runtime
- .NET Runtime Dependencies
- .NET Monitor Tool
- .NET Samples
was a side-mission in its own right.
Side-Mission
A quick deep-dive into Microsoft’s Docker .NET Docs made me think I only need the following docker image:
mcr.microsoft.com/dotnet/aspnet:6.0
So packing everything in a simple Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal
WORKDIR /DotNetVoyage
COPY ./appsettings.json /DotNetVoyage/Server-Files/
# Copy every '.dll' file I was handed
COPY ./* /DotNetVoyage/Server-Files/
CMD [ "dotnet", "Server-Files/Server.dll" ]
Building it locally and accessing the App initially on the dedicated ports seemed to work just fine and I thought I was getting off work a bit early today. I jinxed myself.
Upon some API testing of the image I started seems some logs that were strange to my non .NETian brain.
The specified framework can be found at:
It was not possible to find any compatible framework version
The framework 'Microsoft.AspNetCore.App', version '5.0.0' (x64) was not found.
- The following frameworks were found:
6.0.6 at [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
You can resolve the problem by installing the specified framework and/or SDK.
I was head scratching as to how I can make my runtime which is 6.0.0
potentially have an SDK version that must
have 5.0.0
SDK on it. It got worse, I found out I needed the now defunct and not supported 2.1
version of the SDK
too!
Build everything from scratch?
Unaware if there was a way to introduce the different SDKs into the already slimmed container image, I had lost hope
and decided to build an image with maybe debian
or ubuntu
or alpine
.
However, upon asking the Slack Community and rummaging through some StackExchange queries, I traced out a plan that was pretty familiar to me: Using Multi-Stage Docker Builds.
Charting towards a Destination
I use Multi-Stage Docker builds almost everyday for work and so I decided to design my container image as follows:
Stage 1: pull SDK 2.1
1.1: find out which directory do the SDK Files persist
Stage 2: pull SDK 5.1
2.1: find out which directory do the SDK Files persist
Stage Prod: Pull the 6.0.0 Runtime
Prod.1: copy all the SDK Files (from Stage 1 and Stage 2) to the same directory here in Prod Stage
Prod.2: copy all the DLL files from host and run the dedicated DLL
Where art thou SDKs?
A quick pull:
docker pull mcr.microsoft.com/dotnet/sdk:2.1
and check within the container:
docker run -it --rm --name=sdk21-check mcr.microsoft.com/dotnet/sdk:2.1 dotnet --list-sdks
resulted in:
2.1.818 [/usr/share/dotnet/sdk]
Gotcha! the directory for the SDKs is /usr/share/dotnet/
Multi-Image Image
what I mean by this is whether Docker is smart enough to directly pull an image either using COPY
or ADD
instructions without me having to create Stage 1
and Stage 2
using syntax such as:
FROM mcr.microsoft.com/dotnet/sdk:2.1 as base21
FROM mcr.microsoft.com/dotnet/sdk:5.0 as base50
It turns out that COPY
is extremely flexible! with the COPY --from
instruction in the Dockerfile
is capable of pulling the dedicated image from a registry and the respective directories can be copied easily!
Solution
Final Checks for Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal
# Add those missing SDKs here
## Syntax: COPY --from=<registry/image:version> image_directory ProdStage__dest_directory
COPY --from=mcr.microsoft.com/dotnet/sdk:2.1 /usr/share/dotnet /usr/share/dotnet/
COPY --from=mcr.microsoft.com/dotnet/sdk:5.1 /usr/share/dotnet /usr/share/dotnet/
# List all the SDKs / Runtimes available in the container
CMD ["dotnet", "--list-sdks", "dotnet", "--list-runtimes"]
build the image and run it and the output will be similar to:
Microsoft.AspNetCore.All 2.1.30 [/usr/share/dotnet/shared/Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.30 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.17 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.6 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.30 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.17 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.6 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Voilá ! Runtimes v6.0
, v5.0
, v2.1
all in the same container !!
Final Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal
# Add those missing SDKs here
## Syntax: COPY --from=<registry/image:version> image_directory ProdStage__dest_directory
COPY --from=mcr.microsoft.com/dotnet/sdk:2.1 /usr/share/dotnet /usr/share/dotnet/
COPY --from=mcr.microsoft.com/dotnet/sdk:5.1 /usr/share/dotnet /usr/share/dotnet
WORKDIR /DotNetVoyage
COPY ./appsettings.json /DotNetVoyage/Server-Files/
# Copy every '.dll' file I was handed
COPY ./* /DotNetVoyage/Server-Files/
CMD [ "dotnet", "Server-Files/Server.dll" ]
Just another day on the decks with Docker !!
Get in touch if you feel like dropping some suggestions, feedbacks or criticism! Always happy to learn and improve!