--- # 🐳 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@:/# 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 ` – Container stoppen - `docker rm ` – Container löschen - `docker rmi ` – Image löschen - `docker exec -it 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 # 🔑 In laufenden Container "einsteigen" docker exec -it bash # 🗑️ Container stoppen docker stop # 🗑️ Container löschen docker rm ``` ### **Image-Befehle** ```bash # 📋 Alle Images anzeigen docker images # 🏗️ Image aus Dockerfile bauen docker build -t mein-python-app . # 🗑️ Image löschen docker rmi # 🧹 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! 🐳**