Modulseite

Lektion 1 von 8

Die erste App in der Cloud: PaaS, Region und Logs

Du wählst zwischen App Service, Container Apps und Functions, deployst eine Node.js-App mit der Azure CLI in Switzerland North und findest einen Startfehler im Log Stream.

ca. 45 Min.0/4 Checks gelöst

Worum es geht

Es ist 11.15 Uhr in Buchs SG. In drei Firmenkantinen bestellen gerade über 400 Mitarbeitende ihr Mittagsmenü über die App, die das Team der Rheintal Apps GmbH für ein Cateringunternehmen gebaut hat. Die App läuft auf einem einzigen Server im Büro. Wenn jetzt der Strom ausfällt, ein Windows-Update neu startet oder die Festplatte voll ist, gibt es in drei Kantinen kein Mittagessen. Und wer patcht diesen Server eigentlich am Wochenende?

Sara, Lernende im 2. Lehrjahr Applikationsentwicklung, bekommt den Auftrag: «Bring die Mittagsmenü-App in die Cloud. Und zwar so, dass wir uns um den Code kümmern und nicht um Betriebssysteme.» Genau dafür gibt es PaaS (Platform as a Service): Du lieferst deinen Code oder dein Container-Image, die Plattform kümmert sich um Server, Updates, HTTPS, Neustarts und Skalierung.

In dieser Lektion bringst du eine Node.js-App mit der Azure CLI auf Azure App Service in der Region Switzerland North. Du lernst, wie die Plattform deine App startet, warum der allererste Versuch oft mit einem Fehler 503 endet und wie du die Ursache in den Logs in zwei Minuten findest statt in zwei Stunden. Die Grundbegriffe IaaS, PaaS und SaaS kennst du aus Modul 346. Hier geht es um die Praxis: Befehle, Konfiguration, Fehlersuche.

PaaS im Alltag: die Mietküche

Stell dir vor, das Cateringunternehmen bräuchte für einen Grossanlass eine zweite Küche. Es könnte selbst eine bauen: Raum mieten, Herde kaufen, Lüftung installieren, Hygienekontrolle organisieren. Oder es mietet eine Gastro-Mietküche: Die Herde stehen bereit, jemand anderes wartet die Lüftung und putzt die Fettabscheider. Das Catering bringt nur noch Rezepte, Zutaten und Personal mit.

PaaS ist die Mietküche für Software:

AufgabeEigener Server im BüroPaaS (z.B. App Service)
Hardware, Strom, KühlungduPlattform
Betriebssystem installieren und patchenduPlattform
Node.js-Laufzeit aktualisierenduPlattform (du wählst die Version)
HTTPS-Zertifikat für die StandardadresseduPlattform
Neustart nach AbsturzduPlattform
Code, Abhängigkeiten, Konfigurationdudu
Daten und Zugriffsrechtedudu

Was du abgibst, ist Kontrolle: Du kannst dich nicht auf «den Server» einloggen und schnell etwas umstellen. Alles, was deine App braucht, bekommt sie über den Code oder über Einstellungen der Plattform. Genau diese Disziplin macht die App später reproduzierbar und skalierbar.

Drei Wege für deine App

Azure bietet für eigene Anwendungen vor allem drei PaaS-Dienste. AWS hat für jeden ein Gegenstück.

Azure App ServiceAzure Container AppsAzure Functions
Was du lieferstCode (Node.js, Python, Java, .NET, PHP) oder ContainerContainer-ImageEinzelne Funktionen
Typisch fürKlassische Web-Apps und APIsAPIs und Microservices aus Docker-ImagesHintergrundaufgaben, Ereignisse
AbrechnungFester Plan pro StundePro Rechensekunde, Grundlast möglichPro Ausführung und Rechenzeit
Scale-to-zeronein, der Plan läuft immerjaja (Consumption-Pläne)
Gegenstück bei AWSElastic BeanstalkECS mit FargateLambda

Die Entscheidung hängt vor allem davon ab, was du schon hast: Liegt ein Dockerfile im Repository (Modul 347), ist Container Apps naheliegend. Für ein Node.js-Projekt mit package.json ist App Service der kürzeste Weg. Functions sind für kurze, ereignisgesteuerte Aufgaben (Lektion 6).

