Home/Modul 4

🐳 Modul 4: Container und Docker

Lernziele: Sie verstehen die Unterschiede zwischen Containern und VMs, kennen die Docker-Architektur, können Dockerfiles schreiben, Docker Compose nutzen und Container für Netzwerk-Automatisierung einsetzen.

⏱️ Dauer: ca. 90 Minuten | 🔧 Praxis: 6 Übungen

Teil 1: Container vs. Virtuelle Maschinen

Die Umzugskarton-Analogie

Stellen Sie sich vor, Sie ziehen um. Es gibt zwei Strategien:

🏠

Strategie A: Ganzes Haus mitnehmen (VM)

Sie packen nicht nur Ihre Möbel ein – Sie nehmen das gesamte Haus mit. Fundament, Wände, Dach, Heizung, alles. Funktioniert überall, aber extrem schwer und langsam zu transportieren.

📦

Strategie B: Umzugskartons (Container)

Sie packen nur das ein, was Sie wirklich brauchen – Möbel, Bücher, Kleidung. Das neue Haus (Betriebssystem) ist schon da. Schnell, leicht, effizient.

Technische Architektur im Vergleich

Der fundamentale Unterschied liegt in der Virtualisierungsebene:

Virtuelle Maschine (VM):
App 1
App 2
App 3
Guest OS
Guest OS
Guest OS
Hypervisor (VMware, KVM, Hyper-V)
Host-Betriebssystem
Hardware
Container:
App 1
App 2
App 3
Container Runtime (Docker Engine)
Host-Betriebssystem (Linux Kernel)
Hardware

Vergleichstabelle: VM vs. Container

EigenschaftVirtuelle MaschineContainer
StartzeitMinutenSekunden
GrößeGigabytes (10-50 GB)Megabytes (50-500 MB)
RessourcenFest reserviertDynamisch geteilt
IsolationKomplett (eigenes OS)Prozess-Level
BetriebssystemBeliebig (Linux, Windows)Teilt Host-Kernel
Instanzen pro Host10-20100-1000+
💡Wann was nutzen?
  • VMs: Wenn Sie ein anderes OS brauchen (Windows auf Linux), komplette Isolation kritisch ist, oder Legacy-Software läuft
  • Container: Für Microservices, CI/CD-Pipelines, konsistente Entwicklungsumgebungen, und wenn Geschwindigkeit zählt

Die Magie hinter Containern: Linux-Kernel-Features

Container sind keine neue Technologie – sie nutzen Linux-Features, die seit Jahren existieren:

  • Namespaces: Isolieren Prozesse, Netzwerk, Dateisystem (jeder Container sieht nur "sein" System)
  • cgroups: Limitieren CPU, RAM, I/O pro Container
  • Union Filesystems: Ermöglichen Layer-basierte Images (nur Änderungen speichern)

Teil 2: Docker-Architektur verstehen

Docker ist wie ein Fertiggericht-System: Es gibt Rezepte (Dockerfiles), fertige Gerichte im Kühlregal (Images), und die Mikrowelle zum Aufwärmen (Docker Engine).

Die Docker-Komponenten

⚙️

Docker Daemon (dockerd)

Der Hintergrund-Dienst, der Container erstellt, startet und verwaltet. Läuft als Root-Prozess auf dem Host.

💻

Docker CLI

Die Kommandozeile (docker-Befehl). Kommuniziert mit dem Daemon über eine REST-API.

📦

Docker Images

Read-only Templates mit allen Abhängigkeiten. Bestehen aus Layern – jede Änderung wird als neuer Layer gestapelt.

🏃

Container

Laufende Instanz eines Images. Hat einen schreibbaren Layer oben drauf (wird beim Löschen verworfen).

🏪

Registry

Image-Ablage. Docker Hub (öffentlich), oder private Registries (GitLab Container Registry, Harbor, Nexus).

📝

Dockerfile

Text-Datei mit Bauanleitung für ein Image. Jede Zeile erzeugt einen Layer.

