<!-- /data/docker/apache1/html/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Kabango.eu</title>
</head>
<body>
<h1>Welcome to Kabango.eu</h1>
<p>This is the main website.</p>
<p>Visit our <a href="/special">special section</a>.</p>
</body>
</html>
Special Section (apache2):
<!-- /data/docker/apache2/html/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Special Section - Kabango.eu</title>
</head>
<body>
<h1>Special Section</h1>
<p>This is the special section of Kabango.eu</p>
<p><a href="/">Back to main page</a></p>
</body>
</html>
Copy all configuration files to their respective locations as shown above
Ensure correct file permissions
Configure DNS:
Point these domains to your server's IP:
kabango.eu
www.kabango.eu
traefik.kabango.eu
portainer.kabango.eu
Start services in order:
cd /data/docker/traefik && docker compose up -d
cd /data/docker/apache1 && docker compose up -d
cd /data/docker/apache2 && docker compose up -d
cd /data/docker/portainer && docker compose up -d
Access Points
After setup, the following services will be available:
Ich möchte die nativen Dienste auf meinem Server zur besseren Verwaltung und als Vorbereitung für eine kommende Migration auf Docker umstellen.
Als Vorbereitung für diese Aufgabe habe ich in Lokaler virtueller Server bereits ein grundlegendes Setup lokal evaluiert.
Heute möchte ich den Apache HTTP Server, der auch als Reverse Proxy dient, in einen Container stecken.
Vorbereitung
Docker deinstallieren
Auf dem Server ist bereits eine alte Docker Installation vorhanden. Diese habe ich als erstes rückstandslos entfernt.
# Service finden
systemctl list-unit-files --type service
# Service stoppen
sudo systemctl stop apache2
Container-Apache starten:
cd /data/docker/apache
docker compose up
Ausgabe:
[+] Building 0.0s (0/0)
Attaching to apache
apache | httpd: Could not open configuration file /usr/local/apache2/conf/httpd.conf: No such file or directory
apache exited with code 0
Das hat also schon mal so gar nicht geklappt. Woran kann es liegen? Zur Analyse interaktiv in dem Container agieren:
docker compose run -it --entrypoint /bin/bash apache
Ich kann im Container sehen, dass die Konfigurations-Dateien vorhanden sind, d.h. die Docker-Compose-Konfig ist an der Stelle korrekt.
Allerdings fehlt die geforderte httpd.conf.
Bei Ubuntu heißt die Datei apache2.conf, der Docker Container erwartet aber eine httpd.conf. Als Workaround lege ich eine httpd.confan, die auf die apache2.conf verweist:
Include /usr/local/apache2/conf/apache2.conf
Jetzt bekomme ich beim Starten des Containers andere Fehlermeldungen.
Aufräumen
Das entwickelt sich nicht wie gewünscht, ich breche ab und räume auf:
docker compose down -v
sudo rm -rf /data/docker/apache
Kleiner Apache
Um einen minimalen Teilerfolg feiern zu können, setzte ich einen Apache im Container auf, der die HTML-Seiten auf Port 9080 ausliefert.
mkdir /data/docker/apache
mkdir /data/docker/apache/logs
cd /data/docker/apache
vim docker-compose.yml
docker compose up -d
docker logs apache
Der naive "Lift and Shift" Ansatz hat mal wieder nicht funktioniert.
Die Pfade sind bei der nativen Ubuntu Installation und dem Container Apache unterschiedlich. Der simple Workaround mit der httpd.conf-Datei war ein erster Schritt, danach hätten noch Umgebungsvariablen wie APACHE_RUN_DIR gesetzt werden müssen.
Dann hätte ich noch einige Pfade vom Server in den Container mappen müssen.
Und dann ist da noch der Let's encrypt Certbot, der doch mehr mit der Apache Installation verdrahtet ist, als ich am Anfang dachte. Den hätte ich auch noch im Container installieren müssen.
Sicherlich alles machbar, aber für eine Interimslösung zu aufwändig. Am Ende soll ja Traefik SSL und Reverse Proxy übernehmen. Daher belasse ich es hier erstmal.
Ich habe schon seit langer Zeit einen virtuellen Server gemietet, auf dem ich verschiedene Dienste, wie zB Webseite und Mail, laufen lasse. Die Anwendungen laufen direkt auf dem Server und ich will sie schon seit langem in Container stecken. Das Betriebssystem ist mit Ubuntu 18.04 LTS hoffnungslos veraltet, aktuell ist 24.04 LTS.
Ein Upgrade von Ubuntu 18 auf 24 ist mit Risiko und vermutlich hohem Aufwand verbunden, da erfahrungsgemäß irgendetwas irgendwie anders funktioniert und mühevoll angepasst werden muss. Grade bei einer Upgradekette 18->20->22->24 kann so einiges schief gehen.
Den Server miete ich seit ein paar Jahren und inzwischen gibt es für das gleiche Geld bessere (virtuelle) Hardware.
Aus diesen Gründen plane ich, einen neuen virtuellen Server zu mieten und auf diesen umzuziehen.
Wie es auf dem Neuen laufen soll
Auf dem neuen Server soll Ubuntu 24.04 LTS laufen.
Auf dem Linux System soll Docker installiert werden.
Die Docker Container sollen mit Portainer verwaltet werden.
Die Webseite der Portainer Verwaltung und die weiteren Dienste sollen über einen Reverse Proxy, beispielsweise Traefik, erreichbar sein.
Der Reverse Proxy soll den Zugriff ausschließlich verschlüsselt über HTTPS erlauben und entsprechend konfiguriert sein.
Das HTTPS Zertifikat soll von Let's Encrypt kommen.
lokaler virtueller Server
Zuerst möchte ich einen lokalen virtuellen Server einrichten um auf diesem die Migration vorbereiten zu können. Dazu werde ich Virtual Box von Oracle verwenden.
graph TD
G[Portainer Webinterface] --> F
H[Website: Nginx] --> F
I[Mailserver: Postfix/Dovecot] --> F
F[Traefik] --> E
E[Portainer] --> D
D[Docker] --> C
C[Ubuntu 24.04 LTS] --> B
B[VirtualBox] --> A
A[Host System: PC]
Vom Host System (also mein PC, auf dem Virtual Box läuft) aus soll der Zugriff auf die Webseite per Domain Name funktionieren, nicht nur über die IP. Idealerweise sollte die Verschlüsselung mit Let's Encrypt vorgenommen werden, um so realistisch wie möglich das spätere System vorzubauen.
Leider ist das nicht (mit vertretbarem Aufwand) möglich, daher werde ich alles ohne HTTPS aufsetzen.
In den Netzwerkadapter-Einstellungen der virtuellen Maschine auf "Bridged Adapter" (Brückenadapter) umstellen
nach der Installation einloggen und mittels ip a die IP-Adresse der VM identifizieren (192.168.178.47)
Auf dem Host System in der Datei C:\Windows\System32\drivers\etc\hosts hinzufügen:
192.168.178.47 kaulbach.local
192.168.178.47 traefik.kaulbach.local
192.168.178.47 portainer.kaulbach.local
Subdomain eintragen
Auf dem Host System in der Datei C:\Windows\System32\drivers\etc\hosts den Eintrag für die Subdomain "traefik.kaulbach.local" hinzufügen:
192.168.178.47 traefik.kaulbach.local
Dashboard aufrufen:
Das Traefik-Dashboard ist unter http://traefik.kaulbach.local erreichbar.
4. **Traefik-Labels nutzen**:
- Die Labels im obigen Beispiel sorgen dafür, dass Traefik den Webserver unter `http://kaulbach.local` bereitstellt.
5. **Container starten**:
```bash
docker compose up -d
# Log Files checken
docker logs web
Webseite aufrufen:
Die Webseite ist unter http://kaulbach.local erreichbar.
3. **Traefik-Labels nutzen**:
- Die Labels im obigen Beispiel sorgen dafür, dass Traefik das Portainer Dashboard unter `http://portainer.kaulbach.local` bereitstellt.
4. **Container starten**:
```bash
docker compose up -d
# Log Files checken
docker logs portainer
Subdomain eintragen
Auf dem Host System in der Datei C:\Windows\System32\drivers\etc\hosts den Eintrag für die Subdomain "portainer.kaulbach.local" hinzufügen:
192.168.178.47 portainer.kaulbach.local
Dashboard aufrufen:
Das Traefik-Dashboard ist unter http://portainer.kaulbach.local erreichbar.
Portainer konfigurieren
Passwort für den User "admin" festlegen: "adminpassword".
Environment "local" ist bereits angelegt
6. ein weiterer Webserver
Webserver anlegen und starten:
mkdir -p ~/web2/html
echo "<h1>Hello vom geheimnisvollen 2. Server</h1>" > ~/web2/html/index.html
touch ~/web2/docker-compose.yml # mit Inhalt s.u. befüllen
cd ~/web2
docker compose up -d
3. **`docker-compose.yml` des ersten Webservers anpassen**:
Der Router des ersten Webservers mit dem Host(`kaulbach.local`) wird auch auf /derzweite matchen.<br>
Daher müssen wir die Regel für den Router des ersten Servers anpassen:
```- "traefik.http.routers.nginx.rule=PathPrefix(`/`) && !PathPrefix(`/derzweite`)"```
4. **Webseite aufrufen**:
Die Webseite ist unter `http://kaulbach.local/derzweite` erreichbar.
## Abschluß
Das Fundament ist gelegt, darauf aufbauend kann ich die Migration der einzelnen Anwendungen erarbeiten.<br>
Der Code-Editor, bzw. die Anzeige des Codes, des "Markup Markdown"-Editors ist ziehmlich kaputt. Und das was man sieht, sieht hässlich aus. Ich habe auch schon ein Ticket erstellt, ob und wie man andere Code-Editoren wie CodeMirror Blocks einbinden kann.<br>
Die Dateien habe ich in [GitHub](https://github.com/DerIngo/localvirtualserver) eingecheckt.
Performance-Analyse einer PostgreSQL-Datenbank mit pg_stat_statements
Bei einer Webanwendung mit einer PostgreSQL-Datenbank stießen wir auf Performance-Probleme. Hier beschreibe ich, wie man Performance-Probleme in PostgreSQL mittels der Erweiterung pg_stat_statements analysieren kann. Da die Erweiterung nicht auf der Produktivdatenbank installiert ist, zeige ich, wie man eine lokale Testumgebung aufsetzt.
Überblick der Vorgehensweise
Backup der Produktivdatenbank erstellen
Lokale Testdatenbank mit Docker aufsetzen
pg_stat_statements installieren und konfigurieren
Performance-Analyse durchführen
Vorbereitung
Zuerst prüfen wir die Version der Datenbank, um die passende Docker-Umgebung zu wählen:
SELECT version();
-- PostgreSQL 13.4
Backup erstellen
Das Backup erfolgt mittels Docker, um eine konsistente Umgebung zu gewährleisten. Hier das Script backup.sh:
Verwendet wird Port 6432, um Konflikte mit einer möglicherweise laufenden lokalen PostgreSQL-Instanz zu vermeiden.
Daten importieren
Das Backup wird dann in die lokale Datenbank importiert, über das Script import.sh:
#!/bin/bash
# Check if arguments were passed
if [ $# -eq 0 ]; then
echo "Error: No arguments provided"
echo "Usage: $0 "
exit 1
fi
# Check if file exists
if [ ! -f "$1" ]; then
echo "Error: $1 is not a valid file."
exit 1
fi
echo "File found: $1"
importfile=$(readlink -f "$1")
server=localhost
port=6432
username=myuser
password=PASSWORD
databasename=myuser
echo psql -h $server -U $username -d $databasename -f $importfile
docker run --rm \
--network=host \
-v $importfile:/script.sql \
-e PGPASSWORD=$password \
postgres:13.4-bullseye \
psql -h $server -p $port -U $username -d $databasename -f script.sql
pg_stat_statements installieren
Um pg_stat_statements zu aktivieren, müssen wir die PostgreSQL-Konfiguration anpassen. Dazu bearbeiten wir die postgresql.conf. Da in dem Postgres Container kein Editor enthalten ist, ex- und importieren wir die Datei, um sie bearbeiten zu können:
I was on JCON 2024 and beside other interesting talks I heard one talk about cloud-based IDEs, and I wanted to try out, if GitHub Codespaces could work for me.
Explore the evolving landscape of cloud-based integrated development environments (IDEs), focusing on Gitpod, GitHub codespaces and Devpod. Compare and contrast these cloud IDEs with traditional counterparts, emphasizing the role of container technology, specifically the devcontainer specification. The discussion includes advances, existing limitations, and the potential for developing polyglot, container-based distributed applications. A live demo illustrates the rapid setup and coding process across various languages and frameworks, showcasing testing capabilities and seamless deployment to Kubernetes. Discover how custom additions enhance flexibility. Additionally, uncover the impact of cloud IDEs on teaching and team projects, ensuring consistent development setups for enhanced efficiency and streamlined processes.
[EN] Codespaces, Gitpod, Devpod ... what cloud and container-based IDEs can do for you by Matthias Haeussler (Novatec Consulting GmbH)
Create GitHub Account
Go to GitHub and create an account. Free plan is suitable.
Create Repository
Create a new repository with name “workshop”. Add a README file.
Create Codespace
TODO: funktioniert das GIF?
Change Keyboard Layout to German: In the lower right corner click on “Layout: US” and enter “German” in the upcoming window.
TODO: Ich hätte gerne die Sprache von Visual Code auf Englisch umgestellt. Wie?
Work in the Terminal
Copy & Paste
Type something into the terminal. Mark it with your mouse. One Right Click to copy into Clipboard. Another Right Click to paste from Clipboard.
Timezone
Set Timzone to Europe -> Berlin
sudo dpkg-reconfigure tzdata
Internet
Do we have access to the Internet? Let’s try with curl:
This will take a few minutes. Meanwhile we can work in another Terminal window. Later we come back and test HTTPie:
http google.com
Additional Terminal window
Open a second Terminal with bash:
VIM
ls -lisah
touch test.sh
ls -lisah
vim test.sh
chmod +x test.sh
./test.sh
name=Ingo
echo "My name is $name"
echo "But here I am: $(whoami)"
Python
Do we have Python in our Codespace? Which version(s)?
python3 --version
python --version
vim hello_world.py
python hello_world.py
# Print "Hello World" to the console
print("Hello World")
Docker
docker --version
docker-compose --version
docker run hello-world
Apache HTTPD
docker run -p 8888:80 httpd
Open in Browser:
Find all open Ports in the Ports-Tab:
Normally Port 8888 should be listed here. We need to add Port, just enter 8888:
Open Website just with a click on the Globus-Icon.
When we try to open the address in another browser, we will see a GitHub-Login. When we login with another GitHub-Account, we will get a 404-error. Because the page is Private. Switch to Public:
Now we can access the page in another brower.
At the end we can shutdown HTTPD with + in Terminal window. It should automatically disapear in the Ports-Tab. If not, you can remove it manually.
Microsoft Edge - Caching problem
Open the Public page in MS Edge. Make the page Private again. Try to open in a new browser, won’t work. Reload (the Public loaded) page in MS Edge: You can still see the site! This is a cached version and we need to force MS Edge to reload from server.
Open Developer Tools (F12 or ++), then you can Right Click on the reload button to have additional options:
Java
java --version
vim HelloWorld.java
javac HelloWorld.java
java HelloWorld
rm -f HelloWorld*
class HelloWorld {
public static void main(String args[]) {
System.out.println("Hello World");
}
}
Run Java Source as Shell Scripts
type -a java
# java is /home/codespace/java/current/bin/java
# java is /usr/local/sdkman/candidates/java/current/bin/java
vim HelloWorld.sh
chmod +x HelloWorld.sh
./HelloWorld.sh
rm HelloWorld.sh
#!/home/codespace/java/current/bin/java --source 21
class HelloWorld {
public static void main(String args[]) {
System.out.println("Hello World");
}
}
Maven
Start
We create a new pom.xml from scratch. We need a template. We will take “The Basics”-one from the Apache Maven POM Reference page.
4.0.0org.codehaus.mojomy-project1.0
mvn --version
vim pom.xml
mvn clean verify
Sample Project
Open pom.xml in Explorer (GUI) and change:
org.codehaus.mojo to org.acme
my-project to workshop
No need to save: Changes are automatically saved
To doublecheck that everything is still ok run mvn clean verify again.
I want to login from one Linux server to another Linux server without the need to enter a password.
Create SSH keys
# Login to server 1
ssh-keygen
Output:
Generating public/private rsa key pair.
Enter file in which to save the key (/home/'USERNAME'/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/'USERNAME'/.ssh/id_rsa
Your public key has been saved in /home/'USERNAME'/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:27U2nApZQLSwS1UVh2Lw4aDM/B9/gR0Uum1ppRrqjpg 'USERNAME'@server1
The key\'s randomart image is:
+---[RSA 3072]----+
| +o+ .o |
| + . Bo+o . |
| = o.*... |
| o + o . |
| + o S . |
| . X * + |
| . * @ O |
|E . .. B * . |
| ..+..o o |
+----[SHA256]-----+
The utility will prompt to select a location for the keys that will be generated. By default, the keys will be stored in the ~/.ssh directory within user’s home directory. The private key will be called id_rsa and the associated public key will be called id_rsa.pub.
Now we have a private and public key that we can use to authenticate.
Now we SHOULD connect to server2 without the need to enter a password.
Failing - But why?
Unluckily in my case I still have to enter a password. But why?
# Login to server 2
ssh server2ip
Password:
# check auth.log
less /var/log/auth.log
...
rexec line 15: Deprecated option RSAAuthentication
...
# check sshd_config
less /etc/ssh/sshd_config
...
RSAAuthentication no
...
So in my case the RSA-Authentication has been disabled. As the default is enabled by purpose, this has been done by purpose. Unfortunatly I am not the admin or manager of the server, so I can not change this settings.
Dadurch werden die Logfiles mit dem User root geschrieben und ich kann sie mit meinem User ingo nicht lesen.
Auf der Console kann ich das leicht mit einem vorangestellten sudo lösen, aber um mal eben schnell in die Logfiles rein zu schauen würde ich gerne mein graphisches Tool WinSCP verwenden
Lösung
Man kann Docker / Docker Compose mit einem User starten und mit dem würden dann vermutlich auch die Logfiles geschrieben werden. Als ich das mit einem Tomcat Image getestet hatte, ist es daran gescheitert, dass mit meinem User ingo auf bestimmte Verzeichnisse im Container nicht zugegriffen werden konnte.
Gelöst habe ich es dann so, dass ich nicht den User, oder User und Gruppe, gesetzt habe, sondern nur die Gruppe. So wird mit dem root User gearbeitet, die Dateien gehören dem User root und für die gesetzte Gruppe sind sie lesbar. Mein User muss natürlich ebenfalls in der Gruppe sein.
Gruppe anlegen:
sudo groupadd -g 1001 logfilegroup
Die Group ID ist relativ willkürlich gesetzt. Eigentlich hatte ich groupadd ohne das -g Flag aufgerufen und dann mit cat /etc/group die Group ID rausgesucht. Hier wollte ich das Statement mit explizitem setzen der Group ID hinschreiben, da ich es auch im Projekt verwendet hatte, um auf jedem Server die selbe Group ID zu haben.
User der Gruppe hinzufügen:
sudo usermod --append --groups logfilegroup ingo
Mit den Befehlen groups oder id kann man die Gruppen seines Users sehen, die neue logfilegroup wird aber erst in einer neuen Shell hinzugefügt. Also entweder die Shell schließen und neu öffnen, oder mit su ingo weiter arbeiten.
ingo$ sudo usermod --append --groups logfilegroup ingo
ingo$ groups
ingo adm
ingo$ su ingo
Password:
ingo$ groups
ingo adm logfilegroup
Docker Compose File:
Im Docker Compose File muss die Group ID gesetzt werden, mit dem Namen der Gruppe geht es nicht.
Am Wochenende habe ich zum ersten Mal einen Escape Room spielen dürfen und ich muss sagen, heute habe ich mich ähnlich gefühlt, denn das Bugfixing ähnelt auch einer kreativen rätselratenden Schnitzeljagd.
Der Bug
Die von mir entwickelte Anwendung ist mittlerweile im Pilot-Betrieb und ein Benutzer meldet, dass er einen Vorgang nicht beenden kann: Vermeindlich hochgeladene Bilder, die zwingend erforderlich sind, 'verschwinden' und so kann er nicht abschließend speichern.
Die Bilder werden dankenswerterweise mitgeliefert, so dass ich auch versuchen kann, das Problem nachzustellen.
Es gibt auch einen Hinweis, das das Ausführen einer bestimmten Funktion während des Vorgangs das Problem verursacht haben könnte. ERROR-Einträge in den Logfiles unterstützen diese These, aber letztendlich hatte es damit nichts zu tun. Es war nur eine falsche Fährte, eine Ablenkung, wie bei jedem guten Spiel 😉 Und die Erkenntnis, dass da noch irgendwo ein weiterer Bug schlummert, der von mir gejagt und entdeckt werden will.
Bug reproduzieren
Als erstes versuche ich, den Bug lokal nachstellen zu können, also auf meinem Entwickler-Laptop. Das funktioniert aber nicht, da alles fehlerfrei funktioniert, incl. Upload der beiden Bilder.
Hier wäre jetzt eine gute Gelegenheit, das Ticket mit "Works on my machine" zu schließen.
Aber ich forsche weiter und versuche, das Problem auf dem Server nachzustellen. Und hier gelingt es: Die Bilder verschwinden auf magische Art und Weise. Und ohne Einträge in den Logs.
Analyse
Der Vorgang durchläuft einen Wizard, und nachdem man die Bilder hochgeladen hat, kann man einen Schritt weiter. Wenn man dann einen Schritt zurück geht, sind die Bilder weg.
Mit meiner Testdatei (WOW!!.png) funktioniert es hingegen.
Auffällig ist, dass die andere Datei nicht korrekt in die Tabelle der Dateien übertragen wird, sondern ewig in der Upload-Ansicht verharrt. Es gibt aber keine Fehlermeldung. Die Datei ist 6,4 MB groß, erlaubt sind Dateigrößen bis 10 MB; bei größeren Dateien kommt auch ein entsprechender Hinweis. Hier scheint der Upload nie enden zu wollen, was auch ein Hinweis ist, warum sie später verschwindet: sie war nie auf dem Server.
Mein erster Verdacht war, dass vielleicht ein Virenscanner dazwischenfunkt und die Datei auf dem Server direkt wieder löscht. Alles schon mal vorgekommen. Also lade ich die beiden Bilder per FTP auf den Server, was auch funktioniert und somit gegen die Virenscannerhypothese spricht.
Also zurück in den Browser und mal schauen, was sich so in der Entwicklerkonsole tut. Und dort ist auch ein Eintrag:
Der Upload wurde abgebrochen (413 - Request Entity Too Large). Das hat das JQuery- bzw. PrimeFaces-Framework nicht richtig erkannt und keine Fehlermeldung auf der Oberfläche angezeigt. Ein Bug im Framework, dass dieser spezielle Fall nicht richtig erkannt wird. Das interessiert mich aber nicht so sehr, viel spannender ist der Fehler 413. Erlaubt sind 10 MB, die Datei sind keine 7 MB. Es müsste also eigentlich passen.
In der Anwendung sind 10 MB erlaubt, das sollte nicht das Problem sein, bliebe noch der Anwendungsserver, ein Apache Tomcat, oder der davorgeschaltete ReverseProxy, ein Apache HTTP-Server.
Wenn man dem Netzwerkverkehr folgt sieht man, dass folgende Antwort beim Upload-Versuch zurück kommt:
Das Indiz für die Ursache des Problems ist das unscheinbare Wort: "nginx"
Der Verdacht erhärtet sich beim weiteren Testen: Wenn ich die WOW-Testdatei in der Anwendung hochlade, erscheint das POST in den Logs des vorgelagerten Apache-ReverseProxies:
Der Versuch des Hochladens des Problembildes erzeugt keinen Log-Eintrag.
Es scheint also so zu sein, dass der Upload bereits vor meiner Anwendung abgebrochen wird.
Der Server
Schauen wir mal, wie es auf dem Test Server aussieht:
Meine Anwendung starte ich über ein Docker-Compose, bestehend aus:
Apache HTTP als Reverse Proxy
Apache Tomcat
PG Admin
PostgreSQL Datenbank
Auf dem Server selbst läuft aber nicht nur meine Anwendung, sondern auch noch weitere, die uA über unterschiedliche Domainnamen erreichbar sind. Und das steuert ein Nginx-Server:
Und was finde ich, wenn ich das default File Limit für Nginx suche:
By default, Nginx has a limit of 1MB on file uploads.
Die Lösung
Wir müssen also den file upload für die Anwendung erhöhten auf 10MB, besser 15MB:
Nach der Änderung die Konfiguration prüfen, um sicher zu gehen, dass keine Fehler eingebaut wurden und beim Neustart (bzw. Reload) der ganze Nginx-Server lahm gelegt wird:
nginx -t
Erst dann den Neustart, bzw. Reload durchführen:
service nginx reload
Test
Abschließend muss natürlich noch der Test erfolgen, ob wir damit erfolgreich waren: Der Upload funktioniert jetzt! 🥳
Everyone talks about Kubernetes, therefore I thought, it might be a good idea to get some experiences with this tool and install it on my developer machine.
To run a single node cluster of Kubernetes on my machine I will install Minikube.
My developer machine is a Windows computer with Ubuntu 20.04 on WSL 2.
$ minikube start
😄 minikube v1.30.1 on Ubuntu 20.04
✨ Automatically selected the docker driver. Other choices: none, ssh
📌 Using Docker driver with root privileges
👍 Starting control plane node minikube in cluster minikube
🚜 Pulling base image ...
💾 Downloading Kubernetes v1.26.3 preload ...
> preloaded-images-k8s-v18-v1...: 397.02 MiB / 397.02 MiB 100.00% 3.21 Mi
> gcr.io/k8s-minikube/kicbase...: 373.53 MiB / 373.53 MiB 100.00% 2.76 Mi
🔥 Creating docker container (CPUs=2, Memory=6300MB) ...
🐳 Preparing Kubernetes v1.26.3 on Docker 23.0.2 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔗 Configuring bridge CNI (Container Networking Interface) ...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🔎 Verifying Kubernetes components...
🌟 Enabled addons: default-storageclass, storage-provisioner
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
Step 6: Minikube Basic operations
To check cluster status, run:
$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:32769
CoreDNS is running at https://127.0.0.1:32769/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
Note that Minikube configuration file is located under ~/.minikube/machines/minikube/config.json
Let's doublecheck that minikube is a running Docker container:
$ sudo docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e36590b3ea7e gcr.io/k8s-minikube/kicbase:v0.0.39 "/usr/local/bin/entr…" 28 minutes ago Up 28 minutes 127.0.0.1:32772->22/tcp, 127.0.0.1:32771->2376/tcp, 127.0.0.1:32770->5000/tcp, 127.0.0.1:32769->8443/tcp, 127.0.0.1:32768->32443/tcp minikube
ingo:~$ minikube dashboard --port=42827 &
[6] 55787
ingo:~$ 🤔 Verifying dashboard health ...
🚀 Launching proxy ...
🤔 Verifying proxy health ...
🎉 Opening http://127.0.0.1:42827/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
👉 http://127.0.0.1:42827/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/
Open URL in Browser:
kubernetes dashboard
To enable a module use command:
minikube addons enable
Example:
$ minikube addons enable portainer
❗ portainer is a 3rd party addon and is not maintained or verified by minikube maintainers, enable at your own risk.
❗ portainer does not currently have an associated maintainer.
▪ Using image docker.io/portainer/portainer-ce:2.15.1
🌟 The 'portainer' addon is enabled
But I have no clue, what to do with the enabled 'portainer' addon. 🤷♂️
Deployments are the recommended way to manage the creation and scaling of Pods.
Use the kubectl create command to create a Deployment that manages a Pod. The Pod runs a Container based on the provided Docker image.
# Run a test container image that includes a webserver
kubectl create deployment hello-node --image=registry.k8s.io/e2e-test-images/agnhost:2.39 -- /agnhost netexec --http-port=8080
2. View the Deployment:
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
hello-node 0/1 1 0 9s
3. View the Pod:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-node-7b87cd5f68-rj79x 1/1 Running 0 67s
4. View cluster events:
kubectl get events
5. View the kubectl configuration:
kubectl config view
Create a Service
By default, the Pod is only accessible by its internal IP address within the Kubernetes cluster. To make the hello-node Container accessible from outside the Kubernetes virtual network, you have to expose the Pod as a Kubernetes Service.
Expose the Pod to the public internet using the kubectl expose command:
The --type=LoadBalancer flag indicates that you want to expose your Service outside of the cluster.
The application code inside the test image only listens on TCP port 8080. If you used kubectl expose to expose a different port, clients could not connect to that other port.
2. View the Service you created:
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-node LoadBalancer 10.101.148.235 8080:31331/TCP 2m52s
kubernetes ClusterIP 10.96.0.1 443/TCP 71m
On cloud providers that support load balancers, an external IP address would be provisioned to access the Service. On minikube, the LoadBalancer type makes the Service accessible through the minikube service command.
3. Run the following command:
$ minikube service hello-node
|-----------|------------|-------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------------|-------------|---------------------------|
| default | hello-node | 8080 | http://192.168.49.2:31331 |
|-----------|------------|-------------|---------------------------|
🏃 Starting tunnel for service hello-node.
|-----------|------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------------|-------------|------------------------|
| default | hello-node | | http://127.0.0.1:34597 |
|-----------|------------|-------------|------------------------|
🎉 Opening service default/hello-node in default browser...
👉 http://127.0.0.1:34597
❗ Because you are using a Docker driver on linux, the terminal needs to be open to run it.
Open http://127.0.0.1:34597/ in a browser:
hello-node
4. View Pods and Services created in 'default' namespace:
$ kubectl get pod,svc -n default
NAME READY STATUS RESTARTS AGE
pod/hello-node-7b87cd5f68-rj79x 1/1 Running 0 12m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/hello-node LoadBalancer 10.101.148.235 8080:31331/TCP 8m54s
service/kubernetes ClusterIP 10.96.0.1 443/TCP 77m
5. Cleanup
$ kubectl delete service hello-node
service "hello-node" deleted
$ kubectl delete deployment hello-node
deployment.apps "hello-node" deleted
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
Dockerfile for an nginx webserver to deliver this website:
FROM nginx
COPY index.html /usr/share/nginx/html
EXPOSE 80
Build, run and test image:
docker build -t myweb-image .
docker run -it -p 80:80 --name myweb-container myweb-image
$ http http://localhost
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 763
Content-Type: text/html
Date: Thu, 29 Jun 2023 15:57:54 GMT
ETag: "649da8a5-2fb"
Last-Modified: Thu, 29 Jun 2023 15:52:05 GMT
Server: nginx/1.25.1
Page Title
This is a Heading
This is a paragraph.
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
kubectl apply -f myweb.yaml
kubectl get deployment
kubectl get service
Start Service:
minikube service myweb-service &
Check in Terminal:
$ http http://127.0.0.1:38915
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 763
Content-Type: text/html
Date: Thu, 29 Jun 2023 16:19:18 GMT
ETag: "649da8a5-2fb"
Last-Modified: Thu, 29 Jun 2023 15:52:05 GMT
Server: nginx/1.25.1
Page Title
This is a Heading
This is a paragraph.
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
Cleanup:
$ kubectl delete -f myweb.yaml
Work in a pod
Start some pods & service and display them:
kubectl apply -f myweb.yaml
kubectl get all -o wide
Output:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/myweb-deployment-565b64686c-2nnrl 1/1 Running 0 3m42s 10.244.0.39 minikube
pod/myweb-deployment-565b64686c-m4p4c 1/1 Running 0 3m42s 10.244.0.41 minikube
pod/myweb-deployment-565b64686c-sx6sx 1/1 Running 0 3m42s 10.244.0.40 minikube
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.96.0.1 443/TCP 23h
service/myweb-service NodePort 10.97.251.106 80:32715/TCP 3m42s app=myweb-app
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.apps/myweb-deployment 3/3 3 3 3m42s myweb-container myweb-image:1.0 app=myweb-app
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
replicaset.apps/myweb-deployment-565b64686c 3 3 3 3m42s myweb-container myweb-image:1.0 app=myweb-app,pod-template-hash=565b64686c
i
apt update
apt install httpie inetutils-ping -y
# Connect to another Pod via IP
http 10.244.0.39
# Connect to Service via IP
http 10.97.251.106
# Connect to Service via Service Name
http myweb-service
# Check IP of Service
ping myweb-service
## OUTPUT:
# PING myweb-service.default.svc.cluster.local (10.97.251.106): 56 data bytes
exit
Cleanup:
$ kubectl delete -f myweb.yaml
Environment Variable
Sample how to set an environment variable via deployment file:
Add env section to deployment file:
[...]
spec:
containers:
- name: myweb-container
image: myweb-image:1.0
ports:
- containerPort: 80
env:
- name: MY_ENV_1
value: My Value No 1
- name: MY_ENV_2
value: My Value No 2
---
[...]
Start Pods, jump into Pod and check values:
kubectl apply -f myweb.yaml
kubectl get all -o wide
kubectl exec -it myweb-deployment-864984686b-5p7dn -- /bin/bash
## Inside Pod:
echo $MY_ENV_1
# Output: My Value No 1
echo $MY_ENV_2
# Output: My Value No 2
exit
# Cleanup:
kubectl delete -f myweb.yaml