-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
There is no way to write a Dockerfile for .NET that is (A) succinct, (B) easy to build from the command line, (C) can equally produce Arm64 or x64 images, (D) that will run equally well on both Arm64 and x64 machines, and (E) avoids running the SDK in an emulator (since .NET doesn't support running in QEMU).
The following Dockerfile (which doesn't currently work) would satisfy all four of these requirements.
ARG BUILDARCH
FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-$BUILDARCH AS build
ARG TARGETARCH
WORKDIR /source
# copy csproj and restore as distinct layers
COPY aspnetapp/*.csproj .
RUN dotnet restore -r linux-$TARGETARCH
# copy everything else and build app
COPY aspnetapp/. .
RUN dotnet publish -c Release -r linux-$TARGETARCH --self-contained false --no-restore -o /app
# final stage/image
FROM mcr.microsoft.com/dotnet/aspnet:7.0-bullseye-slim
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["./aspnetapp"]Note: The two ENVs are set via Docker, not my invention. Context: #4387 (comment). The reason this Dockerfile doesn't work is because -r expects x64 not amd64 (which $TARGETARCH returns) and our container tags expect arm64v8 not arm64 (which $BUILDARCH returns). Funny enough, $TARGETARCH returns arm64 for Arm64, which -r likes fine and $BUILDARCH returns amd64 for x64, which our container tags like fine.
Note: Close readers may wonder why $TARGETARCH is not needed for the last FROM statement. That's because --platform is picking the correct image from the multi-arch tag. We could use $TARGETARCH if we wanted, but it is unnecessary since the underlying mechanics will do the right thing. How would one know that the tag is a multi-arch tag just by looking at it? It's because it doesn't include an architecture.
It would enable the following scenarios:
docker build-- will produce a native-arch result on both Arm64 and x64 and use the native arch SDK.docker build --platform linux/arm64-- Will produce an Arm64 container image on both Arm64 and x64 machines but run the SDK as native-arch, avoiding emulation (on x64 machines).docker build --platform linux/amd64-- Same as above, but will produce an x64 container images, and similarly avoid emulation (on Arm64 machines).docker buildx build --platform linux/amd64,linux/arm64-- Same as above, but will produce both Arm64 and x64 container images, and similarly avoid emulation.
Note that the resulting image will not run in emulation. That's the not the purpose of this proposal. Instead, the purpose is to reliably avoid emulation and to make it easy, for example, to build x64 container images on an Apple M1 box and push those to an x64 cloud. You'd be able to do that via the --platform switch and not need to do anything else (other than follow the pattern used in the Dockerfile).
It requires two things:
- For Arm64, we need to push container tags as
arm64(matching$BUILDARCH) in addition toarm64v8. - For x64, teaching the CLI to replace
linux-amd64(matching$TARGETARCH) withlinux-x64
The proposal is to make these changes for .NET 6+.
The alternative is the following.
ARG SDKARCH=amd64
FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-$SDKARCH AS build
ARG TARGETARCH
RUN arch=$TARGETARCH \
&& if [ "$TARGETARCH" = "amd64" ]; then arch="x64"; fi \
&& echo $arch > /tmp/arch
WORKDIR /source
# copy csproj and restore as distinct layers
COPY aspnetapp/*.csproj .
RUN dotnet restore -r linux-$(cat /tmp/arch)
# copy everything else and build app
COPY aspnetapp/. .
RUN dotnet publish -r linux-$(cat /tmp/arch) -c Release --self-contained false --no-restore -o /app
# final stage/image
FROM mcr.microsoft.com/dotnet/aspnet:7.0-bullseye-slim
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["./aspnetapp"]Inspiration: #4387 (comment)
SDKARCH needs to have a default, so you need to know to set the ARG on whatever platform isn't the default. Here, I'm choosing x64 (or really "amd64") as the default. Also, you cannot run any code before the first FROM since a Dockerfile isn't bash.
This pattern enables the following scenarios:
docker build-- only works correctly on x64 and breaks on Arm64 (dotnet restorejust hangs),docker build --build-arg SDKARCH=arm64v8-- builds an Arm64 image on an Arm64 machine, not because theARGis set, however. TheARGjust enables the SDK to run with the native arch.docker build --platform linux/arm64-- Doesn't do anything useful, same for specifyinglinux/amd64.docker build buildx --platform linux/amd64,linux/arm64-- Doesn't work.docker build buildx --platform linux/amd64,linux/arm64 --build-arg SDKARCH=arm64v8-- Produces both Arm64 and x64 container images.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status