Der Docker-Workflow visualisiert


┌─────────────┐    docker build    ┌─────────────┐
│  Dockerfile │ ──────────────────▶ │   Image     │
└─────────────┘                     └──────┬──────┘
                                           │
                                    docker push
                                           │
                                           ▼
┌─────────────┐                     ┌─────────────┐
│  Registry   │ ◀──────────────────│ Docker Hub  │
│  (GitLab)   │     docker pull     │   / Private │
└──────┬──────┘                     └─────────────┘
       │
docker run
       │
       ▼
┌─────────────┐
│  Container  │ ◀── Laufende Instanz
└─────────────┘

Image-Layer: Das Zwiebel-Prinzip

Images bestehen aus Schichten (Layers) – wie eine Zwiebel. Jeder Befehl im Dockerfile erzeugt einen neuen Layer:


┌────────────────────────────────────┐
│  Layer 5: CMD ansible-playbook     │  ← Startbefehl
├────────────────────────────────────┤
│  Layer 4: RUN ansible-galaxy...    │  ← Collections
├────────────────────────────────────┤
│  Layer 3: RUN pip install...       │  ← Python-Pakete
├────────────────────────────────────┤
│  Layer 2: RUN apt-get update...    │  ← System-Pakete
├────────────────────────────────────┤
│  Layer 1: python:3.11-slim         │  ← Basis-Image
└────────────────────────────────────┘
💡Warum Layer wichtig sind
  • Caching: Unveränderte Layer werden wiederverwendet → schnellere Builds
  • Sharing: Mehrere Images teilen sich gleiche Basis-Layer → weniger Speicher
  • Best Practice: Häufig ändernde Befehle ans Ende des Dockerfiles

Teil 3: Docker-Befehle meistern

Images verwalten

# Image von Docker Hub herunterladen
docker pull python:3.11

# Mit spezifischem Tag
docker pull python:3.11-slim

# Alle lokalen Images anzeigen
docker images

# Image löschen
docker rmi python:3.11

# Ungenutzte Images aufräumen
docker image prune -a

Container starten und verwalten

# Container interaktiv starten (mit Terminal)
docker run -it python:3.11 bash

# Container im Hintergrund starten
docker run -d --name mein-python python:3.11 sleep infinity

# Container mit automatischem Löschen nach Exit
docker run --rm -it python:3.11 python -c "print('Hello!')"

# Laufende Container anzeigen
docker ps

# Alle Container (auch gestoppte)
docker ps -a

# Container stoppen
docker stop mein-python

# Container starten (nach Stop)
docker start mein-python

# Container löschen
docker rm mein-python

# Alle gestoppten Container löschen
docker container prune

In laufende Container verbinden

# Shell in laufendem Container öffnen
docker exec -it mein-python bash

# Einzelnen Befehl ausführen
docker exec mein-python cat /etc/os-release

# Als Root einloggen (falls User anders)
docker exec -it --user root mein-python bash

Dateien und Volumes

# Verzeichnis in Container mounten (Bind Mount)
docker run -it -v $(pwd):/workspace python:3.11 bash

# Benanntes Volume erstellen und nutzen
docker volume create mein-volume
docker run -it -v mein-volume:/data python:3.11 bash

# Datei in Container kopieren
docker cp lokale-datei.txt mein-python:/tmp/

# Datei aus Container kopieren
docker cp mein-python:/tmp/ergebnis.txt ./

Netzwerk

# Port weiterleiten (Host:Container)
docker run -d -p 8080:80 nginx

# Container im Host-Netzwerk (kein NAT)
docker run --network host nginx

# Eigenes Netzwerk erstellen
docker network create mein-netzwerk

# Container in Netzwerk starten
docker run -d --network mein-netzwerk --name web nginx

# Netzwerke anzeigen
docker network ls
🔧Übung 1: Docker-Version prüfen und erste Container starten (~10 Min)

