1 Die ganze Kette
Vier Übergaben verbinden Quelltext mit laufenden Pods. An jeder Übergabe entsteht ein anderer überprüfbarer Zustand.
s2i-src/liegt nur auf deinem Laptop
BuildConfigerzeugt einen
Build und Build-PodImageStreamTagzeigt auf einen unveränderlichen Digest
Deploymenterzeugt
ReplicaSet und PodsSource-to-Image, kurz S2I, verbindet deinen Quelltext mit einem Builder-Image. Das Builder-Image kennt die Sprache und enthält Skripte zum Bauen und Starten. Der fertige Container enthält keine besondere S2I-Laufzeit.
Die zwei Signale: Ein neuer Image-Digest beweist den Build. Ein neues ReplicaSet beweist den Rollout. Ein neuer Pod allein reicht als Erklärung nicht, weil auch ein gelöschter Pod vom bestehenden ReplicaSet ersetzt werden kann.
2 Die Objekte einordnen
| Begriff | Aufgabe | Entsteht wie |
|---|---|---|
BuildConfig | dauerhafte Bauanweisung mit Quelle, Strategie, Builder und Ausgabeziel | du legst sie an |
Build | ein konkreter Lauf dieser Bauanweisung | OpenShift generiert ihn |
ImageStreamTag | OpenShift-Zeiger wie hello-s2i:latest mit Historie zu Image-Digests | du definierst den Stream, der Build aktualisiert den Tag |
| Image-Trigger | beobachtet den Tag und schreibt den neuen Digest in die Pod-Vorlage | oc set triggers setzt eine Annotation |
Deployment | beschreibt Rollout-Strategie und gewünschte Pod-Vorlage | du legst es an |
ReplicaSet | hält eine bestimmte Version der Pod-Vorlage in gewünschter Anzahl am Leben | das Deployment generiert es |
3 Cluster und APIs prüfen
Du brauchst einen angemeldeten Benutzer mit Recht auf ein eigenes Projekt. Die interne Registry muss verfügbar sein.
oc whoami oc get co image-registry oc get image.config.openshift.io cluster -o yaml oc api-resources | Select-String -Pattern 'BuildConfig|ImageStream|Deployment'
Bash-Variante anzeigen
oc whoami oc get co image-registry oc get image.config.openshift.io cluster -o yaml oc api-resources | grep -E 'BuildConfig|ImageStream|Deployment'
Abbruchbedingung: Wenn image-registry nicht Available=True ist, zuerst den Registry-Betrieb klären. Der Build kann sonst erfolgreich kompilieren und beim Push des Ergebnisses trotzdem scheitern.
4 Lokalen Quelltext anlegen
Die drei Dateien bilden eine HTTP-Anwendung ohne zusätzliche npm-Pakete. S2I erkennt package.json und startet später npm start.
New-Item -ItemType Directory -Force -Path .\s2i-src | Out-Null
@'
{"name":"s2i-rollout-lab","version":"1.0.0","scripts":{"start":"node server.js"}}
'@ | Set-Content -LiteralPath .\s2i-src\package.json -Encoding utf8
@'
const http = require("http");
const fs = require("fs");
const version = fs.readFileSync("version.txt", "utf8").trim();
const port = Number(process.env.PORT || 8080);
http.createServer((request, response) => {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end(`hello ${version}\n`);
}).listen(port, "0.0.0.0");
'@ | Set-Content -LiteralPath .\s2i-src\server.js -Encoding utf8
'v1' | Set-Content -LiteralPath .\s2i-src\version.txt -Encoding utf8
Get-ChildItem -LiteralPath .\s2i-srcBash-Variante anzeigen
mkdir -p ./s2i-src
cat > ./s2i-src/package.json <<'EOF'
{"name":"s2i-rollout-lab","version":"1.0.0","scripts":{"start":"node server.js"}}
EOF
cat > ./s2i-src/server.js <<'EOF'
const http = require("http");
const fs = require("fs");
const version = fs.readFileSync("version.txt", "utf8").trim();
const port = Number(process.env.PORT || 8080);
http.createServer((request, response) => {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end(`hello ${version}\n`);
}).listen(port, "0.0.0.0");
EOF
printf 'v1\n' > ./s2i-src/version.txt
find ./s2i-src -maxdepth 1 -type f -prints2i-src/package.json, s2i-src/server.js und s2i-src/version.txt legst du an. Das Verzeichnis ist als lokales Lab-Artefakt in .gitignore ausgeschlossen.
5 ImageStream und BuildConfig anwenden
Die Quelle ist vom Typ Binary. Sie wird beim Start des Builds übertragen und nicht dauerhaft in der BuildConfig gespeichert.
oc new-project build-rollout-lab
@'
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
name: hello-s2i
---
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
name: hello-s2i
spec:
runPolicy: Serial
source:
type: Binary
strategy:
type: Source
sourceStrategy:
from:
kind: DockerImage
name: registry.access.redhat.com/ubi9/nodejs-20:latest
output:
to:
kind: ImageStreamTag
name: hello-s2i:latest
'@ | oc apply -f -Bash-Variante anzeigen
oc new-project build-rollout-lab
oc apply -f - <<'EOF'
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
name: hello-s2i
---
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
name: hello-s2i
spec:
runPolicy: Serial
source:
type: Binary
strategy:
type: Source
sourceStrategy:
from:
kind: DockerImage
name: registry.access.redhat.com/ubi9/nodejs-20:latest
output:
to:
kind: ImageStreamTag
name: hello-s2i:latest
EOFWarum DockerImage beim Builder? Der Builder wird direkt aus der Red-Hat-Registry gezogen. Das Build-Ergebnis geht dagegen als ImageStreamTag in die integrierte OpenShift-Registry.
6 Den ersten S2I-Build verfolgen
oc start-build hello-s2i --from-dir .\s2i-src --follow
oc get build,pod,imagestream,imagestreamtag
oc describe build hello-s2i-1
oc get istag hello-s2i:latest -o jsonpath='{.image.dockerImageReference}{"\n"}'Bash-Variante anzeigen
oc start-build hello-s2i --from-dir ./s2i-src --follow
oc get build,pod,imagestream,imagestreamtag
oc describe build hello-s2i-1
oc get istag hello-s2i:latest -o jsonpath='{.image.dockerImageReference}{"\n"}'npm start auf.hello-s2i:latest zeigt jetzt auf einen unveränderlichen Inhalt wie sha256:….Die Skripte assemble und run sind wichtige Bestandteile des Builder-Images. Du legst sie in diesem Lab nicht an. Ein Projekt könnte sie unter .s2i/bin/ bewusst überschreiben.
7 Deployment, Service und Route anlegen
Das Deployment startet zunächst aus dem internen Registry-Namen. Danach bindest du es ausdrücklich an den ImageStreamTag.
oc create deployment hello-s2i --image=image-registry.openshift-image-registry.svc:5000/build-rollout-lab/hello-s2i:latest oc set triggers deployment/hello-s2i --from-image=hello-s2i:latest -c hello-s2i oc expose deployment hello-s2i --port=8080 oc expose service hello-s2i oc rollout status deployment/hello-s2i --timeout=120s oc get deployment,replicaset,pod -o wide oc get route hello-s2i
Das Deployment besitzt das ReplicaSet über ownerReferences. Das ReplicaSet besitzt wiederum die Pods. Der Wert pod-template-hash verbindet jeden Pod mit genau der Vorlagenversion, aus der er entstand.
oc get replicaset -l app=hello-s2i --sort-by=.metadata.creationTimestamp
oc get pod -l app=hello-s2i -o jsonpath='{range .items[*]}{.metadata.name}{" <- "}{.metadata.ownerReferences[0].kind}{"/"}{.metadata.ownerReferences[0].name}{"\n"}{end}'
oc get replicaset -l app=hello-s2i -o jsonpath='{range .items[*]}{.metadata.name}{" <- "}{.metadata.ownerReferences[0].kind}{"/"}{.metadata.ownerReferences[0].name}{"\n"}{end}'8 Den Image-Trigger lesen
Bei einem Kubernetes-Deployment speichert OpenShift den Trigger in metadata.annotations. Er ist kein Feld der Kubernetes-Deployment-API.
oc set triggers deployment/hello-s2i
oc get deployment hello-s2i -o jsonpath='{.metadata.annotations.image\.openshift\.io/triggers}{"\n"}'
oc get deployment hello-s2i -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'Die Annotation nennt den beobachteten ImageStreamTag, den Container und den Pfad zum Image-Feld. Sobald sich der Digest des Tags ändert, schreibt OpenShift die neue aufgelöste Image-Referenz in spec.template. Kubernetes erkennt eine neue Pod-Vorlage und erzeugt das nächste ReplicaSet.
9 Einen automatischen Rollout beweisen
Terminal 1 beobachtet die Kette
oc get build,deployment,replicaset,pod --watch
Terminal 2 baut Version 2
$digestBefore = oc get istag hello-s2i:latest -o jsonpath='{.image.metadata.name}'
'v2' | Set-Content -LiteralPath .\s2i-src\version.txt -Encoding utf8
oc start-build hello-s2i --from-dir .\s2i-src --follow
oc rollout status deployment/hello-s2i --timeout=120s
$digestAfter = oc get istag hello-s2i:latest -o jsonpath='{.image.metadata.name}'
"Digest vorher: $digestBefore"
"Digest nachher: $digestAfter"
oc get replicaset -l app=hello-s2i --sort-by=.metadata.creationTimestamp
$hostName = oc get route hello-s2i -o jsonpath='{.spec.host}'
curl.exe "http://$hostName"Bash-Variante anzeigen
digest_before=$(oc get istag hello-s2i:latest -o jsonpath='{.image.metadata.name}')
printf 'v2\n' > ./s2i-src/version.txt
oc start-build hello-s2i --from-dir ./s2i-src --follow
oc rollout status deployment/hello-s2i --timeout=120s
digest_after=$(oc get istag hello-s2i:latest -o jsonpath='{.image.metadata.name}')
printf 'Digest vorher: %s\nDigest nachher: %s\n' "$digest_before" "$digest_after"
oc get replicaset -l app=hello-s2i --sort-by=.metadata.creationTimestamp
host_name=$(oc get route hello-s2i -o jsonpath='{.spec.host}')
curl "http://$host_name"Erwartung: Die beiden Digests unterscheiden sich, ein zweites ReplicaSet erscheint und der HTTP-Aufruf antwortet mit hello v2. Damit sind Build, Tag-Fortschritt, Trigger und Rollout getrennt belegt.
10 ConfigChange richtig einordnen
Ein Kubernetes-Deployment braucht keinen eigenen ConfigChange-Trigger. Jede Änderung an seiner Pod-Vorlage erzeugt von sich aus einen neuen Rollout.
oc set env deployment/hello-s2i LAB_CONFIG=v2
oc rollout status deployment/hello-s2i --timeout=120s
oc get replicaset -l app=hello-s2i --sort-by=.metadata.creationTimestamp
oc get deployment hello-s2i -o jsonpath='{.spec.template.spec.containers[0].env}{"\n"}'oc set env ändert spec.template. Der Hash der Vorlage ändert sich und das Deployment erzeugt ein weiteres ReplicaSet. Genau das meint die ConfigChange-Wirkung bei einem Kubernetes-Deployment.
Wichtige Grenze: Wenn du nur den Inhalt einer referenzierten ConfigMap oder eines Secrets änderst, ändert sich die Pod-Vorlage nicht. Ein automatischer Rollout braucht dann zum Beispiel eine Prüfsummen-Annotation, einen Reloader-Controller oder bewusst oc rollout restart deployment/hello-s2i.
DeploymentConfig: Das ältere OpenShift-Objekt deklarierte ConfigChange als eigenen Trigger. DeploymentConfig ist seit OpenShift 4.14 veraltet. Für neue Anwendungen ist das Kubernetes-Deployment der normale Weg.
Image-Trigger gezielt entfernen
oc set triggers deployment/hello-s2i --from-image=hello-s2i:latest -c hello-s2i --remove
oc set triggers deployment/hello-s2i
oc get deployment hello-s2i -o jsonpath='{.metadata.annotations.image\.openshift\.io/triggers}{"\n"}'Danach kann ein neuer Build den Tag aktualisieren, ohne dieses Deployment automatisch zu ändern. Ein expliziter Image-Wechsel oder ein erneutes Hinzufügen des Triggers bleibt möglich.
11 Dateien, Ressourcen und Besitz
| Artefakt | Kategorie | Umgang |
|---|---|---|
s2i-src/package.json, server.js, version.txt | du legst an | lokale Binärquelle dieses Labs, nicht committen |
ImageStream, BuildConfig, Deployment, Service, Route | du legst an | deklarierter Sollzustand im Lab-Projekt |
assemble und run | wichtig | kommen aus dem Builder-Image, nur bei absichtlicher Anpassung überschreiben |
Build, Build-Pod, Image-Digest, ReplicaSet, Anwendungs-Pod | wird generiert | beobachten und diagnostizieren, nicht von Hand pflegen |
| integrierter Registry-Inhalt | wird generiert | liegt im Cluster-Storage und wird über Image-Pruning verwaltet |
12 Fehler an der Übergabe eingrenzen
| Fehlerbild | zuerst prüfen | typische Ursache |
|---|---|---|
| Build startet nicht | oc describe bc/hello-s2i | fehlende Binärquelle oder keine Berechtigung |
| Build scheitert beim Push | oc logs build/hello-s2i-1 und Registry-Operator | interne Registry oder ihr Storage nicht verfügbar |
| Tag hat neuen Digest, aber kein Rollout | Trigger-Annotation und Containername | Trigger fehlt, ist entfernt oder zeigt auf das falsche Feld |
| Neues ReplicaSet, Pod startet nicht | oc describe pod -l app=hello-s2i | Image-Pull, Scheduling, Security Context oder Prozessfehler |
| Route antwortet nicht | Service, EndpointSlice und Readiness | Port oder Selector passt nicht zum Pod |
Reproduzierbarkeit: Ein Binary-Build speichert den übertragenen Quelltext nicht als Git-Referenz in der BuildConfig. Das ist für dieses Lernlabor praktisch, aber keine gute Produktions-Pipeline. Dort sollte die Quelle versioniert und der erzeugte Digest nachvollziehbar einem Commit zugeordnet sein.
13 Lab vollständig aufräumen
oc delete project build-rollout-lab Remove-Item -Recurse -LiteralPath .\s2i-src
Bash-Variante anzeigen
oc delete project build-rollout-lab rm -rf -- ./s2i-src
Das Projekt löscht die angelegten Ressourcen und die erzeugten Build- sowie Rollout-Objekte gemeinsam. Der Builder in der externen Registry und OpenShift-Systemkomponenten bleiben unverändert.
14 Lernkontrolle
- Ich kann erklären, welche Arbeit
assembleundrunübernehmen. - Ich kann Tag und Digest unterscheiden und beide am
ImageStreamTagfinden. - Ich kann die Besitzkette Deployment, ReplicaSet und Pod über
ownerReferencesbelegen. - Ich kann zeigen, wo OpenShift den Image-Trigger an einem Kubernetes-Deployment speichert.
- Ich kann erklären, warum eine Pod-Vorlagenänderung rollt, eine externe ConfigMap-Änderung allein aber nicht.
- Ich kann für Build- und Rolloutfehler die richtige Übergabe zuerst prüfen.