Hello Cloud Computing! Nginx build by packer is up and running!
-
-
\ No newline at end of file
+
+
+
Hello Cloud Computing! Nginx build by packer is up and running!
+
+
diff --git a/03-provisionierung/loesung/uebung-packer/nginx.pkr.hcl b/03-provisionierung/loesung/uebung-packer/nginx.pkr.hcl
new file mode 100644
index 00000000..d510dfdc
--- /dev/null
+++ b/03-provisionierung/loesung/uebung-packer/nginx.pkr.hcl
@@ -0,0 +1,29 @@
+packer {
+ required_plugins {
+ docker = {
+ source = "github.com/hashicorp/docker"
+ version = "1.0.9"
+ }
+ }
+}
+
+source "docker" "nginx" {
+ changes = ["EXPOSE 80", "CMD [\"nginx\", \"-g\", \"daemon off;\"]"]
+ commit = true
+ image = "nginx:1.25.4-alpine3.18"
+ run_command = ["-d", "-t", "-i", "{{ .Image }}", "/bin/sh"]
+}
+
+build {
+ sources = ["source.docker.nginx"]
+
+ provisioner "file" {
+ destination = "/usr/share/nginx/html/"
+ source = "./index.html"
+ }
+
+ post-processor "docker-tag" {
+ repository = "packer-nginx"
+ tag = "1.0"
+ }
+}
diff --git a/03-provisionierung/loesung/uebung-packer/nginx.json b/03-provisionierung/loesung/uebung-packer/nginx.pkr.json
similarity index 92%
rename from 03-provisionierung/loesung/uebung-packer/nginx.json
rename to 03-provisionierung/loesung/uebung-packer/nginx.pkr.json
index 5a41fc41..3685534c 100644
--- a/03-provisionierung/loesung/uebung-packer/nginx.json
+++ b/03-provisionierung/loesung/uebung-packer/nginx.pkr.json
@@ -2,7 +2,7 @@
"builders": [
{
"type": "docker",
- "image": "nginx:1.19-alpine",
+ "image": "nginx:1.25.4-alpine3.18",
"commit": true,
"run_command": [ "-d", "-t", "-i", "{{.Image}}", "/bin/sh" ],
"changes": [
@@ -27,4 +27,4 @@
}
]
]
-}
\ No newline at end of file
+}
diff --git a/03-provisionierung/uebung/README.md b/03-provisionierung/uebung/README.md
index 2a58c417..347222da 100644
--- a/03-provisionierung/uebung/README.md
+++ b/03-provisionierung/uebung/README.md
@@ -1,401 +1,418 @@
# Übung: Provisionierung mit Ansible und Docker Compose
## Übung 1: Provisionierung mit Docker und Docker Compose
+
Ziel dieser Übung ist, praktische Erfahrungen mit Docker und Docker Compose zu sammeln.
Wir wollen in dieser Übung:
+
- ein fertiges Docker Image nutzen
- ein Docker Image bauen
- ein Image über Docker Compose starten
### Schritt 1: Docker Image nutzen und Container starten
-Nginx ist ein Open Source Reverse Proxy.
-Unter https://hub.docker.com/_/nginx ist dokumentiert, wie das offizielle Docker Image
+
+NGINX ist ein Open Source Reverse Proxy.
+Unter ist dokumentiert, wie das offizielle Docker Image
genutzt werden kann.
-Schauen Sie sich die Doku an und starten Sie über die Console einen Nginx Container.
+Schauen Sie sich die Doku an und starten Sie über die Console einen NGINX Container.
Sorgen Sie dafür, dass der Port '80' auch auf Ihrem Host exponiert wird.
Hinweis, falls Sie nicht weiterkommen:
+``` shell
+docker run --rm -it -p 80:80 nginx
```
-docker run -d -p 80:80 nginx
-```
+
-Rufen Sie dann testweise localhost:80 in Ihrem Browser auf.
+Rufen Sie dann testweise in Ihrem Browser auf.
### Schritt 2: Dockerfile schreiben und eigene index.html anlegen
+
In diesem Schritt wollen wir nicht mehr das fertige Image nutzen, sondern ein eigenes
angepasstes Image bauen.
-Legen Sie hierfür zunächst eine index.html an. Diese Seite soll unser Webserver ausliefern, wenn er aufgerufen wird.
-Nutzen Sie hierfür ein einfaches HTML, dass eine "Hello World" Begrüßung ausgibt.
+Legen Sie hierfür zunächst eine index.html an.
+Diese Seite soll unser Webserver ausliefern, wenn er aufgerufen wird.
+Nutzen Sie hierfür ein einfaches HTML, dass eine 'Hello World' Begrüßung ausgibt.
Legen Sie ein Dockerfile an.
Führen Sie folgende Schritte aus:
-- Nutzen Sie nginx:1.19-alpine als Basisimage.
-- Kopieren Sie ihre 'index.html' nach '/usr/share/nginx/html/',
-indem Sie das Docker Command 'COPY' nutzen.
-- Exponieren Sie den Port 80.
-- Starten Sie nginx über das Command 'nginx'. Achten Sie darauf, -g 'daemon off;'
-beim Start mit anzugeben, damit Nginx nicht direkt nach dem Containerstart beendet wird,
-sondern im Vordergrund läuft.
+
+- Nutzen Sie 'nginx:1.25.4-alpine3.18' als Basisimage.
+- Kopieren Sie ihre 'index.html' nach '/usr/share/nginx/html/', indem Sie das Docker Command 'COPY' nutzen.
+- Exponieren Sie den Port '80'.
+- Starten Sie NGINX über das Command 'nginx'.
+ Achten Sie darauf, -g 'daemon off;' beim Start mit anzugeben, damit NGINX nicht direkt nach dem Containerstart beendet wird, sondern im Vordergrund läuft.
Hinweis, falls Sie nicht weiterkommen:
-```
-FROM nginx:1.19-alpine
+``` dockerfile
+FROM nginx:1.25.4-alpine3.18
COPY index.html /usr/share/nginx/html
EXPOSE 80
CMD nginx -g 'daemon off;'
```
+
### Schritt 3: Docker Image bauen
+
Bauen Sie das Docker Image mit dem Namen cc-nginx und dem Tag v1.
Hinweis, falls Sie nicht weiterkommen:
-```docker build . -t cc-nginx:v1```
+``` shell
+docker build -t cc-nginx:v1 .
+```
+
-Prüfen Sie danach über ```docker images```, dass das neue Docker Image in Ihrer
-lokalen Registry liegt.
+Prüfen Sie danach über `docker images`, dass das neue Docker Image in Ihrer lokalen Registry liegt.
### Schritt 4: Container starten
+
Starten Sie den Container.
-Mappen Sie dabei den Port 8080 auf Ihrem Host auf den Container Port 80.
+Mappen Sie dabei den Host-Port '8080' auf den Container-Port '80'.
Hinweis, falls Sie nicht weiterkommen:
-```docker run -it -p 8080:80 cc-nginx:v1```
+``` shell
+docker run --rm -it -p 8080:80 cc-nginx:v1
+```
+
-Rufen Sie den gestarteten nginx Container unter localhost:8080 auf.
+Rufen Sie den gestarteten NGINX Container unter auf.
### Schritt 5: Container über Docker Compose starten
-Wir nutzen jetzt unser Docker Image und starten dieses über Docker Compose.
-Legen Sie hierfür Datei docker-compose.yml an, das einen nginx Instanz startet und dabei:
+Wir nutzen jetzt unser Docker-Image und starten dieses über Docker Compose.
-- den Service Namen "cc-nginx" hat
+Legen Sie hierfür die Datei 'docker-compose.yml' an, die eine NGINX Instanz startet und dabei:
+
+- den Service Namen 'cc-nginx' hat
- das Image auf Basis des in Schritt 1 erstellen Dockerfiles baut
-- den Port 80 exportiert und unter 8080 auf Ihrem Host erreichbar macht
-
+- den Port '80' exportiert und unter '8080' auf Ihrem Host erreichbar macht
+
Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
+``` yaml
+services:
+ cc-nginx:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ ports:
+ - "8080:80"
```
-cc-nginx:
- build:
- context: .
- dockerfile: Dockerfile
- ports:
- - "8080:80"
-```
+
### Bonus/Optional:
+
Probieren Sie weitere Docker und Docker Compose Commands aus.
-- Skalieren Sie den nginx auf 3 Instanzen
+
+- Skalieren Sie den NGINX auf 3 Instanzen
- Öffnen Sie eine Shell im laufenden Container
-- Schauen Sie in die nginx Logs
+- Editieren sie die index.html im Container
+- Schauen Sie in die NGINX Logs
- ...
-
## Übung 2: Provisionierung mit Ansible
+
Ziel dieser Übung ist, praktische Erfahrungen mit Ansible zu sammeln und Docker und Docker Compose
noch besser kennenzulernen.
-Um das lokale Setup möglichst einfach zu halten, werden wir sowohl die Ansible Control Node als auch
+Um das lokale Setup möglichst einfach zu halten, werden wir sowohl die Ansible Control Node als auch
die Maschinen, die wir provisionieren wollen, per Docker Compose lokal hochfahren.
Wir wollen:
+
- Die Ansible Control Node per Docker Compose starten
- 3 Managed Nodes per Docker Compose starten
-- ein Playbook erstellen, dass auf allen 3 Managed Nodes einen Apache Http Server installiert,
-konfiguriert und startet
+- ein Playbook erstellen, dass auf allen 3 Managed Nodes einen Apache Http Server installiert, konfiguriert und startet
- das Playbook ausführen und die gestarteten Apache Http Server testweise aufrufen
-### Hinweis:
-Falls Sie nicht weiterkommen, können Sie sich an der Musterlösung orientieren.
-
-Nutzen Sie auch die folgenden Referenzen:
-- Docker Compose Syntax: https://docs.docker.com/compose/compose-file/
-- Ansible Documentation: https://docs.ansible.com/ansible/latest/index.html
+> [!TIP]
+> Falls Sie nicht weiterkommen, können Sie sich an der Musterlösung orientieren.
+>
+> Nutzen Sie auch die folgenden Referenzen:
+>
+> - Docker Compose Syntax:
+> - Ansible Documentation:
### Schritt 1: Image für Managed Nodes bauen
-Erstellen Sie ein Dockerfile für die zu provisionierenden Maschinen / Managed Nodes.
-Diese sollen Ubuntu in Version 20.10 beinhalten und SSH Verbindungen von außen erlauben:
-- Legen Sie dafür parallel zu dieser Readme eine Datei mit dem Namen "Dockerfile_Managed_Node" an.
-- Übernehmen Sie die Inhalte des hier angegebenen Dockerfiles:
-https://docs.docker.com/engine/examples/running_ssh_service/
-- Ersetzen Sie den Platzhalter 'THEPASSWORDYOUCREATED' durch ein geeignetes Passwort.
-Wir wählen für die Übung das Passwort 'verysecretpassword'.
-
-#### Bonus/Optional:
+
+Erstellen Sie ein Dockerfile für die zu provisionierenden Maschinen / Managed Nodes.
+Diese sollen Ubuntu in Version 24.04 beinhalten und SSH-Verbindungen von außen erlauben:
+
+- Legen Sie dafür parallel zu dieser Readme eine Datei mit dem Namen 'Dockerfile_Managed_Node' an.
+- Schreiben sie ein Dockerfile, dass die folgenden Shell-Befehle ausführt um den SSH-Server zu installieren:
+
+ ``` shell
+ apt-get update && apt-get install -y openssh-server
+ mkdir /var/run/sshd
+ echo 'root:verysecretpassword' | chpasswd
+ sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
+ ```
+
+- Mit dem folgenden Befehl wird der ssh Server dann gestartet:
+
+ ``` shell
+ /usr/sbin/sshd -D
+ ```
+
+#### Bonus/Optional:
+
Erstellen Sie alternativ selbst ein Docker Image, das SSH Verbindungen von außen erlaubt:
+
- Wählen Sie ein Ubuntu Basisimage.
-- Sorgen Sie dafür, dass eine SSH-Verbindung zum Container aufgebaut werden kann (per SSH Daemon),
-und hinterlegen Sie hierfür die Credentials Username="root" und Passwort="verysecretpassword".
+- Sorgen Sie dafür, dass eine SSH-Verbindung zum Container aufgebaut werden kann (per SSH Daemon), und hinterlegen Sie hierfür die Credentials Username='root' und Passwort='verysecretpassword'.
### Schritt 2: Managed Node und Ansible Control Node über Docker Compose gemeinsam starten
-Erstellen Sie ein Docker Compose File (Datei mit dem Namen "docker-compose.yml" parallel zu dieser Readme), das
+
+Erstellen Sie ein Docker Compose File (Datei mit dem Namen 'docker-compose.yml' parallel zu dieser Readme), das
+
- Eine Managed Node startet und dabei:
- - den Service Namen "managed-node" hat
- - das Image auf Basis des in Schritt 1 erstellen Dockerfiles baut
- - den Port 80 exportiert
-
-
- Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
-
- ```
+ - den Service Namen 'managed-node' hat
+ - das Image auf Basis des in Schritt 1 erstellen Dockerfiles baut
+ - den Port '80' exportiert
+
+
+ Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
+
+ ``` yaml
managed-node:
build:
context: .
dockerfile: Dockerfile_Managed_Node
+ image: "cc-managed:latest"
ports:
- "80"
- ```
-
+ ```
+
+
+
- Eine Ansible Control Node startet und dabei:
- - den Service Namen "ansible-node" hat
- - das fertige Image "willhallonline/ansible:2.9-alpine" nutzt. In diesem ist Ansible in Version 2.9 mit
- Python3 verfügbar.
- - ein Memory Limit von 100 MB hat
- - erst gestartet wird, wenn die Managed Node läuft
-
-
- Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
-
- ```
+ - den Service Namen 'ansible-node' hat
+ - das fertige Image 'willhallonline/ansible:2.16.4-alpine-3.18' nutzt.
+ In diesem ist Ansible in Version 2.16 mit Python3 verfügbar (siehe ).
+ - erst gestartet wird, wenn die Managed Node läuft
+
+
+
+ Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
+
+ ``` yaml
ansible-node:
- image: "willhallonline/ansible:2.9-alpine"
- networks:
- - cloudcomputing
+ image: "willhallonline/ansible:2.16.4-alpine-3.18"
depends_on:
- managed-node
- ```
-
-- Geben Sie im Docker Compose File ein Netzwerk vom Typ "bridge" an und sorgen Sie dafür, dass die Managed
-Node und die Ansible Control Node im gleichen Netzwerk laufen. Denken Sie daran, das im Docker Compose File
-für jeden der Services mittels "networks" anzugeben.
-
-### Schritt 3: Strict Host Key Checking deaktivieren
-Deaktivieren Sie Strict Host Key Checking beim ersten Verbindungsaufbau zwischen der Control Node und
-der Managed Node, indem Sie
-- einen Ordner "ssh" anlegen
-- darin eine Datei mit dem Namen "config" und folgende Inhalt ablegen:
```
- Host *
- StrictHostKeyChecking no
- ```
-- diese Datei über Docker Compose für die Ansible Control Node unter /root/.ssh/config mounten.
-
- Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
-
- ```
- volumes:
- - "./ssh/config:/root/.ssh/config"
- ```
-### Schritt 4: Setup testen
+- Geben Sie im Docker Compose File ein Netzwerk vom Typ 'bridge' an und sorgen Sie dafür, dass die Managed Node und die Ansible Control Node im gleichen Netzwerk laufen.
+ Denken Sie daran, das im Docker Compose File für jeden der Services mittels 'networks' anzugeben.
+
+### Schritt 3: Setup testen
+
Starten Sie die Managed Node über
-```
+
+``` shell
docker-compose up --build -d
```
+
Starten Sie eine Shell auf der Ansible Control Node über
-```
+
+``` shell
docker-compose run ansible-node /bin/sh
-```
+```
Stellen Sie eine SSH Verbindung zur Managed Node über folgendes Kommando her:
-```
+
+``` shell
ssh managed-node
```
+
Geben Sie das Passwort 'verysecretpassword' ein, wenn Sie danach gefragt werden.
-Sie können die SSH-Verbindung mit ```exit``` wieder beenden.
+Sie können die SSH-Verbindung mit `exit` wieder beenden.
-Sie können sowohl über den Service Namen 'managed-node' als auch mit vorangestelltem Präfix 'uebung' (der Name
-des Ordners) und Suffix '1' eine SSH Verbindung herstellen. Verifizieren Sie das über
-```
-ssh uebung_managed-node_1
+Sie können sowohl über den Service Namen 'managed-node' als auch mit vorangestelltem Präfix 'uebung' (der Name des Ordners) und Suffix '1' eine SSH Verbindung herstellen.
+
+Verifizieren Sie das über
+
+``` shell
+ssh uebung-managed-node-1
```
-### Schritt 5: Erste Schritte mit Ansible
+### Schritt 4: Erste Schritte mit Ansible
Konfigurieren Sie die Maschinen, die Sie mit Ansible provisionieren wollen, über die Hosts Datei.
-Legen Sie hierzu einen Ordner "ansible" und darin die Datei "hosts" an.
+Legen Sie hierzu einen Ordner 'ansible' und darin die Datei 'hosts' an.
+
+Legen Sie die Group 'server_hosts' an, tragen Sie darin die Managed Node ein, und konfigurieren Sie Ansible:
-Legen Sie die Group "server_hosts" an, tragen Sie darin die Managed Node ein, und konfigurieren Sie Ansible:
- Geben Sie python3 als Python Interpreter an
-- Hinterlegen Sie Username und Passwort für die SSH Verbindung
+- Hinterlegen Sie für die SSH-Verbindung Username und Passwort und fügen sie das ssh-Argument '-o StrictHostKeyChecking=no' hinzu
-
- Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
+
+ Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
- ```
- [server_hosts]
- uebung_managed-node_1
+ ``` ini
+ [server_hosts]
+ uebung-managed-node-1
- [server_hosts:vars]
- ansible_python_interpreter=/usr/bin/python3
- ansible_ssh_user=root
- ansible_ssh_pass=verysecretpassword
- ```
+ [server_hosts:vars]
+ ansible_python_interpreter=/usr/bin/python3
+ ansible_ssh_user=root
+ ansible_ssh_pass=verysecretpassword
+ ansible_ssh_common_args='-o StrictHostKeyChecking=no'
+ ```
-
+
Mounten Sie diese Datei über Docker Compose für den Service ansible-node unter /etc/ansible/hosts:
-```
- volumes:
- [...]
- - "./ansible/hosts:/etc/ansible/hosts"
+``` yaml
+volumes:
+ - "./ansible/hosts:/etc/ansible/hosts"
```
Starten Sie die Ansible Node neu über
-```
+
+``` shell
docker-compose run ansible-node /bin/sh
-```
+```
Führen Sie darin folgende Kommandos aus
-- Version ausgeben: ```ansible --version```
-- Alle Managed Nodes pingen: ```ansible all -m ping```
-- Remote Command auf allen Managed Nodes ausführen: ```ansible all -m command -a "echo hello"```
+
+- Version ausgeben: `ansible --version`
+- Alle Managed Nodes pingen: `ansible all -m ping`
+- Remote Command auf allen Managed Nodes ausführen: `ansible all -m command -a "echo hello"`
### Schritt 6: Apache Http Server per Ansible Playbook installieren und konfigurieren
Legen Sie den Ordner 'playbooks' an und darin eine Datei mit dem Namen 'install-apache2.yml'.
-Machen Sie sich über https://docs.ansible.com/ansible/latest/index.html mit der Ansible Syntax vertraut.
+Machen Sie sich über mit der Ansible Syntax vertraut.
Tragen Sie die Hosts 'server_hosts' und den Remote User 'root' ein.
Mounten Sie das Playbook über Docker Compose in die Ansible Control Node unter '/root/playbooks'.
-
- Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
+
+Wenn Sie nicht weiterkommen, können Sie folgenden Codeblock verwenden:
- ```
- volumes:
- [...]]
- - "./playbooks:/root/playbooks"
- ```
+``` yaml
+volumes:
+ - "./playbooks:/root/playbooks"
+```
-
+
Führen Sie im Playbook die folgenden Tasks aus:
-- Testen Sie, ob Sie zur Managed Node eine Verbindung aufbauen können. Verwenden Sie dafür das Ansible Module 'ping'
- (https://docs.ansible.com/ansible/latest/modules/ping_module.html#stq=copy%20module&stp=1).
-- Installieren Sie über das Ansible Module 'apt' apache2 in der Version 'latest' (https://docs.ansible.com/ansible/latest/modules/apt_module.html).
+- Testen Sie, ob Sie zur Managed Node eine Verbindung aufbauen können.
+ Verwenden Sie dafür das Ansible Module 'ping' ().
+- Installieren Sie über das Ansible Module 'apt' apache2 in der Version 'latest' ().
- Legen Sie eine eigene index.html an, die der Apache ausliefern soll, und kopieren Sie diese auf die Managed Node unter '/var/www/html/index.html'.
-Verwenden Sie hierzu das Ansible Module 'copy' (https://docs.ansible.com/ansible/latest/modules/copy_module.html).
-- Stellen Sie über das Ansible Module 'service' sicher, dass der Apache Http Server gestartet wurde (https://docs.ansible.com/ansible/latest/modules/service_module.html).
+ Verwenden Sie hierzu das Ansible Module 'copy' ().
+- Stellen Sie über das Ansible Module 'service' sicher, dass der Apache Http Server gestartet wurde ().
Führen Sie das Playbook auf der Ansible Control Node aus:
-```
+``` shell
ansible-playbook /root/playbooks/install-apache.yml
```
### Schritt 7: Aufruf des gestarteten Webservers
-Finden Sie über
-
-```
-docker ps
-```
-heraus, unter welchem Port der gestartete Webserver auf Ihrem Host erreichbar ist.
+Finden Sie über `docker ps` heraus, unter welchem Port der gestartete Webserver auf Ihrem Host erreichbar ist.
Welcher Port leitet Requests an den exponierten Port '80' des Containers weiter?
-Rufen Sie in Ihrem Browser localhost: für den spezifischen Port auf und verifizieren Sie, dass Ihre index.html
-angezeigt wird.
+Rufen Sie in Ihrem Browser für den spezifischen Port auf und verifizieren Sie, dass Ihre index.html angezeigt wird.
### Schritt 8: Skalieren der Managed Nodes
-Skalieren Sie die Managed Nodes auf 3.
-Nutzen Sie hierfür 'docker-compose scale'.
+Skalieren Sie die Managed Nodes auf 3.
+Nutzen Sie hierfür das docker-compose Kommando 'up' mit der Option '--scale' (siehe ).
-Ändern Sie die Datei 'hosts' so ab, dass alle 3 Managed Nodes provisioniert werden können und führen Sie die
-Provisionierung aus.
+Ändern Sie die Datei 'hosts' so ab, dass alle 3 Managed Nodes provisioniert werden können und führen Sie die Provisionierung aus.
### Troubleshooting
+
Stellen Sie sicher, dass Sie über Docker den Zugriff auf die gemounteten Dateien erlauben.
-## Übung 3: Packer
+## Übung 3: Packer (Optional)
### Schritt 1: Packer installieren
-Installieren Sie Packer, indem Sie die Anleitung auf https://learn.hashicorp.com/tutorials/packer/getting-started-install
-befolgen.
+
+Installieren Sie Packer, indem Sie die Anleitung auf befolgen.
### Schritt 2: Packer kennenlernen
-Machen Sie sich mit Packer vertraut, indem Sie sich die Beispiele auf https://learn.hashicorp.com/tutorials/packer/getting-started-build-image?in=packer/getting-started
-anschauen.
+
+Machen Sie sich mit Packer vertraut, indem Sie sich die Beispiele auf anschauen.
Optional: bauen Sie eins der Beispiele lokal mit Packer.
### Schritt 3: Docker Image mit Packer bauen
-Lesen Sie die Doku zum Bauen von Docker Images mit Packer: https://www.packer.io/docs/builders/docker
+
+Lesen Sie die Doku zum Bauen von Docker Images mit Packer:
Legen Sie ein Packer Template an.
-Verwenden Sie den Docker Builder von Packer, um ein Nginx Image mit
+Verwenden Sie den Docker Builder von Packer, um ein NGINX Image mit.
einer eigenen Welcome Seite zu bauen.
-Verwenden Sie als Basisimage "nginx:1.19-alpine".
+Verwenden Sie als Basisimage 'nginx:1.25.4-alpine3.18'.
-Exponieren Sie Port 80 im Image und führen Sie das CMD "nginx -g daemon off;"
-zum Start von Nginx aus.
+Exponieren Sie Port '80' im Image und führen Sie das CMD 'nginx -g daemon off;' zum Start von NGINX aus.
-Da wir ein Alpine Image verwenden, in dem per Default ```Bash```
-nicht installiert ist, nutzen Sie das folgende ```run_command```
-von Packer, damit später beim Container-Start ```/bin/sh``` anstelle von
-```/bin/bash``` verwendet wird:
-
-```"run_command": [ "-d", "-t", "-i", "{{.Image}}", "/bin/sh" ] ```
+Da wir ein Alpine-Image verwenden, in dem per Default Bash nicht installiert ist, nutzen Sie das folgende 'run_command' von Packer, damit später beim Container-Start `/bin/sh` anstelle von `/bin/bash` verwendet wird:
-Nutzen Sie den File Provisioner von Packer, um eine lokal angelegte index.html
-nach /usr/share/nginx/html/ im Image zu kopieren.
+``` json
+"run_command": [ "-d", "-t", "-i", "{{.Image}}", "/bin/sh" ]
+```
+
+Nutzen Sie den File Provisioner von Packer, um eine lokal angelegte index.html nach /usr/share/nginx/html/ im Image zu kopieren.
-Nutzen Sie den Post Processor "docker-tag" von Packer, um dem Image den
-Namen "packer-nginx" und den Tag "1.0" zu geben.
+Nutzen Sie den Post Processor 'docker-tag' von Packer, um dem Image den Namen 'packer-nginx' und den Tag '1.0' zu geben.
-Führen Sie
-```packer build ``` aus.
+Führen Sie `packer build ` aus.
-Prüfen Sie über ```docker images```, ob das Docker Image in Ihrer lokalen
-Registry verfügbar ist.
+Prüfen Sie über `docker images`, ob das Docker Image in Ihrer lokalen Registry verfügbar ist.
-Starten Sie dann den Container mit ```docker```.
-Mappen Sie dabei den Containerport 80 auf den Host Port 8080.
+Starten Sie dann den Container mit `docker`.
+Mappen Sie dabei den Container-Port '80' auf den Host-Port '8080'.
Hinweis, falls Sie nicht weiterkommen:
+``` shell
+docker run --rm -it -p 8080:80 packer-nginx:1.0
```
-docker run -d -p 8080:80 packer-nginx:1.0
-```
+
-Rufen Sie im Browser ```localhost:8080``` auf und verifizieren Sie,
-dass Ihre Welcome Seite angezeigt wird.
+Rufen Sie im Browser auf und verifizieren Sie, dass Ihre Welcome Seite angezeigt wird.
### Bonus/Optional 1:
-Verwenden Sie anstelle des Nginx Images ein Alpine oder Centos Image.
-Installieren Sie Nginx per shell Provisioner.
+
+Verwenden Sie anstelle des NGINX Images ein Debian oder Alpine Image.
+Installieren Sie NGINX per shell Provisioner.
### Bonus/Optional 2: Ansible Provisionierung mit Packer ausführen
-Nutzen Sie Ansible zur Provisionierung mit Packer.
+Nutzen Sie Ansible zur Provisionierung mit Packer..
Verwenden Sie dafür das Playbook aus Übung 2 und führen Sie dieses mit dem
-Ansible Provisioner von Packer aus.
+Ansible Provisioner von Packer aus..
Nutzen Sie hierzu die Packer Doku und recherchieren Sie Beispiele.
diff --git a/03-provisionierung/vorlesung/m03-provisionierung.pdf b/03-provisionierung/vorlesung/m03-provisionierung.pdf
index a558caf6..c0d716cd 100644
Binary files a/03-provisionierung/vorlesung/m03-provisionierung.pdf and b/03-provisionierung/vorlesung/m03-provisionierung.pdf differ
diff --git a/07-cloud-architektur/beispiele/README.md b/04-cloud-architektur/beispiele/README.md
similarity index 100%
rename from 07-cloud-architektur/beispiele/README.md
rename to 04-cloud-architektur/beispiele/README.md
diff --git a/07-cloud-architektur/beispiele/loesung/fabio.properties b/04-cloud-architektur/beispiele/loesung/fabio.properties
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/fabio.properties
rename to 04-cloud-architektur/beispiele/loesung/fabio.properties
diff --git a/07-cloud-architektur/beispiele/loesung/pom.xml b/04-cloud-architektur/beispiele/loesung/pom.xml
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/pom.xml
rename to 04-cloud-architektur/beispiele/loesung/pom.xml
diff --git a/07-cloud-architektur/beispiele/loesung/src/infrastructure/k8s/consul-rc.yaml b/04-cloud-architektur/beispiele/loesung/src/infrastructure/k8s/consul-rc.yaml
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/infrastructure/k8s/consul-rc.yaml
rename to 04-cloud-architektur/beispiele/loesung/src/infrastructure/k8s/consul-rc.yaml
diff --git a/07-cloud-architektur/beispiele/loesung/src/infrastructure/k8s/consul-svc.yaml b/04-cloud-architektur/beispiele/loesung/src/infrastructure/k8s/consul-svc.yaml
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/infrastructure/k8s/consul-svc.yaml
rename to 04-cloud-architektur/beispiele/loesung/src/infrastructure/k8s/consul-svc.yaml
diff --git a/07-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/api/entities/ZwitscherMessage.java b/04-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/api/entities/ZwitscherMessage.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/api/entities/ZwitscherMessage.java
rename to 04-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/api/entities/ZwitscherMessage.java
diff --git a/07-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/api/resources/ZwitscherMessageResource.java b/04-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/api/resources/ZwitscherMessageResource.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/api/resources/ZwitscherMessageResource.java
rename to 04-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/api/resources/ZwitscherMessageResource.java
diff --git a/07-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherApplication.java b/04-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherApplication.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherApplication.java
rename to 04-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherApplication.java
diff --git a/07-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherConfiguration.java b/04-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherConfiguration.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherConfiguration.java
rename to 04-cloud-architektur/beispiele/loesung/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherConfiguration.java
diff --git a/07-cloud-architektur/beispiele/loesung/src/main/resources/banner.txt b/04-cloud-architektur/beispiele/loesung/src/main/resources/banner.txt
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/main/resources/banner.txt
rename to 04-cloud-architektur/beispiele/loesung/src/main/resources/banner.txt
diff --git a/07-cloud-architektur/beispiele/loesung/src/main/resources/zwitscher-config.yml b/04-cloud-architektur/beispiele/loesung/src/main/resources/zwitscher-config.yml
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/main/resources/zwitscher-config.yml
rename to 04-cloud-architektur/beispiele/loesung/src/main/resources/zwitscher-config.yml
diff --git a/07-cloud-architektur/beispiele/loesung/src/test/java/edu/qaware/cc/zwitscher/TestApi.java b/04-cloud-architektur/beispiele/loesung/src/test/java/edu/qaware/cc/zwitscher/TestApi.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/test/java/edu/qaware/cc/zwitscher/TestApi.java
rename to 04-cloud-architektur/beispiele/loesung/src/test/java/edu/qaware/cc/zwitscher/TestApi.java
diff --git a/07-cloud-architektur/beispiele/loesung/src/test/java/edu/qaware/cc/zwitscher/TestConsul.java b/04-cloud-architektur/beispiele/loesung/src/test/java/edu/qaware/cc/zwitscher/TestConsul.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/loesung/src/test/java/edu/qaware/cc/zwitscher/TestConsul.java
rename to 04-cloud-architektur/beispiele/loesung/src/test/java/edu/qaware/cc/zwitscher/TestConsul.java
diff --git a/07-cloud-architektur/beispiele/vorlage/pom.xml b/04-cloud-architektur/beispiele/vorlage/pom.xml
similarity index 100%
rename from 07-cloud-architektur/beispiele/vorlage/pom.xml
rename to 04-cloud-architektur/beispiele/vorlage/pom.xml
diff --git a/07-cloud-architektur/beispiele/vorlage/src/main/infrastructure/k8s/consul-rc.yaml b/04-cloud-architektur/beispiele/vorlage/src/main/infrastructure/k8s/consul-rc.yaml
similarity index 100%
rename from 07-cloud-architektur/beispiele/vorlage/src/main/infrastructure/k8s/consul-rc.yaml
rename to 04-cloud-architektur/beispiele/vorlage/src/main/infrastructure/k8s/consul-rc.yaml
diff --git a/07-cloud-architektur/beispiele/vorlage/src/main/infrastructure/k8s/consul-svc.yaml b/04-cloud-architektur/beispiele/vorlage/src/main/infrastructure/k8s/consul-svc.yaml
similarity index 100%
rename from 07-cloud-architektur/beispiele/vorlage/src/main/infrastructure/k8s/consul-svc.yaml
rename to 04-cloud-architektur/beispiele/vorlage/src/main/infrastructure/k8s/consul-svc.yaml
diff --git a/07-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/api/entities/ZwitscherMessage.java b/04-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/api/entities/ZwitscherMessage.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/api/entities/ZwitscherMessage.java
rename to 04-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/api/entities/ZwitscherMessage.java
diff --git a/07-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/api/resources/ZwitscherMessageResource.java b/04-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/api/resources/ZwitscherMessageResource.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/api/resources/ZwitscherMessageResource.java
rename to 04-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/api/resources/ZwitscherMessageResource.java
diff --git a/07-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherApplication.java b/04-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherApplication.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherApplication.java
rename to 04-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherApplication.java
diff --git a/07-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherConfiguration.java b/04-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherConfiguration.java
similarity index 100%
rename from 07-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherConfiguration.java
rename to 04-cloud-architektur/beispiele/vorlage/src/main/java/edu/qaware/cc/zwitscher/core/ZwitscherConfiguration.java
diff --git a/07-cloud-architektur/beispiele/vorlage/src/main/resources/banner.txt b/04-cloud-architektur/beispiele/vorlage/src/main/resources/banner.txt
similarity index 100%
rename from 07-cloud-architektur/beispiele/vorlage/src/main/resources/banner.txt
rename to 04-cloud-architektur/beispiele/vorlage/src/main/resources/banner.txt
diff --git a/07-cloud-architektur/beispiele/vorlage/src/main/resources/zwitscher-config.yml b/04-cloud-architektur/beispiele/vorlage/src/main/resources/zwitscher-config.yml
similarity index 100%
rename from 07-cloud-architektur/beispiele/vorlage/src/main/resources/zwitscher-config.yml
rename to 04-cloud-architektur/beispiele/vorlage/src/main/resources/zwitscher-config.yml
diff --git a/04-cloud-architektur/etcd/README.md b/04-cloud-architektur/etcd/README.md
new file mode 100644
index 00000000..e46afb5d
--- /dev/null
+++ b/04-cloud-architektur/etcd/README.md
@@ -0,0 +1,55 @@
+# Übung: etcd
+
+Dokumentation `ectdctl`: https://etcd.io/docs/v3.5/dev-guide/
+
+1. Installieren Sie die aktuelle Version von `etcdctl` auf Ihrem System: https://github.com/etcd-io/etcd/releases
+2. Starten Sie das etcd-Cluster mit `docker compose up -d`.
+3. Sehen Sie sich das etcd-Cluster an:
+
+```shell
+$ docker ps
+CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
+7ff698f8fcbb bitnami/etcd:3.5 "/opt/bitnami/etcd/b…" 37 minutes ago Up 37 minutes 2380/tcp, 0.0.0.0:32823->2379/tcp, :::32823->2379/tcp etcd-etcd-2-1
+781cc8e9a056 bitnami/etcd:3.5 "/opt/bitnami/etcd/b…" 37 minutes ago Up 37 minutes 2380/tcp, 0.0.0.0:32824->2379/tcp, :::32824->2379/tcp etcd-etcd-1-1
+f40bcfb1920b bitnami/etcd:3.5 "/opt/bitnami/etcd/b…" 37 minutes ago Up 37 minutes 2380/tcp, 0.0.0.0:32822->2379/tcp, :::32822->2379/tcp etcd-etcd-3-1
+```
+
+4. Prüfen Sie beispielhaft für den ersten Node seinen Status:
+
+```shell
+$ etcdctl --endpoints=localhost:23791 endpoint status
+localhost:23791, 2848abbfff24c332, 3.5.10, 20 kB, true, false, 3, 12, 12,
+```
+
+5. Setzen Sie einige Werte in den ersten etcd-Node. Beispiel:
+
+```shell
+$ etcdctl --endpoints=localhost:23791 put vorlesung "Cloud Computing WS 2023/24"
+OK
+```
+
+6. Abbonieren Sie an einem der Nodes Änderungen auf einen Key. Was passiert jetzt, wenn Sie den Wert an einem anderen Node ändern? Können Sie das nutzen, um die komplette Versionsgeschichte zu einem Key zu sehen?
+
+```shell
+$ etcdctl --endpoints=localhost:23792 watch vorlesung
+```
+
+7. Fragen Sie Ihre Werte am selben Node ab. Was passiert, wenn Sie die Anfrage an einen anderen Node stellen?
+
+```shell
+$ etcdctl --endpoints=localhost:23791 get vorlesung
+...
+```
+
+8. Überschreiben Sie die Werte mehrfach mit neuen Werten. Welcher Wert steht jetzt als Konsens im Cluster? Können Sie sich auch die vorigen Werte ansehen?
+9. Stoppen Sie einen der etcd-Nodes und wiederholen Sie put und get mit neuen Werten. Was passiert, nachdem Sie den Node wieder starten?
+
+```shell
+$ docker compose stop etcd-1
+
+...
+
+$ docker compose start etcd-1
+```
+
+10. Was passiert, wenn Sie zwei von drei Nodes stoppen?
diff --git a/04-cloud-architektur/etcd/docker-compose.yaml b/04-cloud-architektur/etcd/docker-compose.yaml
new file mode 100644
index 00000000..0c804a83
--- /dev/null
+++ b/04-cloud-architektur/etcd/docker-compose.yaml
@@ -0,0 +1,104 @@
+x-common:
+ initial_cluster_token: &initial_cluster_token '--initial-cluster-token=ANeOBQH5kUz94Cbn'
+ etcd-deployment: &etcd-deployment
+ image: bitnami/etcd:3.5
+ entrypoint: /opt/bitnami/etcd/bin/etcd
+
+version: '3.8'
+services:
+ change-vol-ownership:
+ image: ubuntu
+ user: "root"
+ volumes:
+ - etcd1:/tmp/etcd1
+ - etcd2:/tmp/etcd2
+ - etcd3:/tmp/etcd3
+ command: chown -R 1001:root /tmp/etcd1 /tmp/etcd2 /tmp/etcd3 && chmod -R 700 /tmp/etcd1 /tmp/etcd2 /tmp/etcd3
+
+ etcd-1:
+ <<: *etcd-deployment
+ command:
+ - '--name=etcd-1'
+ - '--initial-advertise-peer-urls=http://etcd-1:2380'
+ - '--listen-peer-urls=http://0.0.0.0:2380'
+ - '--listen-client-urls=http://0.0.0.0:2379'
+ - '--advertise-client-urls=http://etcd-1:2379'
+ - '--heartbeat-interval=250'
+ - '--election-timeout=1250'
+ - '--initial-cluster=etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380,etcd-3=http://etcd-3:2380'
+ - '--initial-cluster-state=new'
+ - '--data-dir=/etcd/data'
+ - '--wal-dir=/etcd/wal'
+ - *initial_cluster_token
+ ports:
+ - "23791:2379"
+ volumes:
+# - "etcd1_data:/etcd/data"
+# - "etcd1_wal:/etcd/wal"
+ - "etcd1:/etcd"
+ depends_on:
+ change-vol-ownership:
+ condition: service_completed_successfully
+
+ etcd-2:
+ <<: *etcd-deployment
+ command:
+ - '--name=etcd-2'
+ - '--initial-advertise-peer-urls=http://etcd-2:2380'
+ - '--listen-peer-urls=http://0.0.0.0:2380'
+ - '--listen-client-urls=http://0.0.0.0:2379'
+ - '--advertise-client-urls=http://etcd-2:2379'
+ - '--heartbeat-interval=250'
+ - '--election-timeout=1250'
+ - '--initial-cluster=etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380,etcd-3=http://etcd-3:2380'
+ - '--initial-cluster-state=new'
+ - '--data-dir=/etcd/data'
+ - '--wal-dir=/etcd/wal'
+ - *initial_cluster_token
+ ports:
+ - "23792:2379"
+ volumes:
+# - "etcd2_data:/etcd/data"
+# - "etcd2_wal:/etcd/wal"
+ - "etcd2:/etcd"
+ depends_on:
+ change-vol-ownership:
+ # Wait for the ownership to change
+ condition: service_completed_successfully
+
+ etcd-3:
+ <<: *etcd-deployment
+ command:
+ - '--name=etcd-3'
+ - '--initial-advertise-peer-urls=http://etcd-3:2380'
+ - '--listen-peer-urls=http://0.0.0.0:2380'
+ - '--listen-client-urls=http://0.0.0.0:2379'
+ - '--advertise-client-urls=http://etcd-3:2379'
+ - '--heartbeat-interval=250'
+ - '--election-timeout=1250'
+ - '--initial-cluster=etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380,etcd-3=http://etcd-3:2380'
+ - '--initial-cluster-state=new'
+ - '--data-dir=/etcd/data'
+ - '--wal-dir=/etcd/wal'
+ - *initial_cluster_token
+ ports:
+ - "23793:2379"
+ volumes:
+# - "etcd3_data:/etcd/data"
+# - "etcd3_wal:/etcd/wal"
+ - "etcd3:/etcd"
+ depends_on:
+ change-vol-ownership:
+ # Wait for the ownership to change
+ condition: service_completed_successfully
+
+volumes:
+# etcd1_data:
+# etcd1_wal:
+ etcd1:
+# etcd2_data:
+# etcd2_wal:
+ etcd2:
+# etcd3_data:
+# etcd3_wal:
+ etcd3:
diff --git a/07-cloud-architektur/uebung-optional/book-service/.gitignore b/04-cloud-architektur/loesung/book-service/.gitignore
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/.gitignore
rename to 04-cloud-architektur/loesung/book-service/.gitignore
diff --git a/07-cloud-architektur/uebung-optional/book-service/.mvn/wrapper/maven-wrapper.jar b/04-cloud-architektur/loesung/book-service/.mvn/wrapper/maven-wrapper.jar
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/.mvn/wrapper/maven-wrapper.jar
rename to 04-cloud-architektur/loesung/book-service/.mvn/wrapper/maven-wrapper.jar
diff --git a/07-cloud-architektur/uebung-optional/book-service/.mvn/wrapper/maven-wrapper.properties b/04-cloud-architektur/loesung/book-service/.mvn/wrapper/maven-wrapper.properties
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/.mvn/wrapper/maven-wrapper.properties
rename to 04-cloud-architektur/loesung/book-service/.mvn/wrapper/maven-wrapper.properties
diff --git a/07-cloud-architektur/uebung-optional/book-service/Dockerfile b/04-cloud-architektur/loesung/book-service/Dockerfile
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/Dockerfile
rename to 04-cloud-architektur/loesung/book-service/Dockerfile
diff --git a/07-cloud-architektur/uebung-optional/book-service/mvnw b/04-cloud-architektur/loesung/book-service/mvnw
old mode 100644
new mode 100755
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/mvnw
rename to 04-cloud-architektur/loesung/book-service/mvnw
diff --git a/07-cloud-architektur/uebung-optional/book-service/mvnw.cmd b/04-cloud-architektur/loesung/book-service/mvnw.cmd
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/mvnw.cmd
rename to 04-cloud-architektur/loesung/book-service/mvnw.cmd
diff --git a/07-cloud-architektur/uebung-optional/book-service/pom.xml b/04-cloud-architektur/loesung/book-service/pom.xml
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/pom.xml
rename to 04-cloud-architektur/loesung/book-service/pom.xml
diff --git a/07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/Book.java b/04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Book.java
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/Book.java
rename to 04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Book.java
diff --git a/07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookController.java b/04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookController.java
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookController.java
rename to 04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookController.java
diff --git a/07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookNotFoundException.java b/04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookNotFoundException.java
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookNotFoundException.java
rename to 04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookNotFoundException.java
diff --git a/07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookServiceApplication.java b/04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookServiceApplication.java
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookServiceApplication.java
rename to 04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookServiceApplication.java
diff --git a/07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/Bookshelf.java b/04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Bookshelf.java
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/src/main/java/de/qaware/edu/cc/bookservice/Bookshelf.java
rename to 04-cloud-architektur/loesung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Bookshelf.java
diff --git a/07-cloud-architektur/uebung-optional/book-service/src/main/resources/application.properties b/04-cloud-architektur/loesung/book-service/src/main/resources/application.properties
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/src/main/resources/application.properties
rename to 04-cloud-architektur/loesung/book-service/src/main/resources/application.properties
diff --git a/07-cloud-architektur/uebung-optional/book-service/src/main/resources/bootstrap.properties b/04-cloud-architektur/loesung/book-service/src/main/resources/bootstrap.properties
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/src/main/resources/bootstrap.properties
rename to 04-cloud-architektur/loesung/book-service/src/main/resources/bootstrap.properties
diff --git a/07-cloud-architektur/uebung-optional/book-service/src/test/java/de/qaware/edu/cc/bookservice/BookServiceApplicationTests.java b/04-cloud-architektur/loesung/book-service/src/test/java/de/qaware/edu/cc/bookservice/BookServiceApplicationTests.java
similarity index 100%
rename from 07-cloud-architektur/uebung-optional/book-service/src/test/java/de/qaware/edu/cc/bookservice/BookServiceApplicationTests.java
rename to 04-cloud-architektur/loesung/book-service/src/test/java/de/qaware/edu/cc/bookservice/BookServiceApplicationTests.java
diff --git a/07-cloud-architektur/uebung-optional/docker-compose.yml b/04-cloud-architektur/loesung/docker-compose.yml
similarity index 74%
rename from 07-cloud-architektur/uebung-optional/docker-compose.yml
rename to 04-cloud-architektur/loesung/docker-compose.yml
index 59fcc192..415826fb 100644
--- a/07-cloud-architektur/uebung-optional/docker-compose.yml
+++ b/04-cloud-architektur/loesung/docker-compose.yml
@@ -1,7 +1,7 @@
-version: '2'
+version: '3'
services:
consul:
- image: consul
+ image: consul:1.14.1
command: consul agent -server -dev -client=0.0.0.0 -ui -bootstrap -log-level warn
ports:
- "8400:8400"
@@ -11,11 +11,11 @@ services:
- cloud-architecture
reverse-proxy:
- image: reverse-proxy
- command: traefik --consulcatalog.endpoint=consul:8500
+ image: traefik:v2.9
+ command: --providers.consulcatalog.endpoint.address="consul:8500" --api.insecure=true
ports:
- - "8080:8080"
- - "8081:80"
+ - 80:80
+ - 8080:8080
depends_on:
- consul
links:
diff --git a/04-cloud-architektur/uebung/README.md b/04-cloud-architektur/uebung/README.md
new file mode 100644
index 00000000..9c22ab9d
--- /dev/null
+++ b/04-cloud-architektur/uebung/README.md
@@ -0,0 +1,243 @@
+# Übung: Cloud Architektur
+
+## Aufgabe 1: Twelve Factor Apps
+
+Die [Twelve Factor Apps](https://12factor.net/) beschreiben Methoden bzw. Empfehlungen zur Entwicklung von
+Cloud-Anwendungen.
+
+Recherchieren Sie in Ihrer Gruppe die angegebenen Faktoren. Sie können dafür diese
+[Slides](https://www.slideshare.net/Alicanakku1/12-factor-apps)
+nutzen oder frei recherchieren.
+
+Bereiten Sie gemeinsam jeweils einen kurzen Foliensatz vor (1 Slide je Factor), in dem Sie
+* die Idee hinter dem jeweiligen Factor benennen
+* die Empfehlung erläutern
+
+Bearbeiten Sie in den Gruppen die folgenden Punkte:
+
+* Gruppe 1:
+ * Codebase
+ * Dependencies
+ * Configuration
+* Gruppe 2:
+ * Backing Services
+ * Build, release, run
+ * Processes
+* Gruppe 3:
+ * Port binding
+ * Concurrency
+ * Disposability
+* Gruppe 4:
+ * Dev/Prod Parity
+ * Logs
+ * Admin Processes
+
+Finden Sie einen Vertreter Ihrer Gruppe, der die Vorstellung übernimmt.
+
+## Aufgabe 2: Das Raft-Konsens-Protokoll
+
+Erarbeiten Sie die Funktionsweise vom Raft Protokoll mithilfe folgender
+[Demo](http://thesecretlivesofdata.com/raft/).
+
+## Aufgabe 3: etcd
+
+Siehe Verzeichnis "etcd".
+
+## Aufgabe 4: Erste Erfahrungen mit Traefik und Consul sammeln
+
+Ziel dieser Übung ist es, erste praktische Erfahrungen mit Traefik und Consul zu machen.
+Dabei wird ein einfacher Spring Cloud REST Service zusammen mit Consul
+für Service Discovery und Configuration und Traeffik als Edge Server aufgesetzt.
+
+Diese Übung orientiert sich an diesem [Tutorial](https://m.mattmclaugh.com/traefik-and-consul-catalog-example-2c33fc1480c0).
+
+## Vorbereitung
+
+* Das Aufsetzen eines Spring Cloud Microservice ist in Übung 1 beschrieben. Die Lösung dieser
+ Übung dient als Startpunkt und wird in dieser Übung erweitert.
+
+## Aufgaben
+
+### Testen sie den vorgefertigten Container
+
+Zuerst müssen sie den Book-Service im Unterverzeichnis `book-service` bauen. Benutzen Sie dazu den Befehl:
+```shell
+./mvnw package
+```
+
+Nun können Sie den Container mit dem Docker-Compose File in diesem Verzeichnis bauen und starten:
+```shell
+docker-compose up --build
+```
+Testen Sie, dass der Book-Service korrekt gestartet wird.
+Nach dem Start sollten sie den Book-Service direkt auf Port `18080` erreichbar sein.
+Sie können dies z.B. mit
+```
+curl http://localhost:18080/api/books
+```
+überprüfen.
+
+### Consul Cluster (Single Node) mit Docker Compose
+
+Erweitern Sie das Docker Compose File um einen Consul Service (im Single Node Betrieb)
+Verwenden Sie das dafür aktuellste offizielle Docker Image von Hashicorp.: [hub.docker.com/_/consul](https://hub.docker.com/_/consul)
+
+Stellen Sie sicher, dass die Consul UI gestartet wird, und alle benötigen Ports exponiert werden.
+
+
+Orientieren Sie sich an folgendem Abschnitt, falls Sie nicht weiterkommen:
+
+```
+ consul:
+ image: hashicorp/consul
+ command: consul agent -server -dev -client=0.0.0.0 -ui -bootstrap -log-level warn
+ ports:
+ - "8400:8400"
+ - "8500:8500"
+ - "8600:53/udp"
+ networks:
+ - cloud-architecture
+```
+
+
+Starten sie den Consul Service. Sie sollten nun mit die UI mit [http://localhost:8500/ui/](http://localhost:8500/ui/) aufrufen können.
+
+### Spring Cloud Microservice
+
+Erweitern Sie den book-service so, dass sich dieser
+
+* beim Start bei der Consul Service Discovery anmeldet,
+* beim Start seine Konfigurationswerte bei Consul abholt,
+* die Service-Schnittstellen (nicht die Admin Schnittstellen) über Traefik aufgerufen werden können
+
+Die folgenden Dependencies müssen der `pom.xml` hinzugefügt werden:
+
+```xml
+
+
+ org.springframework.cloud
+ spring-cloud-starter-consul-config
+
+
+ org.springframework.cloud
+ spring-cloud-starter-consul-discovery
+
+```
+
+Danach muss unter `src/main/resources` die Datei `bootstrap.properties` angelegt werden:
+
+```properties
+spring.application.name=book-service
+
+# specify Consul host and port
+# we use the CONSUL_HOST and CONSUL_PORT env variables
+# later set in docker compose as well as Kubernetes
+spring.cloud.consul.host=${consul.host:consul}
+spring.cloud.consul.port=${consul.port:8500}
+
+spring.cloud.consul.config.enabled=true
+spring.cloud.consul.config.prefix=configuration
+spring.cloud.consul.config.default-context=application
+
+# do not fail at startup if Consul is not there
+spring.cloud.consul.config.fail-fast=false
+
+# store properties as blob in property syntax
+# e.g. configuration/book-service/data
+spring.cloud.consul.config.format=properties
+spring.cloud.consul.config.data-key=data
+```
+
+Im gleichen Verzeichnis müssen sie noch die Datei `application.properties` erweitern:
+```properties
+# assign a unique instance ID
+spring.cloud.consul.discovery.instance-id=${spring.application.name}:${spring.application.instance_id:${random.value}}
+
+# required by Docker compose and Consul to run the health check
+# register IP address and heartbeats
+spring.cloud.consul.discovery.prefer-ip-address=true
+spring.cloud.consul.discovery.heartbeat.enabled=true
+```
+
+
+Falls Sie nicht weiterkommen, verwenden Sie folgenden Abschnitt:
+```yaml
+ book-service:
+ build: ./book-service
+ image: book-service:1.1.0
+ ports:
+ - 18080:18080
+ depends_on:
+ - consul
+ networks:
+ - cloud-architecture
+ environment:
+ - SPRING_CLOUD_CONSUL_HOST=consul
+```
+
+Zuletzt müssen sie dafür sorgen, dass der book-service nach Consul gestartet wird.
+Erweitern sie dazu die book-service docker-compose Konfiguration um den folgenden Block:
+```yaml
+ depends_on:
+ - consul
+
+```
+
+### Traefik Edge Service mit Consul Backend
+
+Betreiben Sie den Traefik Edge Service mittels Docker Compose und verwenden Sie
+Consul als Discovery Backend für Traefik.
+
+Benutzen sie dazu das offizielle Traefik Image: [https://hub.docker.com/_/traefik](https://hub.docker.com/_/traefik)
+* Konfigurieren sie die API (= Traefik Dashboard) mit dem CLI Parameter `--api.insecure=true`.
+* Konfigurieren sie Consul als Catalog, siehe dazu: [https://doc.traefik.io/traefik/providers/consul-catalog/](https://doc.traefik.io/traefik/providers/consul-catalog/)
+* Exponieren Sie folgenden Ports: `80`, `8080` (für die API)
+
+Starten Sie den Container ebenfalls über Docker Compose und übergeben Sie beim Start die nötigen Konfigurationen zur
+Interaktion mit Consul.
+
+
+Falls Sie nicht weiterkommen, verwenden Sie folgenden Abschnitt:
+
+```yaml
+ reverse-proxy:
+ image: traefik
+ command: --providers.consulcatalog.endpoint.address="consul:8500" --api.insecure=true
+ ports:
+ - 80:80
+ - 8080:8080
+ depends_on:
+ - consul
+ links:
+ - consul
+ networks:
+ - cloud-architecture
+```
+
+
+Starten Sie die Services erneut. Wenn alles geklappt hat, sollten Sie nun den Book-Service in der Traefik-UI sehen.
+Sie sollten nun den Book-Service unter Angabe eines Host-Headers aufrufen können:
+```shell
+curl -H 'Host: book-service' 'http://localhost/api/books'
+```
+
+### Umstellung auf Path-based Routing
+
+In der `application.properties` muss folgende Property angelegt werden:
+
+```properties
+spring.cloud.consul.discovery.tags=traefik.http.routers.book-service.rule=Host(`book-service`) || PathPrefix(`/book-service`),traefik.http.middlewares.book-service-stripprefix.stripprefix.prefixes=/book-service,traefik.http.routers.book-service.middlewares=book-service-stripprefix
+```
+
+Starten Sie die Services erneut. Wenn alles geklappt hat, sollten sie nun eine zusätzliche Middleware Traefik-UI sehen.
+Sie sollten den Book-Service nun auch mit einem Pfad-Präfix aufrufen können:
+```shell
+curl 'http://localhost/book-service/api/books'
+```
+
+### Testen
+
+* Die Anwendung sollte nun mit Header `Host: book-service` unter `http://localhost/api/books` erreichbar sein.
+* Die Anwendung sollte nun unter `http://localhost/book-service/api/books` erreichbar sein.
+* Die Consul UI sollte unter `http://localhost:8500/ui` erreichbar sein.
+* Die Traefik UI sollte unter `http://localhost:8080` erreichbar sein.
diff --git a/04-cloud-architektur/uebung/book-service/.dockerignore b/04-cloud-architektur/uebung/book-service/.dockerignore
new file mode 100644
index 00000000..2af7cefb
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/.dockerignore
@@ -0,0 +1,24 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
\ No newline at end of file
diff --git a/04-cloud-architektur/uebung/book-service/.gitignore b/04-cloud-architektur/uebung/book-service/.gitignore
new file mode 100644
index 00000000..2af7cefb
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/.gitignore
@@ -0,0 +1,24 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
\ No newline at end of file
diff --git a/04-cloud-architektur/uebung/book-service/.mvn/wrapper/maven-wrapper.jar b/04-cloud-architektur/uebung/book-service/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 00000000..9cc84ea9
Binary files /dev/null and b/04-cloud-architektur/uebung/book-service/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/10-big-data/uebung/loesung/.mvn/wrapper/maven-wrapper.properties b/04-cloud-architektur/uebung/book-service/.mvn/wrapper/maven-wrapper.properties
similarity index 100%
rename from 10-big-data/uebung/loesung/.mvn/wrapper/maven-wrapper.properties
rename to 04-cloud-architektur/uebung/book-service/.mvn/wrapper/maven-wrapper.properties
diff --git a/04-cloud-architektur/uebung/book-service/Dockerfile b/04-cloud-architektur/uebung/book-service/Dockerfile
new file mode 100644
index 00000000..f485712c
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/Dockerfile
@@ -0,0 +1,11 @@
+FROM maven:3.5-jdk-8-alpine as build
+
+COPY . /src
+RUN cd /src && mvn package
+
+FROM openjdk:8u151-jre-slim
+
+COPY --from=build /src/target/book-service-*.jar /app/book-service.jar
+RUN chmod +x /app/book-service.jar
+
+ENTRYPOINT ["/app/book-service.jar"]
diff --git a/10-big-data/uebung/loesung/mvnw b/04-cloud-architektur/uebung/book-service/mvnw
old mode 100644
new mode 100755
similarity index 100%
rename from 10-big-data/uebung/loesung/mvnw
rename to 04-cloud-architektur/uebung/book-service/mvnw
diff --git a/10-big-data/uebung/loesung/mvnw.cmd b/04-cloud-architektur/uebung/book-service/mvnw.cmd
similarity index 100%
rename from 10-big-data/uebung/loesung/mvnw.cmd
rename to 04-cloud-architektur/uebung/book-service/mvnw.cmd
diff --git a/04-cloud-architektur/uebung/book-service/pom.xml b/04-cloud-architektur/uebung/book-service/pom.xml
new file mode 100644
index 00000000..11c67d7d
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/pom.xml
@@ -0,0 +1,91 @@
+
+
+ 4.0.0
+
+ de.qaware.edu.cc
+ book-service
+ 1.1.0
+ jar
+
+ book-service
+ Demo project for Spring Boot
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.5.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ Greenwich.SR1
+ 2.9.2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ io.springfox
+ springfox-swagger2
+ ${springfox.version}
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ ${springfox.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+ repackage
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.1
+
+
+
+
+
+
diff --git a/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Book.java b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Book.java
new file mode 100644
index 00000000..0fb11ed4
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Book.java
@@ -0,0 +1,78 @@
+package de.qaware.edu.cc.bookservice;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModel;
+
+import java.util.Objects;
+
+/**
+ * Simple book POJO.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@ApiModel("Book")
+public class Book {
+ private String title;
+ private String author;
+ private String isbn;
+
+ public Book() {
+ }
+
+ public Book(String title, String author, String isbn) {
+ this.title = title;
+ this.author = author;
+ this.isbn = isbn;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ public String getIsbn() {
+ return isbn;
+ }
+
+ public void setIsbn(String isbn) {
+ this.isbn = isbn;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Book book = (Book) o;
+ return Objects.equals(title, book.title) &&
+ Objects.equals(author, book.author) &&
+ Objects.equals(isbn, book.isbn);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(title, author, isbn);
+ }
+
+ @Override
+ public String toString() {
+ return "Book{" +
+ "title='" + title + '\'' +
+ ", author='" + author + '\'' +
+ ", isbn='" + isbn + '\'' +
+ '}';
+ }
+}
diff --git a/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookController.java b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookController.java
new file mode 100644
index 00000000..1c4fdc6d
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookController.java
@@ -0,0 +1,97 @@
+package de.qaware.edu.cc.bookservice;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.net.URI;
+import java.util.Collection;
+
+@RestController
+@RequestMapping(value = "/api/books", produces = MediaType.APPLICATION_JSON_VALUE)
+public class BookController {
+ private final Bookshelf bookshelf;
+
+ @Autowired
+ public BookController(Bookshelf bookshelf) {
+ this.bookshelf = bookshelf;
+ }
+
+ @GetMapping
+ @ApiOperation(value = "Find books", response = Book.class, responseContainer = "List")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Found all books")
+ })
+ public Collection books(@ApiParam(value = "title to search") @RequestParam(value = "title", required = false) String title) {
+ return bookshelf.findByTitle(title);
+ }
+
+ @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Create book")
+ @ApiResponses(value = {
+ @ApiResponse(code = 201, message = "Created the book"),
+ @ApiResponse(code = 409, message = "Book already exists")
+ })
+ public ResponseEntity create(@RequestBody Book book) {
+ boolean created = bookshelf.create(book);
+ if (created) {
+ return ResponseEntity.created(URI.create("/api/books/" + book.getIsbn())).build();
+ } else {
+ return ResponseEntity.status(HttpStatus.CONFLICT).build();
+ }
+ }
+
+ @GetMapping(value = "/{isbn}")
+ @ApiOperation(value = "Find book by ISBN", response = Book.class)
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Found the book"),
+ @ApiResponse(code = 404, message = "Book not found")
+ })
+ public Book byIsbn(@ApiParam(value = "ISBN to search") @PathVariable("isbn") String isbn) {
+ return bookshelf.findByIsbn(isbn);
+ }
+
+ @PutMapping(value = "/{isbn}", consumes = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Update book by ISBN")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Updated the book")
+ })
+ @ResponseStatus(HttpStatus.OK)
+ public void update(
+ @ApiParam(value = "ISBN to search") @PathVariable("isbn") String isbn,
+ @RequestBody Book book
+ ) {
+ bookshelf.update(isbn, book);
+ }
+
+ @DeleteMapping("/{isbn}")
+ @ApiOperation(value = "Delete book by ISBN")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Book deleted")
+ })
+ @ResponseStatus(HttpStatus.OK)
+ public void delete(@ApiParam(value = "ISBN to delete") @PathVariable("isbn") String isbn) {
+ bookshelf.delete(isbn);
+ }
+
+ @ExceptionHandler(BookNotFoundException.class)
+ public ResponseEntity handleBookNotFoundException(BookNotFoundException exception) {
+ return ResponseEntity.notFound().build();
+ }
+}
diff --git a/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookNotFoundException.java b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookNotFoundException.java
new file mode 100644
index 00000000..2376bb29
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookNotFoundException.java
@@ -0,0 +1,15 @@
+package de.qaware.edu.cc.bookservice;
+
+/**
+ * Custom exception class in case a book was not found.
+ */
+public class BookNotFoundException extends RuntimeException {
+ /**
+ * Construct message with given ISBN.
+ *
+ * @param isbn the ISBN
+ */
+ public BookNotFoundException(String isbn) {
+ super("Book with ISBN " + isbn + " not found.");
+ }
+}
diff --git a/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookServiceApplication.java b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookServiceApplication.java
new file mode 100644
index 00000000..6d164ef4
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/BookServiceApplication.java
@@ -0,0 +1,17 @@
+package de.qaware.edu.cc.bookservice;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * The main Spring boot application class.
+ */
+@SpringBootApplication
+@EnableSwagger2
+public class BookServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(BookServiceApplication.class, args);
+ }
+}
diff --git a/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Bookshelf.java b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Bookshelf.java
new file mode 100644
index 00000000..fbe35a00
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/src/main/java/de/qaware/edu/cc/bookservice/Bookshelf.java
@@ -0,0 +1,95 @@
+package de.qaware.edu.cc.bookservice;
+
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Simple bookshelf component to hold and manage the Book entities.
+ */
+@Component
+public class Bookshelf {
+
+ private final Set books = new HashSet<>();
+
+ /**
+ * Initialize some test data.
+ */
+ @PostConstruct
+ public void initialize() {
+ books.add(new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", "0345391802"));
+ books.add(new Book("The Martian", "Andy Weir", "0553418025"));
+ books.add(new Book("Guards! Guards!", "Terry Pratchett", "0062225758"));
+ books.add(new Book("Alice in Wonderland", "Lewis Carroll", "3458317422"));
+ books.add(new Book("Life, the Universe and Everything", "Douglas Adams", "0345391829"));
+ }
+
+ /**
+ * Find books, all or by title.
+ *
+ * @param title a title to look for, may be NULL
+ * @return a collection of books
+ */
+ public Collection findByTitle(String title) {
+ if (Objects.isNull(title)) {
+ return books;
+ } else {
+ return books
+ .stream()
+ .filter((Book b) -> b.getTitle().equalsIgnoreCase(title))
+ .collect(Collectors.toList());
+ }
+ }
+
+ /**
+ * Find book by ISBN.
+ *
+ * @param isbn
+ * @return
+ */
+ public Book findByIsbn(String isbn) {
+ return books
+ .stream()
+ .filter((Book b) -> b.getIsbn().equals(isbn))
+ .findFirst()
+ .orElseThrow(() -> new BookNotFoundException(isbn));
+ }
+
+
+ /**
+ * Delete book by ISBN.
+ *
+ * @param isbn
+ */
+ public void delete(String isbn) {
+ books.removeIf(b -> b.getIsbn().equals(isbn));
+ }
+
+ /**
+ * Create book of not already present.
+ *
+ * @param book the book to create
+ * @return true of created, otherwise false
+ */
+ public boolean create(Book book) {
+ return books.add(book);
+ }
+
+ /**
+ * Find and update the book with given ISBN.
+ *
+ * @param isbn the ISBN to update
+ * @param book the updated book
+ */
+ public void update(String isbn, Book book) {
+ Book found = findByIsbn(isbn);
+
+ found.setTitle(book.getTitle());
+ found.setAuthor(book.getAuthor());
+ }
+}
diff --git a/04-cloud-architektur/uebung/book-service/src/main/resources/application.properties b/04-cloud-architektur/uebung/book-service/src/main/resources/application.properties
new file mode 100644
index 00000000..2e22c926
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/src/main/resources/application.properties
@@ -0,0 +1 @@
+server.port=18080
diff --git a/04-cloud-architektur/uebung/book-service/src/main/resources/bootstrap.properties b/04-cloud-architektur/uebung/book-service/src/main/resources/bootstrap.properties
new file mode 100644
index 00000000..51891ce4
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/src/main/resources/bootstrap.properties
@@ -0,0 +1 @@
+spring.application.name=book-service
diff --git a/04-cloud-architektur/uebung/book-service/src/test/java/de/qaware/edu/cc/bookservice/BookServiceApplicationTests.java b/04-cloud-architektur/uebung/book-service/src/test/java/de/qaware/edu/cc/bookservice/BookServiceApplicationTests.java
new file mode 100644
index 00000000..2baaa554
--- /dev/null
+++ b/04-cloud-architektur/uebung/book-service/src/test/java/de/qaware/edu/cc/bookservice/BookServiceApplicationTests.java
@@ -0,0 +1,16 @@
+package de.qaware.edu.cc.bookservice;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class BookServiceApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/04-cloud-architektur/uebung/docker-compose.yml b/04-cloud-architektur/uebung/docker-compose.yml
new file mode 100644
index 00000000..66513f6e
--- /dev/null
+++ b/04-cloud-architektur/uebung/docker-compose.yml
@@ -0,0 +1,14 @@
+version: '3'
+services:
+ book-service:
+ build: ./book-service
+ image: book-service:1.1.0
+ ports:
+ - 18080:18080
+ networks:
+ - cloud-architecture
+ environment:
+ - SPRING_CLOUD_CONSUL_HOST=consul
+
+networks:
+ cloud-architecture:
\ No newline at end of file
diff --git a/04-cloud-architektur/vorlesung/m04-cloud-architektur.pdf b/04-cloud-architektur/vorlesung/m04-cloud-architektur.pdf
new file mode 100644
index 00000000..03711e0d
Binary files /dev/null and b/04-cloud-architektur/vorlesung/m04-cloud-architektur.pdf differ
diff --git a/04-iaas/loesung/teil-2/.gitignore b/04-iaas/loesung/teil-2/.gitignore
deleted file mode 100644
index d38e10a2..00000000
--- a/04-iaas/loesung/teil-2/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.terraform/
-terraform.tfstate*
\ No newline at end of file
diff --git a/04-iaas/loesung/teil-2/README.md b/04-iaas/loesung/teil-2/README.md
deleted file mode 100644
index 5a299e49..00000000
--- a/04-iaas/loesung/teil-2/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Übung: Infrastructure as Code mit Terraform auf AWS
-
-Ziel dieser Übung ist das erlernen grundlegender Infrastructure as Code Fähigkeiten mit Terraform auf der AWS Cloud. Hierzu werden Sie die Architektur aus der letzten Übung mit Terrform nachbauen. Grundlegende Schritte sind hierfür schon vorbereitet.
-
-
-0. Starten Sie den `iaas-container` und mounten Sie das Verzeichnis `teil-2` in den `/root/teil-2` im Container. Beispiel mit Bash aus dem Verzeichnis:
- ```
- docker run -it --rm -w /root -e AWS_ACCESS_KEY_ID= -e AWS_SECRET_ACCESS_KEY= -e AWS_SESSION_TOKEN= iaas-container --mount type=bind,source="$(pwd)",target=/root/teil-2 iaas-container
- ```
- Initialisieren Sie dann im Verzeichnis `teil-2` Terraform mit `terraform init`.
-4. Schauen Sie sich die bereits existierenden Terraform Dateien an und machen Sie sich mit der grundlegenden Struktur vertraut.
-5. Implementieren Sie alle Stellen die mit `#TODO` annotiert sind. Definieren Sie sich selbst eine sinnvolle reihenfolge. In regelmäßigen Abständen sollten Sie ihre Implementierungsarbeiten auf die AWS Cloud anwenden mit `terraform apply`.
-6. Am Ende sollte die Terraform Konfiguration einen Output Parameter mit einer validen und funktionierenden URL enthalten.
-7. Erzeugen Sie einen neuen Workspace mit `terraform workspace new dev` und überprüfen Sie ob Sie hier mit `terraform apply` eine zweite Umgebung erzeugen können. Wenn nicht passen Sie ihre Konfigurationen so an, dass dies möglich ist. Machen Sie dafür insbesondere Benennungen von Ressourcen abhängig vom verwendeten Workspace.
-
diff --git a/04-iaas/loesung/teil-2/init.sh b/04-iaas/loesung/teil-2/init.sh
deleted file mode 100644
index f46fd827..00000000
--- a/04-iaas/loesung/teil-2/init.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-set -euxo pipefail
-
-apt-get update
-apt-get install -y busybox cowsay
-source /etc/environment
-
-echo "
" >> index.html
-
-nohup busybox httpd -f index.html -p 8080 &
diff --git a/04-iaas/loesung/teil-2/loadbalancer.tf b/04-iaas/loesung/teil-2/loadbalancer.tf
deleted file mode 100644
index fc96371f..00000000
--- a/04-iaas/loesung/teil-2/loadbalancer.tf
+++ /dev/null
@@ -1,74 +0,0 @@
-# TODO: Create a Security Group for the Load Balancer
-# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group
-resource "aws_security_group" "lb" {
- name = "${local.env}-lb"
- description = "Allow TLS inbound traffic"
- vpc_id = module.vpc.vpc_id
- tags = local.standard_tags
-}
-
-# TODO: Allow incoming traffic on the Load Balancer on port 80 from everywhere
-# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
-resource "aws_security_group_rule" "lb_http" {
- security_group_id = aws_security_group.lb.id
- description = "Allows HTTP from everywhere"
- type = "ingress"
- from_port = 80
- to_port = 80
- protocol = "tcp"
- cidr_blocks = ["0.0.0.0/0"]
-}
-
-# TODO: Allow outgoing traffic from the LB to everywhere
-# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
-resource "aws_security_group_rule" "lb_all_outgoing" {
- security_group_id = aws_security_group.lb.id
- description = "Allows HTTP to App SG"
- type = "egress"
- from_port = 0
- to_port = 0
- protocol = "-1"
- cidr_blocks = ["0.0.0.0/0"]
-}
-
-
-# TODO: Create the Load Balancer
-# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb
-resource "aws_lb" "app" {
- name = local.env
- load_balancer_type = "application"
- security_groups = [aws_security_group.lb.id]
- subnets = module.vpc.public_subnets
- tags = local.standard_tags
-}
-
-# TODO: Create a target group
-# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group
-resource "aws_lb_target_group" "app" {
- name = local.env
- port = 8080
- protocol = "HTTP"
- vpc_id = module.vpc.vpc_id
- deregistration_delay = 60
-
- health_check {
- interval = 6
- path = "/"
- port = "8080"
- protocol = "HTTP"
- }
-}
-
-# TODO: Create a listener
-# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener
-resource "aws_lb_listener" "lb_forward_to_app" {
- load_balancer_arn = aws_lb.app.arn
- port = "80"
- protocol = "HTTP"
-
- default_action {
- type = "forward"
- target_group_arn = aws_lb_target_group.app.arn
- }
-}
-
diff --git a/04-iaas/loesung/teil-2/main.tf b/04-iaas/loesung/teil-2/main.tf
deleted file mode 100644
index 01ccca42..00000000
--- a/04-iaas/loesung/teil-2/main.tf
+++ /dev/null
@@ -1,44 +0,0 @@
-provider aws {
- version = "3.12.0"
- region = "us-east-1"
-}
-
-provider random {
- version = "3.0.0"
-}
-
-resource random_string id_suffix {
- length = 4
- special = false
- upper = false
-}
-
-locals {
- # use this variable as prefix for all resource names. This avoids conflicts with globally unique resources (all resources with a hostname)
- env = "${terraform.workspace}-${random_string.id_suffix.result}"
-
- # use this map to apply env-specific values for certain components.
- env_config = {
- stg = {
- }
- dev = {
- message = "Hello Workspaces!"
- }
- default = {
- message = "Hello World!"
- }
- }
- config = merge(local.env_config["default"], lookup(local.env_config, terraform.workspace, {}))
-
- # tag all resources at least with these tags
- # allows filtering in AWS and distinction between environments
- standard_tags = {
- "environment" = local.env
- }
-
- standard_tags_asg = [for key in keys(local.standard_tags) : {
- key = key
- value = lookup(local.standard_tags, key)
- propagate_at_launch = "true"
- }]
-}
\ No newline at end of file
diff --git a/04-iaas/loesung/teil-2/network.tf b/04-iaas/loesung/teil-2/network.tf
deleted file mode 100644
index 52977680..00000000
--- a/04-iaas/loesung/teil-2/network.tf
+++ /dev/null
@@ -1,13 +0,0 @@
-# More Info: https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest
-module "vpc" {
- source = "terraform-aws-modules/vpc/aws"
- version = "2.63.0"
-
- name = local.env
- cidr = "10.0.0.0/16"
-
- azs = ["us-east-1a", "us-east-1b"]
- public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
-
- tags = local.standard_tags
-}
\ No newline at end of file
diff --git a/04-iaas/loesung/teil-2/output.tf b/04-iaas/loesung/teil-2/output.tf
deleted file mode 100644
index 6186c113..00000000
--- a/04-iaas/loesung/teil-2/output.tf
+++ /dev/null
@@ -1,5 +0,0 @@
-
-# TODO: Define an output for the LoadBalancer URL
-output "load_balancer_url" {
- value = "http://${aws_lb.app.dns_name}"
-}
\ No newline at end of file
diff --git a/04-iaas/uebung/.devcontainer/devcontainer.json b/04-iaas/uebung/.devcontainer/devcontainer.json
deleted file mode 100644
index b969e93d..00000000
--- a/04-iaas/uebung/.devcontainer/devcontainer.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "IaaS Container",
- "dockerFile": "../iaas-container/Dockerfile",
- "settings": {
- "terminal.integrated.shell.linux": "/bin/bash"
- },
- "extensions": [
- "hashicorp.terraform"
- ]
-}
\ No newline at end of file
diff --git a/04-iaas/uebung/iaas-container/Dockerfile b/04-iaas/uebung/iaas-container/Dockerfile
deleted file mode 100644
index 77629f35..00000000
--- a/04-iaas/uebung/iaas-container/Dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM ubuntu:20.04
-
-WORKDIR /root
-
-COPY setup.sh .
-RUN /bin/bash ./setup.sh \
- rm setup.sh
-COPY configure.sh bin/configure.sh
-ENTRYPOINT ["/bin/bash"]
\ No newline at end of file
diff --git a/04-iaas/uebung/iaas-container/configure.sh b/04-iaas/uebung/iaas-container/configure.sh
deleted file mode 100755
index 902af9be..00000000
--- a/04-iaas/uebung/iaas-container/configure.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-
-echo -n "insert your aws_access_key_id:"
-read -s accessKey
-echo -n "insert your aws_secret_access_key:"
-read -s secretKey
-echo -n "insert your aws_session_token:"
-read -s sessionToken
-
-mkdir -p ~/.aws/
-
-cat > ~/.aws/config <<-EOF
-[default]
-region = us-east-1
-EOF
-
-cat > ~/.aws/credentials <<-EOF
-[default]
-aws_access_key_id=${accessKey}
-aws_secret_access_key=${secretKey}
-aws_session_token=${sessionToken}
-EOF
-
-echo "Your identitiy:"
-aws sts get-caller-identity
\ No newline at end of file
diff --git a/04-iaas/uebung/iaas-container/setup.sh b/04-iaas/uebung/iaas-container/setup.sh
deleted file mode 100644
index c35d4374..00000000
--- a/04-iaas/uebung/iaas-container/setup.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash
-set -euxo pipefail
-
-apt-get update
-DEBIAN_FRONTEND=noninteractive apt-get install -y -q \
- python3 \
- python3-pip \
- python-is-python3 \
- git \
- unzip \
- curl \
- groff
-
-
-mkdir bin
-cd bin
-
-
-curl "https://releases.hashicorp.com/terraform/0.13.5/terraform_0.13.5_linux_amd64.zip" -o "terraform_0.13.5_linux_amd64.zip"
-unzip terraform_0.13.5_linux_amd64.zip
-rm terraform_0.13.5_linux_amd64.zip
-./terraform -install-autocomplete
-
-git clone https://github.com/aws/aws-ec2-instance-connect-cli.git
-cd aws-ec2-instance-connect-cli
-pip3 install -r requirements.txt
-pip3 install -e .
-cd -
-
-curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
-unzip awscliv2.zip
-rm awscliv2.zip
-./aws/install
-
-cd ~
-
-cat >> /root/.bashrc <<-EOF
- alias mssh=/root/bin/aws-ec2-instance-connect-cli/bin/mssh
- export AWS_DEFAULT_REGION=us-east-1
- complete -C /usr/local/bin/aws_completer aws
- alias terraform=/root/bin/terraform
- alias configure=/root/bin/configure.sh
-EOF
\ No newline at end of file
diff --git a/04-iaas/uebung/teil-1/README.md b/04-iaas/uebung/teil-1/README.md
deleted file mode 100644
index ad5b4438..00000000
--- a/04-iaas/uebung/teil-1/README.md
+++ /dev/null
@@ -1,261 +0,0 @@
-# Übung: IaaS mit AWS
-
-## Ziel
-
-Ziel dieser Übung ist, dass Sie ein grundsätzliches Verständnis der AWS Cloud und ihrer grundlegendsten IaaS Komponenten erlangen. Ihre Aufgabe ist es ein virtuelles Netz, eine AutoScaling Gruppe mit mehreren Maschinen und einen LoadBalancer zu erzeugen um eine minimale Webanwendung bereitzustellen. Im Anschluss werden Sie die AWS CLI verwenden um Ihre Anwendung zu skalieren und per SSH zu verändern.
-
-## Voraussetzungen
-
-Im zweiten Teil dieser Übung verwenden wir einen Docker Container zur Verwendung der CLI. Daher brauchen Sie eine eine lokale Docker Installation. Da die Erstellung / der Download des Containers länger dauern kann starten Sie bitte mit der Vorbereitung für Übung zwei.
-
-Sie benötigen einen AWS Account um die Übung zu absolvieren. Wenn Sie awseducate.com verwenden loggen Sie sich auf der Webseite ein und klicken Sie auf _AWS Account_ und erzeugen Sie einen _Educate Starter Account_. Auf der Startseite von _Vocareum_ können Sie über _Account Details_ und den Button _AWS CLI_ die Zugangsdaten für den Kommandozeilen Zugriff abrufen. Die Webkonsole erreichen Sie unter dem Button _AWS Console_.
-
-
-## Aufgaben
-
-### Erzeugen einer AWS Infrastruktur per UI
-
-In dieser Übung erzeugen Sie eine einfache IaaS Architektur mit der AWS Web Konsole.
-
-Hinweise:
-* Folgen Sie der Anleitung, sofern nicht anders genannt, belassen Sie andere Einstellungen unverändert.
-* Stellen Sie sicher, dass die AWS Region _US East (North Virginia) us-east-1_ (rechter, oberer Bildschirmrand) ist und die Sprache auf _English_ (linker, unterer Bildschirmrand) steht.
-* Wichtig wenn Sie Sich einen Account mit anderen Übungsteilnehmern teilen: Denken Sie sich einen eindeutigen Namen für die Benennung/Tags Ihrer Ressourcen aus. Da alle Teilnehmer den gleichen Account verwenden werden ist davon auszugehen, dass wenn Sie ihre Ressourcen nicht wiedererkennbar bennenen Sie und Ihre Kommolitonen Schwierigkeiten haben werden Ihre Ressourcen wiederzufinden. Sie können beispielsweise eine Kombination aus Ihrem echten Namen verwenden z.b. `akrause` wenn ihr Name _Alex Krause_ ist oder Sie wählen einen beliebigen anderen Namen wie z.B. `anonymeameise`. Verwenden Sie bei der Namenswahl keine Sonderzeichen außer `-` und vermeiden Sie Umlaute.
-
-#### Ein VPC erstellen
-
-In diesem Teil der Übung erstellen Sie ein virtuelles Netzwerk, mit einem einzelnen Subnetz mit Internet Zugriff.
-
-1. Klicken Sie in der Menubar auf _Services_, wählen Sie _VPC_ > _Your VPCs_ > Create _VPC_.
- * Vergeben Sie einen eindeutigen Namen, mit dem Sie ihr Netzwerk wiederfinden z.B. _akrause_.
- * Verwenden Sie den IPV4 Adressbereich `10.0.0.0/16`.
- * Klicken Sie auf erstellen. Nach dem erstellen werden Sie feststellen, dass über das Netz hinaus weitere Standard Cloud Ressourcen angelegt wurden z.B. eine Routing Tabelle.
-
- :warning: WICHTIG: Verwenden sie nicht den VPC Launch Wizard. Ziel dieser Übung ist, dass Sie sich mit den Einzelkomponenten vertraut machen und den Wert von Automatisierung kennenlernen.
-2.
- * a) Klicken Sie in der Seitenleiste auf _Subnets_, wähle Sie _Create Subnet_.
- * Verwenden Sie wieder Ihren eindeutigen Namen.
- * Wählen Sie das von Ihnen erstellte VPC als Ziel für ihr Subnetz.
- * Wählen Sie als VPC ihr gerade erstelltes Netz.
- * Wählen Sie als _Availability Zone_ die Zone _us-east-1a_.
- * Wählen Sie den Adressbereich kleiner als den Ihres VPC z.B. die Hälfte: `10.0.0.0/17`.
- * Nach der Erstellung, markieren Sie ihr Netz und klicken Sie nun bei _Actions_ die Option _Modify auto-assign IP settings_ und aktivieren Sie den Haken bei _Auto-assign IPv4_.
- * b) Wiederholen Sie die Schritte in 2a um ein Netz in _us-east-1b_ mit dem Adressbereich `10.0.128.0/17` zu erzeugen.
-3. Erzeugen Sie nun einen Zugang zum Internet indem Sie in der Seitenleiste auf _Internet Gateways_ klicken und dann _Create Internet Gateway Klicken_.
- * Verwenden Sie wieder Ihren eindeutigen Namen.
- * Nach dem Erstellen klicken Sie oben rechts auf _Actions_ und wählen Sie _Attac to VPC_.
- * Wählen Sie ihr VPC und bestätigen Sie.
-5. Klicken Sie nun in der Seitenleiste auf _Route Tables_ und fügen Sie an die Tabelle für Ihr VPC eine weitere Regel ein, die allen Traffic (`0.0.0.0/0`) zu dem von Ihnen erstellten Internet Gateway routed. Die Reihenfolge der Regeln ist hierbei irrelevant, spezifischere greifen zuerst.
-
-#### Einen LoadBalancer erstellen
-
-In diesem Teil der Übung erstellen Sie einen Load Balancer der die Anfragen über die aktuell service-fähigen Instanzen verteilt.
-
-1. Wechseln Sie nun zum Service _EC2_. Klicken Sie in der Seitenleiste auf _Security Groups_ und dann auf _Create security group_.
- * Benennen Sie die Gruppe nach dem Schema `loadbalancer-`.
- * Als Beschreibung ist _"Security Group fuer den Load Balancer."_ geeignet.
- * Unter _VPC_ Wählen Sie ihr VPC aus.
- * Fügen Sie eine Inbound Rule ein, die den Zugriff per Port `80`, also HTTP, von `0.0.0.0/0`, d.h. beliebiger IP, erlaubt.
-
-2. Klicken Sie auf der Seitenleiste auf _Load Balancers_ und wählen Sie _Create Load Balancer_.
- * Wählen Sie den Typ _Application Load Balancer_.
- * Verwenden Sie wieder Ihren eindeutigen Namen.
- * Wählen Sie unter Availability Zones Ihr Netz und Ihre Subnetze aus.
- * Wählen Sie im Folgeschritt _Configure Security Groups_ nur Ihre `loadbalancer-` Security Gruppe aus.
- * Im Schritt _Configure Routing_ wählen Sie das Erstellen einer neuen Target Group und verwenden Sie wieder ihren eindeutigen Namen.
- * Ändern Sie den Port auf 8080.
- * Unter _Health checks_ ändern Sie das Protokol auf `HTTP`.
- * Unter _Advanced health check settings_ setzen Sie den _Healthy_ und _Unhealthy threshold_ auf `2` und den _interval_ auf `10 seconds`.
- * Es ist nicht notwendig _Targets_ zu registrieren, dies übernimmt die AutoScaling Gruppe, schließen Sie den Vorgang ab.
-
-
-#### Eine AutoScaling Gruppe einrichten
-
-In diesem Teil der Übung erstellen Sie eine AutoScaling Gruppe, welche Ihnen erlaubt ihre Instanzen bequem zu skalieren und auszutauschen. Darüber hinaus meldet ihre AutoScaling Gruppe Ihre Instanzen bei der Target Group des Load Balancers an.
-
-1. Erstellen Sie eine weitere Security Group.
- * Benennen Sie die Gruppe nach dem Schema `app-`.
- * Als Beschreibung ist _"Security Group fuer die Applikationsserver."_ geeignet.
- * Unter _VPC_ Wählen Sie ihr VPC aus.
- * Fügen Sie eine Inbound Rule ein, die den Zugriff per Port `22`, also SSH, von `0.0.0.0/0`, d.h. beliebiger IP, erlaubt.
- * Fügen Sie eine weitere Inbound Rule ein, die den Zugriff per Port `8080`, von der Security Group des Load Balancers erlaubt.
-
-2. Klicken Sie in der Seitenleiste auf den Eintrag _Launch Templates_ > _Create Launch Template_.
- * Verwenden Sie wieder Ihren eindeutigen Namen.
- * Als Beschreibung ist "Launch Template für eine einfache Web Anwendung" geeignet.
- * Wählen Sie als _AMI_ ein Ubuntu: `ami-0dba2cb6798deb6d8`.
- * Wählen Sie `t2.micro` als Instanztyp
- * Wählen Sie Ihre Security Group für die Applikation.
- * Verwenden Sie das folgende Skript als _User Data_ unter den _Advanced Details_:
- ```sh
- #!/bin/bash
- set -euxo pipefail
-
- apt-get update
- apt-get install -y busybox cowsay
- source /etc/environment
-
- echo "
" >> index.html
-
- nohup busybox httpd -f index.html -p 8080 &
- ```
- * Schließen Sie den Vorgang ab.
-
-3. Gehen Sie nun in der Seitenleiste auf den Eintrag _Auto Scaling Groups_ und klicken danach auf _Create Auto Scaling group_.
- * _Step 1_: Verwenden Sie wieder Ihren eindeutigen Namen. Wäheln Sie ihr Launch Template.
- * _Step 2_: Wählen Sie die von Ihnen erzeugten Komponenten aus: VPC, **beide** Subnets.
- * _Step 3_: Zum verwenden Ihrer Target Group setzen Sie bei Schritt _Load Balancing_ den Haken bei _Enable Load Balancing_ und wählen Ihre Target Group. Setzen Sie auf dieser Seite auch den Haken bei _ELB_ unter _Health Checks_.
- * _Step 4_:Setzen Sie die maximale Kapazität auf `4`, die minimale auf `0` und die gewünschte auf `2`.
- * Schließen Sie den Vorgang nun ab. Nach Erstellung sollten nun nach einiger Zeit zwei Instanzen nach Ihren Spezifikationen erzeugt werden.
-
-#### Funktionstest
-
-1. Gehen Sie nun auf ihre Target Group und klicken Sie auf den Reiter _Targets_. Ihre Instanzen sollte hier nach kurzer Zeit als _healthy_ auftauchen. Sollten die Instanzen nicht auftauchen oder über 5 Minuten hinaus _unhealthy_ sein, haben Sie wahrscheinlich einen Fehler gemacht.
-2. Gehen Sie auf die Ansicht des Load Balancers und kopieren Sie sich den _DNS name_ heraus und öffnen Sie die Adresse in einem neuen Tab. Wenn Sie einen Drachen sehen Sie die HTTP Antwort einer Ihrer Instanzen.
-3. Verbinden Sie sich nun mit einer _Ihrer_ laufenden Instanzen per EC2 Instance Connect.
-
- :warning: Hinweis: Falls Sie MacOS verwenden, dieser Schritt funktioniert nicht mit Safari. Nehmen Sie für diesen Schritt bitte einen anderen Browser.
-
- * Gehen Sie dazu auf _Instances_ und Markieren (nicht auf die Detail-Ansicht Klicken) Sie einer Ihrer Instanzen.
- * Gehen Sie nun oben rechts auf _Actions_ und wählen Sie _Connect_ und in der folgenden Ansicht nochmals auf _Connect_.
- * Sie sind jetzt mit einer Shell Session auf dem Host verbunden.
- * Auf dem Host läuft ein `httpd` service der die Webseite hostet. Stoppen Sie den Prozess.
- * Was können Sie an der Target Group, AutoScaling Gruppe und an Ihren Instanzen beobachten? Wie verhält sich die Webseite währenddessen?
-
-Lösung Prozess stoppen:
-
-
-
-Finden Sie als erstes die Prozess Id heraus z.B. mit `ps`:
-```
-ps aux | grep httpd
-```
-
-Beispiel Ergebnis:
-```
-root 6634 0.0 0.1 2788 1660 ? S 15:07 0:00 busybox httpd -f index.html -p 8080
-ubuntu 7287 0.0 0.0 7672 652 pts/0 S+ 15:19 0:00 grep --color=auto httpd
-```
-
-Sie können den Prozess `6634` beenden, hierfür brauchen Sie in diesem Fall Root Rechte:
-
-```
-sudo kill 6634
-```
-
-Sie können das Fenster schließen.
-
-
-
-
-Lösung Beobachtungen:
-
-Als erstes wird Ihre Instanz in der Target Group _Unhealthy_ werden, da die Anwendung nicht mehr antwortet.
-Dadurch wird der Load Balancer jeglichen Traffic nur noch an die noch funktionierende Instanz schicken.
-Der Betrieb der Webseite ist ungestört, sie funktioniert weiter.
-Nach einiger Zeit wird die Auto Scaling Gruppe auf den vom Load Balancer gemeldeten Zustand reagieren und die kaputte Instanz gegen eine neue in der gleichen Zone austauschen.
-Das System heilt sich also selbst.
-Für kurze Zeit wird es daher drei Instanzen geben.
-Die kaputte Instanz wird rückstandslos terminiert.
-Der gesamte Prozess dauert normalerweise maximal 10 Min.
-
-
-
-### Arbeiten mit der AWS CLI
-
-#### Vorbereitung
-
-Bauen Sie das Container Image unter `iaas-container` mit:
-
-```
-docker build -t iaas-container ./iaas-container
-```
-
-Starten Sie den Container mit:
-
-```
-docker run -it --rm -w /root iaas-container
-```
-
-Nun sollte eine Bash Session vor Ihnen geöffnet sein. Geben Sie `aws configure`. In der folgenden Abfrage geben Sie ihre AWS Zugangsdaten ein, wählen sie `us-east-1` als Standard Region.
-Testen Sie anschließend mit `aws sts get-caller-identity` Ihre Konfiguration. Es sollte eine Antwort ähnlich dieser auf der Konsole erscheinen:
-
-```
-{
- "UserId": "AIDAIVMF6UC5ZJA4LA2QU",
- "Account": "264524865537",
- "Arn": "arn:aws:iam::264524865537:user/akrause"
-}
-```
-
-#### Arbeiten mit der Kommandozeile
-
-In Ihrer Docker Umgebung ist sowohl eine das Kommandozeilen Programm für AWS installiert (`aws`) als auch ene Programm zur bequemen Verwendung von EC2Connect(`mssh`). Letzeres erlaubt Ihnen das Verbinden mit einer EC2 Instanz per SSH, sofern Sie die Berechtigung dafür haben.
-
-Stellen Sie sich nun vor Sie sind für den zuverlässigen Betrieb des von Ihnen heute erstellten _Dragon Service_ im Auftrag eines Geldgebers verantwortlich. Ihr Geldgeber stellt nun fest, dass die Nutzerzahlen Ihres Services zurückgehen und will daher Geld sparen - er ist bereit eine geringere Verfügbarkeit in Kauf zu nehmen. Reduzieren Sie daher die Anzahl der Instanzen in der AutoScaling Gruppe auf `1` statt `2`. Nutzen Sie `aws autoscaling help` um ein Kommando zusammenzustellen.
-
-Lösung:
-
-
-aws autoscaling set-desired-capacity --auto-scaling-group-name [IHR EINDEUTIGER NAME] --desired-capacity 1
-
-
-
-Um die Nutzerzahlen wieder zu erhöhen möchte Ihr Geldgeber ein neues Format ausprobieren - Pinguine, so hat er gehört, sollen unter Informatikern sehr beliebt sein. Verbinden Sie sich per `mssh` mit der verbleibenden Instanz. Achten Sie darauf, sich nicht mit der Instanz zu verbinden, die eventuell noch am herunterfahren ist.
-
-Setzen Sie ihren Eindeutigen Namen in folgendes Kommando ein und führen Sie es aus, um Ihre Instanz zu finden:
-```
-AWS_PAGER="" aws ec2 describe-instances --filters "Name=tag:aws:autoscaling:groupName,Values=" "Name=instance-state-name,Values=running" --query 'Reservations[].Instances[].InstanceId' --output text
-```
-
-Sie können die Instanz Id mit folgendem Kommando verwenden, um sich mit der Instanz zu verbinden:
-```
-mssh ubuntu@
-```
-
-Editieren Sie die `index.html` unter `/` als, so dass ein Pinguin erscheint - hierzu brauchen Sie root Rechte. Wenn Sie nicht wissen wie Sie einen Pinguin erzeugen ändern Sie nur die Nachricht von _Hello World_ zu _Ich bin ein Pinguin_.
-
-Lösung:
-
-Editieren Sie die Datei beispielsweise mit `nano`:
-
-sudo nano /index.html
-
-Je nach Betriebssystem speichern Sie¨Ihre Änderung mit STRG+O (Windows) oder Control+O (MacOS).
-
-
-Gehen Sie wieder im Browser auf die Adresse Ihres Load Balancers um Ihre Änderung zu überprüfen.
-
-Der Pinguin kommt gut an und die Nutzer Zahlen steigen. Ihr Geldgeber möchte die Last mit einer weiteren Instanz abfangen. Skalieren Sie mit der Kommandozeile Ihre AutoScaling Gruppe auf `2`.
-
-Lösung:
-
-
-aws autoscaling set-desired-capacity --auto-scaling-group-name [IHR EINDEUTIGER NAME] --desired-capacity 2
-
-
-
-Was werden ihre Nutzer sehen sobald die zusätzliche Instanz nach einigen Minuten erfolgreich beim Load Balancer angemeldet wurde?
-
-
-Die neue Instanz wurde mit dem unveränderten Launch Template gestartet und ist daher mit dem Drachen konfiguriert. Die Webseite antwortet abwechselnd mit der alten und neuen Version des Dienstes. Der Load Balancer verteilt die Last abwechselnd auf die Instanzen.
-
-Wenn Sie Instanzen in einer AutoScaling Gruppe ändern wollen sollten Sie zunächst das Launch Template anpassen und dann die Instanzen rollierend austauschen.
-
-1. Erst eine neue Instanz hinzufügen.
-2. Warten bis Sie beim Load Balancer getestet und in die Rotation aufgenommen wurde.
-3. Eine alte Instanz aus der Rotation nehmen (keine neuen Verbindungen) und warten bis alle noch laufenden Anfragen abgearbeitet wurden.
-4. Die alte Instanz herunterfahren.
-5. Wiederholen bis keine alten Instanzen mehr vorhanden sind.
-
-
-Idealerweise haben Sie im neuen Launch Template bereits ein mit Packer erstelltes Image konfiguriert, so dass die Installationsphase für Abhängigkeiten entfällt und immer die gleichen Pakete vorhanden sind. Einen rollierenden Austausch ohne Ausfall des Services können Sie auch vollautomatisch starten:
-
-
-aws autoscaling start-instance-refresh --auto-scaling-group-name [IHR EINDEUTIGER NAME]
-
-
-
-
diff --git a/04-iaas/uebung/teil-2/.gitignore b/04-iaas/uebung/teil-2/.gitignore
deleted file mode 100644
index d38e10a2..00000000
--- a/04-iaas/uebung/teil-2/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.terraform/
-terraform.tfstate*
\ No newline at end of file
diff --git a/04-iaas/uebung/teil-2/README.md b/04-iaas/uebung/teil-2/README.md
deleted file mode 100644
index 3ab6c1a3..00000000
--- a/04-iaas/uebung/teil-2/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Übung: Infrastructure as Code mit Terraform auf AWS
-
-Ziel dieser Übung ist das erlernen grundlegender Infrastructure as Code Fähigkeiten mit Terraform auf der AWS Cloud. Hierzu werden Sie die Architektur aus der letzten Übung mit Terrform nachbauen. Grundlegende Schritte sind hierfür schon vorbereitet.
-
-
-0. Starten Sie den `iaas-container` und mounten Sie das Verzeichnis `teil-2` in den `/root/teil-2` im Container. Beispiel mit Bash aus dem Verzeichnis:
- ```
- docker run -it --rm -w /root iaas-container --mount type=bind,source="$(pwd)",target=/root/teil-2 iaas-container
- ```
- Konfigurieren Sie wieder Ihren AWS Zungang mit `aws configure`.
- Initialisieren Sie dann im Verzeichnis `teil-2` Terraform mit `terraform init`.
-4. Schauen Sie sich die bereits existierenden Terraform Dateien an und machen Sie sich mit der grundlegenden Struktur vertraut.
-5. Implementieren Sie alle Stellen die mit `#TODO` annotiert sind. Definieren Sie sich selbst eine sinnvolle reihenfolge. In regelmäßigen Abständen sollten Sie ihre Implementierungsarbeiten auf die AWS Cloud anwenden mit `terraform apply`.
-6. Am Ende sollte die Terraform Konfiguration einen Output Parameter mit einer validen und funktionierenden URL enthalten.
-7. Erzeugen Sie einen neuen Workspace mit `terraform workspace new dev`, wechseln zu diesem `terraform workspace select dev` und überprüfen Sie ob Sie mit `terraform apply` eine zweite Umgebung erzeugen können. Wenn nicht passen Sie ihre Konfigurationen so an, dass dies möglich ist. Machen Sie dafür insbesondere Benennungen von Ressourcen abhängig vom verwendeten Workspace.
-8. Zerstören Sie alle erzeugten Ressourcen mit `terraform destroy` auf beiden Workspaces.
-
diff --git a/04-iaas/uebung/teil-2/init.sh b/04-iaas/uebung/teil-2/init.sh
deleted file mode 100644
index f46fd827..00000000
--- a/04-iaas/uebung/teil-2/init.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-set -euxo pipefail
-
-apt-get update
-apt-get install -y busybox cowsay
-source /etc/environment
-
-echo "
" >> index.html
-
-nohup busybox httpd -f index.html -p 8080 &
diff --git a/04-iaas/uebung/teil-2/main.tf b/04-iaas/uebung/teil-2/main.tf
deleted file mode 100644
index 01ccca42..00000000
--- a/04-iaas/uebung/teil-2/main.tf
+++ /dev/null
@@ -1,44 +0,0 @@
-provider aws {
- version = "3.12.0"
- region = "us-east-1"
-}
-
-provider random {
- version = "3.0.0"
-}
-
-resource random_string id_suffix {
- length = 4
- special = false
- upper = false
-}
-
-locals {
- # use this variable as prefix for all resource names. This avoids conflicts with globally unique resources (all resources with a hostname)
- env = "${terraform.workspace}-${random_string.id_suffix.result}"
-
- # use this map to apply env-specific values for certain components.
- env_config = {
- stg = {
- }
- dev = {
- message = "Hello Workspaces!"
- }
- default = {
- message = "Hello World!"
- }
- }
- config = merge(local.env_config["default"], lookup(local.env_config, terraform.workspace, {}))
-
- # tag all resources at least with these tags
- # allows filtering in AWS and distinction between environments
- standard_tags = {
- "environment" = local.env
- }
-
- standard_tags_asg = [for key in keys(local.standard_tags) : {
- key = key
- value = lookup(local.standard_tags, key)
- propagate_at_launch = "true"
- }]
-}
\ No newline at end of file
diff --git a/04-iaas/uebung/teil-2/network.tf b/04-iaas/uebung/teil-2/network.tf
deleted file mode 100644
index 52977680..00000000
--- a/04-iaas/uebung/teil-2/network.tf
+++ /dev/null
@@ -1,13 +0,0 @@
-# More Info: https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest
-module "vpc" {
- source = "terraform-aws-modules/vpc/aws"
- version = "2.63.0"
-
- name = local.env
- cidr = "10.0.0.0/16"
-
- azs = ["us-east-1a", "us-east-1b"]
- public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
-
- tags = local.standard_tags
-}
\ No newline at end of file
diff --git a/04-iaas/uebung/teil-2/output.tf b/04-iaas/uebung/teil-2/output.tf
deleted file mode 100644
index 379f5b1b..00000000
--- a/04-iaas/uebung/teil-2/output.tf
+++ /dev/null
@@ -1,2 +0,0 @@
-
-# TODO: Define an output for the LoadBalancer URL
\ No newline at end of file
diff --git a/06-orchestrierung/uebung/.gitignore b/05-orchestrierung/uebung/.gitignore
similarity index 100%
rename from 06-orchestrierung/uebung/.gitignore
rename to 05-orchestrierung/uebung/.gitignore
diff --git a/05-orchestrierung/uebung/01_uebung_pods_deployments.md b/05-orchestrierung/uebung/01_uebung_pods_deployments.md
new file mode 100644
index 00000000..ff063be3
--- /dev/null
+++ b/05-orchestrierung/uebung/01_uebung_pods_deployments.md
@@ -0,0 +1,46 @@
+# Übung 1. Pods & Deployments
+
+Infos:
+
+- [Cheat-Sheet](cheat-sheet.md)
+
+Aufgaben:
+
+1. Installieren oder konfigurieren Sie ein Kubernetes-Cluster. Nutzen Sie dazu *eine* der folgenden Varianten:
+ * Möglichkeit 1: Installieren Sie [kind](https://kind.sigs.k8s.io/)
+ * Möglichkeit 2: Melden Sie sich am [kube07](https://kube.cs.hm.edu/) an, erzeugen Sie einen Cluster und laden Sie die kubeconfig herunter.
+ * Möglichkeit 3: Falls sie schon Docker Desktop oder Podman Desktop installiert haben, aktivieren Sie das eingebaute Kubernetes in den Einstellungen.
+2. Prüfen Sie, ob der Cluster online ist:
+ ```shell
+ $ kubectl version
+ WARNING: This version information is deprecated and will be replaced with the output from kubectl version --short. Use --output=yaml|json to get the full version.
+ Client Version: version.Info{Major:"1", Minor:"26", GitVersion:"v1.26.3", GitCommit:"9e644106593f3f4aa98f8a84b23db5fa378900bd", GitTreeState:"clean", BuildDate:"2023-03-15T13:40:17Z", GoVersion:"go1.19.7", Compiler:"gc", Platform:"linux/amd64"}
+ Kustomize Version: v4.5.7
+ Server Version: version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.3", GitCommit:"434bfd82814af038ad94d62ebe59b133fcb50506", GitTreeState:"clean", BuildDate:"2022-10-25T19:35:11Z", GoVersion:"go1.19.2", Compiler:"gc", Platform:"linux/amd64"}
+ ```
+
+3. Machen Sie sich mit der App `Hello-Service` vertraut (siehe [code/hello-service](code/hello-service)).
+4. Bauen Sie das Container-Image gegen Kubernetes (`build-to-kubernetes.sh`).
+5. Schreiben Sie ein
+ [Kubernetes Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#creating-a-deployment)
+ für die App `Hello-Service`.
+6. Installieren Sie dieses Deployment in den Kubernetes-Cluster (`kubectl apply -f`).
+7. Machen Sie sich mit der Navigation in `k9s` vertraut (siehe [Cheat-Sheet: k9s](cheat-sheet.md#k9s)).
+8. Prüfen Sie mittels `k9s`, ob die App korrekt startet (Ready Flag, Container Logs).
+
+Tipps:
+
+Anstatt `k9s` aufzusetzen, können Sie auch `kubectl` benutzen, um den Zustand des Clusters zu sehen (z.B. mit `kubectl get pods`).
+
+Falls Sie unter Windows Probleme mit `build-to-kubernetes.sh` haben:
+- Versuchen Sie, das Script in der [Git Bash](https://gitforwindows.org/) oder der WSL
+ auszuführen. Starten Sie die Git Bash mit Admin-Rechten.
+
+Bonus:
+
+9. Beenden Sie einen Pod über `k9s` und stellen Sie sicher, dass er neu gestartet wird
+10. Ändern Sie die Anzahl der Replikas des Deployments
+ 1. über k9s > `:deployments` > ` Scale`
+ 2. über `kubectl scale`
+ 3. In welcher Reihenfolge passiert das Rolling Upgrade? (z.B. Zug-um-Zug oder wird erst komplett die neue Version ausgerollt und dann die alte heruntergefahren?)
+
diff --git a/05-orchestrierung/uebung/02_uebung_probes_resources.md b/05-orchestrierung/uebung/02_uebung_probes_resources.md
new file mode 100644
index 00000000..e734837a
--- /dev/null
+++ b/05-orchestrierung/uebung/02_uebung_probes_resources.md
@@ -0,0 +1,45 @@
+# Übung 2. Probes und Resource Constraints
+
+Infos:
+
+- [Cheat-Sheet](cheat-sheet.md)
+
+Aufgaben:
+
+1. Bauen Sie in das Deployment der App `Hello-Service` Liveness- und
+ Readiness-Probes ein, siehe
+ [HTTP Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-http-request).
+2. Vergeben Sie
+ [Resource Requests und Limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes).
+3. Prüfen Sie mittels `k9s`, ob die App korrekt startet.
+
+Bonus:
+
+4. Prüfen Sie, was passiert, wenn die Readiness oder Liveness Probes fehlschlagen.
+5. Prüfen Sie, was passiert, wenn zu wenige oder zu viele Ressourcen angefragt werden.
+ 1. Verifizieren Sie, dass kein Pod gescheduled werden kann (Pending).
+ 2. Nutzen Sie die `` Describe Funktion in k9s, um die Events des Pods anzusehen.
+6. Bauen Sie eine
+ [Startup-Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes)
+ ein.
+
+7. Probe Zeit berechnen
+
+Angenommen:
+
+ - die Liveness Probe des Containers funktioniert und
+ - der Request auf den Endpunkt `/actuator/health/readiness` läuft in einen Timeout.
+
+Wie lange dauert es, bis eine App mit der nachfolgenden Readiness Probe als "Not Ready" markiert wird?
+
+```yaml
+ readinessProbe:
+ httpGet:
+ path: /actuator/health/readiness
+ port: 8000
+ initialDelaySeconds: 10
+ periodSeconds: 20
+ failureThreshold: 3
+ timeoutSeconds: 5
+```
+
diff --git a/05-orchestrierung/uebung/03_uebung_services.md b/05-orchestrierung/uebung/03_uebung_services.md
new file mode 100644
index 00000000..7f670281
--- /dev/null
+++ b/05-orchestrierung/uebung/03_uebung_services.md
@@ -0,0 +1,45 @@
+# Übung 3. Services
+
+Infos:
+
+- [Cheat-Sheet](cheat-sheet.md)
+
+Aufgaben:
+
+1. Legen Sie für die App `Hello-Service` einen
+ [Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service)
+ an.
+2. Starten Sie einen temporären Pod und überprüfen Sie aus diesem heraus, dass der Service für den
+ `Hello-Service` per `curl` über seinen Hostnamen erreichbar ist.
+
+Starten einer Shell in einem temporären Pod
+
+```shell script
+kubectl run my-shell --rm -i --tty --image byrnedo/alpine-curl --command sh
+```
+
+Bonus:
+
+3. Load Balancing testen
+
+- Bauen Sie den `Hello-Service` so um, dass der `/hello` Endpunkt die lokale IP Adresse zurückgibt (im Body oder als Header).
+
+```java
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+public class HelloWorldController {
+ @GetMapping("/hello")
+ public String hello(HttpServletRequest clientRequest) {
+ String localIp = clientRequest.getLocalAddr();
+ // ...
+ }
+}
+```
+
+- Bauen Sie eine neue Version des `helloservice` Docker Images (siehe `build-to-kubernetes.sh`).
+- Deployen Sie den `Hello-Service` mit zwei Replikas in den Kubernetes Cluster und verifizieren Sie über die Antwort auf einen `curl` auf den Service, dass beide Pods angesprochen werden.
+
diff --git a/05-orchestrierung/uebung/04_uebung_config_maps.md b/05-orchestrierung/uebung/04_uebung_config_maps.md
new file mode 100644
index 00000000..1c0b1b51
--- /dev/null
+++ b/05-orchestrierung/uebung/04_uebung_config_maps.md
@@ -0,0 +1,29 @@
+# Übung 4. Config Maps
+
+Infos:
+
+- [Cheat-Sheet](cheat-sheet.md)
+
+Aufgaben:
+
+1. Erstellen Sie eine
+ [Config Map](https://kubernetes.io/docs/concepts/configuration/configmap/#configmaps-and-pods)
+ für die App `Hello-Service`. Hinterlegen Sie eine passende Konfiguration,
+ sodass der Gruß aus der Config geladen wird.
+2. Binden Sie die
+ [Config Map Key als Umgebungsvariable](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#define-container-environment-variables-using-configmap-data)
+ in das Deployment des `Hello-Service` ein.
+
+Tipp: Sehen Sie sich dafür die Nutzung der Umgebungsvariable `GREETING` im
+`HelloWorldController` an.
+
+Bonus:
+
+3. Als Alternative zum expliziten Einbinden einzelner Keys als
+ Umgebungsvariable: Binden Sie alle
+ [Keys der Config Map auf einmal](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variableshttps://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables)
+ ein. Stichwort: `envFrom`.
+4. (Vorwissen: Volumes) Als Alternative zu Umgebungsvariablen: Binden Sie die
+ [Keys einer Config Map als Files](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#populate-a-volume-with-data-stored-in-a-configmap)
+ in den Container ein. Hinweis: Dafür ist natürlich auch eine Code-Anpassung nötig.
+
diff --git a/05-orchestrierung/uebung/05_bonus_uebung_ingress.md b/05-orchestrierung/uebung/05_bonus_uebung_ingress.md
new file mode 100644
index 00000000..6e68b658
--- /dev/null
+++ b/05-orchestrierung/uebung/05_bonus_uebung_ingress.md
@@ -0,0 +1,14 @@
+# Bonus Übung 5. Ingress
+
+Infos:
+
+- [Cheat-Sheet](cheat-sheet.md)
+- In der ersten Übung dieses Semesters haben Sie den nginx-ingress-controller installiert.
+
+Aufgaben:
+
+1. Legen Sie für den `Hello-Service` einen
+ [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/#the-ingress-resource)
+ an.
+2. Prüfen Sie, ob der Service von außerhalb des Clusters erreichbar ist.
+
diff --git a/05-orchestrierung/uebung/06_bonus_uebung_persistent_volumes.md b/05-orchestrierung/uebung/06_bonus_uebung_persistent_volumes.md
new file mode 100644
index 00000000..7568cf51
--- /dev/null
+++ b/05-orchestrierung/uebung/06_bonus_uebung_persistent_volumes.md
@@ -0,0 +1,20 @@
+# Bonus Übung 6. Persistent Volumes (Bonus)
+
+Infos:
+
+- [Cheat-Sheet](cheat-sheet.md)
+
+Aufgaben:
+
+1. Erstellen Sie einen
+ [Persistent Volume Claim](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reserving-a-persistentvolume)
+ für ein neues Volume.
+2. Binden Sie den Persistent Volume Claim an ein Volume und stellen Sie es im
+ Container der App `Hello-Service` bereit. Siehe
+ [Claims as Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reserving-a-persistentvolume)
+3. Öffnen Sie über `k9s` eine Shell im Container.
+ 1. Prüfen Sie, dass das Volume an der gewünschten Stelle gemounted wurde.
+ 2. Legen Sie eine Datei im Volume ab.
+4. Testen Sie, ob die Datei in diesem Volume einen Container-Neustart überlebt.
+
+
diff --git a/05-orchestrierung/uebung/07_bonus_tilt_kustomize.md b/05-orchestrierung/uebung/07_bonus_tilt_kustomize.md
new file mode 100644
index 00000000..4b053913
--- /dev/null
+++ b/05-orchestrierung/uebung/07_bonus_tilt_kustomize.md
@@ -0,0 +1,10 @@
+# Bonus Übung 7. Tilt / Kustomize
+
+Infos:
+
+- [Cheat-Sheet](cheat-sheet.md)
+
+Aufgaben:
+
+1. Benutzen Sie Kustomize, um die bisherigen YAML-Dateien mit nur einem Befehl in Kubernetes zu deployen.
+2. Erstellen Sie ein `Tiltfile`, mit dem Sie die Kustomize-Dateien im Cluster deployen.
diff --git a/05-orchestrierung/uebung/08_bonus_dashboard.md b/05-orchestrierung/uebung/08_bonus_dashboard.md
new file mode 100644
index 00000000..9d22491f
--- /dev/null
+++ b/05-orchestrierung/uebung/08_bonus_dashboard.md
@@ -0,0 +1,10 @@
+# Bonus Übung 8. Dashboard (Bonus)
+
+Infos:
+
+- [Cheat-Sheet](cheat-sheet.md)
+
+Aufgaben:
+
+1. Starten Sie das Dashboard von kubernetes mit dem Befehl `kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml`.
+2. Sehen Sie sich im Dashboard um und machen Sie sich mit den Funktionen vertraut.
diff --git a/05-orchestrierung/uebung/README.md b/05-orchestrierung/uebung/README.md
new file mode 100644
index 00000000..68faea99
--- /dev/null
+++ b/05-orchestrierung/uebung/README.md
@@ -0,0 +1,32 @@
+# Übung Orchestrierung
+
+- [Cheat-Sheet](cheat-sheet.md)
+
+### Übung 1 - Pods & Deployments
+
+- [Beschreibung](01_uebung_pods_deployments.md)
+
+### Übung 2 - Probes und Resource Constraints
+
+- [Beschreibung](02_uebung_probes_resources.md)
+
+### Übung 3 - Services
+
+- [Beschreibung](03_uebung_services.md)
+
+### Übung 4 - Config Maps
+
+- [Beschreibung](04_uebung_config_maps.md)
+
+### Übung 5 - Ingress
+
+- [Beschreibung](05_bonus_uebung_ingress.md)
+
+### Übung 6 - Persistent Volumes (Bonus)
+
+- [Beschreibung](06_bonus_uebung_persistent_volumes.md)
+
+### Übung 7 - Dashboard (Bonus)
+
+- [Beschreibung](08_bonus_dashboard.md)
+
diff --git a/05-orchestrierung/uebung/cheat-sheet.md b/05-orchestrierung/uebung/cheat-sheet.md
new file mode 100644
index 00000000..80816da3
--- /dev/null
+++ b/05-orchestrierung/uebung/cheat-sheet.md
@@ -0,0 +1,72 @@
+# Cheat Sheet
+
+- [kubectl](#kubectl)
+- [k9s](#k9s)
+- [docker](#docker)
+
+## kubectl
+
+Kubernetes YAML anwenden (Do whatever it takes to make the cluster look like this)
+
+```shell script
+kubectl apply -f path/to/file.yaml
+kubectl apply -f path/to/dir
+```
+
+Version ausgeben
+
+```shell script
+kubectl version
+```
+
+## k9s
+
+Starten
+
+```shell script
+k9s
+```
+
+Navigation
+
+- `?` Keyboard Shortcuts anzeigen
+- `:deployment` Kubernetes Resource anzeigen (Bsp.: `:deployments`, `:services`, `:pods`, `:ingress`, ...)
+- `/` Aktuelle Anzeige filtern
+- `enter` Intelligent untergeordnete Resource / Konzept anzeigen (`service` -> `pods` -> `containers` -> `logs`)
+- `esc` Raus aus: Ansicht / Kommando / Filter
+- `d,v,e,l,…` Describe, View, Edit, Logs, ...
+
+Version ausgeben
+
+```shell script
+k9s version
+```
+
+## docker
+
+Container-Image bauen
+
+```shell script
+# docker build -t
+docker build -t my-app:1 .
+```
+
+Shell in einem laufenden Container starten
+
+```shell script
+# docker exec -it /bin/bash
+docker exec -it 066c891518fa /bin/bash
+```
+
+Neuen Container starten und Shell öffnen
+
+```shell script
+# docker run -it --entrypoint
+docker run --rm -it --entrypoint bash my-app
+```
+
+Version ausgeben
+
+```shell script
+docker version
+```
diff --git a/10-big-data/uebung/loesung/spark-lib/after_maven_install_a_jar_file_is_in_this_directory.txt b/05-orchestrierung/uebung/code/.gitkeep
similarity index 100%
rename from 10-big-data/uebung/loesung/spark-lib/after_maven_install_a_jar_file_is_in_this_directory.txt
rename to 05-orchestrierung/uebung/code/.gitkeep
diff --git a/06-orchestrierung/uebung/loesung/aufgabe-2/.mvn/wrapper/MavenWrapperDownloader.java b/05-orchestrierung/uebung/code/hello-service/.mvn/wrapper/MavenWrapperDownloader.java
similarity index 100%
rename from 06-orchestrierung/uebung/loesung/aufgabe-2/.mvn/wrapper/MavenWrapperDownloader.java
rename to 05-orchestrierung/uebung/code/hello-service/.mvn/wrapper/MavenWrapperDownloader.java
diff --git a/06-orchestrierung/uebung/loesung/aufgabe-2/.mvn/wrapper/maven-wrapper.properties b/05-orchestrierung/uebung/code/hello-service/.mvn/wrapper/maven-wrapper.properties
similarity index 100%
rename from 06-orchestrierung/uebung/loesung/aufgabe-2/.mvn/wrapper/maven-wrapper.properties
rename to 05-orchestrierung/uebung/code/hello-service/.mvn/wrapper/maven-wrapper.properties
diff --git a/05-orchestrierung/uebung/code/hello-service/Dockerfile b/05-orchestrierung/uebung/code/hello-service/Dockerfile
new file mode 100644
index 00000000..f050e5e0
--- /dev/null
+++ b/05-orchestrierung/uebung/code/hello-service/Dockerfile
@@ -0,0 +1,6 @@
+FROM azul/zulu-openjdk:17-latest
+
+COPY target/hello-service-*.jar /app.jar
+EXPOSE 8000
+
+ENTRYPOINT ["java", "-jar", "/app.jar"]
diff --git a/05-orchestrierung/uebung/code/hello-service/build-to-kubernetes.sh b/05-orchestrierung/uebung/code/hello-service/build-to-kubernetes.sh
new file mode 100755
index 00000000..fa9e5e45
--- /dev/null
+++ b/05-orchestrierung/uebung/code/hello-service/build-to-kubernetes.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# Package our service
+./mvnw package
+
+# Build container image
+VERSION="1"
+docker build -t "helloservice:${VERSION}" .
+
+kind load docker-image "helloservice:${VERSION}" --name=cc-2023
diff --git a/05-orchestrierung/uebung/code/hello-service/k8s/.gitkeep b/05-orchestrierung/uebung/code/hello-service/k8s/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/05-orchestrierung/uebung/code/hello-service/mvnw b/05-orchestrierung/uebung/code/hello-service/mvnw
new file mode 100755
index 00000000..66df2854
--- /dev/null
+++ b/05-orchestrierung/uebung/code/hello-service/mvnw
@@ -0,0 +1,308 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.2.0
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "$(uname)" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
+ else
+ JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
+ JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="$(which javac)"
+ if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=$(which readlink)
+ if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
+ if $darwin ; then
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
+ else
+ javaExecutable="$(readlink -f "\"$javaExecutable\"")"
+ fi
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=$(cd "$wdir/.." || exit 1; pwd)
+ fi
+ # end of workaround
+ done
+ printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ # Remove \r in case we run on Windows within Git Bash
+ # and check out the repository with auto CRLF management
+ # enabled. Otherwise, we may read lines that are delimited with
+ # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
+ # splitting rules.
+ tr -s '\r\n' ' ' < "$1"
+ fi
+}
+
+log() {
+ if [ "$MVNW_VERBOSE" = true ]; then
+ printf '%s\n' "$1"
+ fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+log "$MAVEN_PROJECTBASEDIR"
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
+if [ -r "$wrapperJarPath" ]; then
+ log "Found $wrapperJarPath"
+else
+ log "Couldn't find $wrapperJarPath, downloading it ..."
+
+ if [ -n "$MVNW_REPOURL" ]; then
+ wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ else
+ wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ fi
+ while IFS="=" read -r key value; do
+ # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
+ safeValue=$(echo "$value" | tr -d '\r')
+ case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
+ esac
+ done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+ log "Downloading from: $wrapperUrl"
+
+ if $cygwin; then
+ wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ fi
+
+ if command -v wget > /dev/null; then
+ log "Found wget ... using wget"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ log "Found curl ... using curl"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ else
+ curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ fi
+ else
+ log "Falling back to using Java to download"
+ javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaSource=$(cygpath --path --windows "$javaSource")
+ javaClass=$(cygpath --path --windows "$javaClass")
+ fi
+ if [ -e "$javaSource" ]; then
+ if [ ! -e "$javaClass" ]; then
+ log " - Compiling MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/javac" "$javaSource")
+ fi
+ if [ -e "$javaClass" ]; then
+ log " - Running MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+# If specified, validate the SHA-256 sum of the Maven wrapper jar file
+wrapperSha256Sum=""
+while IFS="=" read -r key value; do
+ case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
+ esac
+done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+if [ -n "$wrapperSha256Sum" ]; then
+ wrapperSha256Result=false
+ if command -v sha256sum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ elif command -v shasum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
+ echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+ exit 1
+ fi
+ if [ $wrapperSha256Result = false ]; then
+ echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
+ echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
+ echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+# shellcheck disable=SC2086 # safe args
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/05-orchestrierung/uebung/code/hello-service/mvnw.cmd b/05-orchestrierung/uebung/code/hello-service/mvnw.cmd
new file mode 100644
index 00000000..95ba6f54
--- /dev/null
+++ b/05-orchestrierung/uebung/code/hello-service/mvnw.cmd
@@ -0,0 +1,205 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.2.0
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %WRAPPER_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
+SET WRAPPER_SHA_256_SUM=""
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+)
+IF NOT %WRAPPER_SHA_256_SUM%=="" (
+ powershell -Command "&{"^
+ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
+ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
+ " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+ " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+ " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+ " exit 1;"^
+ "}"^
+ "}"
+ if ERRORLEVEL 1 goto error
+)
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/05-orchestrierung/uebung/code/hello-service/pom.xml b/05-orchestrierung/uebung/code/hello-service/pom.xml
new file mode 100644
index 00000000..12a8fc31
--- /dev/null
+++ b/05-orchestrierung/uebung/code/hello-service/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.5
+
+
+ de.qaware.edu.cc.k8s-demo
+ hello-service
+ 0.0.1-SNAPSHOT
+ hello-service
+ Hello World Service
+
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/05-orchestrierung/uebung/code/hello-service/src/main/java/com/example/demo/HelloApplication.java b/05-orchestrierung/uebung/code/hello-service/src/main/java/com/example/demo/HelloApplication.java
new file mode 100644
index 00000000..d4c71c6c
--- /dev/null
+++ b/05-orchestrierung/uebung/code/hello-service/src/main/java/com/example/demo/HelloApplication.java
@@ -0,0 +1,11 @@
+package com.example.demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class HelloApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(HelloApplication.class, args);
+ }
+}
diff --git a/05-orchestrierung/uebung/code/hello-service/src/main/java/com/example/demo/HelloWorldController.java b/05-orchestrierung/uebung/code/hello-service/src/main/java/com/example/demo/HelloWorldController.java
new file mode 100644
index 00000000..7946b56c
--- /dev/null
+++ b/05-orchestrierung/uebung/code/hello-service/src/main/java/com/example/demo/HelloWorldController.java
@@ -0,0 +1,21 @@
+package com.example.demo;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class HelloWorldController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(HelloWorldController.class);
+
+ @GetMapping(value = "/hello", produces = MediaType.TEXT_PLAIN_VALUE)
+ public String hello() {
+ LOGGER.info("hello() called");
+
+ final String greeting = System.getenv("GREETING");
+ return (greeting == null ? "Hello" : greeting) + ", World!";
+ }
+}
diff --git a/05-orchestrierung/uebung/code/hello-service/src/main/resources/application.yml b/05-orchestrierung/uebung/code/hello-service/src/main/resources/application.yml
new file mode 100644
index 00000000..1e131612
--- /dev/null
+++ b/05-orchestrierung/uebung/code/hello-service/src/main/resources/application.yml
@@ -0,0 +1,10 @@
+spring:
+ application:
+ name: "helloservice"
+server:
+ port: 8000
+management:
+ endpoint:
+ health:
+ probes:
+ enabled: true
diff --git a/05-orchestrierung/uebung/loesung/01_loesungen_pods_deployments/01_deployment.yaml b/05-orchestrierung/uebung/loesung/01_loesungen_pods_deployments/01_deployment.yaml
new file mode 100644
index 00000000..c4ce418a
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/01_loesungen_pods_deployments/01_deployment.yaml
@@ -0,0 +1,21 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: helloservice-deployment
+ labels:
+ app: helloservice
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: helloservice
+ template:
+ metadata:
+ labels:
+ app: helloservice
+ spec:
+ containers:
+ - name: helloservice-container
+ image: helloservice:1
+ ports:
+ - containerPort: 8000
diff --git a/05-orchestrierung/uebung/loesung/01_loesungen_pods_deployments/01_loesung_pods_deployments.md b/05-orchestrierung/uebung/loesung/01_loesungen_pods_deployments/01_loesung_pods_deployments.md
new file mode 100644
index 00000000..b89a1242
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/01_loesungen_pods_deployments/01_loesung_pods_deployments.md
@@ -0,0 +1,24 @@
+# Lösung 1. Pods & Deployments
+
+Aufgaben:
+
+4. Schreibt ein Kubernetes Deployment
+
+siehe `01_deployment.yaml`
+
+Bonus:
+
+9. Ändert die Anzahl der Replikas des Deployments
+
+ 2. über `kubectl scale`
+ ```shell script
+ kubectl scale --replicas=2 deployment/helloservice-deployment
+ ```
+
+ 3. In welcher Reihenfolge passiert das Rolling Upgrade? (z.B. Zug-um-Zug oder wird erst komplett die neue Version ausgerollt und dann die alte heruntergefahren?)
+ - Zug-um-Zug / Inkrementell:
+ - Starten Neu-1
+ - Runterfahren Alt-1
+ - Starten Neu-2
+ - Runterfahren Alt-2
+
diff --git a/05-orchestrierung/uebung/loesung/02_loesungen_probes_resources/01_deployment.yaml b/05-orchestrierung/uebung/loesung/02_loesungen_probes_resources/01_deployment.yaml
new file mode 100644
index 00000000..7693ed7b
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/02_loesungen_probes_resources/01_deployment.yaml
@@ -0,0 +1,40 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: helloservice-deployment
+ labels:
+ app: helloservice
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: helloservice
+ template:
+ metadata:
+ labels:
+ app: helloservice
+ spec:
+ containers:
+ - name: helloservice-container
+ image: helloservice:1
+ ports:
+ - containerPort: 8000
+ readinessProbe:
+ httpGet:
+ path: /actuator/health/readiness
+ port: 8000
+ initialDelaySeconds: 15
+ periodSeconds: 3
+ livenessProbe:
+ httpGet:
+ path: /actuator/health/liveness
+ port: 8000
+ initialDelaySeconds: 30
+ periodSeconds: 3
+ resources:
+ requests:
+ memory: "256Mi"
+ cpu: "250m"
+ limits:
+ memory: "512Mi"
+ cpu: "0.5"
diff --git a/05-orchestrierung/uebung/loesung/02_loesungen_probes_resources/02_loesung_probes_resources.md b/05-orchestrierung/uebung/loesung/02_loesungen_probes_resources/02_loesung_probes_resources.md
new file mode 100644
index 00000000..60f01887
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/02_loesungen_probes_resources/02_loesung_probes_resources.md
@@ -0,0 +1,47 @@
+# Lösung 2. Probes und Resource Constraints
+
+Aufgaben:
+
+1. Liveness und Readiness Probes
+
+siehe `01_deployment.yaml`
+
+2. Resource Requests und Limits
+
+siehe `01_deployment.yaml`
+
+Bonus:
+
+4. Prüft, was passiert, wenn die Readiness oder Liveness Probes
+ fehlschlagen
+
+- Liveness Probe: Container wird neu gestartet. Restarts Counter wird inkrementiert.
+- Readiness: Container+Pod wird als "Not Ready" markiert
+
+5. Zu wenige oder zu viele Ressourcen
+
+- zu wenige: Container startet nicht / crasht / wird restarted
+- zu viele: Pod kann nicht gescheduled werden (Pending)
+
+6. Startup Probe
+
+```yaml
+startupProbe:
+ httpGet:
+ path: /actuator/health/liveness
+ port: 8000
+ failureThreshold: 30
+ periodSeconds: 10
+```
+
+7. Probe Zeit berechnen
+
+- 10 Sekunden warten (`initialDelaySeconds`)
+- Readiness Check 1/3 -> nach 5 Sekunden (`timeoutSeconds`) Ergebnis da: fehlgeschlagen
+- 20 Sekunden warten (`periodSeconds`)
+- Readiness Check 2/3 -> nach 5 Sekunden Ergebnis da: fehlgeschlagen
+- 20 Sekunden warten
+- Readiness check 3/3 (`failureThreshold`) -> nach 5 Sekunden Ergebnis da, fehlgeschlagen
+
+Ergebnis: "Not Ready" nach 10 + (20 * 2) + 5 = 55 sec
+
diff --git a/05-orchestrierung/uebung/loesung/03_loesungen_services/02_service.yaml b/05-orchestrierung/uebung/loesung/03_loesungen_services/02_service.yaml
new file mode 100644
index 00000000..d1195a76
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/03_loesungen_services/02_service.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: helloservice-service
+ labels:
+ app: helloservice
+spec:
+ selector:
+ app: helloservice
+ ports:
+ - protocol: TCP
+ port: 8000
+ targetPort: 8000
diff --git a/05-orchestrierung/uebung/loesung/03_loesungen_services/03_loesung_services.md b/05-orchestrierung/uebung/loesung/03_loesungen_services/03_loesung_services.md
new file mode 100644
index 00000000..ce9b69c2
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/03_loesungen_services/03_loesung_services.md
@@ -0,0 +1,24 @@
+# Lösung 3. Services
+
+Aufgaben:
+
+1. Legt für die App `Hello-Service` einen Service an
+
+- siehe `02_service.yaml`
+- deployment: `kubectl apply -f 02_service.yaml`
+
+2. Startet einen temporären Pod und überprüft, dass der Service erreichbar ist.
+
+```shell script
+❯ kubectl run my-shell --rm -i --tty --image byrnedo/alpine-curl --command sh
+If you don't see a command prompt, try pressing enter.
+/ # curl helloservice-service:8000/hello
+Howdy, World!/ #
+```
+
+Bonus:
+
+3. Load Balancing testen
+
+Kommt bald ...
+
diff --git a/05-orchestrierung/uebung/loesung/04_loesungen_config_maps/01_deployment.yaml b/05-orchestrierung/uebung/loesung/04_loesungen_config_maps/01_deployment.yaml
new file mode 100644
index 00000000..15d1d6d3
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/04_loesungen_config_maps/01_deployment.yaml
@@ -0,0 +1,46 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: helloservice-deployment
+ labels:
+ app: helloservice
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: helloservice
+ template:
+ metadata:
+ labels:
+ app: helloservice
+ spec:
+ containers:
+ - name: helloservice-container
+ image: helloservice:1
+ ports:
+ - containerPort: 8000
+ readinessProbe:
+ httpGet:
+ path: /actuator/health/readiness
+ port: 8000
+ initialDelaySeconds: 15
+ periodSeconds: 3
+ livenessProbe:
+ httpGet:
+ path: /actuator/health/liveness
+ port: 8000
+ initialDelaySeconds: 30
+ periodSeconds: 3
+ resources:
+ requests:
+ memory: "256Mi"
+ cpu: "250m"
+ limits:
+ memory: "512Mi"
+ cpu: "0.5"
+ env:
+ - name: GREETING
+ valueFrom:
+ configMapKeyRef:
+ name: helloservice-config
+ key: greeting
diff --git a/05-orchestrierung/uebung/loesung/04_loesungen_config_maps/03_config.yaml b/05-orchestrierung/uebung/loesung/04_loesungen_config_maps/03_config.yaml
new file mode 100644
index 00000000..66f44f4f
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/04_loesungen_config_maps/03_config.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: helloservice-config
+ labels:
+ app: helloservice
+data:
+ greeting: "Howdy"
diff --git a/05-orchestrierung/uebung/loesung/04_loesungen_config_maps/04_loesung_config_maps.md b/05-orchestrierung/uebung/loesung/04_loesungen_config_maps/04_loesung_config_maps.md
new file mode 100644
index 00000000..e2d2b02e
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/04_loesungen_config_maps/04_loesung_config_maps.md
@@ -0,0 +1,45 @@
+# Lösung 4. Config Maps
+
+Aufgaben:
+
+1. Erstellt eine Config Map
+
+siehe `03_config.yaml`
+
+2. Bindet die Config Map ein
+
+siehe `01_deployment.yaml`
+
+Bonus:
+
+3. Als Alternative zum expliziten Einbinden einzelner Keys als
+ Umgebungsvariable: Bindet alle
+ [Keys der Config Map auf einmal](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variableshttps://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables)
+ ein. Stichwort: `envFrom`.
+
+in `01_deployment.yaml`:
+
+```yaml
+ envFrom:
+ - configMapRef:
+ name: helloservice-config
+```
+
+Dann muss die Config Map die Keys so enthalten, wie sie als Umgebungsvariablen erwartet werden:
+
+```yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: helloservice-config
+ labels:
+ app: helloservice
+data:
+ GREETING: "Howdy"
+```
+
+
+4. Config Map als Files
+
+Kommt bald :)
+
diff --git a/05-orchestrierung/uebung/loesung/05_loesung_ingress/04_ingress.yaml b/05-orchestrierung/uebung/loesung/05_loesung_ingress/04_ingress.yaml
new file mode 100644
index 00000000..37d5fe15
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/05_loesung_ingress/04_ingress.yaml
@@ -0,0 +1,15 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: helloservice-ingress
+spec:
+ rules:
+ - http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: helloservice-service
+ port:
+ number: 8000
diff --git a/05-orchestrierung/uebung/loesung/05_loesung_ingress/05_loesung_ingress.md b/05-orchestrierung/uebung/loesung/05_loesung_ingress/05_loesung_ingress.md
new file mode 100644
index 00000000..b648f47a
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/05_loesung_ingress/05_loesung_ingress.md
@@ -0,0 +1,14 @@
+# Lösung Bonus Übung 5. Ingress
+
+Aufgaben:
+
+1. Legt für den `Hello-Service` einen
+
+siehe `04_ingress.yaml`
+
+2. Prüft, ob euer Service von außerhalb des Clusters erreichbar ist
+
+```shell script
+http $(minikube ip)/hello
+```
+
diff --git a/05-orchestrierung/uebung/loesung/06_loesung_persistent_volumes/01_deployment.yaml b/05-orchestrierung/uebung/loesung/06_loesung_persistent_volumes/01_deployment.yaml
new file mode 100644
index 00000000..a5cfcd56
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/06_loesung_persistent_volumes/01_deployment.yaml
@@ -0,0 +1,53 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: helloservice-deployment
+ labels:
+ app: helloservice
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: helloservice
+ template:
+ metadata:
+ labels:
+ app: helloservice
+ spec:
+ containers:
+ - name: helloservice-container
+ image: helloservice:1
+ ports:
+ - containerPort: 8000
+ readinessProbe:
+ httpGet:
+ path: /actuator/health/readiness
+ port: 8000
+ initialDelaySeconds: 15
+ periodSeconds: 3
+ livenessProbe:
+ httpGet:
+ path: /actuator/health/liveness
+ port: 8000
+ initialDelaySeconds: 30
+ periodSeconds: 3
+ resources:
+ requests:
+ memory: "256Mi"
+ cpu: "250m"
+ limits:
+ memory: "512Mi"
+ cpu: "0.5"
+ env:
+ - name: GREETING
+ valueFrom:
+ configMapKeyRef:
+ name: helloservice-config
+ key: greeting
+ volumeMounts:
+ - mountPath: "/data"
+ name: helloservice-volume
+ volumes:
+ - name: helloservice-volume
+ persistentVolumeClaim:
+ claimName: helloservice-pvc
diff --git a/05-orchestrierung/uebung/loesung/06_loesung_persistent_volumes/05_pvc.yaml b/05-orchestrierung/uebung/loesung/06_loesung_persistent_volumes/05_pvc.yaml
new file mode 100644
index 00000000..0a5a7961
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/06_loesung_persistent_volumes/05_pvc.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: helloservice-pvc
+spec:
+ accessModes:
+ - ReadWriteOnce
+ volumeMode: Filesystem
+ resources:
+ requests:
+ storage: 100Mi
diff --git a/05-orchestrierung/uebung/loesung/06_loesung_persistent_volumes/06_loesung_persistent_volumes.md b/05-orchestrierung/uebung/loesung/06_loesung_persistent_volumes/06_loesung_persistent_volumes.md
new file mode 100644
index 00000000..afd82fbc
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/06_loesung_persistent_volumes/06_loesung_persistent_volumes.md
@@ -0,0 +1,13 @@
+# Lösung Bonus Übung 6. Persistent Volumes (Bonus)
+
+Aufgaben:
+
+1. Erstellt einen
+
+siehe `05_pvc.yaml`
+
+2. PVC anbinden und im Container bereitstellen
+
+siehe `01_deployment.yaml`
+
+
diff --git a/05-orchestrierung/uebung/loesung/README.md b/05-orchestrierung/uebung/loesung/README.md
new file mode 100644
index 00000000..c3b93820
--- /dev/null
+++ b/05-orchestrierung/uebung/loesung/README.md
@@ -0,0 +1,34 @@
+# Lösungen
+
+### Lösung Übung 1 - Pods & Deployments
+
+- [Beschreibung](01_loesungen_pods_deployments/01_loesung_pods_deployments.md)
+- [deployment.yaml](01_loesungen_pods_deployments/01_deployment.yaml)
+
+### Lösung Übung 2 - Probes und Resource Constraints
+
+- [Beschreibung](02_loesungen_probes_resources/02_loesung_probes_resources.md)
+- [Angepasste deployment.yaml](02_loesungen_probes_resources/01_deployment.yaml)
+
+### Lösung Übung 3 - Services
+
+- [Beschreibung](03_loesungen_services/03_loesung_services.md)
+- [service.yaml](03_loesungen_services/02_service.yaml)
+
+### Lösung Übung 4 - Config Maps
+
+- [Beschreibung](04_loesungen_config_maps/04_loesung_config_maps.md)
+- [Angepasste deployment.yaml](04_loesungen_config_maps/01_deployment.yaml)
+- [config.yaml](04_loesungen_config_maps/03_config.yaml)
+
+### Lösung Übung 5 - Ingress
+
+- [Beschreibung](05_loesung_ingress/05_loesung_ingress.md)
+- [ingress.yaml](05_loesung_ingress/04_ingress.yaml)
+
+### Lösung Übung 6 - Persistent Volumes (Bonus)
+
+- [Beschreibung](06_loesung_persistent_volumes/06_loesung_persistent_volumes.md)
+- [Angepasste deployment.yaml](06_loesung_persistent_volumes/01_deployment.yaml)
+- [pvc.yaml](06_loesung_persistent_volumes/05_pvc.yaml)
+
diff --git a/05-orchestrierung/vorlesung/.gitattributes b/05-orchestrierung/vorlesung/.gitattributes
new file mode 100644
index 00000000..64e19e0b
--- /dev/null
+++ b/05-orchestrierung/vorlesung/.gitattributes
@@ -0,0 +1 @@
+*.{png,,jpg,jpeg,gif,pdf,doc,docx,ppt,pptx,xls,xlsx} filter=lfs diff=lfs merge=lfs -text
\ No newline at end of file
diff --git a/05-orchestrierung/vorlesung/m05-orchestrierung.pdf b/05-orchestrierung/vorlesung/m05-orchestrierung.pdf
new file mode 100644
index 00000000..b6111360
Binary files /dev/null and b/05-orchestrierung/vorlesung/m05-orchestrierung.pdf differ
diff --git a/07-cloud-architektur/uebung-optional/README.md b/07-cloud-architektur/uebung-optional/README.md
deleted file mode 100644
index 104a903a..00000000
--- a/07-cloud-architektur/uebung-optional/README.md
+++ /dev/null
@@ -1,173 +0,0 @@
-# Übung: Traefik und Consul
-## Aufgabe 1: Erste Erfahrungen mit Traefik und Consul sammeln
-
-Ziel dieser Übung ist es, erste praktische Erfahrungen mit Traefik und Consul zu machen.
-Die Übung basiert dabei auf Tutorials, die auf [Katacoda](https://www.katacoda.com/) verfügbar sind.
-
-### Vorbereitung
-
-1. Bitte richten sie einen [Katacoda](https://www.katacoda.com/) Account für sich ein
-
-### Aufgaben
-Arbeiten Sie die folgenden Tutorials auf Katacoda durch.
-Hinweis: Im Browser steht Ihnen dabei eine umfassende Umgebung zur Verfügung. Nutzen Sie dies auch, um mit Traefik und Consul
-zu experimentieren.
-
-
-1. [Traefic als Router/Edge Server](https://katacoda.com/boxboat/courses/k8s-adv/03-using-traefik-edge-router)
-
-2. [Setup Consul Cluster](https://katacoda.com/courses/consul/launch-docker-cluster)
-
-3. [Consul Konfiguration](https://katacoda.com/hashicorp/scenarios/consul-connect)
-
-## Aufgabe 2
-
-Ziel dieser Übung ist es, einen einfachen Spring Cloud REST Service zusammen mit Consul
-für Service Discovery und Configuration und Traeffik als Edge Server aufzusetzen.
-
-Orientieren Sie sich an diesem [Tutorial](https://m.mattmclaugh.com/traefik-and-consul-catalog-example-2c33fc1480c0).
-
-## Vorbereitung
-
-* Das Aufsetzen eines Spring Cloud Microservice ist in Übung 1 beschrieben. Die Lösung dieser
-Übung dient als Startpunkt und wird in dieser Übung erweitert.
-
-## Aufgaben
-
-### Consul Cluster (Single Node) mit Docker Compose
-
-Legen Sie ein Docker Compose File an.
-Starten Sie darin ein Consul Cluster mit einem Node. Verwenden Sie das dafür aktuellste offizielle Docker Image von Hashicorp.
-
-Stellen Sie sicher, dass die Consul UI gestartet wird, und alle benötigen Ports exponiert werden.
-
-Orientieren Sie sich an folgendem Abschnitt, falls Sie nicht weiterkommen:
-```
- consul:
- image: consul
- command: consul agent -server -dev -client=0.0.0.0 -ui -bootstrap -log-level warn
- ports:
- - "8400:8400"
- - "8500:8500"
- - "8600:53/udp"
-```
-
-### Traefik Edge Service mit Consul Backend
-
-Betreiben Sie den Traefik Edge Service mittels Docker Compose und verwenden Sie
-Consul als Discovery Backend für Traefik.
-
-
-Legen Sie dazu ein Dockerfile und eine Traefik Konfiguration an.
-Falls Sie nicht weiterkommen, verwenden Sie das Dockerfile und die Konfiguration unter /traefik.
-
-Bauen Sie einen Container auf Basis des Dockerfiles und geben Sie diesem den Namen reverse-proxy:
-
-```
-docker build -t reverse-proxy .
-```
-
-Starten Sie den Container ebenfalls über Docker Compose und übergeben Sie beim Start die nötigen Konfigurationen zur
-Interaktion mit Consul.
-
-Falls Sie nicht weiterkommen, verwenden Sie folgenden Abschnitt:
-
-```
- reverse-proxy:
- image: reverse-proxy
- command: traefik --consulcatalog.endpoint=consul:8500
- ports:
- - "8080:8080"
- - "8081:80"
- depends_on:
- - consul
- links:
- - consul
-```
-
-### Spring Cloud Microservice
-
-Ziel dieser Aufgabe ist es, die Microservice aus Übung 1 so zu erweitern, dass sich dieser
-
-* beim Start bei der Consul Service Discovery anmeldet,
-* beim Start seine Konfigurationswerte bei Consul abholt,
-* die Service-Schnittstellen (nicht die Admin Schnittstellen) über Traefik aufgerufen werden können
-
-Die folgenden Dependencies müssen der `pom.xml` hinzugefügt werden:
-
-```xml
-
-
- org.springframework.cloud
- spring-cloud-starter-consul-config
-
-
- org.springframework.cloud
- spring-cloud-starter-consul-discovery
-
-```
-
-Danach muss unter `src/main/resources` die Datei `bootstrap.properties` angelegt werden:
-
-```
-spring.application.name=book-service
-
-# specify Consul host and port
-# we use the CONSUL_HOST and CONSUL_PORT env variables
-# later set in docker compose as well as Kubernetes
-spring.cloud.consul.host=${consul.host:consul}
-spring.cloud.consul.port=${consul.port:8500}
-
-spring.cloud.consul.config.enabled=true
-spring.cloud.consul.config.prefix=configuration
-spring.cloud.consul.config.default-context=application
-
-# do not fail at startup if Consul is not there
-spring.cloud.consul.config.fail-fast=false
-
-# store properties as blob in property syntax
-# e.g. configuration/book-service/data
-spring.cloud.consul.config.format=properties
-spring.cloud.consul.config.data-key=data
-```
-
-In der `application.properties` müssen zudem folgende Properties angelegt werden, um die Service-Registrierung
-in Consul und die Tags für Traefik korrekt zu konfigurieren:
-
-```
-# assign a unique instance ID
-spring.cloud.consul.discovery.instance-id=${spring.application.name}:${spring.application.instance_id:${random.value}}
-
-# required by Docker compose and Consul to run the health check
-# register IP address and heartbeats
-spring.cloud.consul.discovery.prefer-ip-address=true
-spring.cloud.consul.discovery.heartbeat.enabled=true
-
-spring.cloud.consul.discovery.tags=traefik.enable=true,traefik.frontend.rule=PathPrefixStrip:/book-service,traefik.tags=api,traefik.frontend.entrypoint=h
-```
-
-Starten Sie den Service ebenfalls über Docker Compose.
-
-Falls Sie nicht weiterkommen, verwenden Sie folgenden Abschnitt:
-```
- book-service:
- build: ./book-service
- image: book-service:1.1.0
- ports:
- - 18080:18080
- depends_on:
- - consul
- networks:
- - cloud-architecture
- environment:
- - SPRING_CLOUD_CONSUL_HOST=consul
-```
-### Testen
-
-* Die Anwendung sollte nun unter `http://localhost:8081/book-service/api/books` erreichbar sein.
-* Die Consul UI sollte unter `http://localhost:8500/ui` erreichbar sein.
-* Die Traefik UI sollte unter `http://localhost:8080` erreichbar sein.
-
-### Bonus: Orchestrierung mit Kubernetes
-
-Bringen sie das Gespann aus Consul, Traefik und dem Microservice in Kubernetes zum Laufen.
diff --git a/07-cloud-architektur/uebung-optional/traefik/Dockerfile b/07-cloud-architektur/uebung-optional/traefik/Dockerfile
deleted file mode 100644
index 2b1af0ac..00000000
--- a/07-cloud-architektur/uebung-optional/traefik/Dockerfile
+++ /dev/null
@@ -1,5 +0,0 @@
-FROM traefik:v1.4-alpine
-
-EXPOSE 8080
-
-COPY traefik.toml /etc/traefik/traefik.toml
\ No newline at end of file
diff --git a/07-cloud-architektur/uebung-optional/traefik/traefik.toml b/07-cloud-architektur/uebung-optional/traefik/traefik.toml
deleted file mode 100644
index 97a7821e..00000000
--- a/07-cloud-architektur/uebung-optional/traefik/traefik.toml
+++ /dev/null
@@ -1,55 +0,0 @@
-################################################################
-# Consul Catalog configuration backend
-################################################################
-defaultEntryPoints = ["http"]
-
-[entryPoints]
- [entryPoints.http]
- address = ":80"
-
-# Enable web configuration backend
-[web]
-
-# Web administration port
-#
-# Required
-#
-address = ":8080"
-
-# Enable Consul Catalog configuration backend
-[consulCatalog]
-
-# Consul server endpoint
-#
-# Required
-#
-endpoint = "127.0.0.1:8500"
-
-# Default domain used.
-#
-# Optional
-#
-domain = "consul.localhost"
-
-# Expose Consul catalog services by default in traefik
-#
-# Optional
-#
-exposedByDefault = false
-
-# Prefix for Consul catalog tags
-#
-# Optional
-#
-prefix = "traefik"
-
-# Default frontEnd Rule for Consul services
-#
-# The format is a Go Template with:
-# - ".ServiceName", ".Domain" and ".Attributes" available
-# - "getTag(name, tags, defaultValue)", "hasTag(name, tags)" and "getAttribute(name, tags, defaultValue)" functions are available
-# - "getAttribute(...)" function uses prefixed tag names based on "prefix" value
-#
-# Optional
-#
-#frontEndRule = "Host:{{.ServiceName}}.{{Domain}}"
\ No newline at end of file
diff --git a/07-cloud-architektur/uebung/README.md b/07-cloud-architektur/uebung/README.md
deleted file mode 100644
index ed254f6a..00000000
--- a/07-cloud-architektur/uebung/README.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# Übung: Cloud Architektur
-## Aufgabe 1: Eight Fallacies of Distributed Computing
-
-Recherchieren Sie in Ihrer Gruppe die angegebenen Irrtümer / Trugschlüsse der Verteilten Verarbeitung.
-Sie können dafür diesen [Artikel](https://www.simpleorientedarchitecture.com/8-fallacies-of-distributed-systems/)
-nutzen oder frei recherchieren.
-
-Bereiten Sie gemeinsam jeweils einen kurzen Foliensatz vor (1-3 Slides), in dem Sie
-* das angesprochene Problem beschreiben
-* mögliche Lösungen hierfür aufzeigen
-
-Finden Sie einen Vertreter Ihrer Gruppe, der die Vorstellung übernimmt.
-
-Bearbeiten Sie in den Gruppen die folgenden Punkte:
-
-* Gruppe 1:
- * The network is reliable
- * Latency is zero
-* Gruppe 2:
- * Bandwidth is infinite
- * The network is secure
-* Gruppe 3:
- * Topology doesn't change
- * There is one administrator
-* Gruppe 4:
- * Transport cost is zero
- * The network is homogeneous
-
-## Aufgabe 2: Twelve Factor Apps
-
-Die [Twelve Factor Apps](https://12factor.net/) beschreiben Methoden bzw. Empfehlungen zur Entwicklung von
-Cloud Anwendungen.
-
-Recherchieren Sie in Ihrer Gruppe die angegebenen Faktoren. Sie können dafür diese
-[Slides](https://www.slideshare.net/Alicanakku1/12-factor-apps)
-nutzen oder frei recherchieren.
-
-Bereiten Sie gemeinsam jeweils einen kurzen Foliensatz vor (1 Slide je Factor), in dem Sie
-* den Idee hinter dem jeweiligen Factor benennen
-* die Empfehlung erläutern
-
-Bearbeiten Sie in den Gruppen die folgenden Punkte:
-
-* Gruppe 1:
- * Codebase
- * Dependencies
- * Configuration
-* Gruppe 2:
- * Backing Services
- * Build, release, run
- * Processes
-* Gruppe 3:
- * Port binding
- * Concurrency
- * Disposability
-* Gruppe 4:
- * Dev/Prod Parity
- * Logs
- * Admin Processes
-
-Finden Sie einen Vertreter Ihrer Gruppe, der die Vorstellung übernimmt.
-
-### Aufgabe 3: Diagnosablity mit Prometheus
-Ziel dieser Übung ist es, Prometheus als Tool zum Sammeln und Auswerten von Metriken kennen zu lernen.
-
-Arbeiten Sie die folgenden Tutorials auf Katacoda durch.
-Hinweis: Im Browser steht Ihnen dabei eine umfassende Umgebung zur Verfügung. Nutzen Sie dies auch, um mit Prometheus
-zu experimentieren.
-
-1. [Getting started with Prometheus](https://www.katacoda.com/courses/prometheus/getting-started)
-2. [Graphing Docker Metrics with Prometheus](https://www.katacoda.com/courses/prometheus/docker-metrics)
-
-### Aufgabe 4: Raft Konsens Protokoll
-
-Erarbeiten Sie die Funktionsweise vom Raft Protokoll mithilfe folgender
-[Demo](http://thesecretlivesofdata.com/raft/).
-
diff --git a/07-cloud-architektur/vorlesung/m07-cloud-architektur.pdf b/07-cloud-architektur/vorlesung/m07-cloud-architektur.pdf
deleted file mode 100644
index 0c3065fb..00000000
Binary files a/07-cloud-architektur/vorlesung/m07-cloud-architektur.pdf and /dev/null differ
diff --git a/07-iaas/loesung/teil-2/.gitignore b/07-iaas/loesung/teil-2/.gitignore
new file mode 100644
index 00000000..1cd30efd
--- /dev/null
+++ b/07-iaas/loesung/teil-2/.gitignore
@@ -0,0 +1,228 @@
+# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,intellij+all,visualstudiocode,terraform,direnv,dotenv
+# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,intellij+all,visualstudiocode,terraform,direnv,dotenv
+
+### direnv ###
+.direnv
+.envrc
+
+### dotenv ###
+.env
+
+### Intellij+all ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij+all Patch ###
+# Ignore everything but code style settings and run configurations
+# that are supposed to be shared within teams.
+
+.idea/*
+
+!.idea/codeStyles
+!.idea/runConfigurations
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### macOS Patch ###
+# iCloud generated files
+*.icloud
+
+### Terraform ###
+# Local .terraform directories
+**/.terraform/*
+
+# .tfstate files
+*.tfstate
+*.tfstate.*
+
+# Crash log files
+crash.log
+crash.*.log
+
+# Exclude all .tfvars files, which are likely to contain sensitive data, such as
+# password, private keys, and other secrets. These should not be part of version
+# control as they are data points which are potentially sensitive and subject
+# to change depending on the environment.
+*.tfvars
+*.tfvars.json
+
+# Ignore override files as they are usually used to override resources locally and so
+# are not checked in
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+
+# Include override files you do wish to add to version control using negated pattern
+# !example_override.tf
+
+# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
+# example: *tfplan*
+
+# Ignore CLI configuration files
+.terraformrc
+terraform.rc
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+### VisualStudioCode Patch ###
+# Ignore all local history of files
+.history
+.ionide
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,intellij+all,visualstudiocode,terraform,direnv,dotenv
diff --git a/07-iaas/loesung/teil-2/.terraform.lock.hcl b/07-iaas/loesung/teil-2/.terraform.lock.hcl
new file mode 100644
index 00000000..f2bfe2d7
--- /dev/null
+++ b/07-iaas/loesung/teil-2/.terraform.lock.hcl
@@ -0,0 +1,53 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+ version = "5.50.0"
+ constraints = ">= 5.30.0, 5.50.0"
+ hashes = [
+ "h1:5r7HLEw7aTWhgSofDeHXZiTrrssF0Td73gySY7bksrY=",
+ "h1:LevuTzPS4S7t+Vh6Kpz77pBNDAwChaos91/6+CVnD4w=",
+ "h1:OE1Q924lUL15OytvxwkdIspPsLRe0m2044W55j3lihE=",
+ "h1:WL4SfIhP8jI5CksUGSgkOFrXvAfbcGnOw0gVrzZZ4rw=",
+ "h1:r1ccZSTQXCtMrequqbBQDXrIVpRx/iYSk1cnS/RYcMs=",
+ "zh:19be42f5a545d6712dee4bdb704b018d23bacf5d902ac3cb061eb1750dfe6a20",
+ "zh:1d880bdba95ce96efde37e5bcf457a57df2c1effa9b47bc67fa29c1a264ae53b",
+ "zh:1e9c78e324d7492be5e7744436ed71d66fe4eca3fb6af07a28efd0d1e3bf7640",
+ "zh:27ac672aa61b3795931561fdbe4a306ad1132af517d7711c14569429b2cc694f",
+ "zh:3b978423dead02f9a98d25de118adf264a2331acdc4550ea93bed01feabc12e7",
+ "zh:490d7eb4b922ba1b57e0ab8dec1a08df6517485febcab1e091fd6011281c3472",
+ "zh:64e7c84e18dac1af5778d6f516e01a46f9c91d710867c39fbc7efa3cd972dc62",
+ "zh:73867ac2956dcdd377121b3aa8fe2e1085e77fae9b61d018f56a863277ea4b6e",
+ "zh:7ed899d0d5c49f009b445d7816e4bf702d9c48205c24cf884cd2ae0247160455",
+ "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+ "zh:9b93784b3fb13d08cf95a4131c49b56bf7e1cd35daad6156b3658a89ce6fb58f",
+ "zh:b29d77eb75de474e46eb47e539c48916628d85599bcf14e5cc500b14a4578e75",
+ "zh:bbd9cec8ca705452e4a3d21d56474eacb8cc7b1b74b7f310fdea4bdcffebab32",
+ "zh:c352eb3169efa0e27a29b99a2630e8298710a084453c519caa39e5972ff6d1fc",
+ "zh:e32f4744b43be1708b309a734e0ac10b5c0f9f92e5849298cf1a90f2b906f6f3",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/random" {
+ version = "3.6.2"
+ constraints = "3.6.2"
+ hashes = [
+ "h1:5lstwe/L8AZS/CP0lil2nPvmbbjAu8kCaU/ogSGNbxk=",
+ "h1:R5qdQjKzOU16TziCN1vR3Exr/B+8WGK80glLTT4ZCPk=",
+ "h1:UQlmHGddu39vVzG8kruMsde4GHlG+1S7OLqFApbJvtc=",
+ "h1:VavG5unYCa3SYISMKF9pzc3718M0bhPlcbUZZGl7wuo=",
+ "h1:wmG0QFjQ2OfyPy6BB7mQ57WtoZZGGV07uAPQeDmIrAE=",
+ "zh:0ef01a4f81147b32c1bea3429974d4d104bbc4be2ba3cfa667031a8183ef88ec",
+ "zh:1bcd2d8161e89e39886119965ef0f37fcce2da9c1aca34263dd3002ba05fcb53",
+ "zh:37c75d15e9514556a5f4ed02e1548aaa95c0ecd6ff9af1119ac905144c70c114",
+ "zh:4210550a767226976bc7e57d988b9ce48f4411fa8a60cd74a6b246baf7589dad",
+ "zh:562007382520cd4baa7320f35e1370ffe84e46ed4e2071fdc7e4b1a9b1f8ae9b",
+ "zh:5efb9da90f665e43f22c2e13e0ce48e86cae2d960aaf1abf721b497f32025916",
+ "zh:6f71257a6b1218d02a573fc9bff0657410404fb2ef23bc66ae8cd968f98d5ff6",
+ "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
+ "zh:9647e18f221380a85f2f0ab387c68fdafd58af6193a932417299cdcae4710150",
+ "zh:bb6297ce412c3c2fa9fec726114e5e0508dd2638cad6a0cb433194930c97a544",
+ "zh:f83e925ed73ff8a5ef6e3608ad9225baa5376446349572c2449c0c0b3cf184b7",
+ "zh:fbef0781cb64de76b1df1ca11078aecba7800d82fd4a956302734999cfd9a4af",
+ ]
+}
diff --git a/04-iaas/loesung/teil-2/autoscaling.tf b/07-iaas/loesung/teil-2/autoscaling.tf
similarity index 59%
rename from 04-iaas/loesung/teil-2/autoscaling.tf
rename to 07-iaas/loesung/teil-2/autoscaling.tf
index 5a68e825..633b4da0 100644
--- a/04-iaas/loesung/teil-2/autoscaling.tf
+++ b/07-iaas/loesung/teil-2/autoscaling.tf
@@ -5,7 +5,9 @@ resource "aws_security_group" "app" {
tags = local.standard_tags
}
-resource "aws_security_group_rule" "app_ssh" {
+resource "aws_security_group_rule" "app_ingress_ssh_all" {
+ #checkov:skip=CKV_AWS_24:Ensure no security groups allow ingress from 0.0.0.0:0 to port 22
+
security_group_id = aws_security_group.app.id
description = "Allows SSH from everywhere"
type = "ingress"
@@ -15,8 +17,17 @@ resource "aws_security_group_rule" "app_ssh" {
cidr_blocks = ["0.0.0.0/0"]
}
+resource "aws_security_group_rule" "app_ingress_http_lb" {
+ security_group_id = aws_security_group.app.id
+ description = "Allows HTTP access from the load balancer"
+ type = "ingress"
+ from_port = 8080
+ to_port = 8080
+ protocol = "tcp"
+ source_security_group_id = aws_security_group.lb.id
+}
-resource "aws_security_group_rule" "app_outgoing" {
+resource "aws_security_group_rule" "app_egress_all" {
security_group_id = aws_security_group.app.id
description = "Allows all outgoing traffic"
type = "egress"
@@ -26,34 +37,31 @@ resource "aws_security_group_rule" "app_outgoing" {
cidr_blocks = ["0.0.0.0/0"]
}
-# TODO: Allow Ingress from the Security Group of the Load Balancer to the App Security Group in port 8080
-# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
-resource "aws_security_group_rule" "app_to_lb" {
- security_group_id = aws_security_group.app.id
- description = "Allows HTTP access from the load balancer"
- type = "ingress"
- from_port = 8080
- to_port = 8080
- protocol = "tcp"
- source_security_group_id = aws_security_group.lb.id
-}
-
resource "aws_launch_template" "app" {
- name = local.env
+ #checkov:skip=CKV_AWS_88:EC2 instance should not have public IP
- image_id = "ami-0dba2cb6798deb6d8"
+ name = local.env
+ image_id = "ami-01e444924a2233b07"
instance_initiated_shutdown_behavior = "terminate"
update_default_version = true
+ instance_type = "t2.micro"
- instance_type = "t2.micro"
-
- vpc_security_group_ids = [aws_security_group.app.id]
+ network_interfaces {
+ associate_public_ip_address = true
+ security_groups = [aws_security_group.app.id]
+ }
tag_specifications {
resource_type = "instance"
tags = local.standard_tags
}
+ metadata_options {
+ http_endpoint = "enabled"
+ http_tokens = "required"
+ instance_metadata_tags = "enabled"
+ }
+
user_data = base64encode(
templatefile(
"${path.module}/init.sh", { message = local.config.message }
@@ -61,20 +69,25 @@ resource "aws_launch_template" "app" {
)
}
-
-# TODO: Create an AutoScaling Group that manages 0 to 4 instances, with a default of 1
-# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group
-
-resource aws_autoscaling_group app {
- name = local.env
- min_size = 0
- max_size = 4
- launch_template {
- id = aws_launch_template.app.id
- }
+resource "aws_autoscaling_group" "app" {
+ name = local.env
+ min_size = 0
+ max_size = 4
desired_capacity = 1
vpc_zone_identifier = module.vpc.public_subnets
health_check_type = "ELB"
target_group_arns = [aws_lb_target_group.app.arn]
- tags = local.standard_tags_asg
+
+ launch_template {
+ id = aws_launch_template.app.id
+ }
+
+ dynamic "tag" {
+ for_each = local.standard_tags_asg
+ content {
+ key = tag.value.key
+ propagate_at_launch = tag.value.propagate_at_launch
+ value = tag.value.value
+ }
+ }
}
diff --git a/07-iaas/loesung/teil-2/init.sh b/07-iaas/loesung/teil-2/init.sh
new file mode 100644
index 00000000..6ad13720
--- /dev/null
+++ b/07-iaas/loesung/teil-2/init.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+set -euxo pipefail
+
+apt-get update
+apt-get install -y busybox cowsay
+rm -rf /var/lib/apt/lists/*
+
+{
+ echo "
"
+} >> index.html
+
+nohup busybox httpd -f index.html -p 8080 &
diff --git a/07-iaas/loesung/teil-2/loadbalancer.tf b/07-iaas/loesung/teil-2/loadbalancer.tf
new file mode 100644
index 00000000..6417b894
--- /dev/null
+++ b/07-iaas/loesung/teil-2/loadbalancer.tf
@@ -0,0 +1,75 @@
+resource "aws_security_group" "lb" {
+ name = "${local.env}-lb"
+ description = "Allow TLS inbound traffic"
+ vpc_id = module.vpc.vpc_id
+
+ tags = local.standard_tags
+}
+
+resource "aws_security_group_rule" "lb_ingress_http_all" {
+ #checkov:skip=CKV_AWS_260:Ensure no security groups allow ingress from 0.0.0.0:0 to port 80
+
+ security_group_id = aws_security_group.lb.id
+ description = "Allows HTTP from everywhere"
+ type = "ingress"
+ from_port = 80
+ to_port = 80
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
+}
+
+resource "aws_security_group_rule" "lb_egress_all" {
+ security_group_id = aws_security_group.lb.id
+ description = "Allows HTTP to App SG"
+ type = "egress"
+ from_port = 0
+ to_port = 0
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
+}
+
+resource "aws_lb" "app" {
+ #checkov:skip=CKV_AWS_91:Ensure the ELBv2 (Application/Network) has access logging enabled
+ #checkov:skip=CKV_AWS_150:Ensure that Load Balancer has deletion protection enabled
+
+ name = local.env
+ load_balancer_type = "application"
+ security_groups = [aws_security_group.lb.id]
+ subnets = module.vpc.public_subnets
+ drop_invalid_header_fields = true
+
+ tags = local.standard_tags
+}
+
+resource "aws_lb_target_group" "app" {
+ name = local.env
+ port = 8080
+ protocol = "HTTP"
+ vpc_id = module.vpc.vpc_id
+
+ health_check {
+ healthy_threshold = 2
+ unhealthy_threshold = 2
+ interval = 10
+ path = "/"
+ port = "traffic-port"
+ protocol = "HTTP"
+ }
+
+ tags = local.standard_tags
+}
+
+resource "aws_lb_listener" "lb_forward_to_app" {
+ #checkov:skip=CKV_AWS_2:Ensure ALB protocol is HTTPS
+
+ load_balancer_arn = aws_lb.app.arn
+ port = "80"
+ protocol = "HTTP"
+
+ default_action {
+ type = "forward"
+ target_group_arn = aws_lb_target_group.app.arn
+ }
+
+ tags = local.standard_tags
+}
diff --git a/07-iaas/loesung/teil-2/main.tf b/07-iaas/loesung/teil-2/main.tf
new file mode 100644
index 00000000..cb4fe8fd
--- /dev/null
+++ b/07-iaas/loesung/teil-2/main.tf
@@ -0,0 +1,67 @@
+terraform {
+ required_version = ">= 1.8.0"
+
+ required_providers {
+ aws = {
+ # https://github.com/hashicorp/terraform-provider-aws/blob/main/CHANGELOG.md
+ source = "hashicorp/aws"
+ version = "5.50.0"
+ }
+ random = {
+ # https://github.com/hashicorp/terraform-provider-random/blob/main/CHANGELOG.md
+ source = "hashicorp/random"
+ version = "3.6.2"
+ }
+ }
+}
+
+################################################################################
+
+provider "aws" {
+ region = "eu-central-1"
+}
+
+################################################################################
+
+resource "random_string" "id_suffix" {
+ length = 4
+ special = false
+ upper = false
+}
+
+locals {
+ # Use this variable as prefix for all resource names.
+ # This avoids conflicts with globally unique resources (all resources with a hostname).
+ env = "${terraform.workspace}-${random_string.id_suffix.result}"
+
+ # Use this map to apply env-specific values for certain components.
+ env_config = {
+ default = {
+ message = "Hello World!"
+ }
+ dev = {
+ message = "Hello DEV Workspaces!"
+ }
+ test = {
+ message = "Hello TEST Workspaces!"
+ }
+ prod = {
+ message = "Hello PROD Workspaces!"
+ }
+ }
+ config = merge(local.env_config["default"], lookup(local.env_config, terraform.workspace, {}))
+
+ # Tag all resources at least with these tags.
+ # Allows filtering in AWS and distinction between environments.
+ standard_tags = {
+ "environment" = local.env
+ }
+
+ standard_tags_asg = [
+ for key in keys(local.standard_tags) : {
+ key = key
+ value = lookup(local.standard_tags, key)
+ propagate_at_launch = true
+ }
+ ]
+}
diff --git a/07-iaas/loesung/teil-2/network.tf b/07-iaas/loesung/teil-2/network.tf
new file mode 100644
index 00000000..821702e7
--- /dev/null
+++ b/07-iaas/loesung/teil-2/network.tf
@@ -0,0 +1,11 @@
+module "vpc" {
+ # https://github.com/terraform-aws-modules/terraform-aws-vpc/tags
+ source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git?ref=25322b6b6be69db6cca7f167d7b0e5327156a595" # 5.8.1
+
+ name = local.env
+ cidr = "10.0.0.0/16"
+ azs = ["eu-central-1a", "eu-central-1b"]
+ public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
+
+ tags = local.standard_tags
+}
diff --git a/07-iaas/loesung/teil-2/output.tf b/07-iaas/loesung/teil-2/output.tf
new file mode 100644
index 00000000..babcded4
--- /dev/null
+++ b/07-iaas/loesung/teil-2/output.tf
@@ -0,0 +1,4 @@
+output "load_balancer_url" {
+ description = "The URL of the Load Balancer"
+ value = "http://${aws_lb.app.dns_name}"
+}
diff --git a/07-iaas/loesung/teil-2/renovate.json b/07-iaas/loesung/teil-2/renovate.json
new file mode 100644
index 00000000..cf95d296
--- /dev/null
+++ b/07-iaas/loesung/teil-2/renovate.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:recommended",
+ ":semanticCommits",
+ ":enablePreCommit",
+ ":rebaseStalePrs"
+ ]
+}
diff --git a/07-iaas/uebung/.devcontainer/devcontainer.json b/07-iaas/uebung/.devcontainer/devcontainer.json
new file mode 100644
index 00000000..7caced0f
--- /dev/null
+++ b/07-iaas/uebung/.devcontainer/devcontainer.json
@@ -0,0 +1,12 @@
+{
+ "name": "IaaS Container",
+ "dockerFile": "../iaas-container/Dockerfile",
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "terminal.integrated.shell.linux": "/bin/bash"
+ },
+ "extensions": ["hashicorp.terraform"]
+ }
+ }
+}
diff --git a/07-iaas/uebung/iaas-container/Dockerfile b/07-iaas/uebung/iaas-container/Dockerfile
new file mode 100644
index 00000000..a3486752
--- /dev/null
+++ b/07-iaas/uebung/iaas-container/Dockerfile
@@ -0,0 +1,122 @@
+# syntax=docker/dockerfile:1
+
+# https://github.com/bridgecrewio/checkov/blob/master/docs/5.Policy%20Index/all.md
+#checkov:skip=CKV_DOCKER_2: Ensure that HEALTHCHECK instructions have been added to container images
+#checkov:skip=CKV_DOCKER_3: Ensure that a user for the container has been created
+
+# https://hub.docker.com/_/ubuntu/tags
+FROM ubuntu:24.04@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15
+
+LABEL maintainer="Andreas Kowasch "
+
+ARG IMAGE_CREATED="1970-01-01T00:00:00Z"
+ARG IMAGE_REVISION="master"
+
+LABEL org.opencontainers.image.title="cloudcomputing/iaas-container" \
+ org.opencontainers.image.description="Cloud Computing - IaaS Container" \
+ org.opencontainers.image.authors="Andreas Kowasch " \
+ org.opencontainers.image.created="${IMAGE_CREATED}" \
+ org.opencontainers.image.url="https://github.com/JohannesEbke/cloudcomputing/tree/master/07-iaas" \
+ org.opencontainers.image.source="https://github.com/JohannesEbke/cloudcomputing/tree/master/07-iaas/uebung/iaas-container" \
+ org.opencontainers.image.revision="${IMAGE_REVISION}" \
+ org.opencontainers.image.vendor="Munich University of Applied Sciences" \
+ org.opencontainers.image.documentation="https://github.com/JohannesEbke/cloudcomputing/tree/master/07-iaas/uebung/iaas-container"
+
+WORKDIR /root
+
+# DL4006: Set the SHELL option -o pipefail before RUN with a pipe in
+SHELL ["/bin/bash", "-o", "pipefail", "-c"]
+
+################################################################################
+
+# Install required packages
+
+# https://packages.ubuntu.com/noble/gnupg
+# renovate: datasource=repology depName=ubuntu_24_04/gnupg versioning=loose
+ENV GNUPG_VERSION="2.4.4"
+
+# https://packages.ubuntu.com/noble/ca-certificates
+# renovate: datasource=repology depName=ubuntu_24_04/ca-certificates versioning=loose
+ENV CA_VERSION="20240203"
+
+# https://packages.ubuntu.com/noble/curl
+# renovate: datasource=repology depName=ubuntu_24_04/curl versioning=loose
+ENV CURL_VERSION="8.5.0"
+
+# https://packages.ubuntu.com/noble/wget
+# renovate: datasource=repology depName=ubuntu_24_04/wget versioning=loose
+ENV WGET_VERSION="1.21.4"
+
+# https://packages.ubuntu.com/noble/git
+# renovate: datasource=repology depName=ubuntu_24_04/git versioning=loose
+ENV GIT_VERSION="2.43.0"
+
+# https://packages.ubuntu.com/noble/unzip
+# renovate: datasource=repology depName=ubuntu_24_04/unzip versioning=loose
+ENV UNZIP_VERSION="6.0"
+
+# https://packages.ubuntu.com/noble/groff
+# renovate: datasource=repology depName=ubuntu_24_04/groff versioning=loose
+ENV GROFF_VERSION="1.23.0"
+
+# https://packages.ubuntu.com/noble/less
+# renovate: datasource=repology depName=ubuntu_24_04/less versioning=loose
+ENV LESS_VERSION="590"
+
+# https://packages.ubuntu.com/noble/lsb-release-minimal
+# renovate: datasource=repology depName=ubuntu_24_04/lsb-release versioning=loose
+ENV LSB_VERSION="12.0"
+
+RUN apt-get update \
+ && apt-get install --yes --quiet --no-install-recommends \
+ gnupg="${GNUPG_VERSION}-*" \
+ ca-certificates="${CA_VERSION}" \
+ curl="${CURL_VERSION}-*" \
+ wget="${WGET_VERSION}-*" \
+ git="1:${GIT_VERSION}-*" \
+ unzip="${UNZIP_VERSION}-*" \
+ groff="${GROFF_VERSION}-*" \
+ less="${LESS_VERSION}-*" \
+ lsb-release="${LSB_VERSION}-*" \
+ && rm -rf /var/lib/apt/lists/*
+
+################################################################################
+
+# Install Terraform CLI
+
+# https://github.com/hashicorp/terraform/tags
+# renovate: datasource=github-tags depName=hashicorp/terraform
+ENV TF_VERSION="1.8.4"
+
+# Source: https://apt.releases.hashicorp.com/gpg
+COPY terraform-cli-pub.key .
+
+RUN gpg \
+ --output /usr/share/keyrings/hashicorp-archive-keyring.gpg \
+ --dearmor terraform-cli-pub.key \
+ && echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" \
+ > /etc/apt/sources.list.d/hashicorp.list \
+ && apt-get update \
+ && apt-get install --yes --quiet --no-install-recommends \
+ terraform="${TF_VERSION}-*" \
+ && rm -rf /var/lib/apt/lists/*
+
+################################################################################
+
+# Install AWS CLI
+
+# https://github.com/aws/aws-cli/tags
+# renovate: datasource=github-tags depName=aws/aws-cli
+ENV AWS_VERSION="2.15.56"
+
+# Source: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
+COPY aws-cli-pub.key .
+
+RUN gpg --import aws-cli-pub.key \
+ && curl "https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m)-${AWS_VERSION}.zip" -o "awscliv2.zip" \
+ && curl "https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m)-${AWS_VERSION}.zip.sig" -o "awscliv2.sig" \
+ && gpg --verify awscliv2.sig awscliv2.zip \
+ && unzip awscliv2.zip \
+ && rm awscliv2.zip \
+ && ./aws/install \
+ && echo "complete -C /usr/local/bin/aws_completer aws" >> .bashrc
diff --git a/07-iaas/uebung/iaas-container/aws-cli-pub.key b/07-iaas/uebung/iaas-container/aws-cli-pub.key
new file mode 100644
index 00000000..0dcdfccc
--- /dev/null
+++ b/07-iaas/uebung/iaas-container/aws-cli-pub.key
@@ -0,0 +1,29 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBF2Cr7UBEADJZHcgusOJl7ENSyumXh85z0TRV0xJorM2B/JL0kHOyigQluUG
+ZMLhENaG0bYatdrKP+3H91lvK050pXwnO/R7fB/FSTouki4ciIx5OuLlnJZIxSzx
+PqGl0mkxImLNbGWoi6Lto0LYxqHN2iQtzlwTVmq9733zd3XfcXrZ3+LblHAgEt5G
+TfNxEKJ8soPLyWmwDH6HWCnjZ/aIQRBTIQ05uVeEoYxSh6wOai7ss/KveoSNBbYz
+gbdzoqI2Y8cgH2nbfgp3DSasaLZEdCSsIsK1u05CinE7k2qZ7KgKAUIcT/cR/grk
+C6VwsnDU0OUCideXcQ8WeHutqvgZH1JgKDbznoIzeQHJD238GEu+eKhRHcz8/jeG
+94zkcgJOz3KbZGYMiTh277Fvj9zzvZsbMBCedV1BTg3TqgvdX4bdkhf5cH+7NtWO
+lrFj6UwAsGukBTAOxC0l/dnSmZhJ7Z1KmEWilro/gOrjtOxqRQutlIqG22TaqoPG
+fYVN+en3Zwbt97kcgZDwqbuykNt64oZWc4XKCa3mprEGC3IbJTBFqglXmZ7l9ywG
+EEUJYOlb2XrSuPWml39beWdKM8kzr1OjnlOm6+lpTRCBfo0wa9F8YZRhHPAkwKkX
+XDeOGpWRj4ohOx0d2GWkyV5xyN14p2tQOCdOODmz80yUTgRpPVQUtOEhXQARAQAB
+tCFBV1MgQ0xJIFRlYW0gPGF3cy1jbGlAYW1hem9uLmNvbT6JAlQEEwEIAD4CGwMF
+CwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQT7Xbd/1cEYuAURraimMQrMRnJHXAUC
+ZMKcEgUJCSEf3QAKCRCmMQrMRnJHXCilD/4vior9J5tB+icri5WbDudS3ak/ve4q
+XS6ZLm5S8l+CBxy5aLQUlyFhuaaEHDC11fG78OduxatzeHENASYVo3mmKNwrCBza
+NJaeaWKLGQT0MKwBSP5aa3dva8P/4oUP9GsQn0uWoXwNDWfrMbNI8gn+jC/3MigW
+vD3fu6zCOWWLITNv2SJoQlwILmb/uGfha68o4iTBOvcftVRuao6DyqF+CrHX/0j0
+klEDQFMY9M4tsYT7X8NWfI8Vmc89nzpvL9fwda44WwpKIw1FBZP8S0sgDx2xDsxv
+L8kM2GtOiH0cHqFO+V7xtTKZyloliDbJKhu80Kc+YC/TmozD8oeGU2rEFXfLegwS
+zT9N+jB38+dqaP9pRDsi45iGqyA8yavVBabpL0IQ9jU6eIV+kmcjIjcun/Uo8SjJ
+0xQAsm41rxPaKV6vJUn10wVNuhSkKk8mzNOlSZwu7Hua6rdcCaGeB8uJ44AP3QzW
+BNnrjtoN6AlN0D2wFmfE/YL/rHPxU1XwPntubYB/t3rXFL7ENQOOQH0KVXgRCley
+sHMglg46c+nQLRzVTshjDjmtzvh9rcV9RKRoPetEggzCoD89veDA9jPR2Kw6RYkS
+XzYm2fEv16/HRNYt7hJzneFqRIjHW5qAgSs/bcaRWpAU/QQzzJPVKCQNr4y0weyg
+B8HCtGjfod0p1A==
+=gdMc
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/07-iaas/uebung/iaas-container/renovate.json b/07-iaas/uebung/iaas-container/renovate.json
new file mode 100644
index 00000000..cddd3872
--- /dev/null
+++ b/07-iaas/uebung/iaas-container/renovate.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:recommended",
+ ":semanticCommits",
+ ":enablePreCommit",
+ ":rebaseStalePrs"
+ ],
+ "regexManagers": [
+ {
+ "fileMatch": ["^Dockerfile$"],
+ "matchStrings": [
+ "#\\s*renovate:\\s*datasource=(?.*?) depName=(?.*?)( versioning=(?.*?))?\\sENV .*?_VERSION=\"(?.*)\"\\s"
+ ],
+ "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}"
+ }
+ ]
+}
diff --git a/07-iaas/uebung/iaas-container/terraform-cli-pub.key b/07-iaas/uebung/iaas-container/terraform-cli-pub.key
new file mode 100644
index 00000000..c7f9f28c
--- /dev/null
+++ b/07-iaas/uebung/iaas-container/terraform-cli-pub.key
@@ -0,0 +1,65 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBGO9u+MBEADmE9i8rpt8xhRqxbzlBG06z3qe+e1DI+SyjscyVVRcGDrEfo+J
+W5UWw0+afey7HFkaKqKqOHVVGSjmh6HO3MskxcpRm/pxRzfni/OcBBuJU2DcGXnG
+nuRZ+ltqBncOuONi6Wf00McTWviLKHRrP6oWwWww7sYF/RbZp5xGmMJ2vnsNhtp3
+8LIMOmY2xv9LeKMh++WcxQDpIeRohmSJyknbjJ0MNlhnezTIPajrs1laLh/IVKVz
+7/Z73UWX+rWI/5g+6yBSEtj368N7iyq+hUvQ/bL00eyg1Gs8nE1xiCmRHdNjMBLX
+lHi0V9fYgg3KVGo6Hi/Is2gUtmip4ZPnThVmB5fD5LzS7Y5joYVjHpwUtMD0V3s1
+HiHAUbTH+OY2JqxZDO9iW8Gl0rCLkfaFDBS2EVLPjo/kq9Sn7vfp2WHffWs1fzeB
+HI6iUl2AjCCotK61nyMR33rNuNcbPbp+17NkDEy80YPDRbABdgb+hQe0o8htEB2t
+CDA3Ev9t2g9IC3VD/jgncCRnPtKP3vhEhlhMo3fUCnJI7XETgbuGntLRHhmGJpTj
+ydudopoMWZAU/H9KxJvwlVXiNoBYFvdoxhV7/N+OBQDLMevB8XtPXNQ8ZOEHl22G
+hbL8I1c2SqjEPCa27OIccXwNY+s0A41BseBr44dmu9GoQVhI7TsetpR+qwARAQAB
+tFFIYXNoaUNvcnAgU2VjdXJpdHkgKEhhc2hpQ29ycCBQYWNrYWdlIFNpZ25pbmcp
+IDxzZWN1cml0eStwYWNrYWdpbmdAaGFzaGljb3JwLmNvbT6JAlQEEwEIAD4CGwMF
+CwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQR5iuxlTlwVQoyOQu6qFvy8piHnAQUC
+Y728PQUJCWYB2gAKCRCqFvy8piHnAd16EADeBtTgkdVEvct40TH/9HKkR/Lc/ohM
+rer6FFHdKmceJ6Ma8/Qm4nCO5C7c4+EPjsUXdhK5w8DSdC5VbKLJDY1EnDlmU5B1
+wSFkGoYKoB8lUn30E77E33MTu2kfrSuF605vetq269CyBwIJV7oNN6311dW8iQ6z
+IytTtlJbVr4YZ7Vst40/uR4myumk9bVBGEd6JhFAPmr/um+BZFhRf9/8xtOryOyB
+GF2d+bc9IoAugpxwv0IowHEqkI4RpK2U9hvxG80sTOcmerOuFbmNyPwnEgtJ6CM1
+bc8WAmObJiQcRSLbcgF+a7+2wqrUbCqRE7QoS2wjd1HpUVPmSdJN925c2uaua2A4
+QCbTEg8kV2HiP0HGXypVNhZJt5ouo0YgR6BSbMlsMHniDQaSIP1LgmEz5xD4UAxO
+Y/GRR3LWojGzVzBb0T98jpDgPtOu/NpKx3jhSpE2U9h/VRDiL/Pf7gvEIxPUTKuV
+5D8VqAiXovlk4wSH13Q05d9dIAjuinSlxb4DVr8IL0lmx9DyHehticmJVooHDyJl
+HoA2q2tFnlBBAFbN92662q8Pqi9HbljVRTD1vUjof6ohaoM+5K1C043dmcwZZMTc
+7gV1rbCuxh69rILpjwM1stqgI1ONUIkurKVGZHM6N2AatNKqtBRdGEroQo1aL4+4
+u+DKFrMxOqa5b7kCDQRjvbwTARAA0ut7iKLj9sOcp5kRG/5V+T0Ak2k2GSus7w8e
+kFh468SVCNUgLJpLzc5hBiXACQX6PEnyhLZa8RAG+ehBfPt03GbxW6cK9nx7HRFQ
+GA79H5B4AP3XdEdT1gIL2eaHdQot0mpF2b07GNfADgj99MhpxMCtTdVbBqHY8YEQ
+Uq7+E9UCNNs45w5ddq07EDk+o6C3xdJ42fvS2x44uNH6Z6sdApPXLrybeun74C1Z
+Oo4Ypre4+xkcw2q2WIhy0Qzeuw+9tn4CYjrhw/+fvvPGUAhtYlFGF6bSebmyua8Q
+MTKhwqHqwJxpjftM3ARdgFkhlH1H+PcmpnVutgTNKGcy+9b/lu/Rjq/47JZ+5VkK
+ZtYT/zO1oW5zRklHvB6R/OcSlXGdC0mfReIBcNvuNlLhNcBA9frNdOk3hpJgYDzg
+f8Ykkc+4z8SZ9gA3g0JmDHY1X3SnSadSPyMas3zH5W+16rq9E+MZztR0RWwmpDtg
+Ff1XGMmvc+FVEB8dRLKFWSt/E1eIhsK2CRnaR8uotKW/A/gosao0E3mnIygcyLB4
+fnOM3mnTF3CcRumxJvnTEmSDcoKSOpv0xbFgQkRAnVSn/gHkcbVw/ZnvZbXvvseh
+7dstp2ljCs0queKU+Zo22TCzZqXX/AINs/j9Ll67NyIJev445l3+0TWB0kego5Fi
+UVuSWkMAEQEAAYkEcgQYAQgAJhYhBHmK7GVOXBVCjI5C7qoW/LymIecBBQJjvbwT
+AhsCBQkJZgGAAkAJEKoW/LymIecBwXQgBBkBCAAdFiEE6wr14plJaVlvmYc+cG5m
+g2nAhekFAmO9vBMACgkQcG5mg2nAhenPURAAimI0EBZbqpyHpwpbeYq3Pygg1bdo
+IlBQUVoutaN1lR7kqGXwYH+BP6G40x79LwVy/fWV8gO7cDX6D1yeKLNbhnJHPBus
+FJDmzDPbjTlyWlDqJoWMiPqfAOc1A1cHodsUJDUlA01j1rPTho0S9iALX5R50Wa9
+sIenpfe7RVunDwW5gw6y8me7ncl5trD0LM2HURw6nYnLrxePiTAF1MF90jrAhJDV
++krYqd6IFq5RHKveRtCuTvpL7DlgVCtntmbXLbVC/Fbv6w1xY3A7rXko/03nswAi
+AXHKMP14UutVEcLYDBXbDrvgpb2p2ZUJnujs6cNyx9cOPeuxnke8+ACWvpnWxwjL
+M5u8OckiqzRRobNxQZ1vLxzdovYTwTlUAG7QjIXVvOk9VNp/ERhh0eviZK+1/ezk
+Z8nnPjx+elThQ+r16EM7hD0RDXtOR1VZ0R3OL64AlZYDZz1jEA3lrGhvbjSIfBQk
+T6mxKUsCy3YbElcOyuohmPRgT1iVDIZ/1iPL0Q0HGm4+EsWCdH6fAPB7TlHD8z2D
+7JCFLihFDWs5lrZyuWMO9nryZiVjJrOLPcStgJYVd/MhRHR4hC6g09bgo25RMJ6f
+gyzL4vlEB7aSUih7yjgL9s5DKXP2J71dAhIlF8nnM403R2xEeHyivnyeR/9Ifn7M
+PJvUMUuoG+ZANSMkrw//XA31o//TVk9WsLD1Edxt5XZCoR+fS+Vz8ScLwP1d/vQE
+OW/EWzeMRG15C0td1lfHvwPKvf2MN+WLenp9TGZ7A1kEHIpjKvY51AIkX2kW5QLu
+Y3LBb+HGiZ6j7AaU4uYR3kS1+L79v4kyvhhBOgx/8V+b3+2pQIsVOp79ySGvVwpL
+FJ2QUgO15hnlQJrFLRYa0PISKrSWf35KXAy04mjqCYqIGkLsz2qQCY2lGcD5k05z
+bBC4TvxwVxv0ftl2C5Bd0ydl/2YM7GfLrmZmTijK067t4OO+2SROT2oYPDsMtZ6S
+E8vUXvoGpQ8tf5Nkrn2t0zDG3UDtgZY5UVYnZI+xT7WHsCz//8fY3QMvPXAuc33T
+vVdiSfP0aBnZXj6oGs/4Vl1Dmm62XLr13+SMoepMWg2Vt7C8jqKOmhFmSOWyOmRH
+UZJR7nKvTpFnL8atSyFDa4o1bk2U3alOscWS8u8xJ/iMcoONEBhItft6olpMVdzP
+CTrnCAqMjTSPlQU/9EGtp21KQBed2KdAsJBYuPgwaQeyNIvQEOXmINavl58VD72Y
+2T4TFEY8dUiExAYpSodbwBL2fr8DJxOX68WH6e3fF7HwX8LRBjZq0XUwh0KxgHN+
+b9gGXBvgWnJr4NSQGGPiSQVNNHt2ZcBAClYhm+9eC5/VwB+Etg4+1wDmggztiqE=
+=FdUF
+-----END PGP PUBLIC KEY BLOCK-----
+
diff --git a/07-iaas/uebung/teil-1/README.md b/07-iaas/uebung/teil-1/README.md
new file mode 100644
index 00000000..e3ac4b8a
--- /dev/null
+++ b/07-iaas/uebung/teil-1/README.md
@@ -0,0 +1,355 @@
+
+
+# Übung: IaaS mit AWS
+
+## Ziel
+
+Ziel dieser Übung ist, dass Sie ein grundsätzliches Verständnis der AWS Cloud und ihrer grundlegendsten IaaS Komponenten erlangen. Ihre Aufgabe ist es ein virtuelles Netz, eine Auto Scaling Group mit mehreren Maschinen und einen Load Balancer zu erzeugen um eine minimale Webanwendung bereitzustellen. Im Anschluss werden Sie die AWS CLI verwenden um Ihre Anwendung zu skalieren und per SSH zu verändern.
+
+## Voraussetzungen
+
+Im zweiten Teil dieser Übung verwenden wir einen Docker Container zur Verwendung der CLI. Daher brauchen Sie eine eine lokale Docker Installation. Da die Erstellung / der Download des Containers länger dauern kann starten Sie bitte mit der Vorbereitung für Übung zwei.
+
+Sie benötigen einen AWS Account um die Übung zu absolvieren.
+Wenn Sie awseducate.com verwenden, loggen Sie sich auf der Webseite ein und klicken Sie auf AWS Account und erzeugen Sie einen [Educate Starter Account](https://www.geeksforgeeks.org/aws-educate-starter-account/).
+
+## Aufgaben
+
+### Erzeugen einer AWS Infrastruktur per UI
+
+In dieser Übung erzeugen Sie eine einfache IaaS Architektur mit der AWS Web Konsole.
+
+Hinweise:
+
+* Folgen Sie der Anleitung, sofern nicht anders genannt, belassen Sie andere Einstellungen unverändert.
+* Stellen Sie sicher, dass die AWS Region _eu-central-1 (Frankfurt)_ (rechter, oberer Bildschirmrand) ist und die Sprache auf _English_ (linker, unterer Bildschirmrand) steht.
+Falls nach der Anmeldung die Sprache weiterhin Deutsch ist, melden Sie sich wieder ab und wählen Sie unterhalb der Login-Maske die Sprache Englisch aus und melden Sie sich erneut an.
+* Wichtig wenn Sie Sich einen Account mit anderen Übungsteilnehmern teilen: Denken Sie sich einen eindeutigen Namen für die Benennung/Tags Ihrer Ressourcen aus.
+Da alle Teilnehmer den gleichen Account verwenden werden ist davon auszugehen, dass wenn Sie Ihre Ressourcen nicht wiedererkennbar bennenen Sie und Ihre Kommolitonen Schwierigkeiten haben werden Ihre Ressourcen wiederzufinden.
+Sie können beispielsweise eine Kombination aus Ihrem echten Namen verwenden z.B. `akowasch` wenn ihr Name _Andreas Kowasch_ ist oder Sie wählen einen beliebigen anderen Namen wie z.B. `anonymeameise`.
+Verwenden Sie bei der Namenswahl keine Sonderzeichen außer `-` und vermeiden Sie Umlaute.
+
+#### Ein VPC erstellen
+
+In diesem Teil der Übung erstellen Sie ein virtuelles Netzwerk, mit einem einzelnen Subnetz mit Internet Zugriff.
+
+1. Klicken Sie in der Menubar auf _Services_, wählen Sie _VPC_ > _Your VPCs_ > _Create VPC_.
+ * Vergeben Sie einen eindeutigen Namen, mit dem Sie ihr Netzwerk wiederfinden z.B. _akrause_.
+ * Verwenden Sie den IPV4 Adressbereich `10.0.0.0/16`.
+ * Klicken Sie auf _Create VPC_.
+ Nach dem Erstellen werden Sie feststellen, dass über das Netz hinaus weitere Standard Cloud Ressourcen angelegt wurden z.B. eine Routing Tabelle.
+
+2.
+ * a) Klicken Sie in der Seitenleiste auf _Subnets_, wähle Sie _Create subnet_.
+ * Wählen Sie das von Ihnen erstellte VPC als Ziel für ihr Subnetz.
+ * Verwenden Sie wieder Ihren eindeutigen Namen.
+ * Wählen Sie als _Availability Zone_ die Zone _eu-central-1a_.
+ * Wählen Sie den Adressbereich kleiner als den Ihres VPC z.B. die Hälfte: `10.0.0.0/17`.
+ * Klicken Sie auf _Create subnet_.
+ * Nach der Erstellung, markieren Sie ihr Netz und klicken Sie nun bei _Actions_ die Option _Edit subnet settings_ und aktivieren Sie den Haken bei _Enable auto-assign public IPv4 address_.
+ * b) Wiederholen Sie die Schritte in 2a um ein Netz in _eu-central-1b_ mit dem Adressbereich `10.0.128.0/17` zu erzeugen.
+
+3. Erzeugen Sie nun einen Zugang zum Internet indem Sie in der Seitenleiste auf _Internet gateways_ klicken und dann auf _Create internet gateway_ Klicken.
+ * Verwenden Sie wieder Ihren eindeutigen Namen.
+ * Nach dem Erstellen klicken Sie oben rechts auf _Actions_ und wählen Sie _Attac to VPC_.
+ * Wählen Sie ihr VPC und bestätigen Sie.
+
+4. Klicken Sie nun in der Seitenleiste auf _Route tables_ und fügen Sie an die Tabelle für Ihr VPC eine weitere Regel ein, die allen Traffic (`0.0.0.0/0`) zu dem von Ihnen erstellten Internet Gateway routed.
+ Die Reihenfolge der Regeln ist hierbei irrelevant, spezifischere greifen zuerst.
+
+#### Einen Load Balancer erstellen
+
+In diesem Teil der Übung erstellen Sie einen Load Balancer der die Anfragen über die aktuell service-fähigen Instanzen verteilt.
+
+1. Wechseln Sie nun zum Service _EC2_.
+ Klicken Sie in der Seitenleiste auf _Security Groups_ und dann auf _Create security group_.
+ * Benennen Sie die Gruppe nach dem Schema `loadbalancer-`.
+ * Als Beschreibung ist _"Security Group fuer den Load Balancer."_ geeignet.
+ * Unter _VPC_ Wählen Sie ihr VPC aus.
+ * Fügen Sie eine Inbound Rule ein, die den Zugriff per Port `80`, also HTTP, von `0.0.0.0/0`, d.h. beliebiger IP, erlaubt.
+ * Klicken Sie auf _Create security group_.
+
+2. Klicken Sie auf der Seitenleiste auf _Load Balancers_ und wählen Sie _Create Load Balancer_.
+ * Wählen Sie den Typ _Application Load Balancer_.
+ * Verwenden Sie wieder Ihren eindeutigen Namen.
+ * Wählen Sie unter _Network mapping_ Ihr VPC und Ihre Subnetze aus.
+ * Wählen Sie unter _Security groups_ nur Ihre `loadbalancer-` Security Group aus.
+ * Erstellen Sie unter _Listeners and routing_ eine neue Target Group und verwenden Sie dafür wieder Ihren eindeutigen Namen.
+ * Unter _Basic configuration_
+ * Wählen Sie den Typ `Instances`.
+ * Wählen Sie das Protokoll `HTTP`.
+ * Wählen Sie den Port `8080`.
+ * Unter _Health checks_
+ * Wählen Sie das Protokol `HTTP`.
+ * Wählen Sie den Pfad `/`.
+ * Unter _Advanced health check settings_
+ * Wählen Sie den Port `Traffic port`.
+ * Setzen Sie den _Healthy threshold_ auf `2`.
+ * Setzen Sie den _Unhealthy threshold_ auf `2`.
+ * Setzen Sie das _Interval_ auf `10 seconds`.
+ * Es ist nicht notwendig _Targets_ zu registrieren, da dies die Auto Scaling Group übernimmt.
+ * Schließen Sie den Vorgang ab, indem sie auf _Create target group_ klicken.
+ * Wählen Sie die zuvor erstellte Target Group aus.
+ * Klicken Sie abschließend auf _Create load balancer_.
+
+#### Eine Auto Scaling Group einrichten
+
+In diesem Teil der Übung erstellen Sie eine Auto Scaling Group, welche Ihnen erlaubt Ihre Instanzen bequem zu skalieren und auszutauschen.
+Darüber hinaus meldet Ihre Auto Scaling Group Ihre Instanzen bei der Target Group des Load Balancers an.
+
+1. Erstellen Sie eine weitere Security Group.
+ * Benennen Sie die Gruppe nach dem Schema `app-`.
+ * Als Beschreibung ist _"Security Group fuer die Applikationsserver."_ geeignet.
+ * Unter _VPC_ Wählen Sie ihr VPC aus.
+ * Fügen Sie eine Inbound Rule ein, die den Zugriff per Port `22`, also SSH, von `0.0.0.0/0`, d.h. beliebiger IP, erlaubt.
+ * Fügen Sie eine weitere Inbound Rule ein, die den Zugriff per Port `8080`, von der Security Group des Load Balancers erlaubt.
+ * Klicken Sie auf _Create security group_.
+
+2. Klicken Sie in der Seitenleiste auf den Eintrag _Launch Templates_ > _Create Launch Template_.
+ * Verwenden Sie wieder Ihren eindeutigen Namen.
+ * Als Beschreibung ist "Launch Template fuer eine einfache Web Anwendung." geeignet.
+ * Wählen Sie unter _Application and OS Images_ ein Ubuntu als _AMI_. Zum Beispiel `ami-01e444924a2233b07`.
+ * Wählen Sie unter _Instance type_ `t2.micro`.
+ * Wählen Sie unter _Network settings_ Ihre Security Group für die Applikation.
+ * Verwenden Sie unter _Advanced details_ das folgende Skript als _User Data_:
+
+ ``` shell
+ #!/bin/bash
+ set -euxo pipefail
+
+ apt-get update
+ apt-get install -y busybox cowsay
+ source /etc/environment
+
+ {
+ echo "
"
+ /usr/games/cowsay -f dragon Hello World
+ echo "
"
+ } >> index.html
+
+ nohup busybox httpd -f index.html -p 8080 &
+ ```
+
+ * Schließen Sie den Vorgang über _Create launch template_ ab.
+
+3. Gehen Sie nun in der Seitenleiste auf den Eintrag _Auto Scaling Groups_ und klicken danach auf _Create Auto Scaling group_.
+ * Verwenden Sie wieder Ihren eindeutigen Namen.
+ * Wäheln Sie das von Ihnen erstellte Launch Template.
+ * Wählen Sie Ihr VPC, sowie **beide** Subnetz aus.
+ * Zum verwenden Ihrer Target Group setzen Sie unter _Load Balancing_ den Haken bei _Attach to an existing load balancer_ und wählen Ihre Target Group.
+ * Setzen Sie unter _Health Checks_ den Haken bei _Turn on Elastic Load Balancing health checks_.
+ * Setzen Sie die maximale Kapazität auf `4`, die minimale auf `0` und die gewünschte auf `2`.
+ * Schließen Sie den Vorgang nun ab.
+
+Nach Erstellung sollten nun nach einiger Zeit zwei Instanzen nach Ihren Spezifikationen erzeugt werden.
+
+#### Funktionstest
+
+> [!IMPORTANT]
+> Falls Sie MacOS verwenden, dieser Schritt funktioniert nicht mit Safari.
+> Nehmen Sie für diesen Schritt bitte einen anderen Browser.
+
+1. Gehen Sie nun auf Ihre Target Group und klicken Sie auf den Reiter _Targets_.
+ Ihre Instanzen sollte hier nach kurzer Zeit als _healthy_ auftauchen.
+ Sollten die Instanzen nicht auftauchen oder über 5 Minuten hinaus _unhealthy_ sein, haben Sie wahrscheinlich einen Fehler gemacht.
+
+2. Gehen Sie auf die Ansicht des Load Balancers und kopieren Sie sich den _DNS name_ heraus und öffnen Sie die Adresse in einem neuen Tab.
+ Wenn Sie einen Drachen sehen Sie die HTTP Antwort einer Ihrer Instanzen.
+
+3. Verbinden Sie sich nun mit einer _Ihrer_ laufenden Instanzen per EC2 Instance Connect.
+ * Gehen Sie dazu auf _Instances_ und Markieren (nicht auf die Detail-Ansicht Klicken) Sie einer Ihrer Instanzen.
+ * Gehen Sie nun oben rechts auf _Actions_ und wählen Sie _Connect_ und in der folgenden Ansicht nochmals auf _Connect_.
+ * Sie sind jetzt mit einer Shell Session auf dem Host verbunden.
+ * Auf dem Host läuft ein `httpd` service der die Webseite hostet.
+ Stoppen Sie den Prozess.
+ * Was können Sie an der Target Group, Auto Scaling Group und an Ihren Instanzen beobachten? Wie verhält sich die Webseite währenddessen?
+
+Lösung Prozess stoppen:
+
+
+
+
+> Finden Sie als erstes die Prozess Id heraus z.B. mit `ps`:
+>
+> ``` shell
+> ps aux | grep httpd
+> ```
+>
+> Beispiel Ergebnis:
+>
+> ``` text
+> root 6634 0.0 0.1 2788 1660 ? S 15:07 0:00 busybox httpd -f index.html -p 8080
+> ubuntu 7287 0.0 0.0 7672 652 pts/0 S+ 15:19 0:00 grep --color=auto httpd
+> ```
+>
+> Sie können den Prozess `6634` beenden, hierfür brauchen Sie in diesem Fall Root Rechte:
+>
+> ``` shell
+> sudo kill 6634
+> ```
+>
+> Sie können das Fenster schließen.
+
+
+
+
+Lösung Beobachtungen:
+
+
+
+
+> Als erstes wird Ihre Instanz in der Target Group _Unhealthy_ werden, da die Anwendung nicht mehr antwortet.
+> Dadurch wird der Load Balancer jeglichen Traffic nur noch an die noch funktionierende Instanz schicken.
+> Der Betrieb der Webseite ist ungestört, sie funktioniert weiter.
+> Nach einiger Zeit wird die Auto Scaling Gruppe auf den vom Load Balancer gemeldeten Zustand reagieren und die kaputte Instanz gegen eine neue in der gleichen Zone austauschen.
+> Das System heilt sich also selbst.
+> Für kurze Zeit wird es daher drei Instanzen geben.
+> Die kaputte Instanz wird rückstandslos terminiert.
+> Der gesamte Prozess dauert normalerweise maximal 10 Min.
+
+
+
+
+### Arbeiten mit der AWS CLI
+
+#### Vorbereitung
+
+Bauen Sie das Container Image unter `iaas-container` mit:
+
+``` shell
+cd 07-iaas/uebung/iaas-container
+docker build \
+ --build-arg IMAGE_CREATED="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
+ --build-arg IMAGE_REVISION="$(git rev-parse --abbrev-ref HEAD)" \
+ --tag iaas-container \
+ .
+```
+
+Starten Sie den Container mit:
+
+``` shell
+docker run -it --rm iaas-container
+```
+
+Nun sollte eine Bash Session vor Ihnen geöffnet sein.
+Geben Sie `aws configure`.
+In der folgenden Abfrage geben Sie Ihre AWS Zugangsdaten ein, wählen `eu-central-1` als Standard-Region und `json` als Standard-Ausgabeformat.
+Testen Sie anschließend mit `aws sts get-caller-identity` Ihre Konfiguration.
+Es sollte eine Antwort ähnlich dieser auf der Konsole erscheinen:
+
+``` json
+{
+ "UserId": "AIDAIVMF6UC5ZJA4LA2QU",
+ "Account": "264524865537",
+ "Arn": "arn:aws:iam::264524865537:user/akrause"
+}
+```
+
+#### Arbeiten mit der Kommandozeile
+
+In Ihrer Docker-Umgebung ist das Kommandozeilen-Programm (`aws`) installiert, welches es Ihnen das Verbinden mit einer EC2-Instanz per SSH erlaubt.
+
+Stellen Sie sich nun vor Sie sind für den zuverlässigen Betrieb des von Ihnen heute erstellten _Dragon Service_ im Auftrag eines Geldgebers verantwortlich.
+Ihr Geldgeber stellt nun fest, dass die Nutzerzahlen Ihres Services zurückgehen und will daher Geld sparen - er ist bereit eine geringere Verfügbarkeit in Kauf zu nehmen.
+Reduzieren Sie daher die Anzahl der Instanzen in der Auto Scaling Group auf `1` statt `2`.
+Nutzen Sie `aws autoscaling help` um ein Kommando zusammenzustellen.
+
+Lösung:
+
+
+
+
+
+Um die Nutzerzahlen wieder zu erhöhen möchte Ihr Geldgeber ein neues Format ausprobieren - Pinguine, so hat er gehört, sollen unter Informatikern sehr beliebt sein.
+Verbinden Sie sich mit der verbleibenden Instanz.
+Achten Sie darauf, sich nicht mit der Instanz zu verbinden, die eventuell noch am herunterfahren ist.
+
+Setzen Sie Ihren Eindeutigen Namen in folgendes Kommando ein und führen Sie es aus, um Ihre Instanz zu finden:
+
+``` shell
+aws ec2 describe-instances \
+ --filters "Name=tag:aws:autoscaling:groupName,Values=" "Name=instance-state-name,Values=running" \
+ --query 'Reservations[].Instances[].InstanceId' \
+ --output text
+```
+
+Sie können die Instanz Id mit folgendem Kommando verwenden, um sich mit der Instanz zu verbinden:
+
+``` shell
+aws ec2-instance-connect ssh \
+ --instance-id \
+ --os-user ubuntu
+```
+
+Editieren Sie die `index.html` unter `/` als, so dass ein Pinguin erscheint - hierzu brauchen Sie root Rechte.
+Wenn Sie nicht wissen wie Sie einen Pinguin erzeugen ändern Sie nur die Nachricht von _Hello World_ zu _Ich bin ein Pinguin_.
+
+Lösung:
+
+
+
+
+> Editieren Sie die Datei beispielsweise mit `nano`:
+>
+> sudo nano /index.html
+>
+> Je nach Betriebssystem speichern Sie Ihre Änderung mit STRG+O (Windows) oder Control+O (MacOS).
+
+
+
+
+Gehen Sie wieder im Browser auf die Adresse Ihres Load Balancers um Ihre Änderung zu überprüfen.
+
+Der Pinguin kommt gut an und die Nutzer Zahlen steigen.
+Ihr Geldgeber möchte die Last mit einer weiteren Instanz abfangen.
+Skalieren Sie mit der Kommandozeile Ihre Auto Scaling Group auf `2`.
+
+Lösung:
+
+
+
+
+
+Was werden Ihre Nutzer sehen, sobald die zusätzliche Instanz nach einigen Minuten erfolgreich beim Load Balancer angemeldet wurde?
+
+
+
+
+> Die neue Instanz wurde mit dem unveränderten Launch Template gestartet und ist daher mit dem Drachen konfiguriert.
+> Die Webseite antwortet abwechselnd mit der alten und neuen Version des Dienstes.
+> Der Load Balancer verteilt die Last abwechselnd auf die Instanzen.
+>
+> Wenn Sie Instanzen in einer Auto Scaling Group ändern wollen sollten Sie zunächst das Launch Template anpassen und dann die Instanzen rollierend austauschen.
+>
+> 1. Erst eine neue Instanz hinzufügen.
+> 2. Warten bis Sie beim Load Balancer getestet und in die Rotation aufgenommen wurde.
+> 3. Eine alte Instanz aus der Rotation nehmen (keine neuen Verbindungen) und warten bis alle noch laufenden Anfragen abgearbeitet wurden.
+> 4. Die alte Instanz herunterfahren.
+> 5. Wiederholen bis keine alten Instanzen mehr vorhanden sind.
+>
+> Idealerweise haben Sie im neuen Launch Template bereits ein mit Packer erstelltes Image konfiguriert, so dass die Installationsphase für Abhängigkeiten entfällt und immer die gleichen Pakete vorhanden sind.
+> Einen rollierenden Austausch ohne Ausfall des Services können Sie auch vollautomatisch starten:
+>
+> ``` shell
+> aws autoscaling start-instance-refresh \
+> --auto-scaling-group-name
+> ```
+
+
+
diff --git a/07-iaas/uebung/teil-2/.gitignore b/07-iaas/uebung/teil-2/.gitignore
new file mode 100644
index 00000000..1cd30efd
--- /dev/null
+++ b/07-iaas/uebung/teil-2/.gitignore
@@ -0,0 +1,228 @@
+# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,intellij+all,visualstudiocode,terraform,direnv,dotenv
+# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,intellij+all,visualstudiocode,terraform,direnv,dotenv
+
+### direnv ###
+.direnv
+.envrc
+
+### dotenv ###
+.env
+
+### Intellij+all ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij+all Patch ###
+# Ignore everything but code style settings and run configurations
+# that are supposed to be shared within teams.
+
+.idea/*
+
+!.idea/codeStyles
+!.idea/runConfigurations
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### macOS Patch ###
+# iCloud generated files
+*.icloud
+
+### Terraform ###
+# Local .terraform directories
+**/.terraform/*
+
+# .tfstate files
+*.tfstate
+*.tfstate.*
+
+# Crash log files
+crash.log
+crash.*.log
+
+# Exclude all .tfvars files, which are likely to contain sensitive data, such as
+# password, private keys, and other secrets. These should not be part of version
+# control as they are data points which are potentially sensitive and subject
+# to change depending on the environment.
+*.tfvars
+*.tfvars.json
+
+# Ignore override files as they are usually used to override resources locally and so
+# are not checked in
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+
+# Include override files you do wish to add to version control using negated pattern
+# !example_override.tf
+
+# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
+# example: *tfplan*
+
+# Ignore CLI configuration files
+.terraformrc
+terraform.rc
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+### VisualStudioCode Patch ###
+# Ignore all local history of files
+.history
+.ionide
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,intellij+all,visualstudiocode,terraform,direnv,dotenv
diff --git a/07-iaas/uebung/teil-2/.terraform.lock.hcl b/07-iaas/uebung/teil-2/.terraform.lock.hcl
new file mode 100644
index 00000000..f2bfe2d7
--- /dev/null
+++ b/07-iaas/uebung/teil-2/.terraform.lock.hcl
@@ -0,0 +1,53 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+ version = "5.50.0"
+ constraints = ">= 5.30.0, 5.50.0"
+ hashes = [
+ "h1:5r7HLEw7aTWhgSofDeHXZiTrrssF0Td73gySY7bksrY=",
+ "h1:LevuTzPS4S7t+Vh6Kpz77pBNDAwChaos91/6+CVnD4w=",
+ "h1:OE1Q924lUL15OytvxwkdIspPsLRe0m2044W55j3lihE=",
+ "h1:WL4SfIhP8jI5CksUGSgkOFrXvAfbcGnOw0gVrzZZ4rw=",
+ "h1:r1ccZSTQXCtMrequqbBQDXrIVpRx/iYSk1cnS/RYcMs=",
+ "zh:19be42f5a545d6712dee4bdb704b018d23bacf5d902ac3cb061eb1750dfe6a20",
+ "zh:1d880bdba95ce96efde37e5bcf457a57df2c1effa9b47bc67fa29c1a264ae53b",
+ "zh:1e9c78e324d7492be5e7744436ed71d66fe4eca3fb6af07a28efd0d1e3bf7640",
+ "zh:27ac672aa61b3795931561fdbe4a306ad1132af517d7711c14569429b2cc694f",
+ "zh:3b978423dead02f9a98d25de118adf264a2331acdc4550ea93bed01feabc12e7",
+ "zh:490d7eb4b922ba1b57e0ab8dec1a08df6517485febcab1e091fd6011281c3472",
+ "zh:64e7c84e18dac1af5778d6f516e01a46f9c91d710867c39fbc7efa3cd972dc62",
+ "zh:73867ac2956dcdd377121b3aa8fe2e1085e77fae9b61d018f56a863277ea4b6e",
+ "zh:7ed899d0d5c49f009b445d7816e4bf702d9c48205c24cf884cd2ae0247160455",
+ "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+ "zh:9b93784b3fb13d08cf95a4131c49b56bf7e1cd35daad6156b3658a89ce6fb58f",
+ "zh:b29d77eb75de474e46eb47e539c48916628d85599bcf14e5cc500b14a4578e75",
+ "zh:bbd9cec8ca705452e4a3d21d56474eacb8cc7b1b74b7f310fdea4bdcffebab32",
+ "zh:c352eb3169efa0e27a29b99a2630e8298710a084453c519caa39e5972ff6d1fc",
+ "zh:e32f4744b43be1708b309a734e0ac10b5c0f9f92e5849298cf1a90f2b906f6f3",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/random" {
+ version = "3.6.2"
+ constraints = "3.6.2"
+ hashes = [
+ "h1:5lstwe/L8AZS/CP0lil2nPvmbbjAu8kCaU/ogSGNbxk=",
+ "h1:R5qdQjKzOU16TziCN1vR3Exr/B+8WGK80glLTT4ZCPk=",
+ "h1:UQlmHGddu39vVzG8kruMsde4GHlG+1S7OLqFApbJvtc=",
+ "h1:VavG5unYCa3SYISMKF9pzc3718M0bhPlcbUZZGl7wuo=",
+ "h1:wmG0QFjQ2OfyPy6BB7mQ57WtoZZGGV07uAPQeDmIrAE=",
+ "zh:0ef01a4f81147b32c1bea3429974d4d104bbc4be2ba3cfa667031a8183ef88ec",
+ "zh:1bcd2d8161e89e39886119965ef0f37fcce2da9c1aca34263dd3002ba05fcb53",
+ "zh:37c75d15e9514556a5f4ed02e1548aaa95c0ecd6ff9af1119ac905144c70c114",
+ "zh:4210550a767226976bc7e57d988b9ce48f4411fa8a60cd74a6b246baf7589dad",
+ "zh:562007382520cd4baa7320f35e1370ffe84e46ed4e2071fdc7e4b1a9b1f8ae9b",
+ "zh:5efb9da90f665e43f22c2e13e0ce48e86cae2d960aaf1abf721b497f32025916",
+ "zh:6f71257a6b1218d02a573fc9bff0657410404fb2ef23bc66ae8cd968f98d5ff6",
+ "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
+ "zh:9647e18f221380a85f2f0ab387c68fdafd58af6193a932417299cdcae4710150",
+ "zh:bb6297ce412c3c2fa9fec726114e5e0508dd2638cad6a0cb433194930c97a544",
+ "zh:f83e925ed73ff8a5ef6e3608ad9225baa5376446349572c2449c0c0b3cf184b7",
+ "zh:fbef0781cb64de76b1df1ca11078aecba7800d82fd4a956302734999cfd9a4af",
+ ]
+}
diff --git a/07-iaas/uebung/teil-2/README.md b/07-iaas/uebung/teil-2/README.md
new file mode 100644
index 00000000..5b1159ed
--- /dev/null
+++ b/07-iaas/uebung/teil-2/README.md
@@ -0,0 +1,39 @@
+# Übung: Infrastructure as Code mit Terraform auf AWS
+
+Ziel dieser Übung ist das erlernen grundlegender Infrastructure as Code Fähigkeiten mit Terraform auf der AWS Cloud.
+Hierzu werden Sie die Architektur aus der letzten Übung mit Terrform nachbauen.
+Grundlegende Schritte sind hierfür schon vorbereitet.
+
+1. Starten Sie den `iaas-container` und mounten Sie das Verzeichnis `uebung/teil-2` nach `/root/uebung/teil-2` im Container.
+ Beispiel mit Bash aus dem Verzeichnis:
+
+ ``` shell
+ cd 07-iaas/uebung/teil-2
+ docker run -it --rm -w /code --mount type=bind,source="$(pwd)",target=/code iaas-container
+ ```
+
+2. Konfigurieren Sie Ihren AWS Zungang mit `aws configure`.
+
+ ``` text
+ AWS Access Key ID [None]: AKIA...
+ AWS Secret Access Key [None]: M0XY...
+ Default region name [None]: eu-central-1
+ Default output format [None]: json
+ ```
+
+3. Initialisieren Sie das Arbeitsverzeichnis mit `terraform init`.
+
+4. Schauen Sie sich die bereits existierenden Terraform-Dateien an und machen Sie sich mit der grundlegenden Struktur vertraut.
+
+5. Implementieren Sie alle Stellen die mit `#ToDo` annotiert sind.
+ Definieren Sie sich selbst eine sinnvolle reihenfolge.
+ In regelmäßigen Abständen sollten Sie ihre Implementierungsarbeiten auf die AWS-Cloud mit `terraform apply` anwenden.
+
+6. Am Ende sollte die Terraform-Konfiguration einen Output-Parameter mit einer validen und funktionierenden URL enthalten.
+
+7. Erzeugen Sie einen neuen Workspace mit `terraform workspace new dev` und wechseln zu diesem mit `terraform workspace select dev`.
+ Überprüfen Sie, ob Sie mit `terraform apply` eine zweite Umgebung erzeugen können.
+ Wenn nicht passen Sie ihre Konfigurationen so an, dass dies möglich ist.
+ Machen Sie dafür insbesondere Benennungen von Ressourcen abhängig vom verwendeten Workspace.
+
+8. Zerstören Sie alle erzeugten Ressourcen mit `terraform destroy` auf beiden Workspaces.
diff --git a/04-iaas/uebung/teil-2/autoscaling.tf b/07-iaas/uebung/teil-2/autoscaling.tf
similarity index 56%
rename from 04-iaas/uebung/teil-2/autoscaling.tf
rename to 07-iaas/uebung/teil-2/autoscaling.tf
index 650e6546..a49c9d7e 100644
--- a/04-iaas/uebung/teil-2/autoscaling.tf
+++ b/07-iaas/uebung/teil-2/autoscaling.tf
@@ -5,7 +5,7 @@ resource "aws_security_group" "app" {
tags = local.standard_tags
}
-resource "aws_security_group_rule" "app_ssh" {
+resource "aws_security_group_rule" "app_ingress_ssh_all" {
security_group_id = aws_security_group.app.id
description = "Allows SSH from everywhere"
type = "ingress"
@@ -15,8 +15,20 @@ resource "aws_security_group_rule" "app_ssh" {
cidr_blocks = ["0.0.0.0/0"]
}
+# ToDo: Allow Ingress from the Security Group of the Load Balancer to the App Security Group in port 8080
+# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
-resource "aws_security_group_rule" "app_outgoing" {
+# resource "aws_security_group_rule" "app_ingress_http_lb" {
+# security_group_id =
+# description = "Allows HTTP access from the load balancer"
+# type = "ingress"
+# from_port =
+# to_port =
+# protocol = "tcp"
+# source_security_group_id =
+# }
+
+resource "aws_security_group_rule" "app_egress_all" {
security_group_id = aws_security_group.app.id
description = "Allows all outgoing traffic"
type = "egress"
@@ -26,29 +38,17 @@ resource "aws_security_group_rule" "app_outgoing" {
cidr_blocks = ["0.0.0.0/0"]
}
-# TODO: Allow Ingress from the Security Group of the Load Balancer to the App Security Group in port 8080
-# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
-
-# resource "aws_security_group_rule" "app_to_lb" {
-# security_group_id =
-# description = "Allows HTTP access from the load balancer"
-# type = "ingress"
-# from_port =
-# to_port =
-# protocol = "tcp"
-# source_security_group_id =
-# }
-
resource "aws_launch_template" "app" {
- name = local.env
-
- image_id = "ami-0dba2cb6798deb6d8"
+ name = local.env
+ image_id = "ami-01e444924a2233b07"
instance_initiated_shutdown_behavior = "terminate"
update_default_version = true
+ instance_type = "t2.micro"
- instance_type = "t2.micro"
-
- vpc_security_group_ids = [aws_security_group.app.id]
+ network_interfaces {
+ associate_public_ip_address = true
+ security_groups = [aws_security_group.app.id]
+ }
tag_specifications {
resource_type = "instance"
@@ -62,20 +62,28 @@ resource "aws_launch_template" "app" {
)
}
-
-# TODO: Create an AutoScaling Group that manages 0 to 4 instances, with a default of 1
+# ToDo: Create an AutoScaling Group that manages 0 to 4 instances, with a default of 1
# See: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group
-# resource aws_autoscaling_group app {
-# name = local.env
-# min_size =
-# max_size =
+# resource "aws_autoscaling_group" "app" {
+# name = local.env
+# min_size =
+# max_size =
+# desired_capacity =
+# vpc_zone_identifier = module.vpc.public_subnets
+# health_check_type =
+# target_group_arns = []
+
# launch_template {
-# id =
+# id =
+# }
+
+# dynamic "tag" {
+# for_each = local.standard_tags_asg
+# content {
+# key = tag.value.key
+# propagate_at_launch = tag.value.propagate_at_launch
+# value = tag.value.value
+# }
# }
-# desired_capacity =
-# vpc_zone_identifier = module.vpc.public_subnets
-# # health_check_type =
-# # target_group_arns = []
-# tags = local.standard_tags_asg
# }
diff --git a/07-iaas/uebung/teil-2/init.sh b/07-iaas/uebung/teil-2/init.sh
new file mode 100644
index 00000000..6ad13720
--- /dev/null
+++ b/07-iaas/uebung/teil-2/init.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+set -euxo pipefail
+
+apt-get update
+apt-get install -y busybox cowsay
+rm -rf /var/lib/apt/lists/*
+
+{
+ echo "