Back to "Docker Ontwikkeling Deep Dive: Van Basics naar Geavanceerde .NET Containerisatie"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

.NET Containers DevOps Docker

Docker Ontwikkeling Deep Dive: Van Basics naar Geavanceerde .NET Containerisatie

Sunday, 09 November 2025

Inleiding

Docker heeft fundamenteel veranderd hoe we bouwen, schip, en run toepassingen.

Wat begon als een eenvoudige containerization tool is geëvolueerd tot een compleet ecosysteem voor moderne toepassing ontwikkeling en implementatie.

  1. **Dit is het vierde en meest uitgebreide artikel in mijn Docker serie voor .NET ontwikkelaars.**Als je nieuw bent bij Docker, kun je beginnen met:
  2. Docker-composeComment- Aan de slag met basis multi-container opstellingen (juli 2024)
  3. Ontwikkeling afhankelijkheden met Docker Compose- Het opzetten van lokale dev-omgevingen (augustus 2024)

ImageSharp met Docker

  • **- Het oplossen van problemen met volumetoestemming (augustus 2024)**In deze diepe duik bouwen we op die funderingen om te verkennen:
  • Production-ready Docker Compose: De werkelijke stack met deze blog
  • Zelfhosting op beperkte middelen: Praktische optimalisatietechnieken voor budget VPS-implementaties
  • GPU-ondersteuning: Het uitvoeren van ML workloads in containers
  • Multi-architectuur bouwt: Ondersteuning van ARM64 en AMD64
  • .NET 9 container kenmerken: Ingebouwde container publiceren zonder Dockerfiles

.NET Aspire

: De moderne .NET benadering van container orkestratie

OPMERKING: Dit is onderdeel van mijn experimenten met AI / een manier om $1000 Claude Code Web credits te besteden.

Ik heb dit een BUNCH van papieren gevoerd, mijn begrip, vragen die ik moest genereren van dit artikel.

Het is leuk en vult een gat dat ik nergens anders heb gezien.

Of u nu een eenvoudige webapp implementeert of een complexe microservice architectuur orkestreert met GPU-versnelde machine learning modellen, deze gids brengt u van Docker basics naar productie-ready containerized toepassingen - met echte voorbeelden van het uitvoeren van veelallucid.com.

  • Docker Fundamentals: Containers begrijpenWat is Docker, echt?
  • **In de kern, Docker is een containerisatie platform dat pakketten uw toepassing en al haar afhankelijkheden in een gestandaardiseerde eenheid genaamd een container.**In tegenstelling tot virtuele machines die complete besturingssystemen virtualiseren, delen containers de host OS kernel met behoud van geïsoleerde gebruikersruimtes.

Bekijk het zo:

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

# The container solution
"Ship your machine!" 

Virtuele machines

  1. : Elke VM draait een volledige OS stack (Linux kernel, systeembibliotheken, enz.) - zwaar en traag om te startenContainers
  2. : Share the host kernel, package only application code and dependencies - lichtgewicht en snelWaarom Containers Matter voor Ontwikkelaars
  3. **Containers lossen verschillende kritieke problemen op:**Samenhang van het milieu
  4. : Ontwikkeling, testen en productieomgevingen zijn identiekAfhankelijkheid Isolatie
  5. : Geen "DLL hel" of tegenstrijdige bibliotheekversies meerBijbelbouwstenen

: Dezelfde invoer = dezelfde uitvoer, elke keer

Snelle inzet

# 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

: Start containers in seconden, geen minutenEfficiënt gebruik van hulpbronnen

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

: Uitvoeren van tientallen containers op een enkele host

  • Essentiële Docker-conceptenAfbeeldingen vs. containers
  • Afbeeldingenzijn onveranderlijke, gelaagde bestandssystemen.
  • **Elke instructie in een Dockerfile creëert een nieuwe laag:**Deze gelaagdheid is krachtig:

Caching

: Ongewijzigde lagen worden hergebruikt, versnellen bouwDelen

: Meerdere afbeeldingen kunnen basislagen delen

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

Efficiëntie

# 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