Die Mittagsmenü-App hat kein Dockerfile, Sara wählt deshalb App Service. Wie du so eine Wahl mit Kosten und Betriebsaufwand begründest, lernst du in Lektion 8.

Check 1 · Eine AntwortFortgeschritten

Die Auftrags-API eines Velokuriers (Spring Boot) liegt bereits als Docker-Image in einer Registry. Sie soll mit wenig Aufwand als HTTPS-API laufen und nachts, wenn niemand Aufträge erfasst, auf null Instanzen gehen dürfen. Welcher Dienst passt am besten?

Region Schweiz und Kostenbremse

Bevor du die erste Ressource anlegst, klärst du zwei Dinge.

1. Wo liegen die Daten? Die App speichert Namen und Bestellungen von Mitarbeitenden, also Personendaten. Der Kunde verlangt, dass sie in der Schweiz bleiben. Azure hat zwei Schweizer Regionen: Switzerland North (Raum Zürich, CLI-Name switzerlandnorth) und Switzerland West (Raum Genf, switzerlandwest). Switzerland West ist vor allem als Partnerregion für Notfallkopien gedacht und bietet weniger Dienste. Bei AWS heisst die Schweizer Region eu-central-2 (Zürich). Achtung: AWS eu-central-1 liegt in Frankfurt, Azure westeurope in den Niederlanden.

Nicht jeder Dienst und nicht jede Leistungsstufe ist in jeder Region verfügbar. Prüfe das vor der Planung, zum Beispiel mit:

Bash
# Alle Regionen deines Abonnements mit Anzeigename
az account list-locations --query "[?contains(name, 'switzerland')].{Name:name, Anzeige:displayName}" --output table

2. Was darf es kosten? Für die Ausbildung gibt es Azure for Students mit einem Startguthaben ohne Kreditkarte. Setze trotzdem sofort im Portal unter «Cost Management» ein Budget mit Alarm, zum Beispiel bei CHF 20. Ein vergessener Datenbankserver läuft sonst wochenlang weiter.

Check 2 · Mehrere AntwortenEinstieg

Die Bestelldaten der Mittagsmenü-App sind Personendaten und sollen gemäss Kundenvorgabe in der Schweiz gespeichert werden. Welche Regionen erfüllen diese Vorgabe?

Wähle alle zutreffenden Antworten.

Wie die Plattform deine App startet

Damit du Fehler verstehst, musst du wissen, was beim Start passiert. Bei App Service unter Linux mit Code-Deployment läuft vereinfacht Folgendes ab:

  1. Du lädst deinen Code als ZIP-Datei hoch.
  2. Der Build-Dienst der Plattform erkennt package.json, führt npm install aus und merkt sich den Startbefehl (npm start).
  3. Die Plattform startet deine App in einem Container und setzt dabei die Umgebungsvariable PORT, typischerweise auf 8080.
  4. Ein vorgeschalteter Proxy nimmt HTTPS-Anfragen auf Port 443 entgegen und leitet sie intern an genau diesen Port weiter.
  5. Die Plattform sendet Test-Anfragen («HTTP Pings») an deine App. Antwortet sie innerhalb der Wartezeit nicht, gilt der Start als fehlgeschlagen und Besucher sehen einen Fehler.

Daraus folgt die wichtigste Regel dieser Lektion: Deine App muss auf dem Port hören, den die Plattform in PORT vorgibt. Lokal darf sie auf 3000 zurückfallen:

JavaScript
// server.js: Mittagsmenü-App (Ausschnitt)
const express = require("express");
const app = express();

app.get("/health", (req, res) => res.json({ status: "ok" }));

app.get("/api/menues", (req, res) => {
  res.json([
    { kantine: "Buchs", menue: "Älplermagronen mit Apfelmus", preis: 13.5 },
    { kantine: "Sargans", menue: "Gemüsecurry mit Basmatireis", preis: 12.0 },
  ]);
});

// PORT kommt von der Plattform, 3000 nur für lokale Tests
const port = Number(process.env.PORT) || 3000;
app.listen(port, () => console.log(`Menü-App hört auf Port ${port}`));

