# Docker Development Deep Dive: Von Basics zu Advanced .NET Containerization

<datetime class="hidden">2025-11-09T14:00</datetime>

<!--category-- Docker, .NET, DevOps, Containers -->
## Einleitung

Docker hat grundlegend verändert, wie wir Anwendungen bauen, versenden und ausführen.

Was als einfaches Containerisierungstool begann, hat sich zu einem kompletten Ökosystem für die moderne Anwendungsentwicklung und -bereitstellung entwickelt.

1. **[Dies ist der vierte und umfassendste Artikel in meiner Docker-Serie für .NET-Entwickler.](/dockercompose)**Wenn du bei Docker neu bist, solltest du vielleicht anfangen mit:
2. **[Docker-Komposition](/dockercomposedevdeps)**- Erste Schritte mit grundlegenden Multi-Container-Setups (Juli 2024)
3. **[Entwicklungsabhängigkeiten mit Docker Compose](/imagesharpwithdocker)**- Einrichtung von lokalen Dev-Umgebungen (August 2024)

ImageSharp mit Docker

- **- Lösen von Volumenberechtigungen (August 2024)**In diesem tiefen Tauchgang bauen wir auf diesen Grundlagen auf, um zu erkunden:
- **Produktionsfertige Docker-Komposition**: Der eigentliche Stapel mit diesem Blog
- **Selbstbewirtung auf begrenzten Ressourcen**: Praktische Optimierungstechniken für Budget-VPS-Bereitstellungen
- **Unterstützung der GPU**: Laufen von ML-Workloads in Containern
- **Multi-Architektur baut**: Unterstützung von ARM64 und AMD64
- **.NET 9 Containereigenschaften**: Integriertes Container-Publishing ohne Dockerfiles

> .NET-Aspire

: Der moderne .NET-Ansatz zur Container-Orchestrierung

[TOC]

## HINWEIS: Dies ist Teil meiner Experimente mit KI / eine Möglichkeit, $1000 Claude Code Web Credits auszugeben.

### Ich habe dies eine BUNCH Papiere gefüttert, mein Verständnis, Fragen, die ich diesen Artikel generieren musste.

Es macht Spaß und füllt eine Lücke, die ich nirgendwo sonst gefüllt habe.

Ob Sie eine einfache Web-App bereitstellen oder eine komplexe Microservice-Architektur mit GPU-beschleunigten Machine Learning-Modellen orchestrieren, diese Anleitung führt Sie von Docker Basics zu produktionsfähigen containerisierten Anwendungen - mit echten Beispielen von running mostlylucid.com.

- **Docker Grundlagen: Container verstehen**Was ist Docker, wirklich?
- **Im Kern ist Docker eine Containerisierungsplattform, die Ihre Anwendung und all ihre Abhängigkeiten in eine standardisierte Einheit, einen Container, verpackt.**Im Gegensatz zu virtuellen Maschinen, die ganze Betriebssysteme virtualisiert, teilen Container den Host OS Kernel unter Beibehaltung isolierter Benutzerräume.

### Stellen Sie sich das mal so vor:

```bash
# The classic developer problem
"It works on my machine!" 

# The container solution
"Ship your machine!" 
```

Virtuelle Maschinen

1. **: Jeder VM läuft einen kompletten OS Stack (Linux-Kernel, Systembibliotheken, etc.) - schwer und langsam zum Starten**Behälter
2. **: Teilen Sie den Host-Kernel, Paket nur Anwendungscode und Abhängigkeiten - leicht und schnell**Warum Container für Entwickler wichtig sind
3. **Container lösen mehrere kritische Probleme:**Umweltkonsistenz
4. **: Entwicklungs-, Test- und Produktionsumgebungen sind identisch**Abhängigkeitsisolierung
5. **: Keine "DLL Hölle" oder widersprüchliche Bibliotheksversionen mehr**Reproduzierbare Konstruktionen

### : Gleiche Eingabe = gleiche Ausgabe, jedes Mal

#### Schnelle Bereitstellung

```bash
# An image is a template (like a class in OOP)
docker pull mcr.microsoft.com/dotnet/aspnet:9.0

# A container is a running instance (like an object)
docker run -d -p 8080:8080 myapp:latest
```

**: Container in Sekunden starten, nicht Minuten**Ressourceneffizienz

```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0      # Layer 1: Base OS + .NET runtime
WORKDIR /app                                   # Layer 2: Directory structure
COPY *.dll ./                                  # Layer 3: Application files
ENTRYPOINT ["dotnet", "MyApp.dll"]            # Layer 4: Startup command
```

: Führen Sie Dutzende von Containern auf einem einzigen Host

- **Wesentliche Docker-Konzepte**Bilder gegen Container
- **Bilder**sind unveränderliche, geschichtete Dateisysteme.
- **Jede Anweisung in einem Dockerfile erzeugt eine neue Ebene:**Diese Schichtung ist leistungsfähig:

#### Caching

: Unveränderte Schichten werden wiederverwendet und beschleunigen Builds**Teilen**

: Mehrere Bilder können Grundlagen teilen

```
Your Machine (Windows/Mac/Linux)
    ↓ (reads Dockerfile)
Build Image (usually Linux)
    ↓ (executes RUN commands here)
Output Image (contains results)
```

**Effizienz**

```dockerfile
# You're on Windows, writing this Dockerfile
FROM ubuntu:24.04

# This RUN command executes in Ubuntu, NOT on your Windows machine!
RUN apt-get update && apt-get install -y curl

# This copies FROM your Windows filesystem
COPY myapp.exe /app/

# This executes IN the Ubuntu container
RUN chmod +x /app/myapp.exe
```

**: Nur geänderte Schichten müssen heruntergeladen/aufgeladen werden**

1. **Dockerfile Execution verstehen: Nicht Ihre Maschine!**Eine gemeinsame Quelle der Verwirrung für Docker Anfänger:`COPY`Befehle in einem Dockerfile laufen nicht auf Ihrem Rechner - sie laufen im OS des Baucontainers.`ADD`Hier ist, was tatsächlich passiert:
2. **Warum das so wichtig ist:**Wichtigste Erkenntnisse:`RUN`Lokales Dateisystem
3. **: Ihr**und
4. **Befehle lesen von Ihrer Maschine**Bild erstellen

: Ihr`apt-get`Befehle ausführen im Betriebssystem des Containers (nicht auf Ihrem Rechner)

**Ausgabebild**

```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0  # This is Linux-based

# You might think: "But I'm on Windows, how can I use these Linux commands?"
RUN apt-get update  # ← Executes in the Linux build container, not your Windows machine
RUN dotnet restore  # ← Executes in the Linux build container
```

: Das endgültige Bild enthält alle während des Erstellens erstellten Ebenen

#### Plattformübergreifend

: Sie können unter Windows sein, ein Linux-Image bauen, mit Linux-Befehlen

```dockerfile
# Multi-stage build: separates build environment from runtime
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src

# Copy only csproj files first (better layer caching)
COPY ["MyApp/MyApp.csproj", "MyApp/"]
COPY ["MyApp.Core/MyApp.Core.csproj", "MyApp.Core/"]

# Restore dependencies (cached unless csproj changes)
RUN dotnet restore "MyApp/MyApp.csproj"

# Copy everything else
COPY . .

# Build the application
WORKDIR "/src/MyApp"
RUN dotnet build "MyApp.csproj" -c Release -o /app/build

# Stage 2: Publish
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish /p:UseAppHost=false

# Stage 3: Final runtime image
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
WORKDIR /app

# Create non-root user for security
RUN addgroup --gid 1001 appuser && \
    adduser --uid 1001 --gid 1001 --disabled-password --gecos "" appuser

# Copy published output from publish stage
COPY --from=publish /app/publish .

# Switch to non-root user
USER appuser

# Expose port (documentation only, doesn't actually publish)
EXPOSE 8080

# Set environment variables
ENV ASPNETCORE_URLS=http://+:8080
ENV ASPNETCORE_ENVIRONMENT=Production

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

ENTRYPOINT ["dotnet", "MyApp.dll"]
```