Machen Sie sich mit Docker vertraut:

  1. Docker-Installation prüfen:
    # Version anzeigen
    docker --version
    
    # Detaillierte Infos
    docker info
    
    # Test-Container ausführen
    docker run hello-world
  2. Interaktiven Ubuntu-Container starten:
    # Ubuntu-Container mit Bash
    docker run -it ubuntu:22.04 bash
    
    # Im Container: System erkunden
    cat /etc/os-release
    whoami
    pwd
    ls -la
    
    # Container verlassen
    exit
  3. Container-Lebenszyklus verstehen:
    # Container im Hintergrund starten
    docker run -d --name test-container ubuntu:22.04 sleep 300
    
    # Status prüfen
    docker ps
    
    # In Container verbinden
    docker exec -it test-container bash
    exit
    
    # Container stoppen und löschen
    docker stop test-container
    docker rm test-container

Teil 4: Dockerfiles schreiben

Ein Dockerfile ist wie ein Kochrezept: Schritt für Schritt wird beschrieben, wie das Image gebaut wird.

Dockerfile-Befehle

BefehlBeschreibungBeispiel
FROMBasis-ImageFROM python:3.11-slim
RUNBefehl ausführen (beim Build)RUN pip install ansible
COPYDateien ins Image kopierenCOPY requirements.txt /app/
ADDWie COPY, kann auch URLs/ArchiveADD app.tar.gz /app/
WORKDIRArbeitsverzeichnis setzenWORKDIR /app
ENVUmgebungsvariable setzenENV ANSIBLE_HOST_KEY_CHECKING=False
EXPOSEPort dokumentierenEXPOSE 8080
CMDStandard-Befehl (überschreibbar)CMD ["python", "app.py"]
ENTRYPOINTHaupt-Befehl (nicht überschreibbar)ENTRYPOINT ["ansible-playbook"]

Beispiel: Python-Netzwerk-Tools Container

📄 Dockerfile
# Basis-Image: Schlankes Python
FROM python:3.11-slim

# Metadaten
LABEL maintainer="netzwerk-team@firma.de"
LABEL description="Python mit Netzwerk-Automatisierungs-Tools"

# System-Pakete installieren
RUN apt-get update && apt-get install -y --no-install-recommends \
    openssh-client \
    iputils-ping \
    dnsutils \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Python-Pakete installieren
COPY requirements.txt /tmp/
RUN pip install --no-cache-dir -r /tmp/requirements.txt

# Arbeitsverzeichnis
WORKDIR /workspace

# Standard-Befehl
CMD ["python3"]
📄 requirements.txt
netmiko==4.2.0
napalm==4.1.0
paramiko==3.4.0
netaddr==0.9.0
jinja2==3.1.2
pyyaml==6.0.1
requests==2.31.0
nornir==3.4.1
nornir-netmiko==1.0.1

Image bauen und taggen

# Image bauen (im Verzeichnis mit Dockerfile)
docker build -t netzwerk-tools:latest .

# Mit Version taggen
docker build -t netzwerk-tools:1.0.0 .

# Für GitLab Registry taggen
docker build -t registry.gitlab.com/mein-projekt/netzwerk-tools:1.0.0 .