Bei Container-Deployments gilt dasselbe Prinzip mit anderem Namen: In App Service für Container teilst du der Plattform mit der App-Einstellung WEBSITES_PORT mit, auf welchem Port dein Container hört. In Container Apps trägst du den Target Port in der Ingress-Konfiguration ein. Ausserdem muss die App in einem Container auf allen Schnittstellen hören (0.0.0.0), nicht nur auf 127.0.0.1.

Check 3 · Eine AntwortFortgeschritten

Nach dem Deployment zeigt App Service nur «Application Error». Im Log Stream steht:

Text
Menü-App hört auf Port 3000
Container app-menueapp-test-chn didn't respond to HTTP pings on port: 8080, failing site start.

Im Code steht app.listen(3000). Was ist die richtige Korrektur?

Schritt für Schritt: Die Mittagsmenü-App auf App Service bringen

Ausgangslage: Ein Node.js-Projekt mit package.json (Startskript "start": "node server.js"), lokal getestet. Sara arbeitet in der Azure Cloud Shell oder lokal nach der Installation der Azure CLI.

Schritt 1: Anmelden und Abonnement prüfen.

Bash
az login
az account show --query "{Abo:name, Id:id}" --output table

Zwischenergebnis: Als Abo erscheint «Azure for Students». Steht dort ein Firmenabo, wechselst du mit az account set --subscription <Id>.

Schritt 2: Namen festlegen und Ressourcengruppe anlegen.

Bash
RG="rg-menueapp-test-chn"
PLAN="asp-menueapp-test-chn"
APP="app-menueapp-test-chn"   # muss weltweit eindeutig sein
LOC="switzerlandnorth"

az group create --name "$RG" --location "$LOC"

Zwischenergebnis: "provisioningState": "Succeeded" und "location": "switzerlandnorth".

Schritt 3: App Service Plan anlegen. Der Plan ist die Rechenleistung, auf der eine oder mehrere Apps laufen. Für den Test reicht die kleine Stufe B1 unter Linux.

Bash
az appservice plan create --resource-group "$RG" --name "$PLAN" \
  --location "$LOC" --sku B1 --is-linux

Schritt 4: Web-App mit Laufzeit anlegen. Welche Versionen es gibt, zeigt az webapp list-runtimes --os linux.

Bash
az webapp create --resource-group "$RG" --plan "$PLAN" --name "$APP" \
  --runtime "NODE:22-lts"
az webapp update --resource-group "$RG" --name "$APP" --https-only true

Schritt 5: Build beim Deployment einschalten. Damit die Plattform npm install ausführt:

Bash
az webapp config appsettings set --resource-group "$RG" --name "$APP" \
  --settings SCM_DO_BUILD_DURING_DEPLOYMENT=true NODE_ENV=production

Schritt 6: Code als ZIP deployen. Den Ordner node_modules packst du nicht ein, die Plattform installiert die Pakete selbst.

Bash
zip -r app.zip . -x "node_modules/*" ".git/*"
az webapp deploy --resource-group "$RG" --name "$APP" --src-path app.zip --type zip

Schritt 7: Testen und den Fehler finden. Saras erste Version hatte app.listen(3000) fest im Code. Der Browser zeigt nach einigen Minuten nur «Application Error», ein curl liefert Status 503. Jetzt nicht raten, sondern Logs lesen:

Bash
az webapp log config --resource-group "$RG" --name "$APP" \
  --docker-container-logging filesystem
az webapp log tail --resource-group "$RG" --name "$APP"

Im Log Stream steht:

Text
Menü-App hört auf Port 3000
Container app-menueapp-test-chn didn't respond to HTTP pings on port: 8080, failing site start.

Die erste Zeile zeigt: Die App ist gestartet, aber auf Port 3000. Die zweite: Die Plattform wartet auf Port 8080. Ursache gefunden.

Schritt 8: Korrigieren, neu deployen, prüfen. Sara ersetzt die feste Zahl durch process.env.PORT, baut das ZIP neu und deployt nochmals.

