Categories
AI Development Linux

OpenWebUI

# Architektur von OpenWebUI

OpenWebUI ist eine selbstgehostete Benutzeroberfläche zur Interaktion mit LLMs (Large Language Models). Es kombiniert **Frontend und API-Layer** in einer einzigen Webanwendung und bietet Unterstützung für verschiedene Sprachmodelle – lokal oder über APIs (z. B. OpenAI, Claude, Ollama, LM Studio).

## Komponentenübersicht

Ein typisches OpenWebUI-System besteht aus folgenden Hauptkomponenten:

- **Benutzeroberfläche (Client)**
- Darstellung der Chats
- Eingabefeld für Nutzereingaben
- Darstellung von Modellantworten
- Chat-Historie und Session-Management

- **API-Layer (integriert in OpenWebUI)**
- Verarbeitung und Weiterleitung von Anfragen
- Token-Management (z. B. API-Keys)
- Sicherheit (z. B. Authentifizierung)
- Kontextverwaltung (z. B. System-Prompts, Rollen, Modelleinstellungen)

- **Sprachmodell(e)**
- Externe APIs: OpenAI (GPT-4), Anthropic (Claude), etc.
- Lokale Modelle über Ollama, LM Studio, OpenRouter u.a.
- Mehrere Modelle gleichzeitig konfigurierbar

## Architekturdiagramm

```mermaid
flowchart LR
subgraph OpenWebUI ["OpenWebUI (Self-Hosted Webapp)"]
A[Benutzeroberfläche
• Eingabe & Anzeige
• Session-Verwaltung]
B[API-Layer
• Anfrageverarbeitung
• Sicherheit & Token
• Modellrouting]
end

C[Sprachmodell
• GPT-4 / OpenAI API
• LLaMA / Ollama
• Claude / API]

A -->|Anfrage| B
B -->|Verarbeitete Anfrage| C
C -->|Antwort| B
B -->|Formatierte Antwort| A
```

## Besonderheiten

- **Self-Contained**: OpenWebUI enthält sowohl die Benutzeroberfläche als auch den API-Layer – keine zusätzliche Middleware nötig.
- **Multimodellfähig**: Nutzer können zwischen mehreren Modellen und Providern wählen.
- **Privatsphäre**: Lokale Modellanbindung (z. B. Ollama) möglich – keine Datenübertragung an Dritte.
- **Plugins & Funktionen**:
- Dateien hochladen und durchsuchen
- Konversationsverwaltung
- Rollenbasierte Prompts
- Unterstützung für Bildmodelle (optional)

## Hosting-Optionen

- Docker (empfohlen)
- Manuelle Installation (Node.js + Backend)
- Unterstützung für Authentifizierung, mehrere Benutzer und persistente Einstellungen

## Fazit

OpenWebUI ist eine leistungsstarke, modulare Lösung für den selbstgehostenen Einsatz von LLMs. Durch die Integration von Frontend und API eignet es sich besonders für Nutzer, die ohne Cloud-Dienste arbeiten wollen oder mehrere Modelle flexibel einbinden möchten.

# OpenWebUI mit Docker und Traefik installieren

