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@kubernetescrdDamit 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.