Back to "Docker Développement Plongée profonde : des bases à la conteneurisation avancée .NET"

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 Développement Plongée profonde : des bases à la conteneurisation avancée .NET

Sunday, 09 November 2025

Présentation

Docker a fondamentalement transformé notre façon de construire, d'expédier et d'exécuter des applications.

Ce qui a commencé comme un simple outil de conteneurisation a évolué en un écosystème complet pour le développement et le déploiement d'applications modernes.

  1. **C'est le quatrième et l'article le plus complet de ma série Docker pour les développeurs de .NET.**Si vous êtes nouveau à Docker, vous devriez commencer par :
  2. Composez Docker- Commencer avec les configurations de base multi-conteneurs (Juillet 2024)
  3. Dépendances de développement avec Docker Compose- Mise en place d'environnements de dév locaux (août 2024)

ImageSharp avec Docker

  • **- Résoudre les problèmes d'autorisation de volume (août 2024)**Dans cette plongée profonde, nous allons construire sur ces fondations pour explorer:
  • Compose Docker prêt à la production: La pile réelle exécutant ce blog
  • Auto-accueil sur des ressources limitées: Techniques pratiques d'optimisation des déploiements VPS budgétaires
  • Prise en charge du GPU: Charges de travail en ML dans les conteneurs
  • Constructions multi-architectures: Soutenir ARM64 et AMD64
  • Caractéristiques du conteneur .NET 9: Publication de conteneurs intégrés sans Dockerfiles

.NET Aspire

: L'approche moderne du .NET à l'orchestration des conteneurs

REMARQUE: Ceci fait partie de mes expériences avec l'IA / un moyen de dépenser 1000 $ Claude Code crédits Web.

J'ai alimenté ça un BUNCH de papiers, ma compréhension, des questions que j'ai dû générer cet article.

C'est amusant et il comble un vide que je n'ai pas vu comblé nulle part ailleurs.

Que vous déployiez une simple application web ou orchestrez une architecture complexe de microservices avec des modèles d'apprentissage automatique accélérés par GPU, ce guide vous emmène des bases de Docker aux applications containerizzato prêtes à la production - avec de vrais exemples de courir principalementlucid.com.

  • Fondements Docker: Comprendre les conteneursQu'est-ce que Docker, vraiment ?
  • **À son cœur, Docker est une plate-forme de conteneurisation qui emballe votre application et toutes ses dépendances dans une unité normalisée appelée un conteneur.**Contrairement aux machines virtuelles qui virtualisent des systèmes d'exploitation entiers, les conteneurs partagent le noyau OS hôte tout en maintenant des espaces utilisateur isolés.

Pensez-y de cette façon :

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

# The container solution
"Ship your machine!" 

Machines virtuelles

  1. : Chaque VM exécute une pile OS complète (le noyau Linux, les bibliothèques système, etc.) - lourde et lente à démarrerRécipients
  2. : Partager le noyau hôte, le code d'application et les dépendances du paquet - léger et rapidePourquoi les conteneurs comptent pour les développeurs
  3. **Les conteneurs résolvent plusieurs problèmes critiques :**Cohérence environnementale
  4. : Les environnements de développement, de test et de production sont identiquesIsolation de la dépendance
  5. : Plus de "DLL hell" ou de versions de bibliothèque contradictoiresConstructions reproductibles

: Même entrée = même sortie, à chaque fois

Déploiement rapide

# 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

: Démarrer les conteneurs en quelques secondes, pas quelques minutesEfficacité des ressources

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

: Exécuter des dizaines de conteneurs sur un seul hôte

  • Concepts essentiels DockerImages vs Conteneurs
  • Imagessont des systèmes de fichiers immuables et superposés.
  • **Chaque instruction d'un Dockerfile crée un nouveau calque :**Cette superposition est puissante :

Cache

: Les couches non modifiées sont réutilisées, accélérant les constructionsPartage

: Plusieurs images peuvent partager des couches de base

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

Efficacité

# 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

