.netdockerfilegithub-actionsbuildx

Github Action Docker Build Segmentation fault for ARM64/v8


I am trying to build a docker image and publish it to Github Container Registery that can be started on my Raspberry Pi with Ubuntu 22.04, ARM64/v8.

Build for amd64 forks perfectly, but whenever i want to build it for ARM I am getting

...
#13 [build 5/8] RUN dotnet restore "./Imagenerator.Api/Imagenerator.Api.csproj"
#13 6.095 Segmentation fault (core dumped)
#13 ERROR: process "/bin/sh -c dotnet restore \"./Imagenerator.Api/Imagenerator.Api.csproj\"" did not complete successfully: exit code: 139
------
 > [build 5/8] RUN dotnet restore "./Imagenerator.Api/Imagenerator.Api.csproj":
6.095 Segmentation fault (core dumped)
------
Dockerfile:15
--------------------
  13 |     COPY ["Directory.Build.props", "."]
  14 |     COPY ["Imagenerator.Api/Imagenerator.Api.csproj", "Imagenerator.Api/"]
  15 | >>> RUN dotnet restore "./Imagenerator.Api/Imagenerator.Api.csproj"
  16 |     COPY . .
  17 |     WORKDIR "/src/Imagenerator.Api"
--------------------
ERROR: failed to solve: process "/bin/sh -c dotnet restore \"./Imagenerator.Api/Imagenerator.Api.csproj\"" did not complete successfully: exit code: 139

Docker file

ARG DOTNET_VERSION=8.0

FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_VERSION AS base
USER app
WORKDIR /app
EXPOSE 8080
EXPOSE 8081

ARG DOTNET_VERSION
FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_VERSION AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Directory.Build.props", "."]
COPY ["Imagenerator.Api/Imagenerator.Api.csproj", "Imagenerator.Api/"]
RUN dotnet restore "./Imagenerator.Api/Imagenerator.Api.csproj"
COPY . .
WORKDIR "/src/Imagenerator.Api"
RUN dotnet build "./Imagenerator.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build

FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./Imagenerator.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Imagenerator.Api.dll"]

Workflow file

name: Imagenerator Build & Publish

on:
  # release:
  #   types: [published]
  workflow_dispatch 

env:
  REGISTRY: ghcr.io

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    
    permissions:
      contents: read
      packages: write
      
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Log in to the Container registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ${{ vars.DOCKERFILE_PATH }}
          push: true
          tags: |
            "${{ vars.IMAGE_NAME }}:latest"
          platforms: ${{ vars.BUILD_PLATFORM }}
          build-args: |
           "DOTNET_VERSION=${{ vars.DOTNET_VERSION }}"

Variables

BUILD_PLATFORM=linux/arm64/v8
DOCKERFILE_PATH=Imagenerator.Api/Dockerfile
DOTNET_VERSION=8.0-jammy
IMAGE_NAME=ghcr.io/managerbot-net/imagenerator

I am expecting to build & publish docker image to Github registery. I tried to change architectures to ARM64/v7, play with the .NET version to use something different than 8.0-jammy but with no success


Solution

  • The segmentation fault occurs because .NET doesn't support running in emulation with QEMU, which is what is being used here on the build agents in this case.

    Try this Dockerfile instead:

    ARG DOTNET_VERSION=8.0
    
    FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:$DOTNET_VERSION AS base
    ARG TARGETARCH
    USER app
    WORKDIR /app
    EXPOSE 8080
    EXPOSE 8081
    
    ARG DOTNET_VERSION
    FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_VERSION AS build
    ARG BUILD_CONFIGURATION=Release
    WORKDIR /src
    COPY ["Directory.Build.props", "."]
    COPY ["Imagenerator.Api/Imagenerator.Api.csproj", "Imagenerator.Api/"]
    RUN dotnet restore -a $TARGETARCH "./Imagenerator.Api/Imagenerator.Api.csproj"
    COPY . .
    WORKDIR "/src/Imagenerator.Api"
    RUN dotnet build -a $TARGETARCH "./Imagenerator.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build
    
    FROM build AS publish
    ARG BUILD_CONFIGURATION=Release
    RUN dotnet publish -a $TARGETARCH "./Imagenerator.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
    
    FROM base AS final
    WORKDIR /app
    COPY --from=publish /app/publish .
    ENTRYPOINT ["dotnet", "Imagenerator.Api.dll"]
    

    The only changes I made here are the use of --platform=$BUILDPLATFORM in the FROM instruction and -a $TARGETARCH in the restore and publish commands.

    When targeting arm64 from your workflow, this uses an amd64 version of the sdk image to build the app, which matches the host, but then uses the arm64 version of the aspnet image to create your final image. This pattern is explained in detail in https://devblogs.microsoft.com/dotnet/improving-multiplatform-container-support/.