#### Aus diesem Grund können Sie schreiben

Befehle in einem Dockerfile unter Windows - sie laufen nicht unter Windows!

```mermaid
graph TB
    subgraph "Your Machine"
        A[Source Code<br/>*.cs, *.csproj]
        B[Frontend Assets<br/>*.js, *.css]
        C[Configuration<br/>appsettings.json]
        D[Static Files<br/>wwwroot/]
    end

    subgraph "Stage 1: Build Container (SDK Image)"
        E[dotnet/sdk:9.0<br/>~1.5GB]
        F[Copy .csproj files]
        G[dotnet restore<br/>Download NuGet packages]
        H[Copy source code]
        I[dotnet build<br/>Compile to DLLs]
        J[Build artifacts<br/>/app/build/]
    end

    subgraph "Stage 2: Publish Container"
        K[dotnet publish<br/>Optimize & trim]
        L[Published output<br/>/app/publish/]
    end

    subgraph "Stage 3: Final Runtime Container (ASPNET Image)"
        M[dotnet/aspnet:9.0<br/>~220MB]
        N[Create app user<br/>Security]
        O[Copy published files<br/>ONLY production artifacts]
        P[Final image<br/>~250MB total]
    end

    subgraph "Frontend Build Pipeline (Parallel)"
        Q[npm install<br/>node_modules/]
        R[Webpack bundling<br/>*.js → dist/]
        S[TailwindCSS + PostCSS<br/>*.css → dist/]
        T[Optimized assets<br/>wwwroot/js/dist/<br/>wwwroot/css/dist/]
    end

    A --> F
    A --> H
    F --> G
    G --> H
    H --> I
    I --> J
    J --> K
    K --> L

    B --> Q
    Q --> R
    Q --> S
    R --> T
    S --> T

    L --> O
    T --> O
    C --> O
    D --> O

    M --> N
    N --> O
    O --> P
```

**Sie laufen im Linux-basierten Build-Container.**

1. **Beispiel Verwechslungsszenario:**Der Docker-Daemon übernimmt die Übersetzung zwischen Ihrem lokalen Dateisystem und der containerisierten Build-Umgebung.
2. **Dockerfile Best Practices**Hier ist ein produktionsfertiges .NET Dockerfile mit Kommentar:`.csproj`Den Build Flow verstehen
3. **Hier ist, was tatsächlich passiert während einer mehrstufigen Docker-Build - wo Dateien kommen aus und wo sie am Ende:**Wesentliche Erkenntnisse aus diesem Fluss:`npm run build`SDK-Bild verworfen
4. **: Der 1.5GB SDK Container wird nach der Veröffentlichung weggeworfen - nur ~50MB kompilierter Ausgabe bewegt sich vorwärts**: `appsettings.json`Ebenen-Caching`wwwroot/`: Kopieren
5. **bevor Quellcode bedeutet Abhängigkeit Wiederherstellung wird zwischengespeichert, es sei denn Abhängigkeiten ändern**Forderungen an die Front
6. **: separat gebaut (oft über**) und in das endgültige Bild kopiert

**Konfigurationsdateien**

1. **und**Inhalt kopiert von Ihrem Rechner, nicht gebaut
2. **Endgültiges Bild**: Startet von minimalem Laufzeitbild (~220MB) + Ihrer App (~30MB) + Assets (~5MB) = ~255MB insgesamt
3. **Nur Produktionsdateien**: Quellcode, obj/, bin/, node_modules/ kommen nie zum endgültigen Bild
4. **Veranschaulichte Schlüsselprinzipien:**Mehrstufige Aufbauten
5. **: Separate Build/Publish-Stufen reduzieren die endgültige Bildgröße dramatisch**Ebenenoptimierung

#### : Kopieren von Abhängigkeitsdateien vor dem Quellcode für ein besseres Caching

```bash
# Build the image
docker build -t myapp:1.0.0 -t myapp:latest .

# Run with common options
docker run -d \
  --name myapp \
  -p 8080:8080 \
  -e ConnectionStrings__DefaultConnection="Server=db;Database=myapp" \
  -v /data/logs:/app/logs \
  --restart unless-stopped \
  myapp:latest

# View logs
docker logs -f myapp

# Execute commands inside running container
docker exec -it myapp /bin/bash

# Stop and remove
docker stop myapp
docker rm myapp
```

## Sicherheit

: Als nicht-root Benutzer ausführen

### Gesundheitskontrollen

: Container-Orchester können Anwendungsgesundheit überwachen

- Explizite Umgebung
- : Produktionseinstellungen einstellen
- Bauen und Laufen
- Docker Compose: Multi-Container Orchestration
- Docker Compose ermöglicht es Ihnen, Multi-Container-Anwendungen zu definieren und auszuführen.

Anstatt Container individuell zu verwalten, beschreiben Sie Ihren gesamten Anwendungsstapel in einer YAML-Datei.`docker run`Warum Docker Compose?

### Betrachten Sie eine typische .NET Web-Anwendung:

ASP.NET Core Web-App**PostgreSQL-Datenbank`docker-compose.yml`**Zwischenspeicher zurücksetzen

```yaml
services:
  # Main ASP.NET Core application
  mostlylucid:
    image: scottgal/mostlylucid:latest
    restart: always
    healthcheck:
      test: [ "CMD", "curl", "-f -K", "https://mostlylucid:7240/healthy" ]
      interval: 30s
      timeout: 10s
      retries: 5
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    env_file:
      - .env
    environment:
      - Auth__GoogleClientId=${AUTH_GOOGLECLIENTID}
      - Auth__GoogleClientSecret=${AUTH_GOOGLECLIENTSECRET}
      - Auth__AdminUserGoogleId=${AUTH_ADMINUSERGOOGLEID}
      - SmtpSettings__UserName=${SMTPSETTINGS_USERNAME}
      - SmtpSettings__Password=${SMTPSETTINGS_PASSWORD}
      - Analytics__UmamiPath=${ANALYTICS_UMAMIPATH}
      - Analytics__WebsiteId=${ANALYTICS_WEBSITEID}
      - ConnectionStrings__DefaultConnection=${POSTGRES_CONNECTIONSTRING}
      - TranslateService__ServiceIPs=${EASYNMT_IPS}
      - Serilog__WriteTo__0__Args__apiKey=${SEQ_API_KEY}
      - Markdown__MarkdownPath=${MARKDOWN_MARKDOWNPATH}
    volumes:
      - /mnt/imagecache:/app/wwwroot/cache
      - /mnt/logs:/app/logs
      - /mnt/markdown:/app/markdown
      - ./mostlylucid.pfx:/app/mostlylucid.pfx
      - /mnt/articleimages:/app/wwwroot/articleimages
      - /mnt/mostlylucid/uploads:/app/wwwroot/uploads
    networks:
      - app_network
    depends_on:
      - db

  # PostgreSQL database
  db:
    image: postgres:16-alpine
    ports:
      - 5266:5432  # Custom external port to avoid conflicts
    env_file:
      - .env
    networks:
      - app_network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 5s
      timeout: 5s
      retries: 5
    volumes:
      - /mnt/umami/postgres:/var/lib/postgresql/data
    restart: always

  # Cloudflare tunnel for secure external access
  cloudflared:
    image: cloudflare/cloudflared:latest
    command: tunnel --no-autoupdate run --token ${CLOUDFLARED_TOKEN}
    env_file:
      - .env
    restart: always
    networks:
      - app_network

  # Umami analytics
  umami:
    image: ghcr.io/umami-software/umami:postgresql-latest
    env_file: .env
    environment:
      DATABASE_URL: ${DATABASE_URL}
      DATABASE_TYPE: ${DATABASE_TYPE}
      HASH_SALT: ${HASH_SALT}
      APP_SECRET: ${APP_SECRET}
      TRACKER_SCRIPT_NAME: getinfo
      API_COLLECT_ENDPOINT: all
    depends_on:
      - db
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    networks:
      - app_network
    restart: always

  # Translation service (CPU-limited for resource management)
  easynmt:
    image: easynmt/api:2.0.2-cpu
    volumes:
      - /mnt/easynmt:/cache/
    deploy:
      resources:
        limits:
          cpus: "4.0"  # Prevent translation service from consuming all CPU
    networks:
      - app_network

  # Caddy reverse proxy with automatic HTTPS
  caddy:
    image: caddy:latest
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - app_network
    restart: always

  # Seq centralized logging
  seq:
    image: datalust/seq
    container_name: seq
    restart: unless-stopped
    environment:
      ACCEPT_EULA: "Y"
      SEQ_FIRSTRUN_ADMINPASSWORDHASH: ${SEQ_DEFAULT_HASH}
    volumes:
      - /mnt/seq:/data
    networks:
      - app_network

  # Prometheus metrics collection
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    volumes:
      - prometheus-data:/prometheus
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    networks:
      - app_network

  # Grafana visualization
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    volumes:
      - grafana-data:/var/lib/grafana
    networks:
      - app_network
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}

  # Host metrics exporter
  node_exporter:
    image: quay.io/prometheus/node-exporter:latest
    container_name: node_exporter
    command:
      - '--path.rootfs=/host'
    networks:
      - app_network
    restart: unless-stopped
    volumes:
      - '/:/host:ro,rslave'

  # Automatic container updates
  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_LABEL_ENABLE=true
    command: --interval 300  # Check every 5 minutes

volumes:
  grafana-data:
  caddy_data:
  caddy_config:
  prometheus-data:

networks:
  app_network:
    driver: bridge
```