: Alleen veranderde lagen hoeven te worden gedownload/geüpload

  1. **Begrijpen Dockerfile Uitvoering: Niet uw machine!**Een gemeenschappelijke bron van verwarring voor Docker beginners:COPYCommando's in een Dockerfile draaien niet op je machine - ze draaien in het OS van de bouwcontainer.ADDDit is wat er echt gebeurt:
  2. **Waarom dit belangrijk is:**Belangrijkste inzichten:RUNLokaal bestandssysteem
  3. : Uwen
  4. commando's gelezen vanaf uw machineAfbeelding bouwen

: Uwapt-getcommando's uitvoeren in het OS van de container (niet uw machine)

Uitvoerafbeelding

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

: De uiteindelijke afbeelding bevat alle lagen die tijdens build zijn aangemaakt

Kruisplatform

: U kunt op Windows, het bouwen van een Linux image, met behulp van Linux commando's

# 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"]

Dit is waarom je kunt schrijven

commando's in een Dockerbestand op Windows - ze worden niet uitgevoerd op Windows!

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

Ze draaien in de Linux-based build container.

  1. **Voorbeeld verwarringsscenario:**De Docker daemon verwerkt de vertaling tussen uw lokale bestandssysteem en de containerized build omgeving.
  2. Dockerfile Best PracticesHier is een productie-ready .NET Dockerfile met commentaar:.csprojBegrijpen van de bouwstroom
  3. **Dit is wat er gebeurt tijdens een multi-stage Docker build - waar bestanden vandaan komen en waar ze eindigen:**Belangrijkste inzichten uit deze stroom:npm run buildSDK-afbeelding afgewezen
  4. : De 1.5GB SDK container wordt weggegooid na het publiceren - alleen ~50MB van gecompileerde output beweegt vooruit: appsettings.jsonLaagcachingwwwroot/: Kopiëren
  5. voordat broncode betekent dat afhankelijkheidsherstel wordt gecached tenzij afhankelijkheden veranderenFrontend-activa
  6. : Gebouwd afzonderlijk (vaak via) en gekopieerd naar de uiteindelijke afbeelding

Configuratiebestanden

  1. eninhoud gekopieerd van uw machine, niet gebouwd
  2. Laatste afbeelding: Start vanaf minimale runtime image (~220MB) + uw app (~30MB) + activa (~5MB) = ~255MB totaal
  3. Alleen productiebestanden: Broncode, obj/, bin/, node_modules/haal nooit de definitieve afbeelding
  4. **Hieronder worden de belangrijkste beginselen toegelicht:**Meertrapsbouw
  5. : Aparte bouw/publish stadia verminderen de uiteindelijke grootte van de afbeelding dramatischLaagoptimalisatie

: Kopieer afhankelijkheidsbestanden voor broncode voor een betere caching

# 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

Veiligheid

: Uitvoeren als niet-root gebruiker

Gezondheidscontroles

: Container orkestratoren kunnen applicatie gezondheid controleren

  • Expliciete omgeving
  • : Stel de productiestandaarden in
  • Bouwen en lopen
  • Docker samenstelling: Multi-Container Orchestration
  • Met Docker Compose kunt u multi-container toepassingen definiëren en uitvoeren.

In plaats van het beheren van containers individueel, beschrijf je je hele toepassing stack in een YAML-bestand.docker runWaarom Docker Compose?

Beschouw een typische .NET web applicatie:

ASP.NET Core webapp**PostgreSQL-databasedocker-compose.yml**Redis cache

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 voor loggen

  1. Misschien een achtergrond werker dienstDeze individueel beheren metapp_networkCommando's worden onhandig.
  2. **Docker Compose lost dit op.**Complete Real-World Voorbeeld: Productie Blog Platform/mnt/*Hier is de
  3. werkelijke productiedie deze site runt (meestal lucid.com):
  4. **Belangrijkste productiepatronen:**Eén netwerk
  5. : Alle diensten opvoor eenvoud
  6. Bindbeugels: Hostpaden (
  7. ) voor persistente gegevens die u nodig hebt om te back-uppen/toegangGenoemde volumes.env: Docker-beheerde opslag voor gegevens die u niet nodig hebt directe toegang tot

Labels van Watchtower

: Update diensten die expliciet zijn gemarkeerd voor auto-update

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

Middelenlimietencondition: service_healthy: CPU beperkingen op vertaaldienst voorkomen verhongering van hulpbronnen

Mapping van externe poorten

# .env file (never commit to git!)
DB_PASSWORD=super_secret_password
SMTP_PASSWORD=another_secret
services:
  web:
    environment:
      - DB_PASSWORD=${DB_PASSWORD}  # From .env file
      - STATIC_VALUE=production     # Hardcoded
    env_file:
      - .env                        # Load entire file

: PostgreSQL op 5266 in plaats van 5432 om conflicten met andere instanties te voorkomen

Omgevingsbestanden

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

: Geheimen in:

  • bestand (nooit toegezegd aan git)
  • De belangrijkste functies van de docker samenstellen
  • Dienstafhankelijkheden
  • Docker Stel orkesten opstarten orde.

De:

  • vereist dat de gezondheidscontrole van de database doorgaat voordat de webapp wordt gestart.
  • Omgevingsvariabelen en -geheimen
  • Gebruik voor productiegeheimen Docker Secrets of externe geheime managers (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault).

Genoemde volumes vs Bindmounts

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

services:
  web:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend  # Not exposed to frontend

Genoemde volumes

Beheerd door Docker

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

Persist gegevens over container herstarten

  • Kan worden geback-upt/hersteld met Docker commando's
  • Compatibel tussen platforms
  • Bindaankoppels

Directe mapping naar host-bestandssysteem

# 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

Nuttig voor ontwikkeling (herladen van levende code)

Configuratiebestanden, logs, uploads

NetwerkenNetwerken zorgen voor isolatie.

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

**Hier is de database alleen toegankelijk voor backend services, niet direct blootgesteld.**Gezondheidscontroles

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

**Gezondheidscontroles stellen Docker in staat om:**Bepaal of een container daadwerkelijk klaar is (niet alleen gestart)

services:
  web:
    image: registry.example.com/myapp:${VERSION}
    restart: always
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '2'
          memory: 2G
# Development (base + override)
docker-compose up -d

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

Ongezonde containers opnieuw opstarten

Geef status aan orkestrators (Kubernetes, Docker Swarm)

Common Docker componeert commando's

# 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

Ontwikkeling vs Productie Bestanden samenstellen

# 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"]

Aparte zorgen met meerdere compone bestanden:

# 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

docker-compose.yml

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

(base):

docker-compose.override.yml(ontwikkeling - auto-merged):docker-compose.prod.yml

(productie):

  • GPU-ondersteuning in Docker: Versnelt ML-werkbelastingMachine learning, wetenschappelijke computing en video processing toepassingen vereisen vaak GPU versnelling.
  • **Docker ondersteunt NVIDIA GPU's via de NVIDIA Container Toolkit.**Opzetten NVIDIA Container Toolkit
  • Dockerfile voor GPU-versnelde Python/PyTorch-toepassingGPU-containers draaien
  • GPU in Docker componerenReal-World Voorbeeld: Vertaalservice met GPU en CPU Builds

Hier is een echt productievoorbeeld van

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:

meestal lucid-nmt

, een neurale machine vertaling dienst Ik bouwde dat machten auto-vertaling op deze blog.

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:

Het project toont aan:

  • scottgal/mostlylucid-nmt:gpuGPU- en CPU-varianten
  • scottgal/mostlylucid-nmt:cpu- Zelfde codebase, verschillende basisafbeeldingen
  • scottgal/mostlylucid-nmt:gpu-minMulti-architectuur bouwt
  • scottgal/mostlylucid-nmt:cpu-min- Ondersteunt AMD64 en ARM64

Geoptimaliseerde Docker-afbeeldingen

  • - Zowel volledige als minimale variantenProductie gereed
  • - Gezondheidscontroles, volume persistentie, juiste registratieGPU-versnelde vertaaldienst
  • CPU-alleen alternatiefVoor omgevingen zonder GPU's draait dezelfde service op CPU:
  • Beschikbare afbeeldingsvarianten:- CUDA 12.6 met PyTorch GPU ondersteuning (~5GB)
  • - CPU-alleen, kleinere voetafdruk (~2,5GB): /health- Minimale GPU build, geen voorgeladen modellen (~4GB)/ready- Minimale CPU build (~1,5GB)
  • **Belangrijkste kenmerken:**GPU-versnelling

: 10-15x snellere vertaling met CUDAModel Auto-Download: Downloads vertaalmodellen on-demand

Terugvalondersteuning

: Tries Opus-MT → mBART50 → M2M100 voor maximale taaldekking

Volume Persistentie

# 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 modellen over container herstarten

Eindpunten voor de gezondheid

# 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

en

voor orkestratoren

# 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-Architectuur

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

: Runs on x86_64 and ARM64 (Apple Silicium, Raspberry Pi)

Zie de

volledig project op GitHub

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

# Then use in compose
services:
  web:
    image: myapp:latest  # Already built for multiple architectures

voor Dockerfile voorbeelden, bouw scripts, en API documentatie.

#!/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-Architectuur Bouwt met Docker Buildx

Moderne toepassingen moeten draaien op meerdere architecturen: x86_64 (AMD64) voor servers, ARM64 voor Raspberry Pi en Apple Silicon Macs, soms zelfs ARM32 voor embedded apparaten.

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

Waarom Multi-Architectuur Zaken

  • Buildx instellen
  • Docker Buildx is opgenomen in Docker Desktop.
  • Voor Linux:
  • Multi-Architecture Dockerfile
  • De meeste Dockerfiles werken zonder wijzigingen, maar hier zijn enkele tips:

Bouwen voor meerdere platforms

Multi-Architectuur met Docker Compose

Helaas, Docker Compose ondersteunt niet direct buildx.

Werkmiddelen:

# 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

Optie 1: Voorbouwen van afbeeldingen

Optie 2: Bouwscript.csproj:

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

Real-World Voorbeeld: CI/CD Pipeline

dotnet run --project MyDistributedApp.AppHost

GitHub Acties workflow voor multi-architectuur bouwt:

  • Deze workflow:
  • Triggers op pushes naar hoofd- of versietags
  • Stelt QEMU in voor cross-platform emulatie
  • Bouwt voor AMD64 en ARM64

Genereert automatisch tags (branch naam, semantische versies, SHA)

Gebruikt register caching voor snellere builds

  • .NET 9 Containerverbeteringen
  • .NET 9 introduceert belangrijke verbeteringen aan container ondersteuning, waardoor het gemakkelijker dan ooit om containerize .NET toepassingen zonder zelfs het schrijven van een Dockerfile.
  • Ingebouwde Container Publishing
  • Met .NET 9 kunt u een containerized applicatie direct publiceren:

Container-eigenschappen configureren

  • Voeg toe aan uw
  • Voer alles uit:
  • Aspire lanceert:
  • Dashboard op http://localhost:1588

Alle diensten met de juiste configuratieRedis en PostgreSQL in containers

Gedistribueerde tracering over diensten

Aspire vs Docker Compose

# 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 componeren:

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

# Kubernetes
kubectl apply -f deploy/k8s/

Taalagnostisch

Op infrastructuur gerichte

// 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);

Ontdekking van handmatige service

Breng je eigen opmerkzaamheid mee

Aspire:

.NET-specifiekOntwikkelingsgerichtOntdekking van de automatische dienst

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

Ingebouwde telemetrie

  • Genereert implementatie manifestenBeide gebruiken
  • **: Aspire for local development, generates Docker Compose/Kubernetes for production.**Aspire-apps inzetten
  • **Gebruiksmanifesten genereren:**Inzet vervolgens:

Aspire ComponentenVooraf gebouwde integraties maken het toevoegen van diensten triviaal:Self-Hosting op Beperkte Hulpbronnen: Praktische Optimalisatie

Het uitvoeren van een volledige opmerkzaamheid stack zoals hierboven vereist aanzienlijke middelen.

Als je zelf-hosting op een VPS met 4GB RAM of een oude laptop, hier zijn praktische strategieën om het verbruik van hulpbronnen te verminderen met behoud van functionaliteit.

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:

Alleen ontwikkelingsafhankelijkheden

  • **Voor lokale ontwikkeling heb je niet de volledige productiestapel nodig.**De
  • devdeps-docker-compose.ymlaanpak draait alleen wat je nodig hebt:docker logsWaarom dit werkt voor ontwikkeling:
  • SMTP4Dev: Test e-mail functionaliteit zonder een echte SMTP-server
  • PostgreSQL: Komt overeen met de productiedatabase
  • Totale voetafdruk: ~200MB RAM vs 2-4GB voor volledige stack

Zie de

volledige ontwikkeling afhankelijkheden gids

voor installatie-instructies.

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

Productie-instelling met beperkte middelen

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

Voor een budget VPS (2-4GB RAM), prioriteiten essentiële diensten:

# Add everything
docker-compose up -d

Wat wordt verwijderd en alternatieven:

Geen Prometheus/Grafana

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

**: Gebruik externe monitoring (UptimeRobot, BetterStack gratis tier)**Geen Seq

: Bestandsgebaseerde logging gebruiken +

, of Seq Cloud vrije tier

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

Geen Watchtower: Handmatige updates met GitHub Acties meldingen

Geen vertaaldienst

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

: Draai on-demand in een aparte container die u handmatig start/stop

Resourcelimieten

: Voorkom dat een enkele dienst alle geheugen verbruiktProgressieve verbeteringsstrategieStart minimaal, voeg diensten toe indien nodig:

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

**Fase 1: Kern (512MB-1GB VPS)**Fase 2: Observeerbaarheid toevoegen (2GB VPS)

Fase 3: Volledige Stack (4GB+ VPS)

Hulpbronoptimalisatietechnieken |---------|-----------------|---------------------| 1. Alpine Images gebruiken Besparingen

: Alpine beelden zijn 50-70% kleiner2.

Gedeelde instantie voor de databank

Gebruik in plaats van één database per dienst één PostgreSQL instantie met meerdere databases:

# 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

Besparingen: 400MB RAM per extra database die u consolideert

3.

CPU beperken voor achtergronddiensten

# 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

Dit voorkomt dat achtergrondtaken uithongeren van uw webapplicatie.

  • Persistente cachevolumes
  • Zoals behandeld in

ImageSharp met Docker

  • , het monteren van cache directories voorkomt onnodige opwerking:
  • Waarom het belangrijk isdocker logs: Zonder dit, elke container herstart regenereert alle thumbnails / verwerkte afbeeldingen.
  • Externe diensten gebruiken (vrije tiers)
  • De Dienst Zelfverzorgde RAM Extern Alternatief

Quality over Quantity (QoQ) Releases Vertaling:

  • Prometheus + Grafana * ~600MB * Grafana Cloud (free tier) *

Plausible (betaald) of self-host elders

# Simple health check script
#!/bin/bash
while true; do
  curl -f http://localhost/healthz || echo "Health check failed!" | mail -s "Alert" [email protected]
  sleep 300
done

Strategie

# 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" [email protected]
done

: Offload observability naar vrije levels, houd de kerntoepassing op uw VPS.

# Quick resource check
docker stats --no-stream

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

6.

On-Demand Services

Voor zelden gebruikte diensten zoals vertaling:

Besparingen

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

: 1-2GB RAM wanneer de vertaaldienst niet draait

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();

Real-World Budget Hosting Voorbeeld

Hier is wat draait op een $ 6 maand Hetzner VPS (2 vCPU, 4GB RAM):**Totaal gebruik van hulpbronnen:**RAM: ~800MB (laat 3,2GB vrij)

// 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;
    }
}

Schijf: ~2GB

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% stationair, <50% onder belasting

Wat is er anders:

  1. Geen Prometheus/Grafana (gebruik UptimeRobot + Cloudflare Analytics): dotnet run --project Mostlylucid.AppHostGeen Seq (gebruik
  2. **+ occasionele grep)**Geen Umami (gebruik Cloudflare Web Analytics - gratis)
    • Watchtower controleert elk uur in plaats van elke 5 minuten
    • Geen vertaaldienst (draai handmatig indien nodig)
    • Controle op een begroting
    • Gebruik zonder Prometheus/Grafana deze gratis alternatieven:
  3. **Gezondheidsmonitoring:**Log monitoring:
  4. **Gebruik van hulpbronnen:**Aspire Version: The .NET Way.envLaten we ons nu de hele stapel voorstellen met behulp van .NET Aspire.

Dit geeft u alle orkestratie voordelen met betere .NET integratie en een geweldige ontwikkelaar ervaring.

Aspire instellen voor meestal lucid

# 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

**Maak eerst de Aspire App Host:**Meestal lucid.AppHost/Program.cs:

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

Aspire Service-standaarden

Aanmaken |---------|---------------|-------------| | Meestal lucid.ServiceStandaardsproject: | Update Meestallucid/Program.csVoordelen van Aspire Approach | **Ontwikkelingservaring:**Enkelvoudig commando | start alles | docker-compose up | dotnet runDashboard | **: Prachtige UI op http://localhost:1588 toont:**Alle diensten met live status | Logs van alle diensten op één plaats | docker logsGedistribueerde sporen over de diensten | Metrics en gezondheidscontrolesService Discovery | : Diensten vinden elkaar automatisch via namenConfiguratie | : Gecentraliseerd in AppHost, niet meerjongleren

Produktie-inzet:

Genereer implementatie manifesten van Aspire:

  • Gegenereerde docker-compose.yml
  • (vereenvoudigd):
  • Aspire vs Traditional Docker Compose
  • Docker componeren .NET Aspire
  • Instellen

Handmatig YAML C# code met IntelliSense

  • Service Discovery
  • Quality over Quantity (QoQ) Releases Vertaling:
  • Waarneembaarheid
  • Breng je eigen Ingebouwde (OpenTelemetrie)
  • Ontwikkelingsbeleid

+ Dashboard

  • Debuggen
  • F5 in Visual Studio
  • Logs

Gecentraliseerd dashboard

Traceren

  1. **Automatische gedistribueerde tracing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .**Produktie
  2. **Gebruik YAML direct Genereer manifesten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .**Leercurve
  3. **YAML syntaxis C# die je al kent**Wanneer moet u Aspire vs Docker Compose gebruiken?
  4. **Aspire gebruiken wanneer:**Gebouw .NET microservices
  5. U wilt geïntegreerde debuggenTeam is comfortabel met C#

Je hebt tracing out-of-the-box nodig.

  • Je gebruikt Azure Container Apps (native support)
  • Docker Compose gebruiken wanneer:
  • Polyglotdiensten (Node.js, Python, Go, enz.)
  • Team geeft de voorkeur aan infrastructuur-als-code YAML
  • Inschakelen naar elke Docker-compatibele host
  • U heeft maximale controle over de configuratie van de container nodig

Eenvoudige invoering van één enkele dienst

Beide gebruiken:

  1. Aspiratie voor lokale ontwikkeling
  2. Genereren van Docker Compose voor productie-implementatie
  3. Het beste van beide werelden!
  4. Evolution van de Docker-instellingen van dit bloglatest)
  5. Deze blog Docker reis toont typische progressie:
  6. Juli 2024 - Eenvoudige start.dockerignore: Alleen de app, Cloudflared en Watchtower
  7. Augustus 2024 - Dev afhankelijkheden
  8. : Toegevoegde ontwikkeling-only services

Augustus 2024 - ImageSharp Fix

  1. : Opgelost volumerechten voor caching
  2. November 2024 - Volledige Stack
  3. : Complete opmerkzaamheid met Prometheus, Grafana, Seq.envVandaag
  4. : Aspire optie voor .NET-eerste ontwikkeling
  5. Lessen geleerd:depends_onStart eenvoudig, voeg alleen complexiteit toe wanneer dat nodig is
  6. Aparte dev- en productieconfiguraties
  7. Resource limits voorkomen dat één dienst anderen doodt
  8. Volumebeugels voor caches besparen aanzienlijke opwerkingstijd

Watchtower maakt zero-downtime auto-updates mogelijk

  1. Observeerbaarheid is de kosten van de hulpbronnen in de productie waard
  2. Samenvatting van beste praktijken
  3. Dockerfile Best Practices
  4. Meertraps builds gebruiken
  5. Uitvoeren als niet-root gebruiker
  6. Bestelinstructies voor optimale caching
  7. Specifieke basisafbeeldingstags gebruiken (niet
  8. Inclusief gezondheidscontroles

Gebruik

  1. onnodige bestanden uitsluiten
  2. Lagen minimaliseren (opdrachten RUN combineren)
  3. Gebruik bouwargumenten voor flexibiliteit
  4. Docker Stel best practices samen.dockerignoreGenoemde volumes gebruiken voor gegevens
  5. Uitvoeren van gezondheidscontroles
  6. Gebruik
  7. voor geheimen (nooit commit)
  8. Expliciete netwerken definiëren

Gebruik

met gezondheidsvoorschriften

# 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

Herstartbeleid instellen

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

Aparte dev/prod configuraties

# 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

Begrenzing van de hulpbronnen in de productie

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

Best practices voor beveiliging

Uitvoeren als niet-root gebruiker

Beitelde/minimale basisafbeeldingen gebruiken

Afbeeldingen scannen op kwetsbaarheden

Gebruik

  • royaal
  • Alpine/beitelde afbeeldingen voor kleinere afmetingen
  • Volumekoppels gebruiken voor ontwikkeling
  • Passende hulpbronnenlimieten configureren
  • Gebruik gezondheidscontroles voor orkestratie

Problemen met het oplossen van gemeenschappelijke problemen

  • Container start niet
  • Langzaam bouwen
  • Netwerkproblemen
  • Volume-toestemmingskwesties
  • Conclusie
  • Docker is geëvolueerd van een eenvoudige containerization tool naar een uitgebreid platform voor het bouwen, verschepen en draaien van moderne toepassingen.

Deze gids heeft je van fundamentals naar production-ready implementaties gebracht, met real-world voorbeelden van het runnen van mostlylucid.com.

  • Sleutelafhaalpunten
  • Voor beginners:
  • Begin met de
  • basic Docker Compose setup
    • slechts 3 diensten

Gebruik

  • afhankelijkheden alleen voor ontwikkeling
  • voor lokale werkzaamheden
  • Gemeenschappelijke problemen zoals oplossen
  • volumerechten

vroeg

Voor Self-Hosters op Budget VPS:

Start minimaal: App + Database + Reverse Proxy (~800MB RAM) |-------|----------|-----------|------------| | Alpine afbeeldingen en hulpbronnenlimieten gebruikenObservabiliteit uitladen naar vrije lagen (Seq Cloud, Grafana Cloud, UptimeRobot) | Uitvoeren van dure diensten (vertaling, ML) alleen op aanvraagEen VPS van $6/maand kan een productie blog met ruimte om te sparen | **Voor productie-implementaties:**Gezondheidscontroles kunnen nul-downtime updates met Watchtower mogelijk maken | **Afzonderlijke netwerken bieden beveiliging (frontend/backend isolatie)**Bind mounts voor gegevens die u nodig hebt om te back-uppen/toegang | Genoemde volumes voor door Docker beheerde opslagCPU/geheugenlimieten voorkomen verhongering van hulpbronnen

Cloudflare Tunnel elimineert de behoefte aan publieke IP-adressenVoor .NET-ontwikkelaars:

Ingebouwde container publiceren in .NET 9 elimineert Dockerfiles voor eenvoudige apps

Gebeitelde Ubuntu-afbeeldingen zorgen voor een minimaal aanvalsoppervlak

  • .NET Aspire biedt de beste ervaring op het gebied van lokale ontwikkelingAspire kan het genereren van Docker Compose voor productie-implementatieOpenTelemetrie integratie wordt gratis geleverd met Aspire
  • **Voor Advanced Use Cases:**GPU containers inschakelen ML/AI workloads met 10-15x snelheid
  • Multi-architectuur bouwt ondersteuning ARM64 en AMD64 uit één bronGitHub Acties automatiseert multi-platform builds
  • Laag caching versnelt dramatisch herhaalde buildsDe reisDe Docker evolutie van deze blog weerspiegelt typische progressie:
  • **Stage . Diensten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .**Fase 1

(Jul 2024) App + Tunnel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Fase 2

(Vandaag) + Aspire optie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

  1. Het patroon: Start eenvoudig, los problemen op als ze zich voordoen, voeg alleen complexiteit toe wanneer dat nodig is.
  2. Wat is het volgende?Afhankelijk van je pad:
  3. Learning Docker: Beginnen met
  4. Docker Basisbegrippen samenstellen

Self-hosting

: Check out the

Happy containerizing! 🐳

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.