Zum Hauptinhalt springen

Beispiele zum Nachbauen

Jedes Beispiel zeigt die Blöcke, die Schritte und das exakte YAML, das BYOBYaml daraus erzeugt. Das YAML stammt direkt aus dem Generator der App.

BeispielArt
Statischer WebserverCompose
App wartet auf gesunde DatenbankCompose
Image-Tag und Port als VariablenCompose
ConfigMap und Secret in einen ContainerKubernetes
Persistenter SpeicherKubernetes
Ingress mit TLS und zwei PfadenKubernetes
Route mit eigenem HostnamenOpenShift
Nächtlicher CronJobKubernetes
Extras: Felder ohne eigenen BlockKubernetes
Eigene Ressource: PodDisruptionBudgetKubernetes
Autoscaling nach CPU und RAMKubernetes

Statischer Webserver​

Compose Ein nginx, der einen lokalen Ordner read-only ausliefert und nach einem Absturz neu startet.

So baust du es:

  1. Compose-Datei anlegen; der Startblock Docker Compose · Projekt ist schon da.
  2. Compose → Service unter den Startblock ziehen, Name web, Image nginx:1.27-alpine.
  3. Service → Port in den Service legen: Host 8080, Container 80.
  4. Service → Volume in den Service legen: ./html → /usr/share/nginx/html, Häkchen nur lesen.
  5. Service → Neustart unless-stopped.

Blöcke:

Docker Compose · Projekt website
Service web Image nginx:1.27-alpine
Port Host 8080 → Container 80 tcp
Volume ./html → /usr/share/nginx/html nur lesen ☑
Neustart unless-stopped

Ergebnis:

name: website
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
tipp

Ports schreibt BYOBYaml immer in Anführungszeichen ("8080:80"). Sonst kann YAML 1.1 Werte wie 22:22 als Zahl im 60er-System lesen.

App wartet auf gesunde Datenbank​

Compose Die App startet erst, wenn PostgreSQL seinen Healthcheck besteht. Die Daten liegen in einem benannten Volume.

So baust du es:

  1. Service db mit Image postgres:16-alpine, drei Variable-Blöcke für User, Passwort und Datenbank.
  2. Volume pgdata → /var/lib/postgresql/data und Healthcheck pg_isready -U app.
  3. Service app mit leerem Image, dafür Build Ordner ..
  4. Im Service app: wartet auf db bis gesund.
  5. Ganz unten unter dem Startblock: Compose → Volume pgdata. Benannte Volumes müssen auf oberster Ebene deklariert werden.

Blöcke:

Docker Compose · Projekt shop
Service db Image postgres:16-alpine
Variable POSTGRES_USER = app
Variable POSTGRES_PASSWORD = change-me
Variable POSTGRES_DB = app
Volume pgdata → /var/lib/postgresql/data nur lesen ☐
Healthcheck pg_isready -U app alle 10s Timeout 5s Versuche 5
Service app Image
Build Ordner . Dockerfile Dockerfile
Port Host 3000 → Container 3000 tcp
Variable DATABASE_URL = postgres://app:change-me@db:5432/app
wartet auf db bis gesund
Volume pgdata Treiber Standard

Ergebnis:

name: shop
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: change-me
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test:
- CMD-SHELL
- pg_isready -U app
interval: 10s
timeout: 5s
retries: 5
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:change-me@db:5432/app
depends_on:
db:
condition: service_healthy
volumes:
pgdata:

Image-Tag und Port als Variablen​

Compose Derselbe Service mit unterschiedlichen Werten je Umgebung. Die Tabelle zeigt die Variablen, das YAML entsteht mit der Umgebung prod.

So baust du es:

  1. Im Tab Variablen die Variablen TAG und PORT anlegen.
  2. In den Blöcken ${TAG} und ${PORT} in die Felder schreiben.
  3. Oben rechts die Umgebung prod wählen.

Blöcke:

Docker Compose · Projekt
Service api Image registry.example.com/api:${TAG}
Port Host ${PORT} → Container 8080 tcp
Variable RELEASE = api-${TAG}

Ergebnis (Umgebung prod):