Diese Anleitung beschreibt die Installation von [OpenWebUI](https://github.com/open-webui/open-webui) auf einem Webserver mithilfe von Docker und Traefik als Reverse Proxy.

## Voraussetzungen

- Ein Linux-Server mit root-Zugriff
- Installiertes Docker und Docker Compose
- Domain (z. B. `webui.example.com`)
- Docker-Netzwerk für Traefik, z. B. `traefik-net`
- Bereits laufender Traefik-Container

## 1. Verzeichnisstruktur anlegen

```bash
mkdir -p /data/docker/openwebui
cd /data/docker/openwebui
```

## 2. Docker-Compose-Datei erstellen

Erstelle eine Datei namens `docker-compose.yml` mit folgendem Inhalt:

```yaml
services:
openwebui:
image: ghcr.io/open-webui/open-webui:main
container_name: openwebui
restart: unless-stopped
networks:
- traefik-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.openwebui.rule=Host(`webui.kaulbach.de`)"
- "traefik.http.routers.openwebui.entrypoints=websecure"
- "traefik.http.routers.openwebui.tls.certresolver=letsencrypt"
- openwebui_data:/app/backend/data

volumes:
openwebui_data:

networks:
traefik-net:
external: true
```

## 3. Container starten

```bash
docker compose up -d
```

## 4. Zugriff auf die Weboberfläche

Rufe OpenWebUI im Browser auf:

```
https://webui.kaulbach.de
```

## 5. Logs und Verwaltung

- Logs anzeigen: `docker logs -f openwebui`
- Stoppen: `docker compose down`

## 6. Updates

```bash
docker compose pull
docker compose up -d
```

## Weitere Infos
[https://github.com/open-webui/open-webui](https://github.com/open-webui/open-webui)

# Einrichten
## Admin
Beim ersten Login wird automatisch ein Admin-Benutzer angelegt.

## ChatGPT
Im Administrationsbereich über Einstellungen -> Verbindungen kann der OpenAI-API-Key eingetragen werden:
![OpenWebUISettings](/wp-content/uploads/2025/04/OpenWebUISettings-300x70.jpg){.alignnone}

## Groq
Im Administrationsbereich über Einstellungen -> Verbindungen über das Plus-Zeichen eine neue Verbindung anlegen und die Groq-URL und API-Key eintragen:
URL: ```https://api.groq.com/openai/v1```

## Modelle
Im Administrationsbereich über Einstellungen -> Modelle kann ausgewählt werden, welche Modelle den Benutzern zur Auswahl angezeigt werden sollen.

Categories
Database Development Linux

PostgreSQL: Backup

Ich musste grade von meiner Postgres-DB einen Dump ziehen und auf eine zweite DB wieder einspielen.
Problem dabei: ich habe Postgres (pg_dump, psql) nicht auf meinem Host System installiert und möchte daher Docker verwenden.
Zuerst habe ich alle relevanten Parameter in eine Datei (.env) gespeichert:
POSTGRES_USER=myuser
POSTGRES_PASSWORD=mypassword
POSTGRES_DB=mydatabase
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_BACKUPFILE=backup.sql
Dann habe ich mir ```source .env``` die Parameter als Umgebungsvariablen in meiner Shell gesetzt und konnte dann ein Backup meiner Datenbank erstellen:
docker run --rm \
  --network=host \
  -e PGPASSWORD=$POSTGRES_PASSWORD \
  postgres:17.2 \
  pg_dump -h $POSTGRES_HOST -U $POSTGRES_USER $POSTGRES_DB > $POSTGRES_BACKUPFILE
Anschließend habe ich die Parameter (zB Hostname) für das Zielsystem angepasst und wieder ```source .env``` ausgeführt um dann das Backup auf der Ziel-DB einspielen zu können:
docker run --rm \
  --network=host \
  -v ./$POSTGRES_BACKUPFILE:/script.sql \
  -e PGPASSWORD=$POSTGRES_PASSWORD \
  postgres:17.2 \
  psql -h $POSTGRES_HOS -p $POSTGRES_PORT -U $POSTGRES_USER -d $POSTGRES_DB -f script.sql
Categories
Linux

Docker Setup with Traefik, Apache, and Portainer

## Requirements

### Software
- Docker and Docker Compose
- Apache HTTPD 2.4
- Traefik v3.2
- Portainer CE

### Domain Configuration
Base domain: kabango.eu
Required subdomains:
- www.kabango.eu (main website)
- kabango.eu (redirects to www)
- traefik.kabango.eu (Traefik dashboard)
- portainer.kabango.eu (Portainer interface)

### Features
- Automatic HTTPS with Let's Encrypt
- HTTP to HTTPS redirect
- Secure management interfaces
- Path-based routing for special section
- Shared Docker network
- Container management via web interface

## Directory Structure
```
/data/docker/
├── traefik/
│ ├── docker-compose.yml
│ ├── traefik.yml
│ └── config/
│ └── users.txt
├── apache1/
│ ├── docker-compose.yml
│ └── html/
│ └── index.html
├── apache2/
│ ├── docker-compose.yml
│ └── html/
│ └── index.html
└── portainer/
├── docker-compose.yml
└── data/
```

## Configuration Files

### Traefik Static Configuration
```yaml
# /data/docker/traefik/traefik.yml
api:
dashboard: true

entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"

providers:
docker:
exposedByDefault: false
network: traefik-net

certificatesResolvers:
letsencrypt:
acme:
email: admin@kabango.eu
storage: /etc/traefik/acme/acme.json
httpChallenge:
entryPoint: web

log:
level: INFO
```

### Traefik Docker Compose
```yaml
# /data/docker/traefik/docker-compose.yml
services:
traefik:
image: traefik:v3.2
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- traefik-net
ports:
- 80:80
- 443:443
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./config:/etc/traefik/config
- acme:/etc/traefik/acme
labels:
- traefik.enable=true
- traefik.http.routers.dashboard.rule=Host(`traefik.kabango.eu`)
- traefik.http.routers.dashboard.service=api@internal
- traefik.http.routers.dashboard.middlewares=auth
- traefik.http.routers.dashboard.entrypoints=websecure
- traefik.http.routers.dashboard.tls.certresolver=letsencrypt
- traefik.http.middlewares.auth.basicauth.usersfile=/etc/traefik/config/users.txt

volumes:
acme:

networks:
traefik-net:
external: true
```

### Apache1 Docker Compose (Main Website)
```yaml
# /data/docker/apache1/docker-compose.yml
services:
apache1:
image: httpd:2.4
container_name: apache1
restart: unless-stopped
networks:
- traefik-net
volumes:
- ./html:/usr/local/apache2/htdocs
labels:
- traefik.enable=true
- traefik.http.routers.apache1.rule=Host(`kabango.eu`) || Host(`www.kabango.eu`)
- traefik.http.routers.apache1.entrypoints=websecure
- traefik.http.routers.apache1.tls.certresolver=letsencrypt
- traefik.http.services.apache1.loadbalancer.server.port=80
- traefik.http.middlewares.www-redirect.redirectregex.regex=^https://kabango.eu/(.*)
- traefik.http.middlewares.www-redirect.redirectregex.replacement=https://www.kabango.eu/$${1}
- traefik.http.routers.apache1.middlewares=www-redirect

networks:
traefik-net:
external: true
```

### Apache2 Docker Compose (Special Section)
```yaml
# /data/docker/apache2/docker-compose.yml
services:
apache2:
image: httpd:2.4
container_name: apache2
restart: unless-stopped
networks:
- traefik-net
volumes:
- ./html:/usr/local/apache2/htdocs
labels:
- traefik.enable=true
- traefik.http.routers.apache2.rule=Host(`kabango.eu`) && PathPrefix(`/special`) || Host(`www.kabango.eu`) && PathPrefix(`/special`)
- traefik.http.routers.apache2.entrypoints=websecure
- traefik.http.routers.apache2.tls.certresolver=letsencrypt
- traefik.http.services.apache2.loadbalancer.server.port=80
- traefik.http.middlewares.strip-special.stripprefix.prefixes=/special
- traefik.http.routers.apache2.middlewares=strip-special

networks:
traefik-net:
external: true
```

### Portainer Docker Compose
```yaml
# /data/docker/portainer/docker-compose.yml
services:
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- traefik-net
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data:/data
labels:
- traefik.enable=true
- traefik.http.routers.portainer.rule=Host(`portainer.kabango.eu`)
- traefik.http.routers.portainer.entrypoints=websecure
- traefik.http.routers.portainer.tls.certresolver=letsencrypt
- traefik.http.services.portainer.loadbalancer.server.port=9000

networks:
traefik-net:
external: true
```

### Sample HTML Files

Main Website (apache1):
```html




Welcome to Kabango.eu

Welcome to Kabango.eu

This is the main website.

Visit our special section.



```

Special Section (apache2):
```html




Special Section - Kabango.eu

Special Section

This is the special section of Kabango.eu

Back to main page



```

## Installation Steps

1. Create Docker network:
```bash
docker network create traefik-net
```

2. Create required directories:
```bash
mkdir -p /data/docker/{traefik/config,apache1/html,apache2/html,portainer,portainer/data}
```

3. Create Traefik basic auth credentials:
```bash
htpasswd -nb admin secure_password > /data/docker/traefik/config/users.txt
```

4. Create configuration files:
- Copy all configuration files to their respective locations as shown above
- Ensure correct file permissions

5. Configure DNS:
Point these domains to your server's IP:
- kabango.eu
- www.kabango.eu
- traefik.kabango.eu
- portainer.kabango.eu

6. Start services in order:
```bash
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:

- Main website: https://www.kabango.eu
- Special section: https://www.kabango.eu/special
- Traefik dashboard: https://traefik.kabango.eu (login: admin/secure_password)
- Portainer: https://portainer.kabango.eu (create admin account on first access)

## Security Notes

1. Docker Socket:
- The Docker socket (`/var/run/docker.sock`) is only mounted in containers that require it:
- Traefik: For container discovery
- Portainer: For Docker management
- Other containers don't need and shouldn't have access to the Docker socket

2. Authentication:
- Traefik dashboard is protected with basic authentication
- Portainer requires setting up an admin account on first access
- All management interfaces are only accessible via HTTPS

3. Network Security:
- Services communicate through an isolated Docker network
- Only necessary ports (80, 443) are exposed on the host
- Automatic redirection from HTTP to HTTPS

## Maintenance

### Updating Services
To update any service to the latest version:
```bash
cd /data/docker/
docker compose pull
docker compose up -d
```

### Viewing Logs
To view logs for any service:
```bash
cd /data/docker/
docker compose logs
```

Add `-f` flag to follow the logs:
```bash
docker compose logs -f
```

### Backup
Important directories to backup:
- `/data/docker/traefik/config` - Traefik configuration
- `/data/docker/apache1/html` - Main website content
- `/data/docker/apache2/html` - Special section content
- Portainer data volume - Container configurations

## Troubleshooting

1. Certificate Issues:
- Check Traefik logs for Let's Encrypt errors
- Verify DNS records are correct
- Ensure ports 80 and 443 are accessible

2. Routing Problems:
- Verify Traefik router rules in docker-compose labels
- Check if containers are in the correct network
- Inspect Traefik dashboard for routing status

3. Container Access:
- Use `docker compose ps` to check container status
- Verify network connectivity with `docker network inspect traefik-net`
- Check container logs for errors

Categories
Development Linux

Apache HTTP in den Container

# Aufgabe
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](https://ingo.kaulbach.de/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.

## Docker installieren
Hier nur kurz die Befehle, aus [Lokaler virtueller Server](https://ingo.kaulbach.de/lokaler-virtueller-server/) übernommen:
```bash
sudo apt update
sudo apt upgrade -y

# Docker-Repository hinzufügen
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Docker installieren:
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

sudo systemctl enable docker
sudo systemctl start docker

# Rechte für den aktuellen Benutzer konfigurieren
sudo usermod -aG docker $USER
newgrp docker
```

## Ordnerstrukur
Die Dateien der Docker Dienste sollen in ```/data/docker/``` liegen.
Ein symbolischer Link von ```/home/docker``` soll auf das Verzeichnis zeigen.
```bash
sudo mkdir -p /data/docker
sudo ln -s /data/docker /home/docker

sudo chown :docker /data/docker
sudo chmod g+w /data/docker
```

# Apache HTTP Container
## Ordnerstruktur
```bash
mkdir /data/docker/apache
mkdir /data/docker/apache/config \
/data/docker/apache/html \
/data/docker/apache/logs
```

## Daten kopieren
```bash
sudo cp -r /etc/apache2/* /data/docker/apache/config
sudo cp -r /var/www/html/* /data/docker/apache/html
sudo cp -r /var/log/apache2 /data/docker/apache

sudo chown -R :docker /data/docker/apache
sudo chmod -R g+w /data/docker/apache

mv /data/docker/apache/apache2/* /data/docker/apache/logs
rm -rf /data/docker/apache/apache2
```

## Docker Compose Datei
```docker-compose.yml``` für Apache HTTP im Verzeichnis ```/data/docker/apache```:

```yaml
services:
apache:
image: httpd:2.4
container_name: apache
restart: always
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./config:/usr/local/apache2/conf
- ./html:/usr/local/apache2/htdocs
- ./logs:/usr/local/apache2/logs
```

## erster Start
Auf dem Server Apache HTTP stoppen:
```bash
# Service finden
systemctl list-unit-files --type service
# Service stoppen
sudo systemctl stop apache2
```
Container-Apache starten:
```bash
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:
```bash
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.conf```an, die auf die apache2.conf verweist:
```bash
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:
```bash
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.

```bash
mkdir /data/docker/apache
mkdir /data/docker/apache/logs

cd /data/docker/apache
vim docker-compose.yml

docker compose up -d
docker logs apache
```

```yaml
services:
apache:
image: httpd:2.4
container_name: apache
restart: always
ports:
- 9080:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /var/www/html:/usr/local/apache2/htdocs
- ./logs:/usr/local/apache2/logs
```

# Fazit
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.

Categories
Linux

Lokaler virtueller Server

## Motivation
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](https://letsencrypt.org/) 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](https://www.virtualbox.org/) 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.

### virtuellen Server aufsetzen
1. Oracle VirtualBox installieren
2. Neue virtuelle Maschine erzeugen
* 8 GB RAM
* 4 CPUs
* ISO: [Ubuntu 24.04.1 LTS](https://ubuntu.com/download/server)
* Unbeaufsichtigte Installation:
* Benutzername und Passwort
* Hostname: kaulbach
* Domain Name: local
3. In den Netzwerkadapter-Einstellungen der virtuellen Maschine auf "Bridged Adapter" (Brückenadapter) umstellen
4. nach der Installation einloggen und mittels ```ip a``` die IP-Adresse der VM identifizieren (192.168.178.47)
5. 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
6. Test auf dem Host System:
* ping 192.168.178.47
* ping kaulbach.local

### virtuellen Server einrichten
```bash
sudo apt update
sudo apt upgrade -y

# Spracheinstellungen ändern
sudo locale-gen de_DE.UTF-8
sudo update-locale LANG=de_DE.UTF-8

# Tastaturbelegung ändern
sudo dpkg-reconfigure keyboard-configuration

# Zeitzone festlegen
sudo timedatectl set-timezone Europe/Berlin

# SSH Server installieren
sudo apt install -y openssh-server
sudo systemctl start ssh
sudo systemctl enable ssh
```

## Installation von Docker, Traefik, Portainer

### 1. Docker installieren
1. **Paketliste aktualisieren**:
```bash
sudo apt update
sudo apt upgrade -y
```

2. **Abhängigkeiten installieren**:
```bash
sudo apt install -y ca-certificates curl gnupg
```

3. **Docker-Repository hinzufügen**:
```bash
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

4. **Docker installieren**:
```bash
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

5. **Docker-Dienst aktivieren**:
```bash
sudo systemctl enable docker
sudo systemctl start docker
```

6. **Rechte für den aktuellen Benutzer konfigurieren**:
```bash
sudo usermod -aG docker $USER
newgrp docker
```

### 2. Docker Compose installieren
Docker Compose ist bei neueren Docker-Versionen bereits als Plugin enthalten. Es kann direkt über den Docker-CLI-Befehl `docker compose` genutzt werden.

**Installation testen**:
```bash
docker compose version
```

### 3.0 Traefik Netzwerk anlegen
Alle mit dem Traefik Dienst verbundenen Container sollen im selben Netzwerk liegen:
```bash
docker network create traefik-net
```

### 3. Traefik installieren
1. **Arbeitsverzeichnis erstellen**:
```bash
mkdir ~/traefik && cd ~/traefik
```

2. **`docker-compose.yml` für Traefik erstellen**:
```bash
vim docker-compose.yml
```
Inhalt:
```yaml
services:
traefik:
image: traefik:v3.2
container_name: traefik
restart: always
ports:
- "80:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/etc/traefik/traefik.yml:ro"
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.kaulbach.local`)"
- "traefik.http.routers.traefik.entrypoints=web"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"

networks:
default:
name: traefik-net
external: true
```

3. **Traefik-Konfigurationsdatei erstellen**:
```bash
vim traefik.yml
```
Inhalt:
```yaml
entryPoints:
web:
address: ":80"

providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false

api:
dashboard: true
insecure: true
```

4. **Container starten**:
```bash
docker compose up -d
# Log Files checken
docker logs traefik
```

5. **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

6. **Dashboard aufrufen**:
Das Traefik-Dashboard ist unter `http://traefik.kaulbach.local` erreichbar.

### 4. Webserver installieren
1. **Arbeitsverzeichnis erstellen**:
```bash
mkdir ~/web && cd ~/web
```

2. **Beispielseite erstellen**:
```bash
mkdir ~/web/html
echo "<h1>Hello, World! (c) DerIngo</h1>" > ~/web/html/index.html
```

3. **`docker-compose.yml` für den Webserver erstellen**:
```bash
vim docker-compose.yml
```
Inhalt:
```yaml
services:
nginx:
image: nginx
container_name: nginx
restart: always
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx.rule=Host(`kaulbach.local`)"
- "traefik.http.routers.nginx.entrypoints=web"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./html:/usr/share/nginx/html:ro"

networks:
default:
name: traefik-net
external: true
```

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
```

6. **Webseite aufrufen**:
Die Webseite ist unter `http://kaulbach.local` erreichbar.

### 5. Portainer installieren
1. **Arbeitsverzeichnis erstellen**:
```bash
mkdir ~/portainer && cd ~/portainer
```

2. **`docker-compose.yml` für Portainer erstellen**:
```bash
vim docker-compose.yml
```
Inhalt:
```yaml
services:
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: always
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./data:/data"
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`portainer.kaulbach.local`)"
- "traefik.http.routers.portainer.entrypoints=web"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"

networks:
default:
name: traefik-net
external: true
```

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
```

5. **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

6. **Dashboard aufrufen**:
Das Traefik-Dashboard ist unter `http://portainer.kaulbach.local` erreichbar.

7. **Portainer konfigurieren**
* Passwort für den User "admin" festlegen: "adminpassword".
* Environment "local" ist bereits angelegt

### 6. ein weiterer Webserver
1. **Webserver anlegen und starten**:
```bash
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
```

2. **`docker-compose.yml` für den Webserver erstellen**:
```yaml
services:
nginx:
image: nginx
container_name: nginx2
restart: always
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx2.rule=PathPrefix(`/derzweite`)"
- "traefik.http.routers.nginx2.entrypoints=web"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./html:/usr/share/nginx/html:ro"

networks:
default:
name: traefik-net
external: true
```

3. **`docker-compose.yml` des ersten Webservers anpassen**:
Der Router des ersten Webservers mit dem Host(`kaulbach.local`) wird auch auf /derzweite matchen.
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.
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.
Die Dateien habe ich in [GitHub](https://github.com/DerIngo/localvirtualserver) eingecheckt.

Categories
Database Development Linux

Performance-Analyse

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

  1. Backup der Produktivdatenbank erstellen
  2. Lokale Testdatenbank mit Docker aufsetzen
  3. pg_stat_statements installieren und konfigurieren
  4. 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:

targetfile=backup_`date +%Y-%m-%d`.sql

docker run --rm \
  --network=host \
  -e PGPASSWORD=PASSWORD \
  postgres:13.4-bullseye \
  pg_dump -h localhost -U myuser myschema > $targetfile

gzip $targetfile

Die Backup-Datei wird auf den lokalen Rechner übertragen.

Lokale Testdatenbank aufsetzen

Zuerst entfernen wir eine eventuell vorhandene alte Testinstanz und starten dann einen neuen Container:

docker stop mydb-performancetest && docker rm mydb-performancetest

docker run -d \
  --name mydb-performancetest \
  -e POSTGRES_USER=myuser \
  -e POSTGRES_PASSWORD=PASSWORD \
  -e POSTGRES_DB=myuser \
  -p 6432:5432 \
  postgres:13.4-bullseye

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 <filename>"
		    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:

# Export & bearbeiten:
docker cp mydb-performancetest:/var/lib/postgresql/data/postgresql.conf .
## shared_preload_libraries = 'pg_stat_statements' in einem Editor hinzufügen

# Re-Import & Neustart:
docker cp postgresql.conf mydb-performancetest:/var/lib/postgresql/data/postgresql.conf
docker restart mydb-performancetest

Danach aktivieren wir die Erweiterung in der Datenbank:

CREATE EXTENSION pg_stat_statements;

Performance-Analyse

Nach dem Ausführen der kritischen Anwendungsfälle können wir die problematischen Queries identifizieren:

SELECT
    query,
    calls,
    total_exec_time,
    min_exec_time,
    max_exec_time,
    mean_exec_time,
    rows,
    (total_exec_time / calls) AS avg_time_per_call
FROM
    pg_stat_statements
ORDER BY
    total_exec_time DESC
LIMIT 10;

Beispiel-Ergebnisse

Hier ein anonymisiertes Beispiel der Ausgabe:

query                              | calls | total_exec_time | min_exec_time | max_exec_time | mean_exec_time | rows  | avg_time_per_call
----------------------------------+-------+----------------+---------------+---------------+----------------+-------+-------------------
SELECT * FROM large_table WHERE... | 1500  | 350000.23      | 150.32       | 890.45        | 233.33         | 15000 | 233.33
UPDATE complex_table SET...       | 500   | 180000.45      | 250.12       | 750.89        | 360.00         | 500   | 360.00

Die Ergebnisse zeigen:

  • Anzahl der Aufrufe (calls)
  • Gesamtausführungszeit in ms (total_exec_time)
  • Minimale und maximale Ausführungszeit (min/max_exec_time)
  • Durchschnittliche Ausführungszeit (mean_exec_time)
  • Anzahl der betroffenen Zeilen (rows)

Besonders interessant sind Queries mit:

  • Hoher Gesamtausführungszeit
  • Hoher durchschnittlicher Ausführungszeit
  • Großer Differenz zwischen minimaler und maximaler Ausführungszeit
  • Unerwartet hoher Anzahl von Aufrufen

Reset

SELECT pg_stat_statements_reset();

Fazit

Mit dieser Methode können wir Performance-Probleme systematisch analysieren, ohne die Produktivumgebung zu beeinflussen.

Die Ergebnisse aus pg_stat_statements geben uns wichtige Hinweise, welche Queries optimiert werden sollten.

Categories
Development Java Linux

GitHub Codespace

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:

curl google.com

HTTPie

A modern alternative to curl is HTTPie:

Install httpie:

sudo apt update && \
sudo apt upgrade -y && \
sudo apt install httpie -y

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 <STRG>+<C> 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 <STRG>+<SHIFT>+<I>), 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.

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
     
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>my-project</artifactId>
      <version>1.0</version>
    </project>
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.

mkdir -p src/main/java/org/acme
touch src/main/java/org/acme/HelloWorld.java 

Open HelloWorld.java with <STRG>+<MOUSECLICK> in GUI-Editor.

Install Extension Pack for Java as suggested:

And also the next two suggestions:

package org.acme;

class HelloWorld { 
  public static void main(String args[]) { 
      System.out.println("Hello World"); 
  } 
}
mvn package
java -classpath ./target/workshop-1.0.jar org.acme.HelloWorld

Maven - different version

In our Codespace we have Maven 3.9.6 and Java 21.
Let’s test with a different version. We will use Docker.

Official Maven Image on DockerHub.

We want to re-use the local Maven Cache. Let’s find out where it is:

sudo apt install locate -y
sudo updatedb
locate .m2
# /home/codespace/.m2

Adjust the “How to use this image” command:

docker run -it --rm \
--name workshop-maven-project \
-v /home/codespace/.m2:/root/.m2 \
-v "$(pwd)":/usr/src/workshop \
-w /usr/src/workshop \
maven:3.3-jdk-8 \
mvn clean package
java -classpath ./target/workshop-1.0.jar org.acme.HelloWorld

Sourcecode management

We have 7 uncommited changes, but only 2 files should go into the repository:

What we need is a .gitignore file.

touch .gitignore

There are two template files we will copy:

Now there are only 3 files we can commit:

Now we can see these files in our repository:

Secrets

Use GitHub Secrets for API-keys etc.

In the upper-right corner of any page, click your profile photo, then click Settings. Under Codespaces we can set our secrets:

In our Codespace we can access the secret as environment variable:

A running codespace has to be restarted!

Cleanup

Delete all files:

rm -rf target && \
rm -rf src && \
rm pom.xml && \
rm README.md && \
rm .gitignore

Stage & commit changes:

Now we have a clean repository:

Close browser window with codespace and delete the codespace:

Delete the repository:

Go to Settings → General → Danger Zone → Delete this repository

Categories
Linux

SSH Key-Based Authentication on Linux Servers

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.

Copy keys to server 2

We will use the same keys on server 2:

scp ~/.ssh/id_rsa     USERNAME@server2ip:~/.ssh
scp ~/.ssh/id_rsa.pub USERNAME@server2ip:~/.ssh

Create authorized_keys on both servers:

ssh-copy-id USERNAME@localhost
ssh-copy-id USERNAME@server2ip

Connect to server 2

ssh USERNAME@server2ip
# or just
ssh server2ip

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.

Categories
Development Linux

Docker Logs lesen

Problem

Der Server wird gestartet mit:

sudo docker-compose up --detach

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.

version: "3.2"
services:
  melba-web:
    image: tomcat:10.1.18-jre21 # https://hub.docker.com/_/tomcat
    restart: always
    container_name: myapp-tomcat
    user: :1001
    ports: 
      - "8080:8080"
    environment:
      - _JAVA_OPTIONS=-Duser.language=de -Duser.country=DE
    volumes:
      - ./log/tomcat:/usr/local/tomcat/logs
sudo docker-compose up --detach

ls -lisah log/tomcat/*
4211764 4.0K drwxr-xr-x 2 ingo ingo  4.0K Feb  8 16:52 .
4211762 4.0K drwxr-xr-x 4 ingo ingo  4.0K Feb  5 16:42 ..
4205212  24K -rw-r----- 1 root logfilegroup  24K Feb  8 16:21 catalina.2024-02-08.log
4211758  28K -rw-r----- 1 root logfilegroup  24K Feb  8 16:58 localhost_access_log.2024-02-08.txt
Categories
Linux

Upgrade Ubuntu 20.04 to 22.04 on WSL

Check current version:

lsb_release -a

Make 20.04 installation is up-to-date:

sudo apt update && sudo apt full-upgrade

Close Ubuntu on WSL terminal, and open PowerShell terminal:

wsl -l -v 
## Confirm the distribution name and adjust below if needed
wsl --terminate Ubuntu

Open new Ubuntu on WSL terminal:

sudo do-release-upgrade
##
sudo apt update && sudo apt upgrade -y
sudo apt autoremove

Check current version:

lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.3 LTS
Release:        22.04
Codename:       jammy