Zum Hauptinhalt springen
  1. Beiträge/
  2. Kleines Homelab/
  3. k3s-prod: Kubernetes Cluster Konfiguration und Anwendungsbereitstellung/

Maintenance Page

··802 Wörter· ·
Inhaltsverzeichnis

Maintenance Page
#

Quelle
#

Generisches app-template-Helm-Chart von BJW-S Labs mit dem Upstream-Image nginxinc/nginx-unprivileged und git-sync. nginx.conf und die Sicherheits-Header kommen aus der Kustomize-Component components/static-site, Server-Block und Fehlerseite sind eigen.

Dokumentation
#

Der Inhalt der Wartungsseite liegt im Repository pyrox/maintenance-page auf der Forgejo-Instanz.

Funktion
#

Fehlerseite
#

Die Middleware maintenance-page-errors fängt die Statuscodes 502, 503 und 504 ab und holt dafür /error/<status> von diesem Dienst. Traefik schickt die Anfrage per GET mit dem Host der ursprünglichen Anfrage und liefert die Seite mit dem originalen Statuscode aus.

Status Typische Ursache
502 Anwendung lehnt die Verbindung ab oder beendet sie, meist beim Neustart
503 Keine laufende Instanz, etwa nach einem Absturz, oder die Anwendung meldet Wartung
504 Anwendung antwortet nicht rechtzeitig

Die Seite nennt Statuscode, Ursache, den betroffenen Hostnamen und die Uhrzeit und lädt sich alle 30 Sekunden neu. Die Texte je Statuscode stehen in den map-Blöcken von default.conf.

Als Vorlage dient zuerst /_error/index.html aus dem pages-Branch. Hugo baut sie im Repository pyrox/maintenance-page als zweite Fassung der Startseite: die komplette Wartungsseite mit Kontakt, Datenschutz und Impressum, ganz oben die Karte mit Statuscode und Ursache. nginx setzt die Werte per SSI ein. Fehlt die Vorlage, etwa weil Forgejo beim Start nicht erreichbar war, greift die schlichte error.html aus der ConfigMap. Der Pfad /_error/ selbst antwortet mit 404.

Die Fehlerseite erscheint unter dem Host der ausgefallenen Anwendung. Damit CSS, JavaScript, Schriften und Bilder trotzdem ankommen, baut Hugo mit baseURL: /_maintenance/, und die IngressRoute maintenance-page-assets leitet /_maintenance/ auf allen Hosts mit hoher Priorität an diesen Dienst. So sind die Assets auf jedem Host same-origin, CORS ist nicht nötig. Der Pfad /_maintenance/ ist damit auf allen Hosts des Clusters belegt.

500 und 404 fängt die Middleware bewusst nicht ab. Fehlerseiten der Anwendungen enthalten oft nützliche Details, und APIs wie WebDAV, die Container-Registry oder Matrix brauchen ihre eigenen 404-Antworten.

Wirksam wird die Middleware erst, wenn Traefik sie verwendet, entweder für den ganzen Entrypoint oder je Anwendung per Annotation:

1traefik.ingress.kubernetes.io/router.middlewares: maintenance-page-maintenance-page-errors@kubernetescrd

Damit ein abgestürzter Pod überhaupt zu 503 führt statt zu 404, muss Traefik mit allowEmptyServices: true laufen. Sonst entfernt Traefik den Router, und keine Middleware greift.

Fehlersuche
#

Jede Fehlerseite trägt eine Request-ID. Unter derselben ID schreibt nginx eine JSON-Zeile ins Log des Containers main:

Feld Inhalt
time, request_id, status Zeitpunkt, ID, Statuscode
host, url Host und ursprüngliche URL, url prozentkodiert von Traefik
client_ip, forwarded_for IP des Nutzers aus X-Real-Ip und X-Forwarded-For
user_agent, referer, accept Browser, Herkunft, gewünschtes Format
traefik Traefik-Pod, der die Anfrage bearbeitet hat
replica maintenance-page-Pod, der die Fehlerseite ausgeliefert hat