: Seuls les calques modifiés doivent être téléchargés/uploadés

  1. **Comprendre l'exécution de Dockerfile : pas votre machine !**Une source commune de confusion pour les débutants de Docker:COPYLes commandes dans un fichier Docker ne fonctionnent pas sur votre machine - elles fonctionnent à l'intérieur du système d'exploitation du conteneur de construction.ADDVoici ce qui se passe :
  2. **Pourquoi cela importe-t-il :**Principaux points de vue :RUNSystème de fichiers local
  3. : Votreet
  4. commandes lues depuis votre machineConstruire l'image

: Votreapt-getcommandes exécutées dans le système d'exploitation du conteneur (pas votre machine)

Image de sortie

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

: L'image finale contient tous les calques créés pendant la construction

Plate-forme transversale

: Vous pouvez être sous Windows, construire une image Linux, en utilisant des commandes Linux

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

C'est pourquoi vous pouvez écrire

commandes dans un fichier Docker sur Windows - ils ne sont pas en cours d'exécution sur 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

Ils fonctionnent à l'intérieur du conteneur de construction basé sur Linux.

  1. **Exemple de scénario de confusion :**Le démon Docker gère la traduction entre votre système de fichiers local et l'environnement de construction containerized.
  2. Les meilleures pratiques de DockerfileVoici un .NET Dockerfile prêt à la production avec commentaire:.csprojComprendre le flux de construction
  3. **Voici ce qui se passe réellement lors d'une construction en plusieurs étapes de Docker - d'où viennent les fichiers et où ils finissent :**Principales conclusions de ce flux :npm run buildImage SDK supprimée
  4. : Le conteneur SDK de 1,5 Go est jeté après publication - seulement ~50 Mo de sortie compilée va de l'avant: appsettings.jsonCalquewwwroot/: Copier
  5. avant le code source signifie que la restauration de la dépendance est mise en cache à moins que les dépendances changentActifs de couverture
  6. : Construit séparément (souvent via) et copié dans l'image finale

Fichiers de configuration

  1. etcontenu copié à partir de votre machine, non construit
  2. Image finale: Démarre à partir d'une image d'exécution minimale (~220 Mo) + votre application (~30 Mo) + actifs (~5 Mo) = ~255 Mo total
  3. Seulement les fichiers de production: Code source, obj/, bin/, node_modules/ ne jamais arriver à l'image finale
  4. **Principes clés illustrés:**Constructions en plusieurs étapes
  5. : Des étapes distinctes de construction/édition réduisent considérablement la taille de l'image finaleOptimisation des couches

: Copier les fichiers de dépendance avant le code source pour une meilleure mise en cache

# 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

Sécurité

: Exécuter en tant qu'utilisateur non root

Contrôles sanitaires

: Les orchestrateurs de conteneurs peuvent surveiller la santé des applications

  • Environnement explicite
  • : Définir les valeurs par défaut de production
  • Construction et fonctionnement
  • Docker Compose: L'orchestration multi-conteneurs
  • Docker Compose vous permet de définir et d'exécuter des applications multi-conteneurs.

Au lieu de gérer les conteneurs individuellement, vous décrivez toute votre pile d'applications dans un fichier YAML.docker runPourquoi Docker Compose ?

Considérez une application web .NET typique :