**Seq für die Protokollierung**

1. **Vielleicht ein Hintergrund-Arbeiter-Service**Verwaltung dieser individuell mit`app_network`Befehle werden unhandlich.
2. **Docker Compose löst das.**Vollständiges Real-World Beispiel: Produktions-Blog-Plattform`/mnt/*`Hier ist die
3. **tatsächliche Erzeugung**die diese Website betreibt (mostlylucid.com):
4. **Hauptproduktionsmuster:**Einheitliches Netz
5. **: Alle Dienstleistungen auf**für Einfachheit
6. **Verbindliche Halterungen**: Hostpfade (
7. **) für persistente Daten, die Sie benötigen, um zu sichern / zuzugreifen**Benannte Volumes`.env`: Docker-gemanagte Speicherung für Daten, die Sie nicht benötigen direkten Zugriff auf

### Etiketten des Wachtturms

#### : Nur aktualisierte Dienste, die explizit für Auto-Update gekennzeichnet sind

```yaml
services:
  web:
    depends_on:
      db:
        condition: service_healthy  # Wait for health check
      redis:
        condition: service_started  # Just wait for start
```

Ressourcengrenzen`condition: service_healthy`: CPU-Beschränkungen im Übersetzungsdienst verhindern Ressourcenverhungerung

#### Externe Port-Mapping

```bash
# .env file (never commit to git!)
DB_PASSWORD=super_secret_password
SMTP_PASSWORD=another_secret
```

```yaml
services:
  web:
    environment:
      - DB_PASSWORD=${DB_PASSWORD}  # From .env file
      - STATIC_VALUE=production     # Hardcoded
    env_file:
      - .env                        # Load entire file
```

: PostgreSQL auf 5266 statt 5432, um Konflikte mit anderen Instanzen zu vermeiden

#### Umweltdateien

```yaml
services:
  db:
    volumes:
      # Named volume (managed by Docker)
      - postgres_data:/var/lib/postgresql/data

  web:
    volumes:
      # Bind mount (maps host directory to container)
      - ./data/markdown:/app/Markdown
      - ./logs:/app/logs
```

**: Geheimnisse in**:

- Datei (nie zu git verpflichtet)
- Docker Komponieren Sie die wichtigsten Funktionen
- Service-Abhängigkeiten
- Docker Compose orchestriert den Startauftrag.

**Das**:

- erfordert den Datenbank-Gesundheitscheck, um vor dem Start der Web-App passieren.
- Umgebungsvariablen und Geheimnisse
- Verwenden Sie für Produktionsgeheimnisse Docker Secrets oder externe Secret Manager (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault).

#### Benannte Volumes vs Bind Mounts

```yaml
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

services:
  web:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend  # Not exposed to frontend
```

Benannte Bände

#### Von Docker verwaltet

```yaml
services:
  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
```

Persistente Daten über Container starten

- Kann mit Docker-Befehlen gesichert/zurückgesetzt werden
- Plattformübergreifend kompatibel
- Verbindliche Halterungen

### Direktes Mapping auf das Host-Dateisystem

```bash
# Start all services (detached)
docker-compose up -d

# Start specific services
docker-compose up -d web db

# View logs (all services)
docker-compose logs -f

# View logs (specific service)
docker-compose logs -f web

# Stop services (containers remain)
docker-compose stop

# Stop and remove containers
docker-compose down

# Stop, remove containers, and remove volumes
docker-compose down -v

# Rebuild and restart
docker-compose up -d --build

# Scale a service
docker-compose up -d --scale worker=3

# Execute command in running service
docker-compose exec web /bin/bash

# Run one-off command
docker-compose run --rm web dotnet ef database update
```

### Nützlich für die Entwicklung (Live-Code-Reloading)

Konfigurationsdateien, Protokolle, Uploads

**Vernetzung**Netzwerke bieten Isolation.

```yaml
services:
  web:
    build: .
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
```

**Hier ist die Datenbank nur für Backend-Dienste zugänglich, nicht direkt exponiert.**Gesundheitskontrollen

```yaml
services:
  web:
    volumes:
      - .:/app  # Live code reloading
    ports:
      - "5000:8080"
```

**Gesundheitskontrollen ermöglichen es Docker,**Bestimmen, ob ein Container tatsächlich bereit ist (nicht gerade gestartet)

```yaml
services:
  web:
    image: registry.example.com/myapp:${VERSION}
    restart: always
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '2'
          memory: 2G
```

```bash
# Development (base + override)
docker-compose up -d

# Production
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
```

## Ungesunde Behälter neu starten

Orchestratoren Status geben (Kubernetes, Docker Swarm)

### Common Docker Compose Commands

```bash
# Install NVIDIA Container Toolkit (Ubuntu/Debian)
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

# Configure Docker daemon
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

# Test GPU access
docker run --rm --gpus all nvidia/cuda:12.6.0-base-ubuntu24.04 nvidia-smi
```

### Entwicklung vs. Produktions-Compose-Dateien

```dockerfile
# Use NVIDIA CUDA base image
FROM nvidia/cuda:12.6.0-cudnn-runtime-ubuntu24.04

# Install Python
RUN apt-get update && apt-get install -y \
    python3.12 \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Install PyTorch with CUDA support
COPY requirements.txt .
RUN pip3 install --no-cache-dir torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124

# Copy application
COPY . .

# Test GPU on container start
RUN python3 -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}'); print(f'GPU: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else \"None\"}')"

ENTRYPOINT ["python3", "train.py"]
```

### Separate Bedenken mit mehreren komponierten Dateien:

```bash
# Run with all GPUs
docker run --gpus all myapp:gpu

# Run with specific GPUs
docker run --gpus '"device=0,2"' myapp:gpu

# Run with GPU memory limits
docker run --gpus all --memory=16g myapp:gpu
```

### Dock-compose.yml

```yaml
services:
  ml-trainer:
    build:
      context: .
      dockerfile: Dockerfile.gpu
    image: myapp:gpu
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all  # or specific count: 1, 2, etc.
              capabilities: [gpu]
    volumes:
      - ./models:/app/models
      - ./data:/app/data
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
      - CUDA_VISIBLE_DEVICES=0,1  # Use GPUs 0 and 1
```

