Erstes Deployment
Diese Seite geht einmal komplett durch: Container-Image rein, funktionierende HTTPS-Adresse raus. Sie setzt voraus, dass kubectl steht und dein Standard-Namespace gesetzt ist, siehe Zugang und kubectl.
Die Konventionen für Routen, Listener-Namen und Zertifikate stehen unter Gateway API. Hier werden sie angewendet, nicht noch einmal erklärt.
Das Manifest
Drei Objekte, eine Datei. Speicher sie als app.yaml und ersetz zwei Dinge: deinen Namespace im Hostnamen und dein eigenes Image im Container.
Dein Namespace besitzt bereits jeden Namen unterhalb von <dein-namespace>.itsh.dev, myapp.<dein-namespace>.itsh.dev funktioniert also sofort und ohne DNS-Einrichtung. Free-Tier-Namespaces nutzen stattdessen <dein-namespace>.itsh-apps.dev. Eine eigene Domain auszuliefern ist ein separater Schritt, siehe Gateway API.
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: registry.example.com/myapp:1.0.0
ports:
- name: http
containerPort: 8080
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /readyz
port: http
initialDelaySeconds: 3
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myapp
annotations:
cert-manager.io/cluster-issuer: "auto"
spec:
parentRefs:
- name: default
namespace: nginx-gateway
sectionName: https-myapp-dein-namespace-itsh-dev
hostnames:
- myapp.<dein-namespace>.itsh.dev
rules:
- backendRefs:
- name: myapp
port: 80Vier Punkte daran sind keine Kür:
- Setz deine Requests bewusst. Sie bestimmen, wie viel Kapazität für deinen Pod reserviert wird, und sie sind die Grundlage der Abrechnung, siehe Abrechnung und Kontingente. Sie wegzulassen schlägt nicht fehl: Dein Namespace setzt dann einen kleinen Standard ein, aktuell 50m CPU und 64Mi Speicher, mit Limits von 200m und 256Mi. Das passt selten zu deiner Anwendung, abgerechnet wird es trotzdem, und ein späterer HPA hat keine brauchbare Bezugsgröße. Setz die Werte, statt sie zu erben.
- Der
securityContexthält deinen ersten Apply ruhig. Ohne ihn gibt Kubernetes vier Warnungen zu Privilege Escalation, Capabilities, Root und Seccomp aus. Das sind Warnungen, keine Ablehnungen, der Pod startet also trotzdem. Die Einstellungen sind aber sinnvoll und der Lärm ist es nicht. Ein Vorbehalt:runAsNonRoot: truebraucht ein Image, das tatsächlich als Nicht-Root läuft. Wenn deins das nicht tut, lass diese eine Zeile weg und behalte die anderen drei, die weiterhin gelten und drei der vier Warnungen beseitigen. - Probes trennen "läuft" von "kann Anfragen beantworten". Ohne Readiness-Probe schickt der Service Traffic an Pods, die noch starten. Pass die Pfade an das an, was deine Anwendung tatsächlich ausliefert.
- Der
sectionNamemuss zum Hostnamen passen. Nimm den Hostnamen und ersetz jeden Punkt durch einen Bindestrich,myapp.tenant-7.itsh.devergibt alsohttps-myapp-tenant-7-itsh-dev. Passt das nicht zusammen, hängt deine Route am falschen Listener oder an gar keinem.
Deine Workloads laufen in einer Sandbox-Runtime, und Manifeste, die gegen die Regeln der Plattform verstoßen, werden abgelehnt, bevor sie überhaupt im Cluster landen. Welche Regeln das sind, steht unter Berechtigungen und Richtlinien.
Anwenden
kubectl apply -f app.yamlkubectl get pods
kubectl rollout status deployment/myapprollout status kehrt erst zurück, wenn alle Replicas bereit sind. Bleibt der Befehl stehen, ist das schon die halbe Diagnose: Es wird gerade kein Pod bereit. Brich mit Ctrl+C ab und schau dir unten die Fehlersuche an.
DNS
Nichts zu tun. Die Subdomain deines Namespace zeigt bereits auf das Gateway, der Hostname aus dem Manifest oben ist also erreichbar, sobald die Route existiert.
Ein eigener Schritt wird das erst, wenn du eine eigene Domain mitbringst, siehe Gateway API.
Wenn etwas nicht läuft
kubectl describe pod <name> ist bei allen dreien der erste Griff, die Events am Ende der Ausgabe nennen fast immer den Grund.
ImagePullBackOff: Das Image konnte nicht geladen werden. Bei einer privaten Registry fehlen die Zugangsdaten. Leg ein Secret vom Typkubernetes.io/dockerconfigjsonan und trag es unterimagePullSecretsim Pod-Template ein. Ist die Registry öffentlich, prüf Schreibweise und Tag.CrashLoopBackOff: Der Container startet und beendet sich sofort wieder.kubectl logs deployment/myappzeigt, warum. Ist der Pod schon neu gestartet, brauchst du die Logs des vorherigen Versuchs:kubectl logs deployment/myapp --previous.Pending: Der Pod bekommt keinen Platz. In aller Regel ist das Kontingent deines Namespace erschöpft.kubectl get resourcequotazeigt, wie viel davon noch frei ist, und Abrechnung und Kontingente erklärt, was in dieses Kontingent einzahlt.
Wie es weitergeht
- Gateway API für Routing, Hostnamen und eigene Domains
- Abrechnung und Kontingente, was deine Requests kosten
- Fehlersuche, falls nichts hochkam