Voraussetzung: ein Cluster mit oc login.
Diese Anleitung legt selbst ein frisches Project an, du brauchst also nichts vorzubereiten.
1 Project vs Namespace
Ein Namespace ist in reinem Kubernetes nur eine Hülle. Ein Project ist dieselbe Hülle plus Selbstbedienung.
Ein Namespace (Kubernetes) ist nur ein Namensraum mit RBAC- und Quota-Grenze. Ein Project ist derselbe Namespace – nur angelegt über einen Weg, der gleich Rollen, ServiceAccounts und SCC-Annotationen mitbringt (eine Annotation ist eine an ein Objekt geheftete Notiz, die Werkzeuge lesen).
oc new-project
schickt einen ProjectRequest durch ein änderbares Template (Namespace +
deine admin-Rolle), danach hängen Controller die drei ServiceAccounts, die
SCC-Annotationen und den restricted-v2-Zugriff an. LimitRange,
ResourceQuota und NetworkPolicy sind nicht dabei – die
gehören ins Template, wenn jedes Projekt sie haben soll.- ein Namespace in Kubernetes ist nur ein Namensraum mit RBAC- und Quota-Grenze plus ein
default-ServiceAccount, sonst nichts kubectl create namespace demo-fundamentlegt nur die Hülle an, du bist darin nicht automatisch berechtigtoc new-project libraryerzeugt zuerst einenProjectRequest, das einzige Objekt, das ein Nicht-Admin anlegen darf- daraufhin legt das
project-request-Template den Namespace plus eineadmin-RoleBinding für dich an (das Template liegt inopenshift-configund ist editierbar) - danach ergänzen Cluster-Controller (nicht das Template) die ServiceAccounts
default,builder,deployersamt ihren RoleBindingssystem:image-builderundsystem:deployer(A6) - Annotation: ein an ein Objekt geheftetes Schlüssel-Wert-Paar, das Werkzeuge und Controller lesen. Anders als ein
labelist es nicht zum Filtern da, sondern zum Mitgeben von Information (darf lang sein, darf JSON enthalten). Merksatz: Labels sagen „wie man es findet“, Annotationen sind „Notizen dran, meist von Maschinen“ - der SCC-Allocator schreibt drei Annotationen auf den Namespace (
openshift.io/sa.scc.uid-range,.mcs,.supplemental-groups) – dein Pod bekommt dadurch eine zufällige hohe UID (etwa 1000680000) und kein festes$HOME(A7) - der Namespace erhält Zugriff auf die SCC
restricted-v2, über die clusterweite Gruppesystem:authenticated - weitere Annotationen:
openshift.io/description,display-nameundrequester - nicht automatisch da:
LimitRange(sonst Pods ohne Requests),ResourceQuota(sonst kein Deckel),NetworkPolicy(sonst erreicht jeder Pod jeden, F2) - wer das für alle Projekte will, trägt es ins
project-request-Template ein. Über mehrere Projekte hinweg geht eineClusterResourceQuotaper Label-Selektor - Befehle:
oc new-project library(du wirst admin darauf),oc project library(Namespace wechseln, schreibt die kubeconfig),oc projects,oc delete project library(geht inTerminating),oc adm new-project(als Admin, umgeht Template und Selbstbedienung) - ein Project ist keine stärkere Isolation als ein Namespace, nur ein paar Annotationen plus die automatische admin-RoleBinding
- Netz-Trennung zwischen Projekten kommt separat über
NetworkPolicy(F2) – ein frisches Project ist nach außen offen - bleibt der Namespace ewig in
Terminating, klemmt ein Finalizer (nachsehen mitoc get ns library -o yamlim Feldspec.finalizers) - die Rolle
self-provisioner(ansystem:authenticated:oauth) erlaubt jedem angemeldeten Nutzeroc new-project– Admins entfernen dieses Binding oft - im Projekt:
oc new-project libraryist der erste Schritt des Bibliothek-Deploys – danach hängt Jenkins diepipeline-SA und ihre Bindings (A5) dran und Helm installiert die Bitnami-Infra
| Befehl | legt an | du bist danach |
|---|---|---|
kubectl create namespace demo-fundament | nur den Namespace | nicht automatisch berechtigt |
oc new-project library | Namespace + admin-RoleBinding + Annotationen | admin auf dem eigenen Project |
oc new-project erzeugt zuerst einen ProjectRequest – das
einzige Objekt, das ein Nicht-Admin anlegen darf. Das project-request-Template
(liegt in openshift-config, editierbar) legt daraufhin den Namespace plus die
admin-RoleBinding für dich an. Ein Project ist keine stärkere
Isolation als ein Namespace – nur ein paar Annotationen plus die automatische
Binding. Echte Netz-Trennung kommt separat über NetworkPolicy (F2).
2 oc new-project live verfolgen
Ein frisches Project, und alles ansehen, was dabei automatisch entsteht.
oc new-project demo-fundament oc whoami oc get rolebinding -n demo-fundament # -> admin-Binding auf dich, plus system:image-builder/system:deployer fuer builder/deployer oc get sa -n demo-fundament # -> default, builder, deployer - schon da, ohne dass du etwas getan hast oc get ns demo-fundament -o yaml | grep -A5 annotations
Die ServiceAccounts kommen nicht aus dem Template, sondern von
Cluster-Controllern nach der Namespace-Erstellung (A6). Wichtige Annotationen am
Namespace: openshift.io/sa.scc.uid-range, .mcs,
.supplemental-groups (setzt die zufällige UID für Pods, A7), dazu
openshift.io/description, display-name und requester.
Der Namespace bekommt automatisch Zugriff auf die SCC restricted-v2 –
über die clusterweite Gruppe system:authenticated, nicht extra pro Project.
3 Was bewusst nicht entsteht
| Fehlt | Folge ohne es |
|---|---|
LimitRange | Pods ohne Requests/Limits laufen unbegrenzt |
ResourceQuota | kein Deckel – ein Project kann den ganzen Node füllen |
NetworkPolicy | jeder Pod erreicht jeden – ein frisches Project ist nach außen offen (F2) |
Wer das für alle Projects will, trägt es ins
project-request-Template ein. Über mehrere Projects hinweg geht eine
ClusterResourceQuota per Label-Selektor.
Selbstbedienung abschaltenDie Rolle
self-provisioner (an system:authenticated:oauth) erlaubt
jedem angemeldeten Nutzer oc new-project – Admins
entfernen dieses Binding oft, damit Projects nur kontrolliert entstehen.
Bleibt ein gelöschter Namespace ewig in Terminating, klemmt meist ein
Finalizer – nachsehen mit
oc get ns demo-fundament -o yaml im Feld
spec.finalizers.
4 oc, kubectl und die API-Gruppen
oc ist kubectl plus die OpenShift-eigenen Verben
– dieselbe Binary redet auch mit reinem Kubernetes.
Dieselbe Binary, derselbe Port 6443 – nur redet oc zusätzlich mit den OpenShift-eigenen API-Gruppen und bringt Bequem-Verben mit. Die kubeconfig sagt, mit welchem Cluster und als wer.
oc und kubectl sprechen
dieselbe REST-API auf 6443; die kubeconfig liefert Adresse und Identität.
OpenShift hängt seine eigenen Objekttypen (Route, ImageStream, Project, SCC …) über
einen aggregierten API-Server an denselben Port, und Operatoren stecken per CRD weitere dazu.
oc api-resources und oc explain zeigen, was dieser Cluster kann.ocistkubectlplus die OpenShift-Verben (new-app,expose,rollout,rsh,debug,adm) undlogin/project- dieselbe Binary redet auch mit reinem Kubernetes (k3s,
biblio.yaml), dort fehlen nur die*.openshift.io-Objekte - ein Port 6443, viele API-Gruppen:
core/v1, die benannten (apps,batch,rbacund weitere),*.openshift.iound die CRD-gestützten - die OpenShift-Gruppen laufen über den aggregierten API-Server, einen Extra-Prozess hinter denselben Pfaden
- eine CRD registriert einen neuen Typ – danach ist
kind: PostgresClustergleichwertig zuPodund wird genauso mitoc getbedient (D2) oc api-resourceslistet alle Typen (Kurzname, apiVersion, NAMESPACED, KIND),--namespaced=falsefiltert auf Cluster-Objekteoc explain route.spec.tlsgibt die Schema-Referenz direkt aus diesem Cluster- eine kubeconfig besteht aus drei Teilen: Cluster (Adresse und CA), User (Token oder x509-Zertifikat) und Context (welcher Cluster, welcher Namespace)
- im Projekt:
~/.kube/okd-sno-hetzner.yaml(SNO),~/.kube/biblio.yaml(k3s), die Variable$KUBECONFIGwählt –oc loginschreibt die Datei,oc projectsetzt den Namespace - die SNO-API (
api.sno.<ip>.nip.io:6443) ist nur übers Tailnet offen (Ports 22/6443 in der Firewall zu) – einhosts-Eintrag zeigt den Namen auf die Tailnet-IP, sonst passt der Zert-SAN nicht (Diagramm 03) - die Web-Konsole ist eine Route (
console-openshift-console.apps.<cluster>) mit zwei Perspektiven, Administrator und Developer – Adresse mitoc whoami --show-console - alles, was die Konsole tut, geht auch per
oc– sie schreibt nur dieselben Objekte (die Klick-Wege stehen in okd-konfiguration-nachschauen.html) - Bibliothek-Alltag:
oc get pods -n library(Spalte READY),oc get route -n library,oc logs --previous,oc get endpoints,oc rsh <pod>,oc debug node/<name> oc cpunter Git Bash braucht://statt:/(die Umgebung schreibt Pfade um), unter PowerShell nicht- OCP-Unterschied: keiner –
oc, die API-Gruppen und die Konsole sind bei OKD und OCP identisch
new-app, expose, rollout, rsh, debug, adm, login, project – auf reinem Kubernetes (k3s) fehlen nur die *.openshift.io-Objekte, der Rest funktioniert gleichcore/v1, benannte (apps, batch, rbac...), *.openshift.io und CRD-gestützteoc get route sieht aus wie jeder andere AufrufEine CRD registriert einen neuen Typ – danach ist
kind: PostgresCluster gleichwertig zu Pod und wird genauso mit
oc get bedient (D2). Genau so kommen Operatoren zu neuen Ressourcentypen.
5 Die API selbst erkunden
Ohne Doku herausfinden, was ein Cluster kann – drei Befehle.
# alle Typen: Kurzname, apiVersion, NAMESPACED, KIND oc api-resources oc api-resources --namespaced=false # nur Cluster-Objekte # Schema-Referenz direkt aus DIESEM Cluster, nicht aus der Doku: oc explain route.spec.tls oc explain route.spec.tls.termination # eine kubeconfig hat drei Teile: Cluster, User, Context oc config view --minify echo $KUBECONFIG
Eine kubeconfig besteht aus Cluster (Adresse und CA),
User (Token oder x509-Zertifikat) und Context (welcher
Cluster, welcher Namespace). oc login schreibt die Datei,
oc project <name> setzt darin den Namespace.
Web-Konsole = dieselben ObjekteDie Route
console-openshift-console.apps.<cluster> mit den Perspektiven
Administrator und Developer tut nichts, was oc nicht auch könnte –
sie schreibt nur dieselben Objekte. Adresse mit
oc whoami --show-console. Die Klick-Wege stehen in
okd-konfiguration-nachschauen.html.
6 Das ServiceAccount-Modell
Drei Identitätsarten, alle drei RBAC-Subjekte (A5): User (aus dem IdP, A4), Group, und ServiceAccount.
Ein Mensch meldet sich per oc login an (A4). Ein Pod kann das nicht – er braucht trotzdem eine Identität, sobald er mit dem API-Server redet. Diese Identität ist eine ServiceAccount: ein „Benutzer für Maschinen“, an den man genauso Rollen (A5) und SCCs (A7) bindet, nur ohne IdP und ohne Passwort.
ServiceAccount.
Der Pod bekommt ihr Token als projiziertes Volume (token, ca.crt, namespace),
schickt es als Bearer-Header an den API-Server, und ab da entscheidet RBAC (A5). Eine frische
SA darf nichts – man bindet ihr Rollen mit -z, eine SCC mit add-scc-to-user,
ein Pull-Secret mit oc secrets link. Das Token ist heute kurzlebig und rotiert von selbst,
der alte Dauer-Token in etcd ist Geschichte.- es gibt drei Identitätsarten:
User(aus dem IdP, A4),GroupundServiceAccount(system:serviceaccount:<ns>:<name>). Alle drei sind RBAC-Subjekte (A5) - jeder Namespace bekommt automatisch
default,builder,deployer–builderunddeployersind OpenShift-eigen - ein Pod ohne
serviceAccountNameläuft alsdefault– jeder App-Pod hat immer eine SA - der Pod bekommt ein projiziertes Volume unter
/var/run/secrets/kubernetes.io/serviceaccount/mit den Dateientoken,ca.crtundnamespace - das Bound Token ist kurzlebig (etwa eine Stunde) und an eine audience gebunden – der kubelet holt es über die
TokenRequest-API und rotiert es. Es wird generiert, nicht anfassen - früher (bis OCP 4.15) hatte jede SA ein nie ablaufendes Token-Secret (
kubernetes.io/service-account-token) in etcd - einen statischen Token braucht man nur für externe Nutzer:
oc create token <sa> --duration=<dauer>oder ein Secret von Hand (das du anlegst) - im Projekt: genau so ein Secret ist
pipeline-tokeninlibrary(Annotationkubernetes.io/service-account.name: pipeline) – Jenkins nutzt es alspodman login-Passwort und inKUBECONFIG_PROD. Es verschwindet nach jedemsno.ps1 up, der Post-up-Hook legt es neu an automountServiceAccountToken: falsean SA oder Pod, wenn der Pod die API gar nicht ruft (die meisten App-Pods)- eine SA startet mit keinen Rechten – Rollen bindet man mit
oc adm policy add-role-to-user edit -z <sa> -n <ns>(-zsteht für ServiceAccount) - die SCC kommt von der SA, nicht vom User –
oc adm policy add-scc-to-user anyuid -z <sa>steuert den erlaubtensecurityContext(A7) - Pull-Secret anhängen mit
oc secrets link <sa> <secret> --for=pull– diedefault-SA hat schon eins für die interne Registry (B10) builderdarf in die interne Registry pushen (Rollesystem:image-builder),deployersteuertDeploymentConfig-Rollouts- von außen meldet sich Jenkins als
system:serviceaccount:library:pipelinemit dessen Token an (A5, B12, E1) - prüfen mit
oc auth can-i --as=system:serviceaccount:<ns>:<sa>,oc describe sa <sa>oderoc create token <sa>
| SA | ist |
|---|---|
default | jeder Pod ohne serviceAccountName läuft hiermit – jeder App-Pod hat immer eine SA |
builder | OpenShift-eigen, darf in die interne Registry pushen (system:image-builder) |
deployer | OpenShift-eigen, steuert DeploymentConfig-Rollouts (system:deployer) |
Der Pod bekommt ein projiziertes Volume unter
/var/run/secrets/kubernetes.io/serviceaccount/ mit den Dateien
token, ca.crt und namespace. Das
Bound Token ist kurzlebig (etwa eine Stunde), an eine audience
gebunden, wird über die TokenRequest-API geholt und automatisch rotiert – nicht
anfassen. Früher (bis OCP 4.15) hatte jede SA ein nie ablaufendes Token-Secret in
etcd.
7 Eine ServiceAccount live anlegen
Anlegen, binden, Token prüfen – der Weg, den auch die Bibliothek-Pipeline geht.
oc create sa demo-sa -n demo-fundament oc describe sa demo-sa -n demo-fundament # startet ohne jedes Recht: oc auth can-i get pods --as=system:serviceaccount:demo-fundament:demo-sa -n demo-fundament # no # Rolle binden (-z = ServiceAccount im aktuellen Namespace): oc adm policy add-role-to-user edit -z demo-sa -n demo-fundament oc auth can-i get pods --as=system:serviceaccount:demo-fundament:demo-sa -n demo-fundament # yes # ein statisches Token nur fuer externe Nutzer (z.B. eine CI ausserhalb): oc create token demo-sa -n demo-fundament --duration=1h # Pull-Secret anhaengen: oc secrets link demo-sa <secret> --for=pull -n demo-fundament
automountServiceAccountToken: false an SA oder Pod setzen, wenn der Pod die
API gar nicht ruft – die meisten App-Pods brauchen kein Token. Die SCC kommt
von der SA, nicht vom User –
oc adm policy add-scc-to-user anyuid -z demo-sa steuert den erlaubten
securityContext (A7).
8 Typische Fallen
| Symptom | meist |
|---|---|
| frisches Project ist von außen erreichbar | erwartet – keine NetworkPolicy ist Vorgabe, nicht Bug (F2) |
| Pods laufen ohne Limits den Node voll | keine LimitRange/ResourceQuota im Project |
Namespace hängt in Terminating | ein Finalizer wartet – spec.finalizers prüfen |
oc new-project schlägt fehl (Forbidden) | die self-provisioner-Binding wurde entfernt – ein Admin muss anlegen (oc adm new-project) |
| ein Pod kann die API nicht erreichen | fehlende RBAC-Binding auf seiner SA, nicht auf einem User – oc auth can-i --as=system:serviceaccount:... prüft es direkt |
9 Wie die Bibliothek es macht
| Baustein | real |
|---|---|
| Project | oc new-project library ist der erste Schritt des Deploys – danach hängt Jenkins die pipeline-SA und ihre Bindings dran (A5), Helm installiert die Bitnami-Infra |
| ServiceAccount | Jenkins meldet sich extern als system:serviceaccount:library:pipeline an – genau so ein Secret wie pipeline-token in library ist der statische Token-Weg (Annotation kubernetes.io/service-account.name: pipeline) |
| Bekannte Störung | das pipeline-token-Secret verschwindet nach jedem sno.ps1 up (Snapshot-Restore) – der Post-up-Hook (sno-wsl.sh remediate) legt es automatisch neu an |
| kubeconfig | zwei getrennte Dateien – ~/.kube/okd-sno-hetzner.yaml (SNO) und ~/.kube/biblio.yaml (k3s), $KUBECONFIG wählt |
OCP-Unterschied bei allen dreien: keiner – Project-Selbstbedienung,
oc/API-Gruppen/Konsole und das ServiceAccount-Modell sind Kern-OpenShift bzw.
Kern-Kubernetes, identisch auf OKD und OCP.
+ Die Kurzfassung
api-resources/explain erkunden das Cluster-Schema live