### (Basis):

docker-compose.override.yml[(Entwicklung - automatisch zusammengefügt):](https://github.com/scottgal/mostlylucid-nmt)Dock-compose.prod.yml

(Herstellung):

- **GPU-Unterstützung in Docker: Beschleunigung von ML Workloads**Maschinelles Lernen, wissenschaftliches Rechnen und Videoverarbeitungsanwendungen erfordern oft eine GPU-Beschleunigung.
- **Docker unterstützt NVIDIA GPUs über das NVIDIA Container Toolkit.**Einrichtung des NVIDIA Container Toolkits
- **Dockerfile für GPU-beschleunigte Python/PyTorch-Anwendung**GPU-Container laufen lassen
- **GPU in Docker Compose**Real-World Beispiel: Übersetzungsservice mit GPU und CPU Builds

#### Hier ist ein echtes Produktionsbeispiel aus

```yaml
services:
  translation:
    image: scottgal/mostlylucid-nmt:gpu
    container_name: translation-gpu
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    environment:
      - MODEL_FAMILY=opus-mt
      - FALLBACK_MODELS=mbart50,m2m100
      - CUDA_VISIBLE_DEVICES=0
      - LOG_LEVEL=info
    volumes:
      - model_cache:/app/cache  # Persistent model storage
    ports:
      - "8888:8888"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8888/health"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  model_cache:
```

#### meistlucid-nmt

, ein neuronaler maschineller Übersetzungsdienst, den ich gebaut habe, der Auto-Übersetzung auf diesem Blog ermöglicht.

```yaml
services:
  translation:
    image: scottgal/mostlylucid-nmt:cpu
    container_name: translation-cpu
    environment:
      - MODEL_FAMILY=opus-mt
      - FALLBACK_MODELS=mbart50,m2m100
    volumes:
      - model_cache:/app/cache
    ports:
      - "8888:8888"
    restart: unless-stopped

volumes:
  model_cache:
```

**Das Projekt zeigt:**

- `scottgal/mostlylucid-nmt:gpu`GPU- und CPU-Varianten
- `scottgal/mostlylucid-nmt:cpu`- Gleiche Codebase, verschiedene Basisbilder
- `scottgal/mostlylucid-nmt:gpu-min`Multi-Architektur baut
- `scottgal/mostlylucid-nmt:cpu-min`- Unterstützt AMD64 und ARM64

**Optimierte Docker-Bilder**

- **- Volle und minimale Varianten**Produktionsfertig
- **- Gesundheitskontrollen, Volumen Persistenz, korrekte Protokollierung**GPU-Beschleunigte Übersetzungsdienst
- **CPU-nur Alternative**Für Umgebungen ohne GPUs läuft derselbe Service auf CPU:
- **Verfügbare Bildvarianten:**- CUDA 12.6 mit PyTorch GPU Unterstützung (~5GB)
- **- CPU-nur, kleinerer Platzbedarf (~2,5 GB)**: `/health`- Minimale GPU gebaut, keine vorinstallierten Modelle (~4GB)`/ready`- Minimaler CPU-Bau (~1,5GB)
- **Hauptmerkmale:**GPU-Beschleunigung

10-15x schnellere Übersetzung mit CUDA[Auto-Download des Modells](https://github.com/scottgal/mostlylucid-nmt): Downloads Übersetzungsmodelle on-demand

## Rückfallunterstützung

: Versuche Opus-MT → mBART50 → M2M100 für maximale Sprachabdeckung

### Volumen Persistenz

```bash
# Problem: Image built on M1 Mac won't run on Linux server
docker build -t myapp:latest .  # Builds for ARM64
docker push myapp:latest
# Server tries to run it... error: "exec format error"

# Solution: Build for multiple platforms
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest --push .
```

### : Cache-Modelle über Container starten

Gesundheits-Endpunkte

```bash
# Verify buildx is available
docker buildx version

# Create a new builder instance
docker buildx create --name multiarch --driver docker-container --use

# Inspect and bootstrap the builder
docker buildx inspect --bootstrap

# List available platforms
docker buildx inspect | grep Platforms
```

### und

für Orchestratoren

```dockerfile
# Use official multi-arch base images
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base

# For platform-specific operations, use build arguments
ARG TARGETPLATFORM
ARG BUILDPLATFORM

RUN echo "Building on $BUILDPLATFORM for $TARGETPLATFORM"

# Install architecture-specific dependencies
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
        apt-get update && apt-get install -y some-arm64-package; \
    elif [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
        apt-get update && apt-get install -y some-amd64-package; \
    fi
```

### Multi-Architektur

```bash
# Build and push for AMD64 and ARM64
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t myregistry/myapp:latest \
  -t myregistry/myapp:1.0.0 \
  --push \
  .

# Build without pushing (loads into local Docker)
# Note: Can only load one platform at a time
docker buildx build \
  --platform linux/amd64 \
  -t myapp:latest \
  --load \
  .

# Build and export to tar files
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t myapp:latest \
  -o type=tar,dest=./myapp.tar \
  .
```

### : läuft auf x86_64 und ARM64 (Apple Silicon, Raspberry Pi)

Siehe

**vollständiges Projekt zu GitHub**

```bash
# Build multi-arch images first
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest --push .

# Then use in compose
```

```yaml
services:
  web:
    image: myapp:latest  # Already built for multiple architectures
```

**für Dockerfile-Beispiele, Build-Skripte und API-Dokumentation.**

```bash
#!/bin/bash
# build-multiarch.sh

docker buildx build --platform linux/amd64,linux/arm64 \
  -t myregistry/web:latest \
  -f web/Dockerfile \
  --push \
  web/

docker buildx build --platform linux/amd64,linux/arm64 \
  -t myregistry/worker:latest \
  -f worker/Dockerfile \
  --push \
  worker/

docker-compose pull  # Pull the multi-arch images
docker-compose up -d
```

### Multi-Architektur baut mit Docker Buildx

Moderne Anwendungen müssen auf mehreren Architekturen laufen: x86_64 (AMD64) für Server, ARM64 für Raspberry Pi und Apple Silicon Macs, manchmal sogar ARM32 für eingebettete Geräte.

```yaml
name: Build and Push Multi-Arch Images

on:
  push:
    branches: [ main ]
    tags: [ 'v*' ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: myregistry/myapp
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix={{branch}}-

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=registry,ref=myregistry/myapp:buildcache
          cache-to: type=registry,ref=myregistry/myapp:buildcache,mode=max
```

Warum Multi-Architektur zählt

- Einrichtung von Buildx
- Docker Buildx ist in Docker Desktop enthalten.
- Für Linux:
- Multi-Architecture Dockerfile
- Die meisten Dockerfiles funktionieren ohne Änderungen, aber hier sind einige Tipps:

## Gebäude für mehrere Plattformen

Multi-Architektur mit Docker Compose

### Leider unterstützt Docker Compose nicht direkt Buildx.

Workarounds:

```bash
# Publish as a container image (no Dockerfile needed!)
dotnet publish --os linux --arch x64 -p:PublishProfile=DefaultContainer

# Specify image name and tag
dotnet publish \
  --os linux \
  --arch x64 \
  -p:PublishProfile=DefaultContainer \
  -p:ContainerImageName=myapp \
  -p:ContainerImageTag=1.0.0

# Multi-architecture
dotnet publish --os linux --arch arm64 -p:PublishProfile=DefaultContainer
```

### Option 1: Pre-Build-Bilder

Option 2: Skript erstellen`.csproj`:

```xml
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>

    <!-- Container Configuration -->
    <ContainerImageName>myapp</ContainerImageName>
    <ContainerImageTag>$(Version)</ContainerImageTag>
    <ContainerRegistry>myregistry.azurecr.io</ContainerRegistry>

    <!-- Base image (defaults to mcr.microsoft.com/dotnet/aspnet:9.0) -->
    <ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:9.0-alpine</ContainerBaseImage>

    <!-- Container runtime configuration -->
    <ContainerWorkingDirectory>/app</ContainerWorkingDirectory>
    <ContainerPort>8080</ContainerPort>
    <ContainerEnvironmentVariable Include="ASPNETCORE_ENVIRONMENT">Production</ContainerEnvironmentVariable>

    <!-- User (security best practice) -->
    <ContainerUser>app</ContainerUser>

    <!-- Labels -->
    <ContainerLabel Include="org.opencontainers.image.description">My awesome app</ContainerLabel>
    <ContainerLabel Include="org.opencontainers.image. automatically configures these based on AppHost
builder.AddServiceDefaults();
builder.AddRedisClient("cache");
builder.AddNpgsqlDbContext<MyDbContext>("mydb");

var app = builder.Build();
app.MapDefaultEndpoints();  // Health, metrics, etc.
```

**Beispiel für die reale Welt: CI/CD-Pipeline**

```bash
dotnet run --project MyDistributedApp.AppHost
```

GitHub Actions Workflow für Multi-Architektur-Builds:

- Dieser Workflow:
- Auslöser auf Pushes zu Haupt- oder Versions-Tags
- Setzt QEMU für plattformübergreifende Emulation ein
- Builds für AMD64 und ARM64

### Erzeugt automatisch Tags (Zweigname, semantische Versionen, SHA)

**Verwendet Registrierungs-Caching für schnellere Builds**

- .NET 9 Containerverbesserungen
- .NET 9 führt erhebliche Verbesserungen der Containerunterstützung ein, wodurch es einfacher denn je ist, .NET-Anwendungen zu containerisieren, ohne auch nur ein Dockerfile zu schreiben.
- Eingebautes Container Publishing
- Mit .NET 9 können Sie eine containerisierte Anwendung direkt veröffentlichen:

**Container-Eigenschaften konfigurieren**

- Fügen Sie zu Ihrem
- 4. Der Präsident. — Das Wort hat die Fraktion der Europäischen Volkspartei (Christlich-Demokratische Fraktion).
- Alles laufen lassen:
- Aspire startet:
- Dashboard unter http://localhost:15888

**Alle Dienste mit korrekter Konfiguration**Redis und PostgreSQL in Containern

### Verteilte Rückverfolgung über Dienstleistungen hinweg

Aspire vs Docker Compose

```bash
# Generate Docker Compose
dotnet run --project MyDistributedApp.AppHost -- \
  --publisher compose \
  --output-path ../deploy

# Generate Kubernetes manifests
dotnet run --project MyDistributedApp.AppHost -- \
  --publisher manifest \
  --output-path ../deploy/k8s
```

Docker-Komposition:

```bash
# Docker Compose
docker-compose -f deploy/docker-compose.yml up -d

# Kubernetes
kubectl apply -f deploy/k8s/
```

### Sprache-agnostisch

Infrastrukturorientiert

```csharp
// Add various backing services
var redis = builder.AddRedis("cache");
var postgres = builder.AddPostgres("db").AddDatabase("mydb");
var rabbitmq = builder.AddRabbitMQ("messaging");
var mongodb = builder.AddMongoDB("mongo").AddDatabase("docs");
var sql = builder.AddSqlServer("sql").AddDatabase("business");

// Add Azure services
var storage = builder.AddAzureStorage("storage");
var cosmos = builder.AddAzureCosmosDB("cosmos");
var servicebus = builder.AddAzureServiceBus("messaging");

// Use in services
builder.AddProject<Projects.MyService>("service")
       .WithReference(redis)
       .WithReference(postgres)
       .WithReference(rabbitmq);
```

## Entdeckung manueller Dienste

Bringen Sie Ihre eigene Beobachtbarkeit

### Ehrgeiz:

.NET-spezifisch[Entwicklungsschwerpunkte](/dockercomposedevdeps)Automatische Service-Erkennung

```yaml
services:
  smtp4dev:
    image: rnwood/smtp4dev
    ports:
      - "3002:80"
      - "2525:25"
    volumes:
      - e:/smtp4dev-data:/smtp4dev
    restart: always

  postgres:
    image: postgres:16-alpine
    container_name: postgres
    ports:
      - "5432:5432"
    env_file:
      - .env
    volumes:
      - e:/data:/var/lib/postgresql/data
    restart: always
```

**Eingebaute Telemetrie**

- **Erzeugt Bereitstellungsmanifeste**Beides verwenden
- **: Streben nach lokaler Entwicklung, erzeugt Docker Compose/Kubernetes für die Produktion.**Bereitstellung von Aspire-Apps
- **Bereitstellungsmanifeste generieren:**Dann stellen Sie Folgendes bereit:

Anstrebende Komponenten[Vorgefertigte Integrationen machen das Hinzufügen von Diensten trivial:](/dockercomposedevdeps)Selbst-Hosting auf begrenzten Ressourcen: Praktische Optimierung

### Ein vollständiger Beobachtbarkeits-Stack wie der oben genannte erfordert erhebliche Ressourcen.

Wenn Sie selbst auf einem VPS mit 4GB RAM oder einem alten Laptop hosten, hier sind praktische Strategien, um den Ressourcenverbrauch zu reduzieren und gleichzeitig die Funktionalität zu erhalten.

```yaml
services:
  # Core application
  mostlylucid:
    image: scottgal/mostlylucid:latest
    restart: always
    env_file: .env
    volumes:
      - ./markdown:/app/markdown
      - ./logs:/app/logs
    networks:
      - app_network
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
        reservations:
          memory: 256M

  # Database only
  db:
    image: postgres:16-alpine
    env_file: .env
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - app_network
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 30s
      timeout: 5s
      retries: 3

  # Caddy for HTTPS
  caddy:
    image: caddy:latest
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
    networks:
      - app_network
    deploy:
      resources:
        limits:
          memory: 128M

volumes:
  db_data:
  caddy_data:

networks:
  app_network:
```

**Nur Entwicklungsabhängigkeiten**

- **Für die lokale Entwicklung benötigen Sie nicht den kompletten Produktionsstapel.**Das
- **devdeps-docker-compose.yml**Der Ansatz läuft nur, was Sie brauchen:`docker logs`Warum dies für die Entwicklung funktioniert:
- **SMTP4Dev**: Testen Sie E-Mail-Funktionalität ohne einen echten SMTP-Server
- **PostgreSQL**: Passt zur Produktionsdatenbank
- **Gesamter Fußabdruck**: ~200MB RAM vs 2-4GB für vollen Stack

### Siehe

Leitfaden für volle Entwicklungsabhängigkeiten

**für Einrichtungsanweisungen.**

```bash
# Just app + database + reverse proxy
docker-compose up -d mostlylucid db caddy
```

**Resource-Constrained Production Setup**

```bash
# Add lightweight monitoring
docker-compose up -d mostlylucid db caddy seq
# Use Seq free 10GB/month
```

**Für ein Budget VPS (2-4GB RAM), priorisieren wesentliche Dienstleistungen:**

```bash
# Add everything
docker-compose up -d
```

### Was entfernt wird und Alternativen:

#### Kein Prometheus/Grafana

```yaml
services:
  myapp:
    image: postgres:16-alpine     # 50% smaller than postgres:16
    # vs
    image: postgres:16            # Full Debian base
```

**: Externe Überwachung verwenden (UptimeRobot, BetterStack freie Ebene)**Kein Seq

#### : Dateibasiertes Protokollieren + verwenden

, oder Seq Cloud freie Ebene

```yaml
db:
  image: postgres:16-alpine
  # One instance, multiple databases
  # Umami, Mostlylucid, etc. all share this PostgreSQL
```

**Kein Wachtturm**: Manuelle Aktualisierungen mit GitHub Aktionen Benachrichtigungen

#### Kein Übersetzungsdienst

```yaml
easynmt:
  deploy:
    resources:
      limits:
        cpus: "2.0"      # Don't let translation consume all CPU
      reservations:
        cpus: "0.5"      # Guarantee minimum
```

: On-Demand in einem separaten Container ausführen, den Sie manuell starten/stoppen

#### Ressourcengrenzen

: Verhindern, dass jeder einzelne Dienst alle Speicher verbraucht[Fortschrittliche Verbesserungsstrategie](/imagesharpwithdocker)Starten Sie minimal, fügen Sie Dienste nach Bedarf hinzu:

```yaml
mostlylucid:
  volumes:
    - /mnt/imagecache:/app/wwwroot/cache  # ImageSharp cache persists across restarts
```

**Stufe 1: Kern (512MB-1GB VPS)**Stufe 2: Beobachtbarkeit hinzufügen (2 GB VPS)

#### Stufe 3: Voller Stack (4GB+ VPS)

Ressourcenoptimierungstechniken
|---------|-----------------|---------------------|
1.
Alpenbilder verwenden
Einsparungen

**: Alpenbilder sind 50-70% kleiner**2. Der Präsident. — Das Wort hat die Fraktion der Europäischen Volkspartei (Christlich-Demokratische Fraktion).

#### Gemeinsame Datenbank-Instanz

Verwenden Sie statt einer Datenbank pro Dienst eine PostgreSQL-Instanz mit mehreren Datenbanken:

```bash
# Create a separate compose file
# translation-compose.yml
services:
  easynmt:
    image: easynmt/api:2.0.2-cpu
    ports:
      - "8888:8888"
    volumes:
      - /mnt/easynmt:/cache/

# Only run when needed
docker-compose -f translation-compose.yml up -d

# Translate your content
# ...

# Shut down when done
docker-compose -f translation-compose.yml down
```

**Einsparungen**: 400MB RAM pro zusätzliche Datenbank konsolidieren Sie

### 3. Der Präsident. — Das Wort hat die Fraktion der Europäischen Volkspartei (Christlich-Demokratische Fraktion).

CPU für Hintergrunddienste begrenzen

```yaml
# Minimal production compose
services:
  mostlylucid:
    image: scottgal/mostlylucid:latest
    restart: always
    env_file: .env
    volumes:
      - /mnt/markdown:/app/markdown
      - /mnt/logs:/app/logs
      - /mnt/imagecache:/app/wwwroot/cache
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    env_file: .env
    volumes:
      - /mnt/postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 30s

  cloudflared:
    image: cloudflare/cloudflared:latest
    command: tunnel run --token ${CLOUDFLARED_TOKEN}
    restart: always

  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      WATCHTOWER_CLEANUP: "true"
      WATCHTOWER_LABEL_ENABLE: "true"
    command: --interval 3600  # Check once per hour, not every 5 minutes
```

**Dies verhindert, dass Hintergrundjobs Ihre Web-Anwendung verhungern.**

- 4. Der Präsident. — Das Wort hat die Fraktion der Europäischen Volkspartei (Christlich-Demokratische Fraktion).
- Dauerhafte Cache-Volumes
- Wie in

**ImageSharp mit Docker**

- , Installation von Cache-Verzeichnissen verhindert unnötige Wiederaufarbeitung:
- Warum es wichtig ist`docker logs`: Ohne diesen Neustart regeneriert jeder Container alle Thumbnails/processed Images.
- 5. Verordnung (EWG) Nr. 1408/71 des Rates vom 14. Juni 1971 zur Anwendung der Systeme der sozialen Sicherheit auf Arbeitnehmer und Selbständige (ABl.
- Externe Dienste nutzen (Kostenlose Tiere)
- Dienstleistungen, die sich selbst gehostet haben, externe Alternative

### • Seq ~500MB • Seq Cloud (10 GB/Monat kostenlos) • Seq Cloud (10 GB/Monat kostenlos) • Seq Cloud (10 GB/Monat kostenlos) • Seq Cloud (10 GB/Monat kostenlos) • Seq Cloud (10 GB/Monat kostenlos) • Seq Cloud (10 GB/Monat kostenlos) • Seq Cloud (10 GB/Monat kostenlos) • Seq Cloud (10 GB/Monat kostenlos) • Seq Cloud (10 GB/Monat kostenlos)

• Prometheus + Grafana ~600MB • Grafana Cloud (freie Ebene)

**~200MB · Plausible (paid) oder Selbstbewirtung an anderer Stelle**

```bash
# Simple health check script
#!/bin/bash
while true; do
  curl -f http://localhost/healthz || echo "Health check failed!" | mail -s "Alert" you@example.com
  sleep 300
done
```

**Strategie**

```bash
# Watch for errors
docker-compose logs -f --tail=100 | grep -i error

# Email on critical errors
docker-compose logs -f | grep -i "critical" | while read line; do
  echo "$line" | mail -s "Critical Error" you@example.com
done
```

**: Offload-Beobachtbarkeit zu freien Ebenen, halten Sie die Kernanwendung auf Ihrem VPS.**

```bash
# Quick resource check
docker stats --no-stream

# Pretty output
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
```

## 6. Der Präsident. — Das Wort hat die Fraktion der Europäischen Volkspartei (Christlich-Demokratische Fraktion).

On-Demand-Dienste

### Für selten genutzte Dienstleistungen wie Übersetzung:

Einsparungen

```bash
# Add Aspire to your solution
dotnet new aspire-apphost -n Mostlylucid.AppHost
cd Mostlylucid.AppHost
```

**: 1-2 GB RAM, wenn der Übersetzungsdienst nicht läuft**

```csharp
var builder = DistributedApplication.CreateBuilder(args);

// PostgreSQL with persistent data
var postgres = builder.AddPostgres("postgres")
    .WithDataVolume()  // Persistent storage
    .WithPgAdmin();     // Optional: PgAdmin for database management

var mostlylucidDb = postgres.AddDatabase("mostlylucid");
var umamiDb = postgres.AddDatabase("umami");

// Seq for centralized logging
var seq = builder.AddSeq("seq")
    .WithDataVolume();

// Redis for caching (if needed)
var redis = builder.AddRedis("cache")
    .WithDataVolume()
    .WithRedisCommander();  // Optional: Redis Commander UI

// Main blog application
var mostlylucid = builder.AddProject<Projects.Mostlylucid>("web")
    .WithReference(mostlylucidDb)
    .WithReference(seq)
    .WithReference(redis)
    .WithEnvironment("TranslateService__Enabled", "false")  // Disable for dev
    .WithHttpsEndpoint(port: 7240, name: "https");

// Umami analytics
var umami = builder.AddContainer("umami", "ghcr.io/umami-software/umami", "postgresql-latest")
    .WithReference(umamiDb)
    .WithEnvironment("DATABASE_TYPE", "postgresql")
    .WithEnvironment("TRACKER_SCRIPT_NAME", "getinfo")
    .WithEnvironment("API_COLLECT_ENDPOINT", "all")
    .WithHttpEndpoint(port: 3000, name: "http");

// Translation service (CPU version, with resource limits)
var translation = builder.AddContainer("easynmt", "easynmt/api", "2.0.2-cpu")
    .WithDataVolume("/cache")
    .WithHttpEndpoint(port: 8888, name: "http")
    .WithEnvironment("MODEL_FAMILY", "opus-mt");

// Scheduler service (Hangfire background jobs)
var scheduler = builder.AddProject<Projects.Mostlylucid_SchedulerService>("scheduler")
    .WithReference(mostlylucidDb)
    .WithReference(seq);

// Prometheus for metrics
var prometheus = builder.AddContainer("prometheus", "prom/prometheus", "latest")
    .WithDataVolume()
    .WithBindMount("./prometheus.yml", "/etc/prometheus/prometheus.yml")
    .WithHttpEndpoint(port: 9090);

// Grafana for visualization
var grafana = builder.AddContainer("grafana", "grafana/grafana", "latest")
    .WithDataVolume()
    .WithHttpEndpoint(port: 3001)
    .WithEnvironment("GF_SECURITY_ADMIN_PASSWORD", builder.Configuration["Grafana:AdminPassword"] ?? "admin");

builder.Build().Run();
```

### Beispiel für die Real-World-Budget-Hosting

Hier ist, was läuft auf einem $6/Monat Hetzner VPS (2 vCPU, 4GB RAM):**Ressourcennutzung insgesamt:**RAM: ~800MB (lässt 3,2 GB frei)

```csharp
// Extensions.cs
public static class Extensions
{
    public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
    {
        // OpenTelemetry
        builder.Services.AddOpenTelemetry()
            .WithMetrics(metrics =>
            {
                metrics.AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation()
                    .AddRuntimeInstrumentation();
            })
            .WithTracing(tracing =>
            {
                if (builder.Environment.IsDevelopment())
                {
                    tracing.SetSampler(new AlwaysOnSampler());
                }

                tracing.AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation()
                    .AddEntityFrameworkCoreInstrumentation();
            });

        // Health checks
        builder.Services.AddHealthChecks()
            .AddCheck("self", () => HealthCheckResult.Healthy(), tags: new[] { "live" });

        return builder;
    }

    public static IApplicationBuilder MapDefaultEndpoints(this WebApplication app)
    {
        app.MapHealthChecks("/healthz");
        app.MapHealthChecks("/ready", new HealthCheckOptions
        {
            Predicate = check => check.Tags.Contains("ready")
        });

        return app;
    }
}
```

### Festplatte: ~2GB

```csharp
var builder = WebApplication.CreateBuilder(args);

// Add Aspire service defaults (telemetry, health checks)
builder.AddServiceDefaults();

// Add services
builder.AddNpgsqlDbContext<MostlylucidDbContext>("mostlylucid");
builder.AddRedisClient("cache");

// Existing service registrations...
// builder.Services.AddControllersWithViews();
// etc...

var app = builder.Build();

// Map Aspire default endpoints
app.MapDefaultEndpoints();

// Existing middleware...
app.Run();
```

### CPU: <10% Leerlauf, <50% unter Last

**Was ist anders:**

1. **Keine Prometheus/Grafana (Verwendung von UptimeRobot + Cloudflare Analytics)**: `dotnet run --project Mostlylucid.AppHost`Kein Seq (Verwendung)
2. **+ gelegentlicher Schreck)**No Umami (verwenden Sie Cloudflare Web Analytics - kostenlos)
   - Wachturm überprüft stündlich statt alle 5 Minuten
   - Kein Übersetzungsdienst (bei Bedarf manuell ausführen)
   - Überwachung eines Haushalts
   - Verwenden Sie ohne Prometheus/Grafana diese kostenlosen Alternativen:
3. **Gesundheitsüberwachung:**Protokollüberwachung:
4. **Ressourcennutzung:**Aspire Version: Der .NET Weg`.env`Jetzt lassen Sie uns den gesamten Stapel mit .NET Aspire neu abbilden.

**Dies gibt Ihnen alle Vorteile der Orchestrierung mit besserer .NET-Integration und einer erstaunlichen Entwicklererfahrung.**

Anstreben für Mostlylucid

```bash
# Generate Docker Compose
dotnet run --project Mostlylucid.AppHost -- \
  --publisher compose \
  --output-path ./deploy

# This creates a production-ready docker-compose.yml
cd deploy
docker-compose up -d
```

**Erstellen Sie zunächst den Aspire App Host:**Mostlylucid.AppHost/Program.cs:

```yaml
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql/data

  mostlylucid-db:
    image: postgres:16
    # Database initialization

  seq:
    image: datalust/seq:latest
    environment:
      ACCEPT_EULA: Y
    volumes:
      - seq-data:/data

  web:
    image: scottgal/mostlylucid:latest
    environment:
      ConnectionStrings__mostlylucid: Host=postgres;Database=mostlylucid;Username=postgres;Password=${POSTGRES_PASSWORD}
      ConnectionStrings__cache: cache:6379
    depends_on:
      - postgres
      - cache
      - seq

  cache:
    image: redis:7-alpine
    volumes:
      - redis-data:/data

  # ... other services
```

### Anstrebende Service-Standards

Erstellen
|---------|---------------|-------------|
| **Mostlylucid.ServiceDefaults**Projekt:
| **Meistlylucid/Program.cs aktualisieren**Vorteile des Aspire-Ansatzes
| **Entwicklungserfahrung:**Einzelbefehl
| **beginnt alles** | `docker-compose up` | `dotnet run`Dashboard
| **: Schöne Benutzeroberfläche auf http://localhost:15888 zeigt:**Alle Dienstleistungen mit Live-Status
| **Logs von allen Diensten an einem Ort** | `docker logs`Verteilte Spuren über Dienstleistungen hinweg
| **Metrik- und Gesundheitskontrollen**Service Discovery
| **: Dienste finden sich automatisch über Namen**Einrichtung
| **: Zentralisiert in AppHost, nicht mehr**Jonglieren

### Produktionseinsatz:

**Bereitstellungsmanifeste von Aspire generieren:**

- Generierter docker-compose.yml
- (vereinfacht):
- Aspire vs. Traditional Docker Compose
- .NET Aspire .NET Aspire .NET Aspire
- Einrichtung

**Manueller YAML-C#-Code mit IntelliSense**

- Service Discovery
- Manual env vars-Automatic env vars-Automatic env
- Beobachtungsfähigkeit
- Bringen Sie Ihre eigenen Eingebauten (OpenTelemetrie)
- Entwicklung

**+ Dashboard**

- Debugging
- Befestigen Sie mit dem Container. F5 in Visual Studio.
- Protokolle

### Zentrales Dashboard

Aufspüren

1. **[Automatisches verteiltes Tracing](/dockercompose)**Erzeugung
2. **[YAML direkt verwenden Manifeste generieren](/dockercomposedevdeps)**Curve lernen
3. **[YAML-Syntax, C#, die du schon kennst](/imagesharpwithdocker)**Wann wird Aspire vs Docker Compose verwendet?
4. **[Aspire verwenden, wenn:](/dockercompose)**Bau von .NET-Mikrodiensten
5. **Sie wollen integriertes Debugging**Team ist bequem mit C#

**Sie brauchen verteilte Rückverfolgung aus dem Kasten heraus**

- Sie setzen auf Azure Container Apps ein (native Unterstützung)
- Docker Compose verwenden, wenn:
- Polyglottedienste (Node.js, Python, Go, etc.)
- Team bevorzugt Infrastruktur-as-Code YAML
- Einsatz auf jedem Docker-kompatiblen Host
- Sie benötigen maximale Kontrolle über die Containerkonfiguration

## Einfache Ein-Service-Bereitstellungen

### Beides verwenden:

1. Streben nach lokaler Entwicklung
2. Docker-Compose für den Produktionseinsatz generieren
3. Das Beste aus beiden Welten!
4. Evolution des Docker Setups dieses Blogs`latest`)
5. Die Docker-Reise dieses Blogs zeigt typische Fortschritte:
6. Juli 2024 - Einfacher Start`.dockerignore`: Nur die App, Cloudflared und der Wachtturm
7. August 2024 - Dev-Abhängigkeiten
8. : Zusätzliche entwicklungsbezogene Dienstleistungen

### August 2024 - ImageSharp Fix

1. : Löste Volumenberechtigungen für das Caching
2. November 2024 - Voller Stapel
3. : Vollständige Beobachtbarkeit mit Prometheus, Grafana, Seq`.env`Heute
4. : Aspire-Option für .NET-erste Entwicklung
5. Gelernte Erkenntnisse:`depends_on`Einfach starten, Komplexität nur bei Bedarf hinzufügen
6. Separate Dev- und Produktionskonfigurationen
7. Ressourcengrenzen verhindern, dass ein Dienst andere tötet
8. Volume-Mounts für Caches sparen erhebliche Wiederaufarbeitungszeit

### Watchtower ermöglicht Null-Downtime Auto-Updates

1. Beobachtbarkeit ist die Ressourcenkosten in der Produktion wert
2. Zusammenfassung der bewährten Verfahren
3. Dockerfile Best Practices
4. Mehrstufige Builds verwenden
5. Als nicht-root Benutzer ausführen
6. Bestellanleitung für optimales Caching
7. Verwenden Sie bestimmte Basisbild-Tags (nicht
8. Einschließlich Gesundheitskontrollen

### Verwendung

1. unnötige Dateien auszuschließen
2. Ebenen minimieren (Kommbine-RUN-Befehle)
3. Verwenden Sie Build-Argumente für Flexibilität
4. Docker Komponieren Sie Best Practices`.dockerignore`Für Daten benannte Volumes verwenden
5. Durchführung von Gesundheitskontrollen
6. Verwendung
7. für Geheimnisse (nie zu begehen)
8. Definieren Sie explizite Netzwerke

## Verwendung

### mit gesundheitlichen Bedingungen

```bash
# Check logs
docker logs container-name

# Common issues:
# 1. Port already in use
docker ps | grep 8080  # Find conflicting container
docker stop conflicting-container

# 2. Missing environment variables
docker inspect container-name | grep Env

# 3. Failed health check
docker inspect container-name | grep Health -A 20
```

### Richtlinien für den Neustart festlegen

```bash
# Enable BuildKit for faster builds
export DOCKER_BUILDKIT=1

# Use build cache
docker build --cache-from myapp:latest -t myapp:latest .

# Check what's taking time
docker build --progress=plain -t myapp:latest .
```

### Getrennte Dev/Prod-Konfigurationen

```bash
# Containers can't communicate
# Solution: Ensure they're on the same network
docker network ls
docker network inspect network-name

# DNS not working
# Container names are DNS names within Docker networks
docker exec web ping db  # Should work if both on same network
```

### Ressourcengrenzen in der Produktion

```bash
# Permission denied on volume
# Solution: Match user IDs
FROM ubuntu
RUN useradd -u 1000 appuser  # Match host user ID
USER appuser
```

## Bewährte Sicherheitspraktiken

Als nicht-root Benutzer ausführen

### Meißel-/Minimal-Basisbilder verwenden

**Bilder nach Schwachstellen scannen**

- Basisbilder auf dem neuesten Stand halten[Keine Geheimnisse in Bildern enthalten](/dockercompose)Wenn möglich schreibgeschützte Dateisysteme verwenden
- Container-Fähigkeiten begrenzen[Verwenden Sie Docker Geheimnisse oder externe geheime Manager](/dockercomposedevdeps)Best Practices im Leistungsvergleich
- BuildKit für schnellere Builds verwenden[Ausnutzungsschicht-Caching](/imagesharpwithdocker)Mehrstufige Aufbauten zur Reduzierung der Bildgröße

**Verwendung**

- großzügig
- Alpine/chiselierte Bilder für kleinere Größe
- Volumenhalter für die Entwicklung verwenden
- Angemessene Ressourcengrenzen einrichten
- Nutzen Sie Gesundheitskontrollen für die Orchestrierung

**Fehlerbehebung bei gemeinsamen Problemen**

- Container wird nicht gestartet
- Langsames Bauen
- Vernetzungsfragen
- Volume Permission Issues
- Schlußfolgerung
- Docker hat sich von einem einfachen Containerisierungstool zu einer umfassenden Plattform für den Bau, den Versand und den Betrieb moderner Anwendungen entwickelt.

**Dieser Leitfaden hat Sie von Grundlagen zu produktionsfähigen Bereitstellungen gebracht, mit realen Beispielen von running mostlylucid.com.**

- Schlüssel zum Mitnehmen
- Für Anfänger:
- Beginnen Sie mit der
- Grundlegendes Docker Compose-Setup
- - nur 3 Dienstleistungen

**Verwendung**

- entwicklungsbezogene Abhängigkeiten
- für die örtliche Arbeit
- Löse gemeinsame Probleme wie
- Volumenberechtigungen

### Anfang

Für Self-Hoster auf Budget VPS:

Start minimal: App + Datenbank + Reverse Proxy (~800MB RAM)
|-------|----------|-----------|------------|
| **Alpenbilder und Ressourcengrenzen nutzen**Offload-Beobachtbarkeit auf freie Ebenen (Seq Cloud, Grafana Cloud, UptimeRobot)
| **Führen Sie teure Dienstleistungen (Übersetzung, ML) nur on-demand**Ein $6/Monat VPS kann ein Produktions-Blog mit Platz zu ersparen
| **Für Produktionsanlagen:**Gesundheitschecks ermöglichen Null-Downtime-Updates mit dem Wachtturm
| **Getrennte Netze bieten Sicherheit (Frontend/Backend-Isolierung)**Bind mounts for data you need to backup/access
| **Benannte Volumes für Docker-managed Storage**CPU/Memory-Grenzwerte verhindern Ressourcenverhungerung

**Cloudflare Tunnel eliminiert den Bedarf an öffentlichen IP-Adressen**Für .NET Entwickler:

### Eingebaute Container-Veröffentlichung in .NET 9 eliminiert Dockerfiles für einfache Apps

Chiseled Ubuntu Bilder bieten minimale Angriffsfläche

- **.NET Aspire bietet erstklassige lokale Entwicklungserfahrung**Aspire kann Docker Compose für den Produktionseinsatz generieren[OpenTelemetrie Integration ist kostenlos mit Aspire](/dockercompose)
- **Für fortgeschrittene Anwendungsfälle:**GPU-Container ermöglichen ML/AI-Workloads mit 10-15x Speedup
- **Multi-Architektur baut Unterstützung ARM64 und AMD64 aus einer Hand**GitHub Aktionen automatisiert Multi-Plattform-Builds
- **Layer Caching beschleunigt dramatisch wiederholte Builds**Die Reise[Die Docker-Evolution dieses Blogs spiegelt die typische Weiterentwicklung wider:](https://github.com/scottgal/mostlylucid-nmt)
- **Dienstleistungen RAM-Nutzung Komplexität**Stufe 1

### (Juli 2024) > App + Tunnel > ~300MB > Einfach

**Stufe 2**

- [(Aug 2024) + Entwicklung von Abhängigkeiten ~500MB](https://docs.docker.com/)
- [Stufe 3](https://docs.docker.com/compose/compose-file/)
- [(Aug 2024) + Volumenbehebungen ~500MB. Debugging](https://github.com/dotnet/dotnet-docker)
- [Stufe 4](https://learn.microsoft.com/en-us/dotnet/aspire/)
- [(Nov 2024) Vollständige Beobachtbarkeit](https://github.com/NVIDIA/nvidia-container-toolkit)
- [Stufe 5](https://github.com/docker/buildx)

**(Heute) + Aspire-Option**

1. [Das Muster](/dockercompose): Einfach starten, Probleme lösen, wie sie entstehen, nur bei Bedarf Komplexität hinzufügen.
2. [Was kommt als Nächstes?](/dockercomposedevdeps)Abhängig von Ihrem Weg:
3. [Docker lernen](/imagesharpwithdocker): Beginnen Sie mit
4. Docker Compose Grundlagen

**Selbst-Hosting**

- [: Probieren Sie das minimale VPS-Setup aus diesem Artikel](https://github.com/scottgal/mostlylucid-nmt)Bau von Mikrodiensten
- [: Entdecken Sie .NET Aspire](https://github.com/scottgal/mostlylucidweb)Ausführen von ML Workloads

: Schauen Sie sich die

Happy containerizing! 🐳