Bash
HOST=$(az webapp show --resource-group "$RG" --name "$APP" --query defaultHostName --output tsv)
curl --silent "https://$HOST/health"

Zwischenergebnis: {"status":"ok"}, über HTTPS mit einem Zertifikat, um das sich Sara nie kümmern muss.

Im Code-Labor baust du jetzt einen kleinen Log-Detektiv, der aus einem Log Stream die Ursache eines Startfehlers herausliest.

Check 4 · Code-LaborFortgeschritten

Code-Labor: Log-Detektiv für Startfehler

Schreibe diagnose(log). log ist der Text aus dem Log Stream (mehrere Zeilen). Die Funktion gibt die Ursache der frühesten passenden Zeile zurück. Die Muster stehen schon in REGELN:

Zeile enthältRückgabe
Cannot find module oder ModuleNotFoundError"abhaengigkeit"
Konfiguration fehlt"konfiguration"
ECONNREFUSED oder ETIMEDOUT"datenbank-netz"
password authentication failed"datenbank-login"
didn't respond to HTTP pings on port"port"

Gross- und Kleinschreibung spielt keine Rolle. Passt keine Zeile, gib "unbekannt" zurück.

Dazu erwarteter_port(log): die Zahl nach on port: als int, oder None, wenn es keine solche Meldung gibt.

Warum die früheste Zeile? Stürzt die App beim Start ab, meldet die Plattform danach auch, dass niemand auf dem Port antwortet. Diese Meldung ist dann nur die Folge.

Code-Labor · Python
Strg + Enter

Typische Fehler

  • Port fest im Code. app.listen(3000) funktioniert lokal und scheitert in der Cloud mit 503. Korrektur: process.env.PORT lesen, 3000 nur als Rückfallwert.
  • Nur auf localhost hören. Ein Container, der auf 127.0.0.1 hört, ist von aussen nicht erreichbar, auch nicht vom Proxy der Plattform. Korrektur: auf 0.0.0.0 hören (Express macht das ohne Host-Angabe automatisch).
  • Ohne Region anlegen. Wer --location weglässt, übernimmt eine Standardregion oder die der Ressourcengruppe und bemerkt es oft erst Monate später. Korrektur: Region in jedem Befehl explizit angeben und nach dem Anlegen prüfen.
  • node_modules mitschicken oder Build vergessen. Ein riesiges ZIP mit lokal kompilierten Paketen kann unter Linux scheitern, ein ZIP ohne Build-Einstellung startet ohne Pakete. Korrektur: node_modules ausschliessen und SCM_DO_BUILD_DURING_DEPLOYMENT=true setzen.
  • Raten statt Logs lesen. Fünfmal neu deployen kostet Zeit. Korrektur: Log Stream öffnen und die erste Fehlermeldung suchen, spätere sind oft nur Folgen.

Zusammenfassung

  • Bei PaaS lieferst du Code oder Container, die Plattform betreibt Server, Betriebssystem, Laufzeit, HTTPS und Neustarts.
  • App Service passt für Code-Deployments klassischer Web-Apps, Container Apps für Docker-Images, Functions für kurze, ereignisgesteuerte Aufgaben.
  • Personendaten bleiben in der Schweiz mit switzerlandnorth (Azure) oder eu-central-2 (AWS). Die Region gibst du immer ausdrücklich an.
  • Ein Budget mit Alarm schützt dein Guthaben vor vergessenen Ressourcen.
  • Deine App hört auf dem Port aus der Umgebungsvariable PORT. Ein fester Port führt zu «didn't respond to HTTP pings» und Fehler 503.
  • Bei Problemen liest du zuerst den Log Stream (az webapp log tail) und suchst die früheste Fehlermeldung.

Mit deiner eigenen KI vertiefen

Kopiere einen Prompt in Claude, ChatGPT oder Claude Code. Er macht die KI zur Lernbegleitung statt zum Lösungsautomaten.

PaaS und den App-Start sokratisch verstehen Chat-KI

Wenn dir noch nicht klar ist, was beim Start einer App auf App Service passiert und warum der Port so wichtig ist. Die KI fragt, bis du es selbst erklären kannst.