This commit is contained in:
Mathias Schneider
2026-04-01 13:35:27 +02:00
parent 82d7e0e888
commit 07b79e33fa
8 changed files with 920 additions and 0 deletions
+780
View File
@@ -0,0 +1,780 @@
---
# 🐳 Einführung in Docker
## 1. Was ist Docker?
Docker ist eine Plattform, mit der du **Container** erstellen, starten und verwalten kannst.
Ein Container ist eine Art "leichtgewichtige virtuelle Maschine":
- Enthält **nur das Nötigste** (Programm, Abhängigkeiten, Bibliotheken).
- Läuft isoliert, aber nutzt den Kernel des Host-Systems (kein eigener Kernel wie bei einer VM).
- Ist **schneller** und **ressourcenschonender** als klassische VMs.
👉 Bildlich: Statt für jedes Programm einen eigenen PC (VM) aufzubauen, packst du nur das Programm in eine "Transportbox" (Container), die überall läuft, solange Docker installiert ist.
---
## 2. Vorteile und typische Anwendungsfälle
### Vorteile
- **Portabilität**: „Läuft auf meinem Rechner“ = „läuft auch auf Servern“.
- **Isolierung**: Verschiedene Programme/Versionen stören sich nicht.
- **Leichtgewicht**: Start in Sekunden statt Minuten wie bei VMs.
- **Reproduzierbarkeit**: Gleiche Umgebung für Entwicklung, Test und Produktion.
- **Einfache Verteilung**: Images können über Docker Hub oder private Registries geteilt werden.
### Typische Anwendungsfälle
- 🚀 **Entwicklung**: Z. B. [[Python]] 3.10 testen, obwohl auf dem System Python 3.12 läuft.
- 🗄️ **Datenbanken**: [[MySQL]], PostgreSQL, Redis, MongoDB zum Testen ohne Installation.
- 🌐 **Web-Apps**: Schnell mal eine fertige Anwendung ausprobieren (WordPress, Nextcloud, GitLab etc.).
- 🧪 **Testing**: Mehrere Umgebungen simulieren (verschiedene OS, Bibliotheksversionen).
- 📦 **Deployment**: Gleiche App auf mehreren Servern identisch bereitstellen.
---
## 3. Hauptfunktionalitäten von Docker
- **Docker Engine**: Herzstück, das Container ausführt.
- **Docker Images**: Baupläne (Templates) für Container (z. B. Python 3.12 + Flask).
- **Docker Containers**: Laufende Instanzen von Images.
- **Docker Hub/Registry**: Cloud-Marktplatz für fertige Images.
- **Docker Compose**: Orchestrierung mehrerer Container (z. B. App + DB + Cache).
---
## 4. Installation unter Linux (Beispiel: Ubuntu/Debian)
```bash
# 1. Alte Versionen entfernen
sudo apt-get remove docker docker-engine docker.io containerd runc
# 2. Repository vorbereiten
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
# Docker GPG-Key hinzufügen
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Repository einrichten
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
# 3. Docker installieren
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 4. Testen
sudo docker run hello-world
```
👉 Danach kannst du optional dich in die `docker`-Gruppe aufnehmen (sonst musst du immer `sudo` schreiben):
```bash
sudo usermod -aG docker $USER
# Abmelden/neu anmelden, damit es wirkt
```
---
## 5. Erste Schritte in der Praxis
### Beispiel 1: Linux — Container starten
```bash
# Einen Ubuntu-Container starten (interaktiv)
docker run -it ubuntu bash
# Danach bist du im Container:
root@<id>:/# cat /etc/os-release
```
Wenn du rausgehst (`exit`), bleibt der Container gespeichert.
---
### Beispiel 2: Linux — Nginx-Webserver starten
```bash
# Nginx starten, Port 8080 weiterleiten
docker run -d -p 8080:80 nginx
# Nun im Browser: http://localhost:8080
```
---
### Beispiel 3: Python im Container
Nehmen wir an, du willst ein Python-Skript in einer isolierten Umgebung ausführen.
**hello.py**
```python
print("Hello from Dockerized Python!")
```
**Dockerfile**
```dockerfile
FROM python:3.11-slim
# Skript ins Image kopieren
COPY hello.py /app/hello.py
# Arbeitsverzeichnis setzen
WORKDIR /app
# Default-Befehl
CMD ["python", "hello.py"]
```
**Bauen und starten**
```bash
# Image bauen
docker build -t mypythonapp .
# Container starten
docker run --rm mypythonapp
```
Ausgabe:
```
Hello from Dockerized Python!
```
---
### Beispiel 4: Python mit externer Bibliothek
```python
# requirements.txt
flask==3.0.0
```
**app.py**
```python
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, Docker!"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
```
**Dockerfile**
```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]
```
**Starten**
```bash
docker build -t flaskapp .
docker run -d -p 5000:5000 flaskapp
```
👉 Browser öffnen: `http://localhost:5000`
---
## 6. Nützliche Docker-Befehle (Cheatsheet)
- `docker ps` laufende Container
- `docker ps -a` alle Container (inkl. gestoppter)
- `docker images` vorhandene Images
- `docker stop <id>` Container stoppen
- `docker rm <id>` Container löschen
- `docker rmi <image>` Image löschen
- `docker exec -it <id> bash` in Container einsteigen
---
✅ Damit hast du einen vollständigen Überblick über Docker:
- **Grundidee**
- **Vorteile/Anwendungsfälle**
- **Installation**
- **Praxis mit Linux und Python**
---
# Anwendungsbeispiel
# 🐳 Docker Anwendungsbeispiel - Detaillierte Erklärung für Einsteiger
## 📚 Was ist Docker überhaupt?
**Docker** ist wie ein **"Umzugskarton für Software"**:
- Stell Dir vor, Du willst Deine App an einen Freund verschicken
- Statt nur den Code zu senden, packst Du **alles** ein: Code, Python, alle Bibliotheken, Konfiguration
- Dein Freund öffnet den "Karton" (Container) und alles läuft sofort - egal welches Betriebssystem er hat
---
## 📁 Projektstruktur mit Erklärungen
```
python-docker-project/
├── app/ # 📂 Unser Python-Code
│ ├── __init__.py # (macht 'app' zu einem Python-Paket)
│ ├── main.py # Hauptprogramm
│ ├── data_processor.py # Modul für Datenverarbeitung
│ └── database.py # Modul für Datenbankzugriff
├── input/ # 📁 Hier liegen die Input-Dateien (vom Host)
│ └── sample_data.csv # Beispieldatei
├── output/ # 📁 Hier kommen verarbeitete Dateien rein
├── requirements.txt # 📝 Liste aller Python-Pakete die wir brauchen
├── Dockerfile # 🐳 "Bauanleitung" für unseren Container
├── docker-compose.yml # 🎼 "Dirigent" für mehrere Container
└── .env # 🔐 Geheime Einstellungen (Passwörter etc.)
```
---
## 🐍 Python-Code (wie gewohnt)
### **requirements.txt** - "Einkaufsliste" für Python-Pakete
```txt
# 🛒 Diese Pakete braucht unser Programm
pandas==2.1.4 # Für Excel/CSV-Verarbeitung
psycopg2-binary==2.9.9 # Um mit PostgreSQL zu sprechen
python-dotenv==1.0.0 # Um .env-Dateien zu lesen
sqlalchemy==2.0.23 # Einfache Datenbankanbindung
```
**💡 Warum brauche ich das?**
- Ohne Docker müsstest Du auf jedem PC manuell `pip install pandas` etc. machen
- Mit Docker installiert sich das automatisch im Container
### **app/main.py** - Unser Hauptprogramm
```python
#!/usr/bin/env python3
"""
🎯 Das macht unser Programm:
1. Schaut in /app/input nach CSV-Dateien
2. Verarbeitet sie (quadriert Werte, etc.)
3. Speichert Ergebnis in PostgreSQL-Datenbank
4. Exportiert auch als CSV in /app/output
"""
import os
import time
from pathlib import Path
from dotenv import load_dotenv
from .data_processor import DataProcessor
from .database import DatabaseManager
def main():
print("🚀 Starte Data Processing Pipeline...")
# 📁 Diese Pfade sind INSIDE dem Container!
input_dir = Path("/app/input") # Gemountet von Host ./input/
output_dir = Path("/app/output") # Gemountet von Host ./output/
output_dir.mkdir(exist_ok=True)
# 🗄️ Verbindung zur Datenbank (anderer Container!)
db_manager = DatabaseManager()
processor = DataProcessor(db_manager)
# 📊 Alle CSV-Dateien finden und verarbeiten
csv_files = list(input_dir.glob("*.csv"))
if not csv_files:
print("❌ Keine CSV-Dateien im Input-Verzeichnis gefunden!")
return
for csv_file in csv_files:
print(f"📄 Verarbeite: {csv_file.name}")
# 1️⃣ Daten aus CSV laden und verarbeiten
result_data = processor.process_file(csv_file)
# 2️⃣ In PostgreSQL-Container speichern
processor.save_to_database(result_data, csv_file.stem)
# 3️⃣ Als neue CSV-Datei exportieren
output_file = output_dir / f"processed_{csv_file.name}"
result_data.to_csv(output_file, index=False)
print(f"💾 Gespeichert als: {output_file}")
print("✅ Pipeline abgeschlossen!")
if __name__ == "__main__":
main()
```
### **app/database.py** - Spricht mit der Datenbank
```python
import os
import pandas as pd
from sqlalchemy import create_engine, text
class DatabaseManager:
def __init__(self):
# 🔌 Diese Werte kommen aus der docker-compose.yml!
self.host = os.getenv('DB_HOST', 'postgres') # Name des DB-Containers!
self.port = os.getenv('DB_PORT', '5432')
self.database = os.getenv('DB_NAME', 'dataprocessing')
self.user = os.getenv('DB_USER', 'postgres')
self.password = os.getenv('DB_PASSWORD', 'password')
# 🌐 Connection String - wie eine Adresse zur Datenbank
self.connection_string = (
f"postgresql://{self.user}:{self.password}@"
f"{self.host}:{self.port}/{self.database}"
)
self.engine = None
self._connect()
def _connect(self):
"""Verbindung zur Datenbank herstellen"""
try:
self.engine = create_engine(self.connection_string)
# 🏓 Kurzer Test: "Hallo Datenbank!"
with self.engine.connect() as conn:
conn.execute(text("SELECT 1"))
print("✅ Datenbankverbindung erfolgreich")
except Exception as e:
print(f"❌ Datenbankverbindung fehlgeschlagen: {e}")
raise
def save_dataframe(self, df: pd.DataFrame, table_name: str):
"""Speichert DataFrame als Tabelle in PostgreSQL"""
df.to_sql(
name=table_name,
con=self.engine,
if_exists='replace', # Überschreibt existierende Tabelle
index=False
)
```
*(Die anderen Python-Dateien bleiben gleich)*
---
## 🐳 Dockerfile - "Bauanleitung" für unseren Container
```dockerfile
# 🏗️ SCHRITT 1: Basis-Image wählen
# Das ist wie ein "Rohbau" - Linux + Python sind schon installiert
FROM python:3.11-slim
# 🛠️ SCHRITT 2: System-Tools installieren
# Wir brauchen gcc und libpq-dev für PostgreSQL-Verbindung
RUN apt-get update && apt-get install -y \
gcc \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# 💡 WARUM: psycopg2 (PostgreSQL-Driver) braucht diese Tools zum Kompilieren
# 📂 SCHRITT 3: Arbeitsverzeichnis setzen
WORKDIR /app
# Das ist wie "cd /app" - alle folgenden Befehle laufen hier
# 📦 SCHRITT 4: Python-Pakete installieren
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 💡 WARUM separat? Docker cached diesen Layer - muss nur neu gebaut werden wenn requirements.txt ändert
# 📋 SCHRITT 5: Unseren Code ins Container kopieren
COPY app/ ./app/
# Kopiert alles aus Host-Ordner "app/" nach Container "/app/app/"
# 📁 SCHRITT 6: Verzeichnisse erstellen
RUN mkdir -p /app/input /app/output
# Diese werden später durch Volume-Mounts "überschrieben"
# ⚙️ SCHRITT 7: Umgebungsvariablen setzen
ENV PYTHONPATH=/app # Python findet unsere Module
ENV PYTHONUNBUFFERED=1 # Print-Ausgaben erscheinen sofort
# 🚀 SCHRITT 8: Was soll beim Container-Start passieren?
CMD ["python", "-m", "app.main"]
# Startet unser Hauptprogramm
```
**🤔 Was passiert beim `docker build`?**
1. Docker lädt `python:3.11-slim` herunter (falls nicht da)
2. Installiert gcc und PostgreSQL-Tools
3. Kopiert `requirements.txt` und installiert Python-Pakete
4. Kopiert unseren Code rein
5. Setzt Umgebungsvariablen
6. **Ergebnis:** Ein fertiger Container mit allem was wir brauchen!
---
## 🎼 docker-compose.yml - "Dirigent" für mehrere Container
```yaml
# 🎯 docker-compose orchestriert MEHRERE Container gleichzeitig
version: '3.8'
services:
# 🗄️ CONTAINER 1: PostgreSQL-Datenbank
postgres:
image: postgres:15 # Fertiges PostgreSQL-Image von Docker Hub
environment: # Umgebungsvariablen für PostgreSQL
POSTGRES_DB: dataprocessing # Name der Datenbank die erstellt wird
POSTGRES_USER: postgres # Benutzername
POSTGRES_PASSWORD: password # 🔐 Passwort (in Produktion geheimer!)
volumes:
- postgres_data:/var/lib/postgresql/data # 💾 Daten persistent speichern
- ./db_init:/docker-entrypoint-initdb.d # 📂 Optional: SQL-Skripts beim Start
ports:
- "5432:5432" # Port freigeben (Host:Container)
networks:
- app_network # 🌐 Internes Netzwerk für Container-Kommunikation
# 🐍 CONTAINER 2: Unsere Python-Anwendung
python_app:
build: . # 🏗️ Baue Container aus Dockerfile in diesem Verzeichnis
environment: # Diese Variablen kann unser Python-Code lesen
DB_HOST: postgres # ❗ WICHTIG: Name des DB-Containers (nicht localhost!)
DB_PORT: 5432
DB_NAME: dataprocessing
DB_USER: postgres
DB_PASSWORD: password
volumes: # 📂 Verbinde Host-Verzeichnisse mit Container
- ./input:/app/input:ro # Host ./input → Container /app/input (read-only)
- ./output:/app/output # Host ./output → Container /app/output (read-write)
depends_on: # ⏱️ Warte bis postgres-Container läuft
- postgres
networks:
- app_network # 🌐 Gleiches Netzwerk = können miteinander sprechen
restart: unless-stopped # 🔄 Automatisch neu starten bei Absturz
# 🌐 CONTAINER 3: pgAdmin (Web-Interface für PostgreSQL)
pgadmin:
image: dpage/pgadmin4:8
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: admin
ports:
- "8080:80" # 🌐 Zugriff via http://localhost:8080
depends_on:
- postgres
networks:
- app_network
# 💾 VOLUMES: Persistente Datenspeicherung
volumes:
postgres_data: # Docker verwaltet diesen Speicher automatisch
# 💡 Auch wenn Container gelöscht wird, bleiben Daten erhalten
# 🌐 NETWORKS: Virtuelle Netzwerke für Container-Kommunikation
networks:
app_network:
driver: bridge # Standard-Netzwerk-Typ
# 💡 Alle Container in diesem Netzwerk können sich über Namen erreichen
```
**🤔 Warum docker-compose?**
- **Ohne compose:** `docker run postgres`, dann `docker run python_app` - sehr umständlich!
- **Mit compose:** Ein Befehl (`docker-compose up`) startet ALLES automatisch
- **Networks:** Container können sich über Namen finden (`postgres` statt IP-Adresse)
- **Volumes:** Daten gehen nicht verloren beim Container-Neustart
---
## 🔧 .env - Geheime Konfiguration
```env
# 🔐 Sensible Daten sollten NICHT in docker-compose.yml stehen
# Diese Datei wird von docker-compose automatisch gelesen
# Datenbankverbindung
DB_HOST=postgres
DB_PORT=5432
DB_NAME=dataprocessing
DB_USER=postgres
DB_PASSWORD=password # ❗ In Produktion: sicheres Passwort!
# Python-Einstellungen
PYTHONPATH=/app
PYTHONUNBUFFERED=1
```
**💡 Wichtig:** Die `.env`-Datei niemals in Git committen! (→ `.gitignore`)
---
## 📊 Beispiel-Daten
### **input/sample_data.csv**
```csv
id,name,value,category
1,Item A,10,electronics
2,Item B,25,clothing
3,Item C,15,electronics
4,Item D,30,books
5,Item E,8,clothing
```
**💡 Das ist eine normale CSV-Datei auf Deinem Host-PC**
- Liegt in `./input/sample_data.csv`
- Wird in den Container als `/app/input/sample_data.csv` "gemountet"
- Unser Python-Code kann sie lesen, als wäre sie im Container
---
## 🚀 Schritt-für-Schritt Anleitung
### **1. Projekt erstellen**
```bash
# Verzeichnis erstellen
mkdir python-docker-project
cd python-docker-project
# Struktur erstellen
mkdir -p app input output
touch requirements.txt Dockerfile docker-compose.yml .env
touch app/__init__.py app/main.py app/data_processor.py app/database.py
```
### **2. Code schreiben**
- Alle Dateien wie oben gezeigt erstellen und befüllen
### **3. Container bauen und starten**
```bash
# Alle Container bauen und starten
docker-compose up --build
# 🔍 Was passiert:
# 1. Docker baut unseren Python-Container (aus Dockerfile)
# 2. Lädt PostgreSQL-Image herunter
# 3. Lädt pgAdmin-Image herunter
# 4. Erstellt Netzwerk "app_network"
# 5. Startet postgres-Container
# 6. Startet python_app-Container (wartet auf postgres)
# 7. Startet pgAdmin-Container
```
### **4. Überprüfen ob alles läuft**
```bash
# Laufende Container anzeigen
docker-compose ps
# Logs ansehen
docker-compose logs python_app
docker-compose logs postgres
# In pgAdmin einloggen: http://localhost:8080
# Email: admin@example.com, Passwort: admin
```
### **5. Neue Daten verarbeiten**
```bash
# Neue CSV-Datei ins input-Verzeichnis kopieren
cp neue_daten.csv input/
# Python-App neu starten um neue Dateien zu verarbeiten
docker-compose restart python_app
# Ergebnis ansehen
ls -la output/
```
### **6. System stoppen**
```bash
# Alle Container stoppen
docker-compose down
# Container stoppen UND Daten löschen (Vorsicht!)
docker-compose down -v
```
---
## 🔍 Docker-Befehle erklärt
### **Grundlegende Container-Befehle**
```bash
# 📋 Alle laufenden Container anzeigen
docker ps
# 📋 Alle Container anzeigen (auch gestoppte)
docker ps -a
# 🔍 Container-Logs ansehen
docker logs <container-name>
# 🔑 In laufenden Container "einsteigen"
docker exec -it <container-name> bash
# 🗑️ Container stoppen
docker stop <container-name>
# 🗑️ Container löschen
docker rm <container-name>
```
### **Image-Befehle**
```bash
# 📋 Alle Images anzeigen
docker images
# 🏗️ Image aus Dockerfile bauen
docker build -t mein-python-app .
# 🗑️ Image löschen
docker rmi <image-name>
# 🧹 Unbenutzte Images löschen
docker image prune
```
### **docker-compose-Befehle**
```bash
# 🚀 Alle Services starten
docker-compose up
# 🚀 Im Hintergrund starten
docker-compose up -d
# 🏗️ Images neu bauen und starten
docker-compose up --build
# 🔄 Einzelnen Service neu starten
docker-compose restart python_app
# 📋 Status aller Services
docker-compose ps
# 🔍 Logs aller Services
docker-compose logs
# 🔍 Logs eines bestimmten Service
docker-compose logs python_app
# 📈 Live-Logs verfolgen
docker-compose logs -f python_app
# 🔑 In Container einsteigen
docker-compose exec python_app bash
docker-compose exec postgres psql -U postgres -d dataprocessing
# ⏹️ Alle Services stoppen
docker-compose down
# 🗑️ Services stoppen und Volumes löschen
docker-compose down -v
```
---
## 🌟 Die wichtigsten Docker-Konzepte
### **1. Images vs Container**
- **Image** = "Bauplan" (wie ein Kuchenrezept)
- **Container** = "Laufende Instanz" (wie ein gebackener Kuchen)
- Ein Image kann viele Container erstellen
### **2. Volumes**
```yaml
volumes:
- ./input:/app/input # Host-Verzeichnis → Container-Verzeichnis
```
- **Warum?** Container-Dateisystem ist temporär
- **Volume** = persistenter Speicher der überlebt
### **3. Networks**
- Container in gleichem Netzwerk können sich über Namen erreichen
- `python_app` kann `postgres` kontaktieren (nicht IP-Adresse!)
### **4. Environment Variables**
```yaml
environment:
DB_HOST: postgres # Diese Variable kann Python-Code lesen
```
### **5. Ports**
```yaml
ports:
- "8080:80" # Host-Port : Container-Port
```
- Container-Port 80 wird auf Host-Port 8080 freigegeben
- Zugriff: `http://localhost:8080`
---
## ❓ Häufige Anfänger-Probleme
### **Problem 1: "Connection refused" zur Datenbank**
```
❌ FALSCH: DB_HOST=localhost
✅ RICHTIG: DB_HOST=postgres
```
- In Docker ist `localhost` der Container selbst
- Verwende den Service-Namen aus `docker-compose.yml`
### **Problem 2: "File not found"**
```
❌ FALSCH: input_dir = Path("./input")
✅ RICHTIG: input_dir = Path("/app/input")
```
- Pfade sind INSIDE dem Container, nicht auf dem Host
### **Problem 3: "Module not found"**
```
❌ FALSCH: Umgebungsvariable vergessen
✅ RICHTIG: ENV PYTHONPATH=/app in Dockerfile
```
### **Problem 4: Änderungen nicht sichtbar**
```bash
# Nach Code-Änderungen Container neu bauen:
docker-compose up --build
```
### **Problem 5: Port bereits belegt**
```
❌ Error: Port 5432 already in use
✅ Lösung: Anderen Port verwenden: "5433:5432"
```
---
## 🎯 Zusammenfassung - Was macht was?
| Datei/Komponente | Was es macht | Warum wichtig |
|------------------|--------------|---------------|
| **Dockerfile** | "Bauanleitung" für Python-Container | Definiert was IN den Container kommt |
| **docker-compose.yml** | Orchestriert mehrere Container | Ein Befehl startet alles |
| **requirements.txt** | Liste der Python-Pakete | Automatische Installation |
| **.env** | Geheime Konfiguration | Passwörter nicht in Code |
| **volumes** | Datenaustausch Host ↔ Container | Input/Output-Dateien |
| **networks** | Container-Kommunikation | python_app kann postgres erreichen |
| **ports** | Zugriff von außen | Web-Interface erreichbar |
**🚀 Mit einem Befehl hast Du:**
- ✅ Python-Umgebung mit allen Paketen
- ✅ PostgreSQL-Datenbank
- ✅ Web-Interface für DB
- ✅ Automatische File-Verarbeitung
- ✅ Persistente Datenspeicherung
**Das ist die Macht von Docker! 🐳**