Cookies und Authorization werden nicht protokolliert. Suchen:

1kubectl -n maintenance-page logs -l app.kubernetes.io/name=maintenance-page -c main --prefix --tail=-1 | grep <request-id>

Mit -l gibt kubectl logs ohne --tail=-1 nur die letzten zehn Zeilen je Pod aus.

Im Access-Log von Traefik steht bei abgefangenen Fehlern als Backend-URL die Adresse eines maintenance-page-Pods, weil die Middleware die Antwort ersetzt. Welche Anwendung betroffen war, zeigt dort der Router-Name.

Auf der Seite selbst stehen Host, URL, Zeitpunkt und Request-ID. „Details kopieren“ legt diese Angaben samt Browser in die Zwischenablage, „Störung melden“ öffnet eine vorausgefüllte Mail. Beides braucht JavaScript, ohne bleiben Host, Zeitpunkt und Request-ID sichtbar.

API-Clients, deren Accept-Header application/json und nicht text/html enthält, bekommen statt HTML ein JSON-Objekt mit status, title, detail, host, time und request_id.

Wartungsseite
#

nginx beantwortet die Startseite mit 503 Service Unavailable und dem Inhalt von index.html, samt Retry-After: 120. Jede andere Adresse ohne passende Datei wird auf die Startseite umgeleitet. Den Inhalt zieht git-sync alle 60 Sekunden aus dem pages-Branch nach.

Catch-all-Route
#

Eine IngressRoute mit HostRegexp(.+) und priority: 1 nimmt alle Anfragen an, für die keine spezifischere Route existiert. Weil Traefik mit sniStrict läuft, erreichen Anfragen für Hosts ohne passendes Zertifikat diese Route nicht, der TLS-Handshake scheitert vorher.

Betrieb
#

Zwei Replikas mit RollingUpdate, damit die Fehlerseite auch während eines eigenen Rollouts erreichbar bleibt. Die Readiness-Probe prüft /healthz, das nicht vom git-sync-Inhalt abhängt. Beide Container laufen ohne Root-Rechte und mit schreibgeschütztem Root-Dateisystem, nginx lauscht auf Port 8080.

Lokale Anpassungen
#

Die Konfiguration erfolgt über values.yaml.

Einstellung Ort
Abgefangene Statuscodes rawResources.errors
Texte der Fehlerseite map-Blöcke in configMaps.server (default.conf)
Aussehen der Fehlerseite layouts/partials/custom_body.html und custom_head.html im Repository pyrox/maintenance-page
Asset-Route für alle Hosts rawResources.assets
Ersatzvorlage ohne git-sync-Inhalt error.html in configMaps.server
503-Verhalten der Wartungsseite default.conf in configMaps.server
Repository und Branch GITSYNC_REPO, GITSYNC_REF im Container gitsync
nginx.conf, Sicherheits-Header components/static-site

Installation
#

Bereitgestellt über ArgoCD mit Kustomize und Helm. Von Hand:

1kubectl kustomize --enable-helm apps/maintenance-page | kubectl apply -n maintenance-page -f -

Abhängigkeiten
#

  • Traefik, da Route und Middlewares auf Traefik-CRDs beruhen
  • reloader, damit Änderungen an der nginx-Konfiguration ankommen
  • Forgejo-Instanz mit dem Repository, nur für die Wartungsseite

Wird die Middleware für den ganzen Entrypoint eingetragen, hängt jede Route an diesem Middleware-Objekt. Fehlt es, etwa weil diese App gelöscht wurde, verweigert Traefik alle Routen des Entrypoints. Die App darf dann nicht entfernt werden, ohne vorher den Eintrag in apps/traefik zu löschen.

Fabrice Kirchner
Autor
Fabrice Kirchner
stolzer Vater, Nerd, Admin