services:
api:
image: registry.example.com/api:1.4.2
ports:
- "80:8080"
environment:
RELEASE: api-1.4.2
Variabledevprod
TAGlatest1.4.2
PORT808080

ConfigMap und Secret in einen Container​

Kubernetes Drei Dokumente in einer Datei: ConfigMap, Secret und ein Deployment, das alles als Umgebungsvariablen bekommt.

So baust du es:

  1. Ressourcen → ConfigMap auf die Fläche ziehen, darunter zwei Config & Daten → Eintrag-Blöcke.
  2. Ressourcen → Secret daneben ziehen, darunter ein Eintrag DB_PASSWORD.
  3. Deployment mit Container. Im Container: alle Variablen aus ConfigMap und Variable … aus Secret … Schlüssel … für einen einzelnen Wert.

Blöcke:

ConfigMap app-config
Eintrag LOG_LEVEL = info
Eintrag FEATURE_X = true
Secret app-secret Typ Opaque
Eintrag DB_PASSWORD = change-me
Deployment app Replicas 1
Container app Image registry.example.com/app:1.0
alle Variablen aus ConfigMap app-config
Variable DB_PASSWORD aus Secret app-secret Schlüssel DB_PASSWORD

Ergebnis:

apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: info
FEATURE_X: "true"
---
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
stringData:
DB_PASSWORD: change-me
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: registry.example.com/app:1.0
envFrom:
- configMapRef:
name: app-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: DB_PASSWORD
tipp

stringData nimmt Klartext an; Kubernetes speichert ihn base64-kodiert. Das ist keine Verschlüsselung.

Persistenter Speicher​

Kubernetes Ein PVC wird als Volume im Pod definiert und im Container eingehängt. Der Name data verbindet beides.

So baust du es:

  1. Ressourcen → Speicher (PVC) app-data, 5Gi, ReadWriteOnce.
  2. Im Deployment: Pod → Volume aus PVC mit Name data und Claim app-data.
  3. Im Container: Container → Volume einhängen mit Name data und Pfad /var/lib/app.

Blöcke:

Speicher (PVC) app-data Größe 5Gi Zugriff ReadWriteOnce
Deployment app Replicas 1
Volume data aus PVC app-data
Container app Image registry.example.com/app:1.0
Volume data einhängen in /var/lib/app nur lesen ☐

Ergebnis:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: registry.example.com/app:1.0
volumeMounts:
- name: data
mountPath: /var/lib/app
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data
tipp

Bei ReadWriteOnce kann nur ein Node das Volume einhängen. Für mehrere Replicas auf verschiedenen Nodes brauchst du ReadWriteMany oder ein StatefulSet mit pro Pod Speicher.

Ingress mit TLS und zwei Pfaden​

Kubernetes Ein Hostname, zwei Backends: / geht ans Frontend, /api an die API. Das Zertifikat stellt cert-manager aus.

So baust du es:

  1. Ressourcen → Ingress mit Klasse nginx.
  2. Netzwerk → cert-manager Issuer letsencrypt und TLS für Host plus Secret-Name.
  3. Zwei Regel-Blöcke mit demselben Host. BYOBYaml fasst sie unter einem host zusammen.

Blöcke:

Ingress shop Klasse nginx
cert-manager Issuer letsencrypt
TLS für shop.example.com mit Secret shop-tls
Regel Host shop.example.com Pfad / → Service frontend Port 80
Regel Host shop.example.com Pfad /api → Service api Port http

Ergebnis:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
ingressClassName: nginx
tls:
- hosts:
- shop.example.com
secretName: shop-tls
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
name: http
tipp

Steht im Port-Feld eine Zahl, wird daraus port.number. Steht dort ein Name wie http, wird daraus port.name.

Route mit eigenem Hostnamen​

OpenShift Eine HTTPS-Route mit Edge-Termination: HTTP wird auf HTTPS umgeleitet.

So baust du es:

  1. Ressourcen → Route mit Service web und Port http (Name des Service-Ports).
  2. Netzwerk → Hostname und HTTPS edge mit umleiten.

Blöcke:

Route web → Service web Port http
Hostname web.apps.example.com
HTTPS edge unsicheres HTTP umleiten