L'application web ASP.NET Core**Base de données PostgreSQLdocker-compose.yml**Redis le 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 pour l'exploitation forestière

  1. Peut-être un service d'arrière-planGérer individuellement ceux-ci avecapp_networkles commandes deviennent incompréhensibles.
  2. **Docker Compose résout ça.**Exemple complet du monde réel : Production Blog Platform/mnt/*Voici le
  3. Production réellequi gère ce site même (presquelylucid.com):
  4. **Principaux modèles de production :**Réseau unique
  5. : Tous les services surpour la simplicité
  6. Supports de reliure: Chemins d'accueil (
  7. ) pour les données persistantes, vous devez sauvegarder / accéderVolumes nommés.env: Stockage Docker-géré pour les données vous n'avez pas besoin d'accès direct à

Étiquettes de la Tour de Garde

: Seuls les services de mise à jour explicitement étiquetés pour la mise à jour automatique

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

Limites de ressourcescondition: service_healthy: Les contraintes du CPU sur le service de traduction empêchent la famine des ressources

Cartographie des ports externes

# .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 sur 5266 au lieu de 5432 pour éviter les conflits avec d'autres instances

Fichiers d'environnement

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

: Les secrets en:

  • fichier (ne s'est jamais engagé à git)
  • Docker Composez les caractéristiques clés
  • Dépendances des services
  • Docker Compose orchestre l'ordre de démarrage.

Les:

  • exige que le contrôle de santé de la base de données passe avant de démarrer l'application web.
  • Variables d'environnement et secrets
  • Pour les secrets de production, utilisez Docker Secrets ou des gestionnaires secrets externes (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault).

Volumes désignés par rapport aux monts de bind

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

services:
  web:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend  # Not exposed to frontend

Volumes désignés

Géré par Docker

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

Données persistantes sur les redémarrages de conteneurs

  • Peut être sauvegardé/restauré avec les commandes Docker
  • Compatible avec les plates-formes croisées
  • Supports de reliure

Cartographie directe vers le système de fichiers hôte

# 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

Utile pour le développement (rechargement de code en direct)

Fichiers de configuration, journaux, téléchargements

Mise en réseauLes réseaux assurent l'isolement.

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

**Ici, la base de données n'est accessible qu'aux services de backend, non directement exposés.**Contrôles de santé

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

**Les contrôles de santé permettent à Docker:**Déterminer si un conteneur est prêt (pas seulement commencé)

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

Redémarrer les contenants malsains

Fournir un statut aux orchestres (Kubernetes, Docker Swarm)

Commandes communes de composition Docker

# 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

Développement vs Production Compose Files

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

Séparez les préoccupations avec plusieurs fichiers de composition :

# 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(développement - fusion automatique):docker-compose.prod.yml

(production):

  • Support GPU dans Docker: Accélérer les charges de travail MLL'apprentissage automatique, l'informatique scientifique et les applications de traitement vidéo nécessitent souvent une accélération du GPU.
  • **Docker prend en charge les GPU NVIDIA via la boîte à outils NVIDIA Container.**Configuration de NVIDIA Container Toolkit
  • Dockerfile pour l'application Python/PyTorch Accéléré GPURécipients GPU en cours d'exécution
  • GPU dans Docker ComposeExemple Real-World: Service de traduction avec GPU et CPU Builds

Voici un véritable exemple de production de

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:

principalement lucide-nmt

, un service de traduction automatique neuronale que j'ai construit qui alimente la traduction automatique sur ce 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:

Le projet démontre :

  • scottgal/mostlylucid-nmt:gpuVariantes GPU et CPU
  • scottgal/mostlylucid-nmt:cpu- Même base de code, différentes images de base
  • scottgal/mostlylucid-nmt:gpu-minConstructions multi-architectures
  • scottgal/mostlylucid-nmt:cpu-min- Supporte AMD64 et ARM64

Images Docker optimisées

  • - Variantes pleines et minimalesPrête à la production
  • - Contrôles de santé, persistance du volume, exploitation forestière appropriéeService de traduction accélérée GPU
  • CPU-seulement alternativePour les environnements sans GPU, le même service fonctionne sur CPU:
  • Variantes d'image disponibles :- CUDA 12,6 avec support GPU PyTorch (~5 Go)
  • - CPU seulement, empreinte plus petite (~2,5 Go): /health- Minimal GPU build, pas de modèles préchargés (~4 Go)/ready- Construction d'un processeur minimal (~1.5 Go)
  • **Principales caractéristiques:**Accélération du GPU

: 10-15x traduction plus rapide avec CUDAModèle de téléchargement automatique: Téléchargements modèles de traduction à la demande

Soutien en cas d'échec

: Tries Opus-MT → mBART50 → M2M100 pour une couverture linguistique maximale

Persistance en volume

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

: Les modèles de cache à travers les redémarrages de conteneurs

Points finals pour la santé

# 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

et

pour orchestres

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

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

: Fonctionne sur x86_64 et ARM64 (Apple Silicon, Raspberry Pi)

Voir

Projet complet sur 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

pour les exemples de Dockerfile, construire des scripts et la documentation de l'API.

#!/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

Constructions multi-Architecture avec Docker Buildx

Les applications modernes doivent fonctionner sur plusieurs architectures : x86_64 (AMD64) pour les serveurs, ARM64 pour Raspberry Pi et Apple Silicon Macs, parfois même ARM32 pour les appareils embarqués.

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

Pourquoi l'Architecture Multi-Architecture compte-t-elle?

  • Configuration du buildx
  • Docker Buildx est inclus dans Docker Desktop.
  • Pour Linux :
  • Fichier Docker multi-Architecture
  • La plupart des Dockerfiles fonctionnent sans changement, mais voici quelques conseils :

Bâtir des plates-formes multiples

Multi-Architecture avec Docker Compose

Malheureusement, Docker Compose ne supporte pas directement buildx.

Rapprochement des travaux :

# 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: Pré-construire des images

Option 2: Créer un script.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.

Exemple du monde réel : pipeline CI/CD

dotnet run --project MyDistributedApp.AppHost

Le workflow GitHub Actions pour les constructions multi-architectures :

  • Ce flux de travail :
  • Déclencheurs sur les pousses vers les balises principales ou de version
  • Configuration de QEMU pour l'émulation multiplateforme
  • Constructions pour AMD64 et ARM64

Génére automatiquement des balises (nom de la branche, versions sémantiques, SHA)

Utilise le cache de registre pour les constructions plus rapides

  • .NET 9 Améliorations des conteneurs
  • .NET 9 introduit des améliorations significatives au support des conteneurs, ce qui facilite plus que jamais la conteneurisation des applications .NET sans même écrire un Dockerfile.
  • Édition de conteneurs intégrée
  • Avec .NET 9, vous pouvez publier directement une application conteneurisée :

Configuration des propriétés des conteneurs

  • Ajouter à votre
    1. Le Président. — L'ordre du jour appelle le rapport (doc.
  • Exécutez tout :
  • Aspire lance :
  • Tableau de bord à l'adresse http://localhost:15888

Tous les services avec une configuration appropriéeRedis et PostgreSQL dans des conteneurs

Traçage réparti entre les services

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

Composez Docker :

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

# Kubernetes
kubectl apply -f deploy/k8s/

Linguistique-agnostique

Orientation vers l'infrastructure

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

Découverte manuelle du service

Apportez votre propre observabilité

Aspirez :

Spécifique au .NETOrientation vers le développementDécouverte automatique du service

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

Télémétrie intégrée

  • Génère des manifestes de déploiementUtiliser les deux
  • **: Aspire au développement local, génère Docker Compose/Kubernetes pour la production.**Déploiement d'applications d'Aspire
  • **Générer des manifestes de déploiement:**Ensuite, déployez :

Composants d'aspirationLes intégrations préconçues rendent l'ajout de services trivial :Self-Hosting sur les ressources limitées: Optimisation pratique

L'exploitation d'une pile d'observation complète comme celle ci-dessus nécessite des ressources importantes.

Si vous vous auto-hébergez sur un VPS avec 4 Go de RAM ou un ancien ordinateur portable, voici des stratégies pratiques pour réduire la consommation de ressources tout en maintenant la fonctionnalité.

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:

Dépendances de développement seulement

  • **Pour le développement local, vous n'avez pas besoin de la pile de production complète.**Les
  • devdeps-docker-compose.ymlapproche ne fonctionne que ce dont vous avez besoin:docker logsPourquoi cela fonctionne pour le développement:
  • SMTP4Dev: Tester la fonctionnalité d'email sans véritable serveur SMTP
  • PostgreSQLTM: Correspond à la base de données de production
  • Empreinte totale: ~200 Mo RAM vs 2-4 Go pour pile complète

Voir

Guide des dépendances en matière de développement

pour les instructions d'installation.

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

Mise en place de la production axée sur les ressources

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

Pour un VPS budgétaire (2-4 Go RAM), prioriser les services essentiels :

# Add everything
docker-compose up -d

Ce qui est enlevé et les alternatives:

Pas de Prométhée/Grafana

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

**: Utiliser la surveillance externe (UptimeRobot, BetterStack free tier)**Pas de seq

: Utiliser l'enregistrement par fichier +

, ou niveau sans nuage Seq

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

Pas de Tour de Garde: Mises à jour manuelles avec les notifications GitHub Actions

Pas de service de traduction

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

: Exécutez à la demande dans un conteneur séparé que vous démarrez/arrêtez manuellement

Limites de ressources

: Empêcher tout service unique de consommer toute la mémoireStratégie d'amélioration progressiveCommencer au minimum, ajouter des services au besoin :

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

**Étape 1 : Noyau (512MB-1GB VPS)**Étape 2: Ajouter l'observabilité (2 Go VPS)

Étape 3: Stack complet (4 Go + VPS)

Techniques d'optimisation des ressources |---------|-----------------|---------------------|

  1. Le Conseil de l'Europe a adopté une résolution du Conseil de l'Europe sur la situation des droits de l'homme dans le monde. Utiliser les images alpines Économies

: Les images alpines sont 50-70% plus petites2. Le Président. — L'ordre du jour appelle le rapport (doc.

Instance de base de données partagée

Au lieu d'une base de données par service, utilisez une instance PostgreSQLTM avec plusieurs bases de données :

# 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

Économies: 400 Mo de RAM par base de données supplémentaire que vous consolidez

3. Les droits de l'homme sont garantis par le Pacte international relatif aux droits économiques, sociaux et culturels.

Limiter le processeur pour les services d'arrière-plan

# 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

Cela empêche les tâches de fond de mourir de faim votre application web.

    1. Le Président. — L'ordre du jour appelle le rapport (doc.
  • Volumes de cache persistants
  • Tels que couverts par

ImageSharp avec Docker

  • , le montage de répertoires cache empêche le retraitement inutile:
  • Pourquoi c'est importantdocker logs: Sans cela, chaque redémarrage de conteneur régénère toutes les vignettes/images traitées.
  • Utiliser les services externes (niveaux gratuits)
  • Service d'auto-hébergement RAM d'alternative externe

Seq, ~500MB, Seq Nuage (10GB/mois gratuit)

Prométhée + Grafana

Umami, ~200MB, Plausible (payé) ou auto-accueil ailleurs

# 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

Stratégie

# 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

: Déchargez l'observabilité aux niveaux libres, gardez l'application centrale sur votre VPS.

# Quick resource check
docker stats --no-stream

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

6.

Services sur demande

Pour les services peu utilisés comme la traduction:

Économies

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

: 1-2 Go de RAM lorsque le service de traduction n'est pas en cours d'exécution

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

Exemple d'accueil du budget mondial réel

Voici ce qui fonctionne sur un Hetzner VPS de 6 $/mois (2 vCPU, 4 Go RAM):**Utilisation totale des ressources :**RAM: ~800 Mo (libère 3,2 Go)

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

Disque : ~2 Go

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 % au ralenti, <50 % sous charge

Qu'est-ce qui est différent :

  1. Pas de Prométhée/Grafana (utiliser UptimeRobot + Cloudflare Analytics): dotnet run --project Mostlylucid.AppHostPas de Seq (utilisation
  2. **+ grep occasionnelle)**Non Umami (utilisez Cloudflare Web Analytics - gratuit)
    • La Tour de Garde vérifie toutes les heures au lieu de toutes les 5 minutes
    • Pas de service de traduction (exécutez manuellement au besoin)
    • Suivi d'un budget
    • Sans Prométhée/Grafana, utilisez ces alternatives gratuites:
  3. **Surveillance de la santé:**Surveillance des registres:
  4. **Utilisation des ressources :**Version d'Aspire: La Voie .NET.envRepensons l'ensemble de la pile en utilisant .NET Aspire.

Cela vous donne tous les avantages de l'orchestration avec une meilleure intégration .NET et une expérience de développeur incroyable.

Mise en place d'Aspire pour Mostlylucide

# 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

**Tout d'abord, créez l'hôte d'Aspire App :**Pour la plupart, l'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 par défaut de service

Créer |---------|---------------|-------------| | La plupart du temps, c'est le serviceDéfautsprojet: | Mettre à jour Mostlylucide/Program.csAvantages de l'approche d'aspiration | **Expérience en matière de développement :**Commande unique | commence tout | docker-compose up | dotnet runTableau de bord | **: Belle UI à http://localhost:15888 montrant :**Tous les services en direct | Registres de tous les services en un seul endroit | docker logsTraces réparties entre les services | Statistiques et bilans de santéDécouverte des services | : Les services se retrouvent automatiquement via des nomsConfiguration | : Centralisé dans AppHost, pas plusJonglage

Déploiement de la production :

Générer des manifestes de déploiement d'Aspire:

  • Produite docker-compose.yml
  • (simplifié):
  • Aspire vs Compose de Docker Traditionnel
  • Caractéristiques de Docker Compose.NET Aspire.
  • Configuration

Code YAML (model) C# avec IntelliSense

  • Découverte des services
  • Manuel env vars. Automatique.
  • Observabilité
  • Apportez votre propre (OpenTelemetry)
  • et le développement

+ Tableau de bord

  • Déboguement
  • Attachez-vous au conteneur de F5 dans Visual Studio
  • Registres

Tableau de bord centralisé

Recherche

  1. **Configuration manuelle Retraçage distribué automatique**Production
  2. **Utiliser directement YAML Générer des manifestes**Courbe d'apprentissage
  3. **Syntaxe YAML C# que vous connaissez déjà**Quand utiliser Aspire vs Docker Compose
  4. **Utiliser Aspire lorsque :**Construction de microservices .NET
  5. Vous voulez le débogage intégréL'équipe est à l'aise avec C#

Vous avez besoin d'un tracage distribué hors-de-la-boîte

  • Vous vous déployez sur Azure Container Apps (assistance native)
  • Utilisez Docker Compose lorsque :
  • Services en polyglotte (Node.js, Python, Go, etc.)
  • L'équipe préfère l'infrastructure comme code YAML
  • Déploiement à tout hôte compatible Docker
  • Vous avez besoin d'un contrôle maximal sur la configuration du conteneur

Déploiements simples à un service

Utilisez les deux :

  1. Aspirer au développement local
  2. Générer Docker Compose pour le déploiement de la production
  3. Le meilleur des deux mondes !
  4. Évolution de la configuration Docker de ce bloglatest)
  5. Le parcours Docker de ce blog montre une progression typique:
  6. Juillet 2024 - Début simple.dockerignore: Juste l'application, Cloudflared, et La Tour de Garde
  7. Août 2024 - Dépendances du Dev
  8. : Ajout de services de développement uniquement

Août 2024 - Correction d'images

  1. : Autorisations de volume résolues pour la mise en cache
  2. Novembre 2024 - Stack complet
  3. : Observabilité complète avec Prométhée, Grafana, Seq.envAujourd'hui
  4. : Aspirer option pour .NET-premier développement
  5. Enseignements tirés :depends_onCommencer simple, ajouter la complexité seulement lorsque nécessaire
  6. Configurations de dev et de production séparées
  7. Les limites de ressources empêchent un service d'en tuer d'autres
  8. Les montures de volume pour caches économisent beaucoup de temps de retraitement

La Tour de Garde permet une mise à jour automatique du temps zéro

  1. L'observabilité vaut le coût des ressources dans la production
  2. Résumé des pratiques exemplaires
  3. Les meilleures pratiques de Dockerfile
  4. Utiliser des constructions multi-étapes
  5. Exécuter en tant qu'utilisateur non root
  6. Instructions de commande pour une mise en cache optimale
  7. Utiliser des balises d'image de base spécifiques (pas
  8. Inclure les contrôles de santé

Utilisation

  1. pour exclure les fichiers inutiles
  2. Minimiser les calques (commandes RUN combinées)
  3. Utiliser des arguments de construction pour la flexibilité
  4. Docker Compose les meilleures pratiques.dockerignoreUtiliser des volumes nommés pour les données
  5. Mettre en œuvre des contrôles sanitaires
  6. Utilisation
  7. pour des secrets (ne commit jamais)
  8. Définir des réseaux explicites

Utilisation

avec des conditions de santé

# 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

Définir les politiques de redémarrage

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

Configurations dev/prod séparées

# 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

Limites de ressources dans la production

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

Meilleures pratiques en matière de sécurité

Exécuter en tant qu'utilisateur non root

Utiliser des images de base ciselées/minimales

Analyser les images pour détecter les vulnérabilités

Utilisation

  • généreusement
  • Images alpines/chisélisées pour une taille plus petite
  • Utiliser des montures de volume pour le développement
  • Configurer les limites de ressources appropriées
  • Utiliser des contrôles de santé pour l'orchestration

Dépannage de problèmes communs

  • Le conteneur ne démarre pas
  • Constructions lentes
  • Questions liées aux réseaux
  • Questions relatives à la permission de volume
  • Le présent règlement entre en vigueur le vingtième jour suivant celui de sa publication au Journal officiel de l'Union européenne.
  • Docker est passé d'un simple outil de conteneurisation à une plate-forme complète pour la construction, l'expédition et l'exécution d'applications modernes.

Ce guide vous a amené des fondamentaux aux déploiements prêts à la production, avec des exemples du monde réel de courir principalementlucid.com.

  • Prises en charge des clés
  • Pour les débutants :
  • Commencez par le
  • Configuration de base de Docker Compose
    • seulement 3 services

Utilisation

  • dépendances exclusivement liées au développement
  • pour le travail local
  • Résoudre des problèmes communs comme
  • permissions de volume

tôt

Pour les auto-hébergements dans le budget VPS:

Début minimal: App + base de données + proxy inverse (~800 Mo RAM) |-------|----------|-----------|------------| | Utiliser les images alpines et les limites des ressourcesDécharge l'observabilité aux niveaux libres (Seq Cloud, Grafana Cloud, UptimeRobot) | Exécuter des services coûteux (traduction, ML) à la demande seulementUn VPS de 6 $/mois peut gérer un blog de production avec de la place à épargner | **Pour les déploiements de production :**Les contrôles de santé permettent des mises à jour à temps zéro avec La Tour de Garde | **Des réseaux séparés assurent la sécurité (isolement frontal/arrière-fin)**Bind montures pour les données dont vous avez besoin pour sauvegarder / accéder | Volumes désignés pour le stockage géré par DockerLes limites du CPU/mémoire empêchent la famine des ressources

Cloudflare Tunnel élimine le besoin d'adresses IP publiquesPour les développeurs .NET :

L'édition de conteneurs intégrée dans .NET 9 élimine Dockerfiles pour des applications simples

Les images ciselées Ubuntu fournissent une surface d'attaque minimale

  • .NET Aspire offre la meilleure expérience de développement local de classeAspire peut générer Docker Compose pour le déploiement de la productionL'intégration OpenTelemetry est gratuite avec Aspire
  • **Pour les cas d'utilisation avancée :**Les conteneurs GPU permettent des charges de travail ML/AI avec une accélération de 10-15x
  • Multi-architecture construit support ARM64 et AMD64 à partir d'une source uniqueGitHub Actions automatise les constructions multiplateformes
  • La mise en cache des couches accélère considérablement les constructions répétéesLe voyageL'évolution de Docker de ce blog reflète la progression typique:
  • Services de la scène Utilisation de la RAM ComplexitéÉtape 1

(Juillet 2024): App + Tunnel: ~300MB: Simple:

Étape 2

(Aujourd'hui)

  1. Le modèle: Commencer simple, résoudre les problèmes au fur et à mesure qu'ils surviennent, ajouter de la complexité seulement lorsque nécessaire.
  2. Qu'est-ce qu'il y a ?Selon votre chemin :
  3. Apprendre le Docker: Commencez par
  4. Docker Composez les bases

Accueillant soi-même

: Regardez la

Happy containerizing! 🐳

logo

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