· DevOps  · 4 Min. Lesezeit

Wie man eine .NET Applikation in Docker für ARM64 bereitstellt

In der Cloud lassen sich durch den Einsatz von ARM-Prozessoren Kosten einsparen. So containerisiert man eine .NET-Applikation für ARM64.

In der Cloud lassen sich durch den Einsatz von ARM-Prozessoren Kosten einsparen. So containerisiert man eine .NET-Applikation für ARM64.

Die Container-Technologie ist mittlerweile ein fester Bestandteil des DevOps-Ökosystems. Einer der Vorteile von Docker ist die Möglichkeit, Anwendungen unabhängig von der zugrunde liegenden Infrastruktur bereitzustellen. Ein besonderes Augenmerk liegt dabei auf der Unterstützung für ARM64-Architekturen, die in einer Vielzahl von Anwendungen, von IoT-Geräten bis zu Servern, zum Einsatz kommen. Besonders in der Cloud lassen sich durch den Einsatz von ARM-Prozessoren Kosten einsparen. In diesem Beitrag erfahrt ihr, wie ihr eine .NET-Applikation für ARM64 in Docker containerisiert.

Voraussetzungen

Bevor es losgeht, sollten Docker und das .NET SDK in der neuesten Version auf eurem System installiert sein. In diesem Beispiel nutze ich .NET 7. Außerdem ist ein grundlegendes Verständnis für die Arbeit mit der Kommandozeile und Docker von Vorteil.

Die .NET Applikation

Um nicht das einfachste Setup zu wählen, wähle ich als Applikation eine Blazor Hosted WASM app. Das heißt, dass sie aus drei Projekten besteht:

  1. Einem Server, der sich um das Prerendering kümmert und als Webserver agiert
  2. Einem Client, der die Blazor WASM Anwendung beinhaltet
  3. Einer Shared Library, die geteilte Elemente enthält

Anpassungen für ARM64

In der Server-Applikation muss in die Projektdatei folgende Zeile zur PropertyGroup hinzugefügt werden:

<RuntimeIdentifiers>linux-arm64;osx-arm64</RuntimeIdentifiers>

Hiermit legt ihr die Zielsysteme fest. Ich möchte die Anwendung in diesem Fall auf ARM64 auf Linux veröffentlichen. Und da ich einen Mac nutze, kommt die Konfiguration dafür auch hinzu.

Eventuell sind einige weitere Anpassungen am Code nötig, um die Applikation für die ARM64-Architektur zu optimieren. Meistens beschränken sich diese Änderungen jedoch auf Konfigurationsdateien oder Abhängigkeiten. Bei relativ einfachen Applikationen reicht die genannte Änderung in der Server-Projektdatei.

Dockerfile erstellen

Jetzt ist es an der Zeit, das Dockerfile zu schreiben. Ein Dockerfile ist die schriftliche Anleitung für Docker, wie der Container erstellt werden soll. Für eine ARM64-Applikation könnte das Dockerfile wie folgt aussehen:

FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:7.0-alpine AS build
ARG TARGETARCH
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "Building on $BUILDPLATFORM, targeting $TARGETPLATFORM"

WORKDIR /source
COPY . .
RUN dotnet restore -a $TARGETARCH Server/
RUN dotnet build -a $TARGETARCH --no-restore Server/
RUN dotnet publish -a $TARGETARCH --no-restore -c Release -o /app /p:UseAppHost=false Server/

FROM --platform=$TARGETARCH mcr.microsoft.com/dotnet/aspnet:7.0-alpine
RUN apk add curl

WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "MeineApp.Server.dll"]

Ich nutze einen Multi Stage Build. Das heißt dass ich für das Erstellen und das Bereitstellen der Anwendung zwei getrennte Images nutze. Nur letzteres (ohne SDK, daher kleiner) wird im finalen Image verpackt.

Die wichtigsten Teile sind:

Block 1: Mit --platform=$BUILDPLATFORM sagen wir Docker dass wir für das Build-Image die gleiche Architektur verwenden wollen, wie die des Systems, auf dem wir den Build ausführen. Dies reduziert Inkompatibilität. Außerdem nutze ich Alpine, da im Gegensatz zu Debian, das Zielimage ca 100MB kleiner ist. Kleinere Images lassen sich später schneller ausrollen, was besonders bei skalierenden Anwendungen wichtig ist.

Block 3: Hier wird das Ausgangssystem und das Zielsystem ausgegeben.

Block 4: Hier wird die Anwendung für das Zielsystem erstellt.

Block 5: Jetzt nehmen wir das Basisimage für die Zielarchitektur, mit --platform=$TARGETARCH. Außerdem nutzen wir nur aspnet und kein volles SDK. Das SDK ist zu Ausführen nicht mehr nötig.

Docker Image bauen

Nachdem das Dockerfile fertiggestellt ist, muss das zugehörige Docker-Image erstellt werden. Das geht mit folgendem Befehl:

docker buildx build --platform=linux/arm64 . -t homepage:latest --no-cache

Github Actions

Hier noch für die Github-Nutzer ein Job, der das Image erstellt, mit dem aktuellen Git Tag tagged (z.B. 1.0.2) und auf AWS ECR (eine Docker Registry) pusht.

build-and-push-docker:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3

    - name: Set output
      id: vars
      run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT

    - name: Set up QEMU
      uses: docker/setup-qemu-action@v2

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2

    - uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '7.0.x'

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: eu-central-1

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - name: Build, tag, and push docker image to Amazon ECR
      env:
        REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        REPOSITORY: mein-ecr-repository
        IMAGE_TAG: ${{ steps.vars.outputs.tag }}
      run: |
        docker buildx build --push --platform=linux/arm64 -t ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }} . --no-cache

    - name: Logout of Amazon ECR
      run: docker logout ${{ steps.login-ecr.outputs.registry }}

Best Practices

Es lohnt sich, Tags für ARM64-spezifische Images zu verwenden, damit diese leichter zu finden und zu verwalten sind. Zudem sollten Images regelmäßig aktualisiert werden, um von Sicherheitspatches und Performance-Verbesserungen zu profitieren.

Fazit

Das Containerisieren und Bereitstellen einer .NET-Applikation für ARM64 ist ein relativ einfacher Prozess, der jedoch einiges an Vorbereitung erfordert. Mit den in diesem Beitrag vorgestellten Schritten sollte es möglich sein, eigene .NET-Projekte effizient für ARM64 zu containerisieren und zu deployen.

Ich nutze dieses Vorgehen auch für meine Webseite. Bereitstellen tue ich sie per Terraform und AWS ECS mit Fargate. Um Kosten zu sparen nutze ich eine Mischung aus On-Demand und Spot.

Zurück zum Blog

Ähnliche Beiträge

Alle Beiträge »