Ergebnis:

apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: web
spec:
host: web.apps.example.com
to:
kind: Service
name: web
port:
targetPort: http
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect

Nächtlicher CronJob​

Kubernetes Ein Backup um 02:00 Uhr. Läuft noch ein alter Job, startet kein neuer; dafür sorgt ein Extras-Feld.

So baust du es:

  1. Ressourcen → CronJob mit Zeitplan 0 2 * * *.
  2. Extras → Feld spec.concurrencyPolicy = Forbid. Diese Option hat keinen eigenen Block.
  3. Container mit Command. Anführungszeichen halten Argumente zusammen.

Blöcke:

CronJob backup Zeitplan 0 2 * * *
Feld spec.concurrencyPolicy = Forbid
Container backup Image alpine:3.20
Command /bin/sh -c "tar czf /backup/data.tgz /data"

Ergebnis:

apiVersion: batch/v1
kind: CronJob
metadata:
name: backup
spec:
schedule: 0 2 * * *
jobTemplate:
spec:
template:
metadata:
labels:
app: backup
spec:
containers:
- name: backup
image: alpine:3.20
command:
- /bin/sh
- -c
- tar czf /backup/data.tgz /data
restartPolicy: OnFailure
concurrencyPolicy: Forbid

Extras: Felder ohne eigenen Block​

Kubernetes Tolerations, ein Lifecycle-Hook und ein abweichendes Feld. Alles mit den drei Extras-Blöcken.

So baust du es:

  1. zu Liste … hinzufügen direkt unter dem Deployment: Der Pfad beginnt bei der Ressource (spec.template.spec.tolerations).
  2. Feld im Container: Der Pfad beginnt beim Container (lifecycle.preStop).
  3. YAML einmischen für Schlüssel mit Punkten (Annotation).

Blöcke:

Deployment worker Replicas 2
zu Liste spec.template.spec.tolerations hinzufügen {key: dedicated, operator: Equal, value: batch, effect: NoSchedule}
YAML einmischen {metadata: {annotations: {app.openshift.io/connects-to: db}}}
Container worker Image registry.example.com/worker:2.0
Feld lifecycle.preStop.exec.command = [sh, -c, sleep 5]

Ergebnis:

apiVersion: apps/v1
kind: Deployment
metadata:
name: worker
annotations:
app.openshift.io/connects-to: db
spec:
replicas: 2
selector:
matchLabels:
app: worker
template:
metadata:
labels:
app: worker
spec:
containers:
- name: worker
image: registry.example.com/worker:2.0
lifecycle:
preStop:
exec:
command:
- sh
- -c
- sleep 5
tolerations:
- key: dedicated
operator: Equal
value: batch
effect: NoSchedule

Eigene Ressource: PodDisruptionBudget​

Kubernetes Für Ressourcen ohne eigenen Startblock. Hier sorgt ein PDB dafür, dass bei Node-Wartungen immer ein Pod läuft.

So baust du es:

  1. Ressourcen → Eigene Ressource: kind PodDisruptionBudget, apiVersion policy/v1.
  2. Inhalt mit Feld-Blöcken.

Blöcke:

Eigene Ressource web-pdb kind PodDisruptionBudget apiVersion policy/v1
Feld spec.minAvailable = 1
Feld spec.selector.matchLabels = {app: web}

Ergebnis:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 1
selector:
matchLabels:
app: web

Autoscaling nach CPU und RAM​

Kubernetes Zwischen 2 und 10 Pods; das Herunterskalieren beginnt erst nach 5 Minuten ohne Last.

So baust du es:

  1. Ressourcen → Autoscaler für Deployment web, min 2, max 10.
  2. Zwei Skalierung → Ziel-Blöcke und ein herunterskalieren erst nach.

Blöcke:

Autoscaler web für Deployment web min 2 max 10
Ziel CPU Auslastung 70 %
Ziel RAM Auslastung 80 %
herunterskalieren erst nach 300 Sekunden Ruhe

Ergebnis:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
tipp

Auslastung bezieht sich auf die requests der Container. Ohne resources.requests kann der HPA nicht rechnen.