Nehmt euren User userX
und loggt euch im CodeReadyWorkspaces ein.
Link kommt im Chat
Default Credentials:
Username:
userX
(z.B. user25
)
Password:
r3dh4t1!
Geh mit Maus links über deinen Workspace (z.b. user25-workspace), öffne das Menü (3 Punkte) und klick auf "Edit Workspace"
Kopiere folgende YAML in das Textfeld und klick auf "Save"
https://raw.githubusercontent.com/nikolaus-lemberski/openshift-modul3/main/cluster-preperation/workshop-tools/workshop-devfile.yaml
Danach öffne wieder das Menü an deinem Workspace und klick auf "Restart Workspace" Das erstellen der Workspaces wird jetzt ein wenig dauern (da alle gleichzeitig starten).
Wenn der Workspace startet, bekommt ihr unten rechts 3 Popups
Do you trust the authors of https://github.com/nikolaus-lemberski/openshift-modul3.git ?
klickt hier auf Yes, I trust
Do you trust the authors of https://github.com/quarkusio/quarkus-quickstarts.git ?
klickt hier auf Yes, I trust
Do you want to install the recommended extensions redhat/java,vscode/typescript-language-features for your workspace ?
klickt hier auf No
In dem Workspace findet ihr ein paar Beispielprojekte und ein paar "Tooling" Container. Die Tooling Container findet ihr rechts, wenn ihr auf das "Box" Icon klickt (Rechte Tooling Leiste, drittes Icon von oben)
Öffnet openshift-tools
und klickt auf New terminal
. Wählt als working dir
modul3
aus.
Es öffnet sich jetzt ein neues Terminal, in welchem ihr euch noch via oc
CLI anmelden könnt
ersetzt
userX
durch euren Benutzernamen
oc login --insecure-skip-tls-verify https://${KUBERNETES_SERVICE_HOST} -u userX -p r3dh4t1!
Nach der Eingabe des Passwortes sind wir erfolgreich eingeloggt und erhalten eine Willkommens-Nachricht von OpenShift. Username und Adresse werden vom Trainer für jeden Teilnehmer zur Verfügung gestellt. Anschließend loggen wir uns noch in die Web Konsole von OpenShift ein (Adresse wird ebenfalls vom Trainer zur Verfügung gestellt).
⚠ HINWEIS:
Bitte alle Aufgaben selbst lösen und dann über die Lösung prüfen, ob alles richtig gemacht wurde.
Alle Applikationen in OpenShift werden in Projekten organisiert. In einem Projekt können viele Applikationen enthalten sein, sie befinden sich im gleichen namespace und können miteinander über Services kommunizieren.
Als erstes erstellen wir über die Web Konsole oder über die OpenShift CLI (mit oc new-project userX-hello
- userX bitte entsprechend auf eigenen Usernamen anpassen) ein Projekt hello. Damit wir mit den Projektnamen nicht durcheinanderkommen, stellt jeder vor den Projektnamen seinen Usernamen, also z.B. userX-hello.
In unserem neuen Projekt deployen wir eine fertige nodejs Anwendung über OpenShift Source-to-Image (s2i). Über oc new-app -h
kann die Hilfe aufgerufen werden, wie eine Applikation aus einem Git repository von OpenShift gebaut und deployed werden kann.
Die Applikation:
- liegt in Git unter folgender Adresse:
https://github.com/nikolaus-lemberski/openshift-modul3 - dort im Unterordner (context directory) projects/project-1
- erhält als Applikationsnamen "hello"
- nutzt nodejs in der Version 14 mit dem ubi8 baseimage, als ImageStream openshift/nodejs:14-ubi8-minimal in OpenShift bereitgestellt Hinweis: es muss kein Dockerfile erstellt werden!
- Tipp: der zu verwendende ImageStream openshift/nodejs:14-ubi8-minimal wird einfach vor die Github Adresse des Projektes geschrieben, durch eine Tilde (~) getrennt).
- als build strategy soll source verwendet werden
Über oc get all
kann alles, was der oc new-app
command erstellt hat, angesehen werden. Es werden zwei pods gebaut, erst ein "build" pods der die Anwendung baut, danach der pod mit der Anwendung.
Zusatzaufgaben:
- Den Build Log verfolgen
- Eine shell im container der app öffnen und mit curl den app root öffnen
- Die Applikation in der Web Konsole, Developer Perspektive untersuchen
Health Checks dienen in Kubernetes dazu, dass Kubernetes den Status einer Anwendung überprüfen kann und sollte der Health Check fehlschlagen, die Anwendung neu startet. Health Checks können bei der Erstellung einer Anwendung über das Deployment oder die DeploymentConfig konfiguriert werden. Bei einer bestehenden Anwendung wird der Health Check am einfachsten über die Web Konsole konfiguriert.
Die Anwendung stellt zwei Health Checks bereit:
/health/readiness
Der readiness check, der - sobald erfolgreich - an OpenShift das Signal gibt, dass nun traffic an die Anwendung geroutet werden darf./health/liveness
Der liveness check, der von OpenShift kontinuierlich überprüft wird. Schlägt die liveness probe fehl, wird der pod mit der Anwendung automatisch gestoppt. Da dann weniger pods aktiv sind als im ReplicaSet vorgebeben, wird automatisch ein neuer pod mit der Anwendung erstellt.
Aufgabe: Konfiguration von readiness und liveness Health Checks über die Web Konsole.
Zuletzt erstellen wir eine route für die app, um diese öffentlich aufrufbar zu machen, und testen (z.B. mit curl), ob die Anwendung erreichbar ist.
Um Ressourcen für weitere Projekte freizugeben, löschen wir das Projekt wieder über die Web Konsole oder die OpenShift CLI mit oc delete project userX-hello
.
⚠ HINWEIS:
Bitte alle Aufgaben selbst lösen und dann über die Lösung prüfen, ob alles richtig gemacht wurde.
Wie gehabt erstellen wir wieder ein Projekt (oc new-project
), diesmal mit dem Namen nodeapp und vorangestelltem Usernamen, also nach dem Schema userX-nodeapp.
Zuerst speichern wir das folgende Deploymentfile als "Deployment.yml" im Ordner modul3
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodeapp
labels:
app: nodeapp
spec:
replicas: 1
selector:
matchLabels:
app: nodeapp
template:
metadata:
labels:
app: nodeapp
spec:
containers:
- name: nodeapp
image: quay.io/nlembers/project-2:v1.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: nodeapp
spec:
selector:
app: nodeapp
ports:
- protocol: TCP
port: 8080
targetPort: 8080
Anschließend deployen wir die Applikation in unserem neu erstellten Projekt:
oc create -f Deployment.yml
Nachdem die app erstellt ist, wird der pod mit der app crashen. Die Aufgabe ist nun, die Ursache für den crash zu finden und das Problem zu beheben.
Nachdem das Problem erfolgreich behoben wurde, erstellen wir eine route für die app, um diese öffentlich aufrufbar zu machen, und testen (z.B. mit curl), ob die Anwendung erreichbar ist.
Um Ressourcen für weitere Projekte freizugeben, löschen wir das Projekt wieder.
⚠ HINWEIS:
Bitte alle Aufgaben selbst lösen und dann über die Lösung prüfen, ob alles richtig gemacht wurde.
Wie gehabt erstellen wir wieder ein Projekt, diesmal mit dem Namen helmapp und vorangestelltem Usernamen, also nach dem Schema user123-helmapp.
Helm ist ein Paketmanager für Kubernetes und erlaubt - neben dem Deployment fertiger Anwendungen als helm chart - die Erstellung eigener Anwendungslandschaften. Dazu installieren wir Helm auf unserem Rechner, falls noch nicht vorhanden:
Wer zum ersten mal mit Helm zu tun hat, kann also Vorbereitung den Helm Chart Template Guide durcharbeiten.
Wir erstellen ein neues Helm Chart mit dem Namen tasks:
helm create tasks
Und wechseln in das neu erstellte Verzeichnis tasks.
Unsere app besteht aus einer Java Spring Anwendung, die eine REST Schnittstelle anbietet und Daten in einer Datenbank (MariaDB) speichert. Wir möchten die Java Anwendung mit der Datenbank zusammen in unserem Helm Chart konfigurieren und über Helm deployen.
Wir möchten MariaDB als fertiges Helm Chart von Bitnami beziehen. Dazu fügen wir zuerst das Bitnami Repo hinzu:
helm repo add bitnami https://charts.bitnami.com/bitnami
Und konfigurieren in Chart.yaml die MariaDB als dependency. Dazu fügen wir am Ende von Chart.yaml an:
dependencies:
- name: mariadb
version: 11.1.7
repository: https://charts.bitnami.com/bitnami
Ein anschließendes helm dependency update
lädt die MariaDB in das Projekt.
In values.yaml erfolgt nun noch die Konfiguration der MariaDB:
mariadb:
auth:
username: tasksuser
password: supersecretpwd
database: tasksdb
primary:
podSecurityContext:
enabled: false
containerSecurityContext:
enabled: false
Damit die dependency hinzugefügt wird, weisen wir Helm zum Aktualisieren der Abhängigkeiten an:
helm dependency update
Ebenfalls in values.yaml wird das image für unsere Java Anwendung konfiguriert. In values.yml am Anfang den image Eintrag wie folgt aktualisieren:
image:
repository: quay.io/nlembers/spring-tasks
pullPolicy: IfNotPresent
tag: "v1.1"
Die Spring Anwendung benötigt die Zugangsdaten für die Datenbank sowie den Treibernamen. Diese müssen als environment Variablen übergeben werden. Environment Variablen werden in values.yaml hinzugefügt und müssen dann noch im Deployment (/templates/deployment.yaml) über das Helm templating eingelesen werden.
In values.yaml fügen wir ganz unten die environment Variablen der MariaDb hinzu:
env:
- name: "SPRING_DATASOURCE_DRIVER_CLASS_NAME"
value: "org.mariadb.jdbc.Driver"
- name: "SPRING_DATASOURCE_URL"
value: "jdbc:mariadb://tasks-mariadb:3306/tasksdb"
- name: "SPRING_DATASOURCE_USERNAME"
value: "tasksuser"
- name: "SPRING_DATASOURCE_PASSWORD"
value: "supersecretpwd"
Im file templates/deployment.yml in der containers section unterhalb von imagePullPolicy hinzufügen (auf korrekte Einrückung achten!):
env:
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value }}
{{- end }}
Gleich darunter die ports und probes konfigurieren und auf port 8080 umstellen:
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
In unserem Verzeichnis tasks nutzen wir Helm für die Installation:
helm install tasks .
Wie in den vorherigen Übungen können wir nun über oc get all
, oc logs
etc. die Anwendung prüfen. Läuft alles korrekt, erstellen wir eine Route auf den Service der Java Anwendung und rufen diese im Browser unter dem Pfad '/api' auf. Es öffnet sich ein HAL Explorer, mit dem man mit dem REST Service interagieren kann. Curl oder httpie etc. können natürlich ebenso verwendet werden.
Zusatzaufgaben:
- Erstellen von ein paar Tasks über den HAL Explorer oder curl
- Eine shell in die MariaDB öffnen
- In der MariaDB die Tabelle in der tasksdb auf das Vorhandensein der erstellten Tasks prüfen
Um Ressourcen für weitere Projekte freizugeben, löschen wir das Projekt wieder.
⚠ HINWEIS:
Bitte alle Aufgaben selbst lösen und dann über die Lösung prüfen, ob alles richtig gemacht wurde.
Wie gehabt erstellen wir zuerst ein Projekt meshapp mit vorangestelltem Username, also z.B. userX-meshapp.
Service Mesh ist bereits installiert und nun benötigen wir noch eine ServiceMeshMemberRoll für unser neu erstelltes Projekt. Dazu erstellen wir die member roll im namespace userX-istio_system (yaml file lokal speichern, den Usernamen anpassen und über die oc cli anwenden):
apiVersion: maistra.io/v1
kind: ServiceMeshMemberRoll
metadata:
name: default
namespace: userX-istio-system
spec:
members:
- userX-meshapp
Anschließend deployen wir 3 kleine Anwendungen:
curl -H \
"Accept: application/vnd.github.v4.raw" \
-L "https://api.github.com/repos/nikolaus-lemberski/openshift-modul3/contents/projects/project-4/customer.yml" \
| kubectl create -f -
Anschließend erstellen wir ein virtuelles Gateway, über das wir das Projekt aufrufen können. Da wir ein Service Mesh verwenden, möchten wir den gesamten traffic durch das Service Mesh routen und verwenden daher nicht wie sonst eine route.
curl -H \
"Accept: application/vnd.github.v4.raw" \
-L "https://api.github.com/repos/nikolaus-lemberski/openshift-modul3/contents/projects/project-4/customer-gateway.yml" \
| kubectl create -f -
Nachdem das Gateway erstellt ist, können wir es von außen über curl auf der Adresse des Service Mesh Gateway im Kontext-Pfad /customer aufrufen:
curl istio-ingressgateway-USERNAME-istio-system.apps.cluster-XYZ-SEE-YOUR-OPENSHIFT-ADDRESS.opentlc.com/customer
Zum Beispiel: istio-ingressgateway-user1-istio-system.apps.cluster-rdbr4.rdbr4.sandbox1817.opentlc.com/customer
Interessant ist im Vergleich zu unseren vorherigen Projekten: In der READY Spalte von oc get pods
stehen nun zwei (2/2) statt wie bisher eins (1/1). In jedem pod sind zwei container:
- der Container mit der Applikation
- der Container mit dem Service Mesh Proxy (Envoy Proxy)
Möchte man mit oc exec
auf einen Container zugreifen, spezifiziert man den gewünschten über das -c
flag.
Wir können nun mit curl den endpoint (sh. unser Service Mesh Gateway weiter oben) aufrufen und bekommen folgenden response:
customer => UnknownHostException: preference
Nun deployen wir die preference Anwendung:
curl -H \
"Accept: application/vnd.github.v4.raw" \
-L "https://api.github.com/repos/nikolaus-lemberski/openshift-modul3/contents/projects/project-4/preference.yml" \
| kubectl create -f -
Wir können nun mit curl den endpoint (route) aufrufen und bekommen folgenden response:
customer => Error: 503 - preference => UnknownHostException: recommendation
Die recommendation app wird für unser Szenario in zwei Versionen deployed:
curl -H \
"Accept: application/vnd.github.v4.raw" \
-L "https://api.github.com/repos/nikolaus-lemberski/openshift-modul3/contents/projects/project-4/recommendation-v1.yml" \
| kubectl create -f -
curl -H \
"Accept: application/vnd.github.v4.raw" \
-L "https://api.github.com/repos/nikolaus-lemberski/openshift-modul3/contents/projects/project-4/recommendation-v2.yml" \
| kubectl create -f -
Wir können nun mit curl den endpoint (route) aufrufen und bekommen folgenden response:
customer => preference => recommendation v1 from 'recommendation-abc': 1
Und nach nochmaligem Aufruf:
customer => preference => recommendation v2 from 'recommendation-xyz': 1
Aufgabe ist nun, das Service Mesh zu konfigurieren:
Die recommendation app liegt in zwei Versionen vor. Diese beiden Versionen sollen dem Service Mesh als DestinationRule angegeben werden.
Docs: DestinationRule
Dazu wenden wir folgendes yaml file an:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: recommendation
spec:
host: recommendation
subsets:
- labels:
version: v1
name: version-v1
- labels:
version: v2
name: version-v2
Als nächstes soll das Service Mesh angewiesen werden, Service-Calls auf den recommendation Service 50/50 zwischen v1 und v2 zu verteilen. Hierfür wird ein VirtualService benötigt.
Docs: VirtualService
Dazu wenden wir folgendes yaml file an:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: recommendation
spec:
hosts:
- recommendation
http:
- route:
- destination:
host: recommendation
subset: version-v1
weight: 50
- destination:
host: recommendation
subset: version-v2
weight: 50
Über curl kann nun das Ergebnis geprüft werden, ggf. auch zum Test die Last anders verteilt werden.
Nun fügen wir im recommendation-v2 service einen Fehler ein. Wir gehen mit oc exec
in den app container von recommendation-v2 und rufen dort mit curl die Adresse localhost:8080/misbehave auf (wer experimentieren mag: der endpoint localhost:8080/behave stellt die app wieder auf funktionstüchtig).
Anschließend überprüfen wir das Ergebnis durch mehrfachen Aufruf des customer Service.
Wir simulieren hier einen fehlerhaften Pod oder temporäre Netzwerk-Fehler. Das Service Mesh kann uns nun helfen, unsere Services mehr "resilient" gegen solche Fehler zu machen.
Dazu soll das oben erstellte VirtualService geändert werden, so dass bei Fehlern ein retry ausgeführt wird. Aufgabe ist es, über die Dokumentation selbst herauszufinden, wie ein retry konfiguriert wird. VirtualService
Ist alles korrekt konfiguriert, treten beim Aufruf des customer Service nun keine Fehler von recommendation-v2 mehr auf. Fehlerhafter response vom recommendation-v2 wird vom Service Mesh durch ein "retry" behoben. Wer möchte kann nun auch noch die pods skalieren und das Verhalten beobachten.
Das genannte Verhalten schauen wir uns nun in Kiali und Jaeger an. Die Routes dazu finden wir in > Networking > Routes.
Zum Schluss löschen wir wieder das Projekt, um Ressourcen freizugeben.