# Build-Cache ignorieren (sauberer Build)
docker build --no-cache -t netzwerk-tools:latest .
🔧Übung 2: Python-Container mit Netzwerk-Libraries (~15 Min)
  1. Projektverzeichnis erstellen:
    mkdir ~/docker-netzwerk
    cd ~/docker-netzwerk
  2. requirements.txt anlegen:
    cat > requirements.txt << 'EOF'
    netmiko==4.2.0
    netaddr==0.9.0
    paramiko==3.4.0
    jinja2==3.1.2
    pyyaml==6.0.1
    EOF
  3. Dockerfile erstellen:
    cat > Dockerfile << 'EOF'
    FROM python:3.11-slim
    
    RUN apt-get update && apt-get install -y --no-install-recommends \
        openssh-client iputils-ping \
        && rm -rf /var/lib/apt/lists/*
    
    COPY requirements.txt /tmp/
    RUN pip install --no-cache-dir -r /tmp/requirements.txt
    
    WORKDIR /workspace
    CMD ["python3"]
    EOF
  4. Image bauen:
    docker build -t netzwerk-python:1.0 .
  5. Container testen:
    docker run -it --rm netzwerk-python:1.0
    
    # Im Python-Prompt testen:
    from netaddr import IPNetwork
    for ip in IPNetwork('192.168.1.0/30'):
        print(ip)
    
    exit()
🔧Übung 3: Eigenes Dockerfile für Netzwerk-Analyse (~15 Min)

Bauen Sie einen Container mit Netzwerk-Analyse-Tools:

  1. Neues Verzeichnis und Dockerfile:
    mkdir ~/docker-nettools
    cd ~/docker-nettools
    
    cat > Dockerfile << 'EOF'
    FROM ubuntu:22.04
    
    LABEL maintainer="ihr-name"
    LABEL purpose="Network Troubleshooting Tools"
    
    # Interaktive Prompts vermeiden
    ENV DEBIAN_FRONTEND=noninteractive
    
    # Netzwerk-Tools installieren
    RUN apt-get update && apt-get install -y --no-install-recommends \
        iputils-ping \
        traceroute \
        dnsutils \
        netcat-openbsd \
        curl \
        wget \
        tcpdump \
        nmap \
        iproute2 \
        net-tools \
        iperf3 \
        mtr \
        && rm -rf /var/lib/apt/lists/*
    
    WORKDIR /work
    
    CMD ["/bin/bash"]
    EOF
  2. Image bauen:
    docker build -t nettools:1.0 .
  3. Tools testen:
    # Container mit Host-Netzwerk starten (für echte Tests)
    docker run -it --rm --network host nettools:1.0
    
    # Im Container:
    ping -c 3 8.8.8.8
    dig google.com
    nmap -sn 192.168.1.0/24  # Vorsicht: nur eigenes Netz!
    traceroute google.com
    exit

Teil 5: Volumes und persistente Daten

Container sind flüchtig wie Seifenblasen – wenn sie weg sind, sind die Daten auch weg. Für persistente Daten brauchen Sie Volumes.

Drei Arten von Volumes

📁

Bind Mounts

Host-Verzeichnis direkt einbinden. Gut für Entwicklung.

-v /host/pfad:/container/pfad
💾

Named Volumes

Docker-verwaltete Volumes. Portabel und einfach.

-v volume-name:/container/pfad
💨

tmpfs Mounts

Im RAM, nicht persistent. Für temporäre/sensible Daten.

--tmpfs /container/pfad
# Bind Mount: Lokales Verzeichnis einbinden
docker run -it -v $(pwd):/workspace python:3.11 bash

# Named Volume erstellen und nutzen
docker volume create ansible-data
docker run -it -v ansible-data:/data python:3.11 bash

# Volume inspizieren
docker volume inspect ansible-data

# Alle Volumes anzeigen
docker volume ls

# Ungenutzte Volumes löschen
docker volume prune
🔧Übung 4: Container mit Volume mounten (~10 Min)
  1. Test-Skript erstellen:
    mkdir ~/docker-volume-test
    cd ~/docker-volume-test
    
    cat > hello.py << 'EOF'
    #!/usr/bin/env python3
    from datetime import datetime
    from netaddr import IPNetwork
    
    print(f"=== Netzwerk-Analyse Tool ===")
    print(f"Ausgeführt: {datetime.now()}")
    print()
    
    subnet = "10.100.0.0/29"
    print(f"Subnet: {subnet}")
    print("Nutzbare IPs:")
    for ip in IPNetwork(subnet).iter_hosts():
        print(f"  - {ip}")
    EOF
  2. Mit Bind Mount ausführen:
    docker run --rm -v $(pwd):/workspace netzwerk-python:1.0 python /workspace/hello.py
  3. Interaktiv entwickeln:
    # Container starten, Verzeichnis gemountet
    docker run -it --rm -v $(pwd):/workspace netzwerk-python:1.0 bash
    
    # Im Container: Skript bearbeiten und testen
    cd /workspace
    python hello.py
    
    # Änderungen am Host sichtbar!
    exit
  4. Named Volume für persistente Daten:
    # Volume erstellen
    docker volume create test-data
    
    # Daten hineinschreiben
    docker run --rm -v test-data:/data python:3.11 bash -c "echo 'Persistent!' > /data/test.txt"
    
    # Daten lesen (in neuem Container!)
    docker run --rm -v test-data:/data python:3.11 cat /data/test.txt
    
    # Volume aufräumen
    docker volume rm test-data

Teil 6: Ansible im Container

Ansible in einem Container zu betreiben bringt enorme Vorteile:Konsistente Versionen, keine Abhängigkeitskonflikte, und perfekt für CI/CD-Pipelines.

Ansible-Container Dockerfile

📄 Dockerfile.ansible
# Ansible Container für NDFC/Netzwerk-Automatisierung
FROM python:3.11-slim

# Labels für Wartbarkeit
LABEL maintainer="netzwerk-team@firma.de"
LABEL version="1.0"
LABEL description="Ansible mit Cisco DCNM/NDFC Collections"

# Umgebungsvariablen
ENV ANSIBLE_HOST_KEY_CHECKING=False
ENV ANSIBLE_RETRY_FILES_ENABLED=False
ENV PYTHONUNBUFFERED=1

# System-Abhängigkeiten
RUN apt-get update && apt-get install -y --no-install-recommends \
    openssh-client \
    sshpass \
    git \
    && rm -rf /var/lib/apt/lists/*

# Python-Pakete installieren
RUN pip install --no-cache-dir \
    ansible-core==2.15.* \
    ansible-pylibssh \
    jmespath \
    netaddr \
    requests \
    paramiko

# Ansible Collections installieren
RUN ansible-galaxy collection install \
    cisco.dcnm \
    ansible.netcommon \
    ansible.utils

# Arbeitsverzeichnis
WORKDIR /ansible

# Ansible-Version anzeigen beim Start
CMD ["ansible", "--version"]

Container bauen und nutzen

# Image bauen
docker build -t ansible-ndfc:1.0 -f Dockerfile.ansible .

# Ansible-Version prüfen
docker run --rm ansible-ndfc:1.0

# Playbook ausführen (mit gemounteten Dateien)
docker run --rm \
    -v $(pwd):/ansible \
    -v ~/.ssh:/root/.ssh:ro \
    ansible-ndfc:1.0 \
    ansible-playbook -i inventory.yml playbook.yml

# Interaktiv für Tests
docker run -it --rm \
    -v $(pwd):/ansible \
    ansible-ndfc:1.0 bash
🔧Übung 5: Ansible in Container ausführen (~15 Min)
  1. Ansible-Container Verzeichnis erstellen:
    mkdir ~/docker-ansible
    cd ~/docker-ansible
  2. Dockerfile erstellen:
    cat > Dockerfile << 'EOF'
    FROM python:3.11-slim
    
    ENV ANSIBLE_HOST_KEY_CHECKING=False
    ENV PYTHONUNBUFFERED=1
    
    RUN pip install --no-cache-dir \
        ansible-core==2.15.* \
        jmespath \
        netaddr
    
    RUN ansible-galaxy collection install ansible.utils
    
    WORKDIR /ansible
    CMD ["ansible", "--version"]
    EOF
    
    docker build -t ansible-test:1.0 .
  3. Test-Playbook erstellen:
    cat > playbook.yml << 'EOF'
    ---
    - name: Container-Test Playbook
      hosts: localhost
      connection: local
      gather_facts: yes
      
      vars:
        vlans:
          - id: 100
            name: SERVERS
          - id: 200
            name: CLIENTS
          - id: 300
            name: MANAGEMENT
      
      tasks:
        - name: System-Informationen anzeigen
          ansible.builtin.debug:
            msg: |
              Ansible läuft in Container!
              Hostname: {{ ansible_hostname }}
              Python: {{ ansible_python_version }}
              
        - name: VLAN-Konfiguration generieren
          ansible.builtin.debug:
            msg: "VLAN {{ item.id }}: {{ item.name }}"
          loop: "{{ vlans }}"
          
        - name: IP-Berechnung mit netaddr
          ansible.builtin.debug:
            msg: "Gateway für 10.{{ item.id }}.0.0/24: 10.{{ item.id }}.0.1"
          loop: "{{ vlans }}"
    EOF
  4. Playbook im Container ausführen:
    docker run --rm -v $(pwd):/ansible ansible-test:1.0 \
        ansible-playbook playbook.yml
  5. Interaktive Ansible-Shell:
    docker run -it --rm -v $(pwd):/ansible ansible-test:1.0 bash
    
    # Im Container:
    ansible localhost -m debug -a "msg='Hello from Container!'"
    ansible localhost -m setup | head -50
    exit

Teil 7: Docker Compose

Docker Compose ist wie ein Orchester-Dirigent: Es startet mehrere Container gleichzeitig und koordiniert sie.

Warum Docker Compose?

  • Multi-Container Apps: Web-Server + Datenbank + Cache gleichzeitig
  • Deklarativ: YAML-Datei beschreibt gewünschten Zustand
  • Networking: Container können sich per Namen erreichen
  • Entwicklung: Komplexe Setups mit einem Befehl starten

docker-compose.yml Struktur

📄 docker-compose.yml
version: '3.8'

services:
  # Service 1: Web-Anwendung
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    depends_on:
      - api
    
  # Service 2: API Backend
  api:
    build: ./api
    environment:
      - DATABASE_URL=postgres://db:5432/app
    depends_on:
      - db
      
  # Service 3: Datenbank
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

Compose-Befehle

# Alle Services starten
docker compose up

# Im Hintergrund starten
docker compose up -d

# Logs anzeigen
docker compose logs -f

# Status prüfen
docker compose ps

# In Service verbinden
docker compose exec web sh

# Alles stoppen
docker compose down

# Stoppen und Volumes löschen
docker compose down -v

Netzwerk-Automatisierung mit Compose

📄 docker-compose.yml
version: '3.8'

services:
  # Ansible Automation Container
  ansible:
    build: .
    volumes:
      - ./playbooks:/ansible/playbooks
      - ./inventory:/ansible/inventory
      - ./group_vars:/ansible/group_vars
      - ~/.ssh:/root/.ssh:ro
    environment:
      - ANSIBLE_HOST_KEY_CHECKING=False
      - NDFC_HOST=${NDFC_HOST}
      - NDFC_USER=${NDFC_USER}
      - NDFC_PASSWORD=${NDFC_PASSWORD}
    working_dir: /ansible
    command: tail -f /dev/null  # Container am Leben halten
    
  # Git-Server für lokale Tests (optional)
  gitea:
    image: gitea/gitea:latest
    ports:
      - "3000:3000"
    volumes:
      - gitea-data:/data
      
volumes:
  gitea-data:
🔧Übung 6: Docker Compose für Multi-Container Setup (~15 Min)
  1. Projektverzeichnis erstellen:
    mkdir ~/compose-demo
    cd ~/compose-demo
  2. docker-compose.yml anlegen:
    cat > docker-compose.yml << 'EOF'
    version: '3.8'
    
    services:
      # Python Netzwerk-Tools
      nettools:
        image: python:3.11-slim
        volumes:
          - ./scripts:/scripts
        working_dir: /scripts
        command: tail -f /dev/null
        networks:
          - automation-net
          
      # Web-UI für Dokumentation
      docs:
        image: nginx:alpine
        ports:
          - "8080:80"
        volumes:
          - ./docs:/usr/share/nginx/html:ro
        networks:
          - automation-net
          
      # Redis für Caching (Beispiel)
      cache:
        image: redis:alpine
        networks:
          - automation-net
    
    networks:
      automation-net:
        driver: bridge
    EOF
  3. Dateien für Services erstellen:
    # Script-Verzeichnis
    mkdir scripts
    cat > scripts/analyze.py << 'EOF'
    #!/usr/bin/env python3
    import socket
    print("=== Container Netzwerk-Info ===")
    print(f"Hostname: {socket.gethostname()}")
    print(f"IP: {socket.gethostbyname(socket.gethostname())}")
    print()
    # Test: Andere Container erreichen
    for host in ['docs', 'cache']:
        try:
            ip = socket.gethostbyname(host)
            print(f"{host}: {ip} ✓")
        except:
            print(f"{host}: nicht erreichbar")
    EOF
    
    # Docs-Verzeichnis
    mkdir docs
    cat > docs/index.html << 'EOF'
    <!DOCTYPE html>
    <html>
    <head><title>Automation Docs</title></head>
    <body>
    <h1>🚀 Netzwerk-Automatisierung</h1>
    <p>Dokumentation läuft in einem Container!</p>
    <ul>
      <li>nettools: Python mit Netzwerk-Libraries</li>
      <li>docs: Diese Nginx-Seite</li>
      <li>cache: Redis für Caching</li>
    </ul>
    </body>
    </html>
    EOF
  4. Services starten:
    # Starten
    docker compose up -d
    
    # Status prüfen
    docker compose ps
    
    # Logs anzeigen
    docker compose logs
  5. Services testen:
    # Web-UI öffnen
    echo "Öffnen Sie: http://localhost:8080"
    
    # Python-Script im Container ausführen
    docker compose exec nettools python /scripts/analyze.py
    
    # Redis testen
    docker compose exec cache redis-cli ping
  6. Aufräumen:
    docker compose down

Teil 8: Docker Troubleshooting

Wenn Container nicht tun, was sie sollen, sind diese Befehle Ihre besten Freunde:

Container-Logs analysieren

# Logs eines Containers anzeigen
docker logs container-name

# Live-Logs (wie tail -f)
docker logs -f container-name

# Letzte 100 Zeilen
docker logs --tail 100 container-name

# Mit Zeitstempeln
docker logs -t container-name

# Logs seit bestimmter Zeit
docker logs --since 10m container-name

Container inspizieren

# Alle Infos zu einem Container
docker inspect container-name

# Spezifische Infos extrahieren (IP-Adresse)
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container-name

# Environment-Variablen anzeigen
docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' container-name

# Mounted Volumes anzeigen
docker inspect -f '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}}' container-name

In laufenden Container debuggen

# Shell im Container öffnen
docker exec -it container-name bash
# Oder für Alpine-basierte Images:
docker exec -it container-name sh

# Als Root einloggen
docker exec -it --user root container-name bash

# Prozesse im Container anzeigen
docker exec container-name ps aux

# Netzwerk-Status prüfen
docker exec container-name netstat -tlnp
docker exec container-name cat /etc/resolv.conf

Ressourcen-Verbrauch überwachen

# Live-Statistiken aller Container
docker stats

# Für bestimmte Container
docker stats container-name

# Einmalig (nicht live)
docker stats --no-stream

Häufige Probleme und Lösungen

ProblemDiagnoseLösung
Container startet nichtdocker logsFehlermeldung prüfen, oft fehlende Env-Vars
Port schon belegtnetstat -tlnpAnderen Port wählen: -p 8081:80
Volume-Berechtigungls -la /mountchmod auf Host oder --user
Container beendet sofortdocker ps -aCMD/ENTRYPOINT prüfen, Prozess läuft nicht
Kein Internet im Containerdocker exec ... ping 8.8.8.8Docker-Netzwerk oder Firewall prüfen
Image zu großdocker history imageMulti-stage Build, slim-Image nutzen
⚠️Container-Debugging Tipps
  • Container beendet sofort? Starten Sie mit tail -f /dev/null als CMD
  • Keine Shell im Container? Nutzen Sie docker cp um Dateien zu extrahieren
  • Netzwerk-Probleme? Testen Sie mit --network host

Teil 9: Best Practices für Container in CI/CD

Dockerfile-Optimierung

📄 Dockerfile (optimiert)
# ✅ GOOD: Spezifische Version nutzen
FROM python:3.11.7-slim-bookworm

# ✅ GOOD: Kombinierte RUN-Befehle (weniger Layer)
RUN apt-get update && apt-get install -y --no-install-recommends \
    openssh-client \
    && rm -rf /var/lib/apt/lists/*

# ✅ GOOD: Requirements zuerst (Cache nutzen)
COPY requirements.txt /tmp/
RUN pip install --no-cache-dir -r /tmp/requirements.txt

# ✅ GOOD: Code als letztes (ändert sich oft)
COPY . /app

# ✅ GOOD: Non-root User
RUN useradd -m appuser
USER appuser

# ✅ GOOD: HEALTHCHECK definieren
HEALTHCHECK --interval=30s --timeout=3s \
    CMD curl -f http://localhost:8080/health || exit 1

Image-Größe reduzieren

📄 Multi-Stage Build
# Stage 1: Build
FROM python:3.11 AS builder

COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt

# Stage 2: Runtime (schlank)
FROM python:3.11-slim

COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/*

COPY app/ /app/
CMD ["python", "/app/main.py"]

Security Best Practices

✅ Tun

  • • Spezifische Image-Tags nutzen
  • • Non-root User verwenden
  • • Secrets als Env-Vars (nicht im Image)
  • • Images regelmäßig updaten
  • • Minimale Base-Images (slim, alpine)
  • • .dockerignore nutzen

❌ Vermeiden

  • • latest Tag in Produktion
  • • Root im Container
  • • Passwörter im Dockerfile
  • • Veraltete Base-Images
  • • Unnötige Pakete installieren
  • • SSH im Container

CI/CD-Integration (GitLab CI)

📄 .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

variables:
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

build-image:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG

run-ansible:
  stage: deploy
  image: $IMAGE_TAG
  script:
    - ansible-playbook -i inventory.yml playbook.yml
  only:
    - main

.dockerignore nicht vergessen

📄 .dockerignore
# Git
.git
.gitignore

# IDE
.vscode
.idea
*.swp

# Python
__pycache__
*.pyc
.pytest_cache
.venv
venv

# Secrets (NIEMALS ins Image!)
*.pem
*.key
.env
secrets/

# Build-Artefakte
*.log
.coverage
htmlcov/

Zusammenfassung

✅ Das haben Sie gelernt

  • ☑️ Container vs. VMs: Unterschiede in Architektur und Anwendung
  • ☑️ Docker-Architektur: Daemon, CLI, Images, Container, Registry
  • ☑️ Dockerfiles schreiben und optimieren
  • ☑️ Volumes für persistente Daten nutzen
  • ☑️ Docker Compose für Multi-Container-Setups
  • ☑️ Netzwerk-Tools (Ansible, Python, netmiko) in Containern
  • ☑️ Troubleshooting: logs, exec, inspect
  • ☑️ Best Practices für CI/CD-Integration

📋 Spickzettel: Docker-Befehle

docker pull <image>
Image laden
docker run -it <image>
Interaktiv starten
docker run -d <image>
Im Hintergrund
docker run -v <src>:<dst>
Volume mounten
docker run -p 8080:80
Port weiterleiten
docker ps
Laufende Container
docker ps -a
Alle Container
docker logs <name>
Logs anzeigen
docker exec -it <name> bash
Shell öffnen
docker inspect <name>
Details anzeigen
docker stop <name>
Stoppen
docker rm <name>
Löschen
docker images
Lokale Images
docker build -t <name> .
Image bauen
docker compose up -d
Compose starten
docker compose down
Compose stoppen
💡Nächste Schritte

Im nächsten Modul lernen Sie, wie Container in CI/CD-Pipelines eingesetzt werden. Dort bauen wir automatisierte Workflows, die Ansible-Container nutzen, um Netzwerk-Konfigurationen zu deployen.