
.NET + Angular - vom Quellcode zum Container
Lass uns wie immer zuerst ein Ziel definieren: Was soll erreicht werden?
- SPA SSR und API in einem Container in Azure implementieren, deployen und ausführen.
Um dieses Ziel zu erreichen, müssen wir mehrere Schritte durchlaufen:
- Die Anwendung implementieren.
- Einen Container bauen.
- Eine Azure Container Registry erstellen.
- Die Anwendung nach GitHub pushen.
- GitHub Workflow für Build und Tests konfigurieren.
- Die Anwendung konfigurieren und starten.
Implementierung:
Hier gibt es grundsätzlich zwei Möglichkeiten:
- Eine Vorlage verwenden, in der .NET API und Angular gemeinsam konfiguriert sind und als eine Solution bereitgestellt werden.
- Zwei unterschiedliche Anwendungen erstellen.
Zwei Anwendungen zu erstellen, die bereits vorkonfiguriert zusammenarbeiten, kann attraktiv wirken. Man sollte jedoch bedenken, dass dieser Ansatz auch viele versteckte Konfigurationen mitbringt, die sich nicht immer so verhalten, wie ein Entwickler es erwartet.
Wenn du von Anfang an zwei unabhängige Anwendungen erstellst, behältst du die volle Kontrolle über ihre Konfiguration und Einrichtung. Außerdem reduziert das die Kopplung zwischen den Anwendungen deutlich, sodass jede Anwendung unabhängiger verwaltet, entwickelt und deployt werden kann. Aus diesen Gründen halte ich den zweiten Ansatz für die bessere Wahl.
Die Implementierungsdetails der Anwendung selbst liegen außerhalb des Umfangs dieses Artikels und können in zukünftigen Beiträgen behandelt werden. Hier konzentrieren wir uns auf die Konfiguration der Umgebung und die Organisation des Deployment-Prozesses.
Container bauen
Zu diesem Zeitpunkt besteht die Anwendung aus zwei getrennten Teilen. Aus Deployment-Sicht ist es jedoch deutlich bequemer, beide Teile in einen einzigen Container zu packen.
Dafür können wir mit dem Standard-.NET-SDK-Image beginnen und es um Node.js erweitern.
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
# Install Node.js
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl gnupg \
&& mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*Als Nächstes kopieren wir den Quellcode beider Anwendungen in den Container.
WORKDIR /src
#
# Copy projects
#
COPY api/ ./api/
COPY ui/ ./ui/Danach bauen wir jede Anwendung separat.
# ---------- Angular ----------
WORKDIR /src/ui
RUN npm ci --legacy-peer-deps
RUN npm run build
RUN ls -la /src/ui/dist
RUN find /src/ui/dist -maxdepth 3 -type f | head -20
# ---------- .NET ----------
WORKDIR /src/api
RUN dotnet restore "app/app.csproj"
RUN dotnet build "app/app.csproj" -c Release -o /app/build --no-restore
FROM build AS publish
WORKDIR /src/api
RUN dotnet publish "app/app.csproj" -c Release -o /app/publish /p:UseAppHost=falseZum Schluss kopieren wir das Angular-Build-Ergebnis in den wwwroot-Ordner der ASP.NET-Anwendung, sodass beide Teile in einem einzigen Deployment-Artefakt verpackt sind.
# Copy Angular to wwwroot
RUN mkdir -p /app/publish/wwwroot
RUN cp -R /src/ui/dist/*/browser/* /app/publish/wwwroot/ \
&& cp /app/publish/wwwroot/index.csr.html /app/publish/wwwroot/index.htmlDer letzte Schritt besteht darin, das Runtime-Image zu erstellen.
FROM base AS final
WORKDIR /app
EXPOSE 80
EXPOSE 443
ENV ASPNETCORE_ENVIRONMENT=Production
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "app.dll"]Image erstellen und in Azure Container Registry pushen
Das Docker-Image lokal zu bauen und nach jeder Änderung in die Azure Container Registry (ACR) zu pushen, ist beim Experimentieren ein vollkommen gültiger Ansatz. Es wird jedoch schnell zu einer wiederholten manuellen Aufgabe. Wenn etwas automatisiert werden kann, lohnt es sich, das zu tun.
Der GitHub-Actions-Workflow führt die folgenden Schritte aus:
- Das API-Repository in das Verzeichnis
apiauschecken. - Das UI-Repository in das Verzeichnis
uiauschecken. - Die .NET API bauen.
- Alle Backend-Tests ausführen und Code-Coverage-Berichte erzeugen.
- Die Angular-Anwendung bauen.
- Alle Frontend-Tests ausführen und Code-Coverage-Berichte erzeugen.
- Bei Azure authentifizieren.
- Bei Azure Container Registry authentifizieren.
- Das Docker-Image bauen.
- Das Image in Azure Container Registry pushen.
Obwohl der Workflow aus vielen einzelnen Schritten besteht, läuft der gesamte Prozess nach der Konfiguration vollständig automatisiert und erfordert keinen manuellen Eingriff mehr.
Anwendung konfigurieren und starten
Für dieses Projekt wurde Azure Container Apps aus mehreren Gründen als Hosting-Plattform ausgewählt:
- Es ist speziell für containerisierte Anwendungen ausgelegt, einschließlich Skalierung und Umgebungskonfiguration.
- Es unterstützt Anwendungsrevisionen und macht Deployments dadurch sicherer.
- Es integriert sich nahtlos mit Azure Container Registry, Log Analytics, Azure Storage, Azure Key Vault und anderen Azure-Diensten.
- Anwendungsmetriken und Grafana-Dashboards sind direkt verfügbar.
Die anfängliche Konfiguration besteht aus mehreren Schritten:
- Ingress. Ingress aktivieren, den Zielport konfigurieren, HTTP als Ingress-Typ wählen und externen Traffic erlauben, sofern keine andere Netzwerkkonfiguration erforderlich ist.
- Container.
- Properties. Das zu verwendende Docker-Image konfigurieren.
- Environment variables. Die erforderlichen Konfigurationswerte definieren. Diese können bei Bedarf direkt aus Azure-Secrets gefüllt werden.
- Health probes. Startup-, Readiness- und Liveness-Probes konfigurieren, indem Endpoint, Port und Protokoll angegeben werden. Dieser Schritt ist kritisch, denn wenn die Probes fehlschlagen, betrachtet Azure Container Apps den Container als fehlerhaft und startet ihn automatisch neu oder deaktiviert ihn.
- Volume mounts. Azure-Storage-Volumes einbinden, wenn die Anwendung persistente Dateispeicherung benötigt.
Es ist erwähnenswert, dass Azure bei jedem Speichern der Container-App-Konfiguration eine neue Anwendungsrevision erstellt und den Container neu deployt.
Azure Container Apps erkennt jedoch nicht automatisch, dass ein neueres Image mit demselben Tag, zum Beispiel latest, in die Azure Container Registry gepusht wurde.
Ein einfacher Workaround besteht darin, eine Umgebungsvariable wie FORCE_REDEPLOY zu erstellen und als Wert das aktuelle Datum oder einen Timestamp zu verwenden. Immer wenn sich dieser Wert ändert und die Konfiguration gespeichert wird, erstellt Azure eine neue Revision, zieht das neueste Docker-Image und startet einen neuen Container, ohne dass weitere Konfigurationsänderungen nötig sind.
Zusammenfassung
Die oben beschriebenen Schritte waren ausreichend, um eine Anwendung aus mehreren Repositories zu bauen, in ein einziges Docker-Image zu packen, in die Azure Container Registry zu veröffentlichen, in Azure Container Apps zu deployen und schließlich eine stabile, wiederholbare und vollständig automatisierte Deployment-Pipeline zu erhalten.
Auch wenn jeder einzelne Schritt relativ überschaubar ist, vereinfacht ihre Kombination zu einem vollständigen CI/CD-Prozess die zukünftige Entwicklung erheblich. Nach der anfänglichen Einrichtung kann jeder Commit automatisch ein getestetes Docker-Image erzeugen, das mit minimalem Aufwand deployt werden kann.