← Übersicht  ·  Skripte & Dateien  ·  OpenShift · Build · Rollout

Build bis Rollout: S2I, ImageStream und Deployment

Du baust eine winzige Node.js-Anwendung aus lokalen Quellen mit Source-to-Image, verfolgst das Ergebnis bis zum Image-Digest und lässt einen OpenShift-Image-Trigger automatisch ein neues ReplicaSet ausrollen. Danach beweist du den Kubernetes-Weg für Konfigurationsänderungen.

Stand: 5. September 2026OKD / OCP 4.xAtlas B1, C3, C6ca. 45 Minuten

1 Die ganze Kette

Vier Übergaben verbinden Quelltext mit laufenden Pods. An jeder Übergabe entsteht ein anderer überprüfbarer Zustand.

Quelles2i-src/
liegt nur auf deinem Laptop
BuildBuildConfig
erzeugt einen Build und Build-Pod
ImageImageStreamTag
zeigt auf einen unveränderlichen Digest
RolloutDeployment
erzeugt ReplicaSet und Pods

Source-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

BegriffAufgabeEntsteht wie
BuildConfigdauerhafte Bauanweisung mit Quelle, Strategie, Builder und Ausgabezieldu legst sie an
Buildein konkreter Lauf dieser BauanweisungOpenShift generiert ihn
ImageStreamTagOpenShift-Zeiger wie hello-s2i:latest mit Historie zu Image-Digestsdu definierst den Stream, der Build aktualisiert den Tag
Image-Triggerbeobachtet den Tag und schreibt den neuen Digest in die Pod-Vorlageoc set triggers setzt eine Annotation
Deploymentbeschreibt Rollout-Strategie und gewünschte Pod-Vorlagedu legst es an
ReplicaSethält eine bestimmte Version der Pod-Vorlage in gewünschter Anzahl am Lebendas 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.

Laptop · PowerShell
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
Laptop · Bash
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.

Laptop · PowerShell · legt lokale Lab-Dateien an
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-src
Bash-Variante anzeigen
Laptop · Bash · legt lokale Lab-Dateien an
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 -print

s2i-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.

Laptop · PowerShell
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
Laptop · Bash
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
EOF

Warum 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

Laptop · PowerShell
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
Laptop · Bash
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"}'
assembleDas Skript aus dem Builder-Image übernimmt den Quelltext, erkennt Node.js und bereitet das Laufzeit-Image vor.
runDieses Skript wird zum Startbefehl des fertigen Images und ruft für dieses Projekt npm start auf.
BuildahOpenShift lässt aus den S2I-Schritten ein Container-Image bauen und schiebt es in die integrierte Registry.
Digesthello-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.

Laptop · PowerShell oder Bash
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.

Laptop · nur lesen
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.

Laptop · PowerShell oder Bash · nur lesen
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

Laptop · PowerShell oder Bash · laufen lassen
oc get build,deployment,replicaset,pod --watch

Terminal 2 baut Version 2

Laptop · PowerShell
$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
Laptop · Bash
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.

Laptop · ändert die Pod-Vorlage
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
Laptop · optional
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

ArtefaktKategorieUmgang
s2i-src/package.json, server.js, version.txtdu legst anlokale Binärquelle dieses Labs, nicht committen
ImageStream, BuildConfig, Deployment, Service, Routedu legst andeklarierter Sollzustand im Lab-Projekt
assemble und runwichtigkommen aus dem Builder-Image, nur bei absichtlicher Anpassung überschreiben
Build, Build-Pod, Image-Digest, ReplicaSet, Anwendungs-Podwird generiertbeobachten und diagnostizieren, nicht von Hand pflegen
integrierter Registry-Inhaltwird generiertliegt im Cluster-Storage und wird über Image-Pruning verwaltet

12 Fehler an der Übergabe eingrenzen

Fehlerbildzuerst prüfentypische Ursache
Build startet nichtoc describe bc/hello-s2ifehlende Binärquelle oder keine Berechtigung
Build scheitert beim Pushoc logs build/hello-s2i-1 und Registry-Operatorinterne Registry oder ihr Storage nicht verfügbar
Tag hat neuen Digest, aber kein RolloutTrigger-Annotation und ContainernameTrigger fehlt, ist entfernt oder zeigt auf das falsche Feld
Neues ReplicaSet, Pod startet nichtoc describe pod -l app=hello-s2iImage-Pull, Scheduling, Security Context oder Prozessfehler
Route antwortet nichtService, EndpointSlice und ReadinessPort 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

Laptop · PowerShell · löscht nur Lab-Projekt und lokale Lab-Quelle
oc delete project build-rollout-lab
Remove-Item -Recurse -LiteralPath .\s2i-src
Bash-Variante anzeigen
Laptop · Bash · löscht nur Lab-Projekt und lokale Lab-Quelle
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 assemble und run übernehmen.
  • Ich kann Tag und Digest unterscheiden und beide am ImageStreamTag finden.
  • Ich kann die Besitzkette Deployment, ReplicaSet und Pod über ownerReferences belegen.
  • 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.