= Quarkus-Seminar
Christian Aberger & Thomas W. Stütz
1.0.0, {docdate}: 15.-17.3.2023, Sperlhof/Windischgarsten
ifndef::imagesdir[:imagesdir: images]
//:toc-placement!:  // prevents the generation of the doc at this position, so it can be printed afterwards
:sourcedir: ../src/main/java
:icons: font
:sectnums:    // Nummerierung der Überschriften / section numbering
:toc: left
:experimental:

//Need this blank line after ifdef, don't know why...
ifdef::backend-html5[]
// https://fontawesome.com/v4.7.0/icons/
icon:file-text-o[link=https://raw.githubusercontent.com/quarkus-seminar/2023-ph-seminar/main/asciidocs/{docname}.adoc] ‏ ‏ ‎
icon:github-square[link=https://github.com/orgs/quarkus-seminar/repositories] ‏ ‏ ‎
icon:home[link=https://quarkus-seminar.github.io/2023-ph-seminar/]
endif::backend-html5[]


== Tag 1

* https://htl-leonding-college.github.io/quarkus-lecture-notes/[^]
* https://github.com/htl-leonding/vehicle[vehicle-github-repo^]
* https://medium.com/sahibinden-technology/package-by-layer-vs-package-by-feature-7e89cde2ae3a[Package by Layer vs Package by Feature^]
* http://edufs.edu.htl-leonding.ac.at/~t.stuetz/hugo/2021/01/technology-notes/[^]



== Tag 2

=== Mapping von Entities

==== Dto

* reine Transfer Objekte


* Frage:
** ansehen der Constraints für PK in postgres (nullable?)

** Performance bei DevServices für Datenbanken


=== Panache

image::panache-jpa-jdbc.png[]


* https://quarkus.io/guides/hibernate-orm-panache

=== Dev Services

* https://quarkus.io/guides/dev-services


=== AssertJ-Core

* https://assertj.github.io/doc/#assertj-core

=== AssertJ-DB

* https://assertj.github.io/doc/#assertj-db



=== Minikube installieren

----
brew install minikube
brew install kubectl
----


----
minikube start
minikube addons list
minikube addons enable dashboard
minikube addons enable metrics-server
kubectl get nodes
minikube dashboard
----

.namespace.yaml
[source,yaml]
----
apiVersion: v1
kind: Namespace
metadata:
  name: demo-namespace
----

----
cd k8s
kubectl apply -f namespace.yaml
----

.output
----
namespace/demo-namespace created
----

* https://kubernetes.io/docs/reference/kubectl/cheatsheet/

.autocomplete für Kubectl enablen
----
source <(kubectl completion zsh)  # set up autocomplete in zsh into the current shell
echo '[[ $commands[kubectl] ]] && source <(kubectl completion zsh)' >> ~/.zshrc # add autocomplete permanently to your zsh shell
----

.Namespace fix einstellen
----
kubectl config set-context --current --namespace=demo-namespace
kubectl get namespaces
kubectl get pods
----

* Man darf sich nicht für Kleinigkeiten ein neues Docker-Image bauen

* Man sollte immer ein Standard Image zB für postgres verwenden und dieses konfigurieren

* https://github.com/docker-library/postgres/tree/ef45b990868d5a0053bd30fdbae36551b46b76c9/15/bullseye[Dockerfile für Postgres xxx]

==== ConfigMaps

* Key-Value-Maps zur Konfiguration dieser Images

k8s/postgres.yaml
[source,yaml]
----
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-setup

data:
  setup.sql: |
    DROP database if exists demo;
    DROP user if exists demo;
    CREATE USER demo WITH
      LOGIN
      NOSUPERUSER
      NOCREATEDB
      NOCREATEROLE
      INHERIT
      NOREPLICATION
      CONNECTION LIMIT -1
      PASSWORD 'demo';

    CREATE DATABASE demo
        WITH
        OWNER = demo
        ENCODING = 'UTF8'
        CONNECTION LIMIT = -1;
  allow-all.sh: |
    echo "allow all hosts..."
    echo "host all  all    0.0.0.0/0  md5" >> /var/lib/postgresql/data/pg_hba.conf
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgresql-data

  annotations:
    nfs.io/storage-path: "postgresql-data"
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: standard
  resources:
    requests:
      storage: 100Mi
---
apiVersion: v1
kind: Secret
metadata:
  name: postgres-admin

type: kubernetes.io/basic-auth
stringData:
  username: demo
  password: demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres

  labels:
    component: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      component: postgres
  template:
    metadata:
      labels:
        component: postgres
    spec:
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      containers:
        - name: postgres
          image: postgres:14
          ports:
            - containerPort: 5432
              protocol: TCP
              name: postgres
          readinessProbe:
            tcpSocket:
              port: 5432
            initialDelaySeconds: 20
            periodSeconds: 30
          volumeMounts:
            - name: postgres-data
              mountPath: /var/lib/postgresql/data
            - name: setup-scripts
              mountPath: /docker-entrypoint-initdb.d/setup.sql
              subPath: setup.sql
              readOnly: true
            - name: allowall
              mountPath: /docker-entrypoint-initdb.d/allow-all.sh
              subPath: allow-all.sh
              readOnly: true
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-admin
                  key: password
      volumes:
        - name: postgres-data
          persistentVolumeClaim:
            claimName: postgresql-data
        - name: setup-scripts
          configMap:
            name: postgres-setup
            items:
              - key: setup.sql
                path: setup.sql
        - name: allowall
          configMap:
            name: postgres-setup
            items:
              - key: allow-all.sh
                path: allow-all.sh
---
apiVersion: v1
kind: Service
metadata:
  name: postgres

spec:
  ports:
    - port: 5432
      targetPort: 5432
      protocol: TCP
  selector:
    component: postgres
----

----
kubectl apply -f postgres.yaml
----

Um auf die DB zuzugreifen, verwendet man port-forwarding

* Schauen, wie der Pod heißt

----
kubectl get pods
----

----
NAME                        READY   STATUS    RESTARTS   AGE
postgres-5468d5c66c-78lcv   1/1     Running   0          12m
----


----
kubectl port-forward postgres-5468d5c66c-78lcv 5432:5432
----

* Das Terminal verliert Fokus

* Neues Terminal öffnen und kontrollieren:

----
netstat -ant | grep 5432
----

image::datasource-mit-k8s.png[]


image::port-forwarding-in-intellij.png[]

[source,properties]
----
# datasource configuration
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = demo
quarkus.datasource.password = demo
%dev.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/demo
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://postgres:5432/db

# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation=drop-and-create

quarkus.hibernate-orm.log.sql=true
%dev.quarkus.hibernate-orm.sql-load-script=db/import.sql
----


* für entwickeln verwenden wir immer "latest" und "imagePullPolicy: Always"
* erst in der Produktion wird eine Version vergeben und die imagePullPolicy wird entfernt


=== Docker-Image in die ghcr.io pushen

==== Secret erstellen

* github.com - Settings - Developer Settings - Personal Access Tokens - Tokens (classic)

image::create-secret.png[]

* Erstelltes Token in Editor kopieren

==== Mit Docker CLI in ghcr.io einloggen

----
docker login ghcr.io
----

.output
----
Username: htl-leonding
Password: <token einfügen>
Login Succeeded
----

==== Docker Image taggen

----
docker image tag busybox ghcr.io/htl-leonding/my-busybox:latest
----

==== Docker Image ins ghcr.io pushen

----
docker push ghcr.io/htl-leonding/my-busybox:latest
----

.output
----
The push refers to repository [ghcr.io/htl-leonding/my-busybox]
5f5f687a05d8: Pushed
latest: digest: sha256:afebab8e3d8cbef70c0632b5a7aa5c003f253d4f4f1ca47fe6b094ef7fe0cd07 size: 528
----


==== Kontrollieren, ob Image in ghcr.io

----
https://github.com/htl-leonding?tab=packages
----

IMPORTANT: Package public setzen

==== Docker Image lokal löschen

----
docker image rm ghcr.io/htl-leonding/my-busybox
----

.output
----
Untagged: ghcr.io/htl-leonding/my-busybox:latest
Untagged: ghcr.io/htl-leonding/my-busybox@sha256:afebab8e3d8cbef70c0632b5a7aa5c003f253d4f4f1ca47fe6b094ef7fe0cd07
----


==== Docker aus ghcr.io pullen (herunterladen)

----
docker pull ghcr.io/htl-leonding/my-busybox:latest
----

.output
----
latest: Pulling from htl-leonding/my-busybox
814c8b675ca3: Pull complete
Digest: sha256:afebab8e3d8cbef70c0632b5a7aa5c003f253d4f4f1ca47fe6b094ef7fe0cd07
Status: Downloaded newer image for ghcr.io/htl-leonding/my-busybox:latest
ghcr.io/htl-leonding/my-busybox:latest
----




=== Deployment eines vorhandenen Images ins Minikube

* Wir brauchen ein yaml-File

.postgres.yaml
[%collapsible]
====
[source,yaml]
----
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-setup

data:
  setup.sql: |
    DROP database if exists demo;
    DROP user if exists demo;
    CREATE USER demo WITH
      LOGIN
      NOSUPERUSER
      NOCREATEDB
      NOCREATEROLE
      INHERIT
      NOREPLICATION
      CONNECTION LIMIT -1
      PASSWORD 'demo';

    CREATE DATABASE demo
        WITH
        OWNER = demo
        ENCODING = 'UTF8'
        CONNECTION LIMIT = -1;
  allow-all.sh: |
    echo "allow all hosts..."
    echo "host all  all    0.0.0.0/0  md5" >> /var/lib/postgresql/data/pg_hba.conf
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgresql-data

  annotations:
    nfs.io/storage-path: "postgresql-data"
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: standard
  resources:
    requests:
      storage: 100Mi
---
apiVersion: v1
kind: Secret
metadata:
  name: postgres-admin

type: kubernetes.io/basic-auth
stringData:
  username: demo
  password: demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres

  labels:
    component: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      component: postgres
  template:
    metadata:
      labels:
        component: postgres
    spec:
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      containers:
        - name: postgres
          image: postgres:14
          ports:
            - containerPort: 5432
              protocol: TCP
              name: postgres
          readinessProbe:
            tcpSocket:
              port: 5432
            initialDelaySeconds: 20
            periodSeconds: 30
          volumeMounts:
            - name: postgres-data
              mountPath: /var/lib/postgresql/data
            - name: setup-scripts
              mountPath: /docker-entrypoint-initdb.d/setup.sql
              subPath: setup.sql
              readOnly: true
            - name: allowall
              mountPath: /docker-entrypoint-initdb.d/allow-all.sh
              subPath: allow-all.sh
              readOnly: true
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-admin
                  key: password
      volumes:
        - name: postgres-data
          persistentVolumeClaim:
            claimName: postgresql-data
        - name: setup-scripts
          configMap:
            name: postgres-setup
            items:
              - key: setup.sql
                path: setup.sql
        - name: allowall
          configMap:
            name: postgres-setup
            items:
              - key: allow-all.sh
                path: allow-all.sh
---
apiVersion: v1
kind: Service
metadata:
  name: postgres

spec:
  ports:
    - port: 5432
      targetPort: 5432
      protocol: TCP
  selector:
    component: postgres
----
====

----
cd k8s
kubectl apply -f postgres.yaml
----

* kontrollieren im minikube dashboard, ob erfolgreich

----
minikube dashboard
----


=== Deployment einer Quarkus - App ins Minikube

* Kompilieren und in die Docker Registry (ghcr.io) pushen

----
./mvnw clean package -DskipTests -Dquarkus.container-image.push=true
----

.output
----
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Pushed container image ghcr.io/htl-leonding/vehicle (sha256:90f8f348139fb81830d0eef70b8aad9cf8da545f14ed4dd6b191ce0511713116)

[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 11371ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  13.944 s
[INFO] Finished at: 2023-03-17T08:25:35+01:00
[INFO] ------------------------------------------------------------------------
----

* Das Package in den Package Setting public setzen

.appsrv.yaml erstellen
[%collapsible]
====
[source,yaml]
----
# Quarkus Application Server
apiVersion: apps/v1
kind: Deployment
metadata:
  name: appsrv

spec:
  replicas: 1
  selector:
    matchLabels:
      app: appsrv
  template:
    metadata:
      labels:
        app: appsrv
    spec:
      containers:
        - name: appsrv
          image: ghcr.io/htl-leonding/vehicle:latest
          # remove this when stable. Currently we do not take care of version numbers
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
          #startupProbe:
          #  httpGet:
          #    path: /api/q/health
          #    port: 8080
          #  timeoutSeconds: 5
          #  initialDelaySeconds: 15
          #readinessProbe:
          #  tcpSocket:
          #    port: 8080
          #  initialDelaySeconds: 5
          #  periodSeconds: 10
          #livenessProbe:
          #  httpGet:
          #    path: /api/q/health
          #    port: 8080
          #  timeoutSeconds: 5
          #  initialDelaySeconds: 60
          #  periodSeconds: 120
---
apiVersion: v1
kind: Service
metadata:
  name: appsrv

spec:
  ports:
    - port: 8080
      targetPort: 8080
      protocol: TCP
  selector:
    app: appsrv
----
====

----
kubectl apply -f appsrv.yaml
----

==== Port Forwarding
----
kubectl get pods
----

----
NAME                        READY   STATUS    RESTARTS   AGE
appsrv-8545fd6488-qckbw     1/1     Running   0          19m
postgres-5468d5c66c-cxq7n   1/1     Running   0          34m
----

.den korrekten pod übernehmen (ev. kubectl port-for tab tab)
----
kubectl port-forward postgres-5468d5c66c-cxq7n 5432:5432
----

* das Terminal verliert den Fokus, es läuft der Prozess zum Port-Forwarding



* Alternative: Verwendung des Shell-Scripts port-forward.sh


=== Hinzufügen von Health zur Quarkus App

----
./mvnw quarkus:add-extension -Dextensions='smallrye-health'
----

----
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-smallrye-health</artifactId>
    </dependency>
----

IMPORTANT: Zuerst lokal testen, dann in die Cloud deployen, dh wir verwenden die DB vom Minikube (port-forward nicht vergessen) und starten die quarkus app lokal


----
./mvnw clean quarkus:dev
----

=== Neuerliches Deployment

* Zuerst neu kompilieren

* Deployment lösschen (besser wäre ein sanfter Übergang mit rollout)

----
kubectl delete -f appsrv.yaml
kubectl apply -f appsrv.yaml
----

=== Periodisches Überprüfen der Healthiness (Liveness Probe)

* Einkommentieren im yaml File

=== Installieren eines Reverse-Proxy

* Auch hier verwenden wir ein bestehendes nginx-image
** Das könnte zum Problem beim Deployment in eine kommerzielle Cloud werden, da ein ReadWriteMany VolumeClaim verwendet wird
** dies kann teuer werden

----
kubectl apply -f nginx.yaml
----

* Port-Forward 4200:80 im nginx-pod

* http://localhost:4200

==== Zugriff auf Quarkus über nginx

----
http://localhost:4200/api/q/health
http://localhost:4200/api/vehicles
----


==== Eröffen einer shell am nginx

----
kubectl get pods
----

----
NAME                        READY   STATUS    RESTARTS   AGE
appsrv-5f65b54df-w6rtx      1/1     Running   0          27m
nginx-f88cd74d5-bgcqx       1/1     Running   0          6m36s
postgres-5468d5c66c-cxq7n   1/1     Running   0          101m
----

----
 kubectl exec -it nginx-f88cd74d5-bgcqx -- bash
----

* Aus Security Gründen wurde der Volume Mount schreibgeschützt

* Daher spielen wir einen Busybox-Job ein

----
kubectl apply -f busybox-job.yaml
----

image::busybox-als-pod-und-job.png[]


* http://localhost:4200/


image::busy-box-web-page.png[]


=== Creating a Linux executable without GraalVM installed

----
./mvnw install -Dnative -DskipTests -Dquarkus.native.container-build=true
----


=== Skalieren der Anzahl der Pods

* in den yaml-Files des appsrv und nginx den Wert der `replicas` auf zB 3 setzen
* im dashboard unter "Replica Sets" kontrollieren


=== Erstellen eines Ingress im minikube

* https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/



== Links

* https://academy.oracle.com/en/oa-web-overview.html
* https://k3s.io/

