Back to "Docker Development Deep Dive: De la base à l’avant-garde .Containerisation 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 Development Deep Dive: De la base à l’avant-garde .Containerisation NET

Sunday, 09 November 2025

Introduction

Docker a fondamentalement transformé la façon dont nous construisons le navire, , et exploitons les applications. Ce qui commençait à être un simple outil de conteneurisation s’est transformé en un écosystème complet pour le développement et le déploiement d’applications modernes

C'est le quatrième et le plus complet article de ma série de Docker pour les développeurs .NETM SK1 Si vous êtes nouveau à Docker,, il se peut que vous désiriez commencer par :

  1. Docker Compose - Démarrer avec les installations de conteneurs multiples de base -Container 2 July
  2. Dépendances de développement avec Docker Compose - Établissement d'environnements dev locaux Août
  3. ImageSharp avec Docker - Règlement des questions relatives aux permis de volume Août

Dans cette plongée profonde, nous nous fonderons sur ces fondations pour étudier :

  • Production-Docker Compose prêt: L’enchaînement réel qui exploite ce blog
  • Self- hébergement sur des ressources limitées: Techniques pratiques d’optimisation pour les déploiements de VPS budgétaire
  • Soutien GPU: Faire fonctionner les charges de travail ML dans des conteneurs
  • Les architectures multi-: Soutien ARM64 et AMDM SK2
  • Caractéristiques du conteneur .NET 9: Built-in container publishing without Dockerfiles
  • .NET Aspire: L'approche moderne de l'organisation des conteneurs par le réseau .NET

NOTE: Il s'agit d'une partie de mes expériences avec l'AI / une façon d'épargner $1000 Claude Code Crédits WebM SK3 J'ai alimenté cet article par un BUNCH de documentsMST5 ma compréhensionMst6 des questions que j'ai eues pour générer cet articleM st7 C'est fun et remplit une lacune que je n'ai jamais vu remplie ailleursMSt9

Que vous déployez une simple application Web ou orchestriez une architecture de microservices complexe avec GPU, ce guide vous mène des bases du Docker à la production.

Docker Fundamentals: Comprendre les conteneurs

Qu'est-ce que Docker, Really?

À son cœur, Docker est une plate-forme de containerisation qui emballe votre application et toutes ses dépendances dans une unité standardisée appelée « container ».

Pensez-en ainsi:

  • Machines virtuelles: Chacune des VMs exploite un stade d'exploitation complet.
  • Contenants: Partagez le noyau de l'hôte, seuls les paquets code d'application et dépendances - léger et rapide

Pourquoi les contenants sont importants pour les développeurs

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

# The container solution
"Ship your machine!" 

Les contenants permettent de résoudre plusieurs problèmes critiques:

  1. Cohérence de l'environnement: DéveloppementM SK1 Essais, et environnements de production sont identiques
  2. Isolation par dépendance: Il n'y a plus de versions conflictantes de la bibliothèque
  3. Structures reproductibles: La même entrée = la même sortieM SK2 chaque fois
  4. Déploiement rapide: Démarrer les contenants en secondes , pas en minutes
  5. Efficacité des ressources: Faire fonctionner des dizaines de conteneurs sur un seul hôte

Concepts Docker essentiels

Images vs Containers

# 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

Images sont immuables, Systèmes de fichiers stratifiés. Chaque instruction dans un Dockerfile crée une nouvelle couche

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

Cette couche est puissante:

  • Cachement: Les couches non modifiées sont réutilisées, accélérer les constructions
  • Partage: Plusieurs images peuvent partager des couches de base
  • Efficacité: Seules les couches modifiées doivent être téléchargées

Comprendre l’exécution du Dockerfile: Non votre machine!

Une source de confusion commune pour les débutants de Docker: Les commandes contenues dans un Dockerfile ne fonctionnent pas sur votre machine.

Voici ce qui se passe en réalité :

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

Pourquoi cela importe:

# 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

Principaux points de vue:

  1. Système de fichier local: Votre COPY et ADD Les commandes sont lues de votre machine
  2. Créer une image: Votre RUN les commandes sont exécutées dans le contenant's OS (pas votre machine)
  3. Image de sortie: L'image finale contient toutes les couches créées durant la construction
  4. Plateforme Cross-: Vous pouvez être sur Windows, bâtir une image Linux , en utilisant des commandes Linux

C'est pourquoi vous pouvez écrire apt-get commands dans un Dockerfile sur Windows - ils'ils ne fonctionnent pas sur Windows ! IlsMSC3ils fonctionnent à l’intérieur du conteneur de construction LinuxM SK4basé sur le contenant de construction

Exemple de situation de confusion:

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

Le démon Docker traite la traduction entre votre système de fichier local et l'environnement de construction containerisé.

Les meilleures pratiques en matière de dockerfile

Voici une production de ', - et . .

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

Comprendre le flux de construction

Voici ce qui se passe au cours d’un docker à plusieurs étapes :

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

Principales constatations tirées de ce processus:

  1. L'image du SDK est discardée: Le contenant SDK de 1.5GB est éjecté après avoir publié
  2. Cache de la couche: Copie .csproj avant le code source signifie que la restauration de dépendance est cachée à moins que les dépendances ne changent
  3. Assets de frontend: Construit séparément ( fréquemment par npm run build) et copie dans l'image finale
  4. Fichiers de configuration: appsettings.json et wwwroot/ contenu copié de votre machine, non construit
  5. Image finale: Commence à partir de l'image minimale du temps d'exécution (~220MBM SK2 MSC3 votre app
  6. Seules les fichiers de production: Code de source , obj/, binM SK3 nodeMSC4modulesMST5 ne parvient jamais à l'image finale

Principes clés illustrés:

  1. Multi-stage builds: Établissement séparéM SK1 Étapes d'édition réduisent considérablement la taille finale de l'image
  2. Optimisation des couches: Copie des fichiers de dépendance avant le code source pour un meilleur caching
  3. Sécurité: Suivre en tant que non-utilisateur root
  4. Contrôles de santé: Les orchestrateurs de conteneurs peuvent surveiller la santé des applications
  5. Environnement explicite: Définir les défauts de production

Établissement et fonctionnement

# 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

Docker Compose: Multi-Container Orchestration

Docker Compose vous permet de définir et d'exécuter plusieurs applications de conteneurs-Containers1 Au lieu de gérer les contenants individuellement,, vous décritez l'ensemble de votre stack d'applications dans un fichier YAMLM SK3

Pourquoi Docker Compose?

Examiner une application Web typique .NET:

  • ASP.NET Application Web de base
  • Base de données PostgreSQL
  • Redis cache
  • Seq pour l'enregistrement
  • Peut-être un service de travail de base

La gestion de ces questions individuellement avec docker run les commandes deviennent inefficaces. Docker Compose règle ce problème.

Complément Real-Exemple mondiale: Production Blog Platform

Voici ce qui suit : production réelle docker-compose.yml qui exploite ce même site (mostlylucid.netM SK2

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

Principaux modèles de production:

  1. Réseau unique: Tous les services sur app_network pour la simplicité
  2. Établissement d'un réseau de télécommunications: Voies d'accueil (/mnt/*) pour les données persistantes que vous devez sauvegarder
  3. Volumes désignés: Docker-entreposage géré pour les données que vous n'avez pas
  4. Étiquettes de la tour d'observation: Seules les services de mise à jour marqués explicitement pour l'auto
  5. Limites de ressources: Les contraintes du CPU sur le service de traduction évitent la faim des ressources
  6. La cartographie externe des ports: PostgreSQL sur 5266 au lieu de 5432 pour éviter les conflits avec d'autres instances
  7. Les fichiers environnementaux: Secrets in .env fichier (ne s'est jamais engagé à git)

Caractéristiques clés du Docker Compose

Dépendances en matière de services

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

Docker Compose orchestre l’ordre de démarrage. Le condition: service_healthy Il faut que le contrôle de santé de la base de données passe avant de lancer l’application Web.

Variables environnementales et secrets

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

Pour les secrets de production, utilisez Docker Secrets ou des gestionnaires secrets externes (AWS Secrets Manager, Azure Key VaultM SK3 HashiCorp Vault≥).

Volumes désignés vs Montages soudées

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

Volumes désignés:

  • géré par Docker
  • Les données persistantes à travers le contenant reprend
  • Possibilité de sauvegarde/restored avec les commandes Docker
  • Cross-compatible avec la plateforme

Montants d'engrenage:

  • Planification directe vers le système de fichiers d'hôte
  • Utilisable pour le développement (rechargement de code vivantM SK1
  • Fichiers de configuration, logs, téléchargements

Réseautage

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

services:
  web:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend  # Not exposed to frontend

Réseaux assurent l’isolement. ici, la base de données n’est accessible que pour les services de backendM SK2 non directement exposéeMSC3

Examens de santé

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

Les vérifications de santé permettent à Docker de:

  • Déterminer si un contenant est effectivement prêt (non seulement commencé)
  • Démarrer les contenants non sains
  • Fournir le statut aux orchestrateurs (KubernetesM SK1 Docker Swarm)

Commandes de rédaction communes du Docker

# 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

Développement vs Production Composer les fichiers

Préoccupations séparées avec plusieurs fichiers de compose:

docker-composition.yml (baseM SK1

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

docker-composition.overrideM SK2yml (développement

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

docker-composition.prodM SK2yml (productionM SK1

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

Support GPU dans Docker: Accélérer les charges de travail ML

Apprentissage à la machine, calculs scientifiques , et applications de traitement vidéo nécessitent souvent une accélération des GPUM SK2 Docker appuie les GPU NVIDIA par l’entremise du jeu d’outils pour conteneurs NVIDIA.

Établissement de la trousse d'outils pour le conteneur NVIDIA

# 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

Dockerfile for GPU-Accelerated Python/PyTorch Application

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

Contenants GPU en cours

# 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

GPU dans Docker Compose

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

Real-Exemple mondiale: Service de traduction avec GPU et CPU Builds

Voici un exemple de production réelle de ' mostlylucid-nmt, un service de traduction des machines neuronales que j’ai construit qui permet à l’auto-translation sur ce blogM SK2

Le projet montre:

  • Variations GPU et CPU - La même base de code, différentes images de base
  • Les architectures multi- - Supporte AMD64 et ARMM SK2
  • Images de Docker optimisées - Variations complètes et minimales
  • Production-réaliste - Contrôles de santé, persistance du volumeM SK2 signalement approprié

GPU-Service de traduction accélérée

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:

CPU-Autres options seulement

Pour les environnements sans GPU, le même service fonctionne sur CPU:

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:

Variations d'image disponibles:

  • scottgal/mostlylucid-nmt:gpu - CUDA 12.6 avec support de GPU PyTorch (~5GB)
  • scottgal/mostlylucid-nmt:cpu - CPUM SK1seulement, impression plus petite ~2.5GBMSC4
  • scottgal/mostlylucid-nmt:gpu-min - Construction minimale d'un GPUM SK1 aucun modèle préchargé (~4GB)
  • scottgal/mostlylucid-nmt:cpu-min - Construction minimale de la CPU (~1.5GB)

Principales caractéristiques:

  • Accélération du GPU: 10-15x traduction plus rapide avec CUDA
  • Modèle Auto-Trouver: Télécharge les modèles de traduction sur la demande
  • Soutien en cas de rechute: Tries OpusM SK1MT → mBART50 → MMSC5MMST6 pour une couverture linguistique maximale
  • Persistence du volume: Démarrage des modèles de cache à travers le réservoir
  • Points finals de la santé: /health et /ready pour les orchestrateurs
  • Multi-Architecture: Fonctionne sur xM SK1 et ARM64 (Apple SiliconMSC4 Raspberry PiMST5

Voir le projet complet sur GitHub pour les exemples de Dockerfile, scripts de construction, et documentation APIM SK2

Multi-Architecture Builds avec Docker Buildx

Les applications modernes doivent fonctionner sur plusieurs architectures.

Pourquoi la multi-Architecture est importante

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

Établissement de Buildx

Docker Buildx est inclus dans Docker Desktop. Pour Linux:

# 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

Multi-Architecture Dockerfile

La plupart des Dockerfiles fonctionnent sans changements, mais voici quelques conseils:

# 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

Établissement pour plusieurs plates-formes

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

Multi-Architecture avec Docker Compose

Malheureusement, Docker Compose ne supporte pas directement le buildxM SK2 Méthodes de travail:

Option 1: Pré-montrer les images

# 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

Option 2: Ébauche de script

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

Exemple réelle-Exemplaire mondial:ICM SK2Pipeline CD

GitHub Actions workflow for multi-architecture builds:

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

Ce flux de travail:

  • Triggers sur les pushes à l'onglet principal ou aux tags de version
  • Établissement de QEMU pour l’émulation transversale de platforms
  • Équipements pour AMD64 et ARM64
  • Génie automatiquement les balises (nom de la brancheM SK1versions sémantiques,SHAMSC3
  • Utilise le caching du registre pour les éditions plus rapides

Modèles de construction avancés du docker: Réel-Exemple mondiale

L'article 5 de la Loi sur l'immigration et le statut des réfugiés mostlyucid-nmt Ce projet montre comment construire et distribuer plusieurs variantes d'image à partir d'une base de code unique.

Établir une matrice de stratégie pour les variables

Le projet produit huit variantes d'image différentes pour les différents cas d'utilisation:

Tag d'image Architecture Accélérateur Préchargement du modèle
:cpu (:latest) AMDM SK2 ARM64 CPU 5 Oui Modèles 6 7 8 9 GB 10 Déploiements de CPU de production 11
:cpu-min AMDM SK1 ARM64 CPU Non ~1.5GB \ Chargement de modèles dynamiques ≥
:gpu AMDM SK1 seulement NVIDIA CUDA CSK3 Oui Models de (~100) SSK6 ~5GB SSK8 Déploiement des GPU de production
:gpu-min AMD64 seulement NVIDIA CUDA Non M ~4GB S GPU avec modèles dynamiques R

Pourquoi plusieurs variantes?

  1. Production (fullM SK1: Modèles préchargés = traduction instantanée
  2. Développement (minimalM SK1: Équipements rapides (~30s par rapport aux modèles montés
  3. GPU: 10-15x plus rapide pour les scénarios de hauts débitsM SK2
  4. CPU: Fonctionne partout, aucun matériel spécial
  5. ARM64: Raspberry PiM SK1 Apple Silicon, Serveurs ARM

Stratégie de stockage sous couche

Les Dockerfiles utilisent l’ordre des couches stratégiques pour maximiser la réutilisation du cache de construction:

Structure standard de Dockerfile du CPU:

FROM python:3.12-slim

# Layer 1: System dependencies (rarely changes)
RUN apt-get update && apt-get install -y --no-install-recommends \
    git curl \
    && rm -rf /var/lib/apt/lists/*

# Layer 2: Python dependencies (changes when requirements.txt updates)
WORKDIR /app
COPY requirements-prod.txt .
RUN pip install --no-cache-dir -r requirements-prod.txt

# Layer 3: Model preloading (slowest layer, changes when model list changes)
ARG PRELOAD_LANGS="de,es,fr,it,nl,pl,pt,ru,zh,ja,ar,uk"
ARG PRELOAD_PAIRS=""
RUN python -c "from easynmt import EasyNMT; \
    import os; \
    langs = os.environ.get('PRELOAD_LANGS', '').split(','); \
    pairs = os.environ.get('PRELOAD_PAIRS', '').split(',') if os.environ.get('PRELOAD_PAIRS') else []; \
    model = EasyNMT('opus-mt'); \
    [model.translate('warmup', source_lang='en', target_lang=lang) for lang in langs if lang]; \
    [model.translate('warmup', source_lang=src, target_lang=tgt) for pair in pairs for src,tgt in [pair.split('->')] if pair]"

# Layer 4: Application code (changes frequently, rebuilds in seconds)
COPY app.py .
COPY src/ ./src/

# Runtime configuration
EXPOSE 8000
ENV PYTHONUNBUFFERED=1 \
    MODEL_CACHE_DIR=/app/models \
    WEB_CONCURRENCY=4

CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000", \
     "--worker-class", "uvicorn.workers.UvicornWorker", \
     "--timeout", "300", "--graceful-timeout", "5"]

Pourquoi cette ordonnance est-elle importante:

Layer Frequence de reconstitution MSC2 Temps de construction MSC3 Taux d'interruption du cache SMC4
Image de base Néanmoins Python2à moins que les versions ne changent
Défaillances du système Rarement
Des défaillances de Python Occasionnellement (Mises à jour des dépendances) M ~60s
Précharge du modèle Rarement changements de la liste des modèlesM SK3 M 5-10min
Code d'application Tous les engagements

Résultat: La plupart des mises en place sont terminées dans ~3 secondes à l'aide de couches cachées

GPU vs CPU: Différences d'image de base

Dockerfile de CPU:

FROM python:3.12-slim

# CPU-optimized PyTorch (smaller, no CUDA)
RUN pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu

# CPU performance tuning
ENV OMP_NUM_THREADS=4 \
    MKL_NUM_THREADS=4 \
    EASYNMT_BATCH_SIZE=16

Dockerfile GPU (Dockerfile .gpu):

FROM nvidia/cuda:12.6.2-cudnn-runtime-ubuntu24.04

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

# CUDA-enabled PyTorch matching CUDA 12.6
RUN pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124

# GPU performance tuning
ENV CUDA_VISIBLE_DEVICES=0 \
    EASYNMT_BATCH_SIZE=64 \
    EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}' \
    MAX_INFLIGHT_TRANSLATIONS=1

# Runtime needs GPU access
# docker run --gpus all scottgal/mostlylucid-nmt:gpu

Principales différences:

  • Image de base: python:3.12-slim (300MBM SK1 vs nvidia/cuda:12.6.2-cudnn-runtime (1.8GBM SK1
  • Voie PyTorch: CPU (~140MB) vs CUDA
  • Taille des lots: CPUM SK1 vs GPU=64 (Les GPUs gèrent de façon efficace des lots plus importantsMSC4
  • Précision: CPUM SK1FP32 par rapport à GPUMSC3FP~16 ( la moitié de la précision permet d'économiser la mémoire GPU

ARM64 Optimisations

Dockerfile. inclut ARM-ajustement spécifique:

FROM python:3.11-slim-bookworm  # Debian Bookworm has better ARM support

# Minimal dependencies (ARM devices often storage-constrained)
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc g++ git curl \
    && rm -rf /var/lib/apt/lists/*

# ARM performance tuning (Raspberry Pi 4/5 with 4GB RAM)
ENV OMP_NUM_THREADS=3 \
    MKL_NUM_THREADS=3 \
    OPENBLAS_NUM_THREADS=3 \
    EASYNMT_BATCH_SIZE=4 \
    MAX_CACHED_MODELS=1 \
    MEMORY_CRITICAL_THRESHOLD=70.0

# Single worker (memory constrained)
CMD ["gunicorn", "app:app", "-w", "1", \
     "--bind", "0.0.0.0:8000", \
     "--timeout", "300"]

ARM-optimisations spécifiques:

  1. Réduction du filage: 3 threads (vs S4 on xM SK4 for thermal throttling
  2. Échantillons plus petits: 4 vs 16 sur xM SK3 ≥(RAM limité)
  3. Employé unique: Prévient la SOOM sur les dispositifs 4GB
  4. Surveillance de la mémoire: Nettoyage agressif à l'utilisation de RAM 70%
  5. Démarrages conservateurs: Les périphériques ARM sont plus lentes , permettront davantage de temps

Variants minimaux: Chargement du modèle de temps d'exécution

Dockerfile.min approche:

FROM python:3.12-slim

# Dependencies only, NO model preloading
COPY requirements-prod.txt .
RUN pip install --no-cache-dir -r requirements-prod.txt

# Application code
COPY app.py .
COPY src/ ./src/

# Models loaded on-demand from volume
ENV MODEL_CACHE_DIR=/models

VOLUME ["/models"]

Utilisation avec mount de volume:

# Create persistent model cache
docker volume create translation-models

# First run: downloads models on-demand
docker run -d \
  -v translation-models:/models \
  -p 8888:8000 \
  scottgal/mostlylucid-nmt:cpu-min

# Subsequent runs: reuse cached models
# Same volume mount, instant startup

Minimale vs Commerce complet-offs:

Aspect Image complète MSC2 Image minimale MSC3
Taille de l'image 2.5GB
Temps de construction 5-10 minutes
Premier début Traduction immédiate MSC2 téléchargement min de modèle SMC3
Démarrages subséquents instantaneus instantanés MSC2 si le volume persisteMSc3 MSc4
Utilisation du disque 2.5GB par contenant
Développement itération lente iteration rapide
Production Aucune dépendance de réseau Besoins d'une persistance du volume

Scripts de construction multi-Variant automatisé

construction-total.sh construit toutes les variantes avec une version uniforme:

#!/bin/bash

# Generate version tags
VERSION=$(date +%Y%m%d.%H%M%S)
GIT_SHA=$(git rev-parse --short HEAD)

# Enable buildx for multi-platform
docker buildx create --use --name multiarch 2>/dev/null || true

echo "Building all variants for mostlylucid-nmt:${VERSION}"

# CPU Full (multi-platform)
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -f Dockerfile \
  -t scottgal/mostlylucid-nmt:cpu \
  -t scottgal/mostlylucid-nmt:latest \
  -t scottgal/mostlylucid-nmt:${VERSION} \
  --build-arg VERSION=${VERSION} \
  --build-arg VCS_REF=${GIT_SHA} \
  --push \
  .

# CPU Minimal (multi-platform)
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -f Dockerfile.min \
  -t scottgal/mostlylucid-nmt:cpu-min \
  -t scottgal/mostlylucid-nmt:${VERSION}-min \
  --build-arg VERSION=${VERSION} \
  --push \
  .

# GPU Full (AMD64 only)
docker build \
  -f Dockerfile.gpu \
  -t scottgal/mostlylucid-nmt:gpu \
  -t scottgal/mostlylucid-nmt:${VERSION}-gpu \
  --build-arg VERSION=${VERSION} \
  --build-arg PRELOAD_LANGS="de,es,fr,it,nl,pl,pt,ru,zh,ja,ar,uk" \
  --push \
  .

# GPU Minimal (AMD64 only)
docker build \
  -f Dockerfile.gpu.min \
  -t scottgal/mostlylucid-nmt:gpu-min \
  --push \
  .

echo "All builds complete!"
echo "Images tagged with version: ${VERSION}"

Avantages de l’automatisation des bâtiments:

  1. Versionnement cohérent: Timbre + Git SHA pour la traçabilité
  2. Multi-platform: Ensembles de commande unique pour AMD64 et ARMM SK2
  3. Publication parallèle: Toutes les variantes sont poussées simultanément
  4. Préchargement souple: Contrôle des args pour construire les modèles à inclure
  5. CI/CD prêt: peut être déclenché par les actions de GitHub

Scénarios d'utilisation pratique

Scénario 1: Développement (Iteration rapideM SK2

# Use minimal image with volume mount
docker run -d \
  -v ./models:/models \
  -v ./src:/app/src \
  -p 8888:8000 \
  scottgal/mostlylucid-nmt:cpu-min

# Edit code locally, container picks up changes
# Models cached in volume, no re-download

Scénario 2:CPU de production (haute disponibilité)

# Use full image, no volume needed
docker run -d \
  --restart always \
  --memory=2g \
  --cpus=2 \
  -p 8888:8000 \
  scottgal/mostlylucid-nmt:cpu

# Models preloaded, instant translation
# No external dependencies

Scénario 3: Production GPU (high throughputM SK2

# docker-compose.yml
services:
  translation:
    image: scottgal/mostlylucid-nmt:gpu
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    environment:
      - CUDA_VISIBLE_DEVICES=0
      - EASYNMT_BATCH_SIZE=64

Scénario 4: ARMM SK1 déploiement à bord (Raspberry Pi)

# Automatic platform selection
docker run -d \
  --restart always \
  --memory=3g \
  -p 8888:8000 \
  scottgal/mostlylucid-nmt:cpu

# Docker pulls ARM64 variant automatically
# Optimized for ARM performance

Principaux points à tirer des modèles de construction avancés

  1. Le caching de la couche est essentiel: Commander les instructions du Dockerfile par changement de fréquence
  2. Construire des variantes qui répondent à différents besoins: Production vs développement vs matériel restreint
  3. Multi-appui à la plateforme: Utiliser buildx pour AMD64 + ARMM SK3 à partir d'un seul Dockerfile
  4. Préchargement par rapport à la charge du temps d'exécution: Temps de construction du commerce pour le temps de démarrage
  5. Architecture-Optimisation spécifique: Fabrication de threads en tuneM SK1 Taille des lots, Limites de mémoire par plate-forme
  6. Questions d'automatisation: Les scripts assurent la cohérence entre les variantes
  7. Version tout: Timestamps + Git SHA pour la traçabilité
  8. Stratégie de volume: Volumes de cache persistants pour économiser le temps de régénération

Ces modèles provenant de la plupartucid-nmt démontrent la production-les pratiques de Docker prêtes applicables à toute application complexe avec plusieurs scénarios de déploiement .

.NET 9 Amélioration des conteneurs

.NET 9 introduit d'importantes améliorations au support des conteneurs, rendant plus facile que jamais de contenant les applications .NET sans même écrire un Dockerfile

Élaboré- dans Container Publishing

Avec .NET 9, vous pouvez publier directement une application contenant

# 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

Configuration des propriétés du conteneur

Ajouter à votre .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.source">https://github.com/me/myapp</ContainerLabel>
  </PropertyGroup>
</Project>

Puis publier:

dotnet publish -p:PublishProfile=DefaultContainer

Avantages de la prise en charge des conteneurs -

  1. Aucun Dockerfile n'est nécessaire: Réduit la complexité
  2. Images optimisées: Microsoft ajuste les images pour leur performance
  3. Cohérence: Images standard dans toutes les applications .NET
  4. Sécurité: Mise à jour automatique des images de base
  5. Images plus petites: Soutien à la compilation du trimming et de l'OT

Trimming et AOT pour des images plus petites

<PropertyGroup>
  <!-- Enable trimming to remove unused code -->
  <PublishTrimmed>true</PublishTrimmed>
  <TrimMode>full</TrimMode>

  <!-- OR use Native AOT for even smaller, faster images -->
  <PublishAot>true</PublishAot>
</PropertyGroup>

Résultats:

  • Norme: ~220MB
  • Avec le triage: ~110MB
  • Avec AOT: ~12MB pour les applications simplesM SK2

Comparaison: Dockerfile vs Built-in

Approche traditionnelle de Dockerfile:

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

Équipement construit-approche en conteneur:

# Just this!
dotnet publish -p:PublishProfile=DefaultContainer

Les deux produits produisent des images similaires, mais l’approche structurée-in est plus simple et plus entretenable .

Quand utiliser Dockerfile vs Built-in

Utiliser Dockerfile lorsque:

  • Vous avez besoin du contrôle complet de l'image
  • Installation des dépendances du système
  • Structures complexes multi-stage
  • Non-images de base standard

Utiliser l’architecture édifiée-in lorsque:

  • Applications standard .NET web/console
  • Vous voulez la simplicité
  • Utiliser les optimisations de Microsoft
  • Fabrication rapide de prototypes

Chiseled Ubuntu Images

.NET 9 appuie "chiseled"images Ubuntu

<PropertyGroup>
  <ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:9.0-noble-chiseled</ContainerBaseImage>
</PropertyGroup>

Avantages:

  • Petite: ~50% plus petits que les images normales
  • Sécurité: Surface d'attaque minimale (aucun gestionnaire de paquetsM SK2shell)
  • Soutien rapide: Appuie et démarre plus rapidement

Commerce-offs

  • Il n'y a pas de coque (qui est plus difficile à débuger)
  • Utilités de réseau limitées

Perfect pour la production où la sécurité et la taille sont les plus importantes.

Introduction à .NET Aspire

.NET Aspire est un groupe d’experts opinionésM SK1 nuage - boucle prête à bâtir des applications distribuées. Pensez-le comme Docker Compose sur stéroïdesMSC4 spécialement conçu pour les microservices de

Qu'est-ce qu'Aspire?

Aspire fournit:

  1. Orchestrement: Faire fonctionner et relier plusieurs services localement
  2. Dévoilement des services: Les services se trouvent mutuellement automatiquement
  3. Télémétrie: Équipement construit- enregistrement interneM SK2 métriques , traçage
  4. Components: Pré-Intégrations configurées (RedisM SK3 PostgreSQLMST4 RabbitMQMSC5 etc MST6
  5. Déploiement: Générer des Kubernetes / Manifestes de compilation du Docker

Architecture d'Aspire

Une solution Aspire comporte trois types de projets:

  1. App Host: Orchestre vos services
  2. Projets de services: Vos services réels
  3. Défaults du service: Configuration partagée

Exemple rapide

1. Créer un projet Aspire:

dotnet new aspire-starter -n MyDistributedApp
cd MyDistributedApp

2. App Host MyDistributedApp.AppHostM SK3ProgramMSC4csMST5

var builder = DistributedApplication.CreateBuilder(args);

// Add Redis cache
var cache = builder.AddRedis("cache");

// Add PostgreSQL database
var db = builder.AddPostgres("postgres")
               .AddDatabase("mydb");

// Add API service (references cache and db)
var api = builder.AddProject<Projects.MyApi>("api")
                 .WithReference(cache)
                 .WithReference(db);

// Add frontend (references API)
builder.AddProject<Projects.MyWeb>("web")
       .WithReference(api);

builder.Build().Run();

3. Utiliser dans votre service:

// MyApi/Program.cs
var builder = WebApplication.CreateBuilder(args);

// Aspire automatically configures these based on AppHost
builder.AddServiceDefaults();
builder.AddRedisClient("cache");
builder.AddNpgsqlDbContext<MyDbContext>("mydb");

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

4. Faire tout

dotnet run --project MyDistributedApp.AppHost

Aspire lance des missiles

  • Le tableau de bord à http://localhost:15888
  • Tous les services avec une configuration appropriée
  • Redis et PostgreSQL dans les conteneurs
  • Traçage réparti entre les services

Aspire vs Docker Compose

Docker Compose:

  • Langue-agnostique
  • Infrastructure-focused
  • Détection manuelle du service
  • Trouvez votre propre observabilité

Aspire:

  • .NET-specific
  • Développement
  • Détection automatique du service
  • Méthode de télémétrie intégrée
  • génère des manifestes de déploiement

Utiliser les deux: Aspiration pour le développement local , produit Docker Compose/Kubernetes pour la productionM SK3

Mettre en place des applications Aspire

Générer les manifestes de déploiement:

# 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

Puis déployer:

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

# Kubernetes
kubectl apply -f deploy/k8s/

Components d'Aspire

Pré-les intégrations construites rendent l’ajout de services trivial

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

Self-Hosting on Limited Resources: Optimisation pratique

L'exploitation d'un stade de pleine observabilité comme celui ci requiert des ressources importantes. Si vous 'êtes vous-mêmesM SK2vous êtes hébergé sur un VPS avec 4GB RAM ou un vieux ordinateur portableMスク4 voici des stratégies pratiques pour réduire la consommation de ressources tout en maintenant la fonctionnalitéMSC5

Dependences de développement seulement

Pour le développement local, vous n’avez pas besoin de la totalité du plate-forme de production devdeps-dockerM SK1compose.yml approche ne fonctionne que ce dont vous avez besoin:

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

Pourquoi cela fonctionne pour le développement:

  • SMTP4Dev: Tester la fonctionnalité du courrier électronique sans un serveur SMTP réel
  • PostgreSQL: Base de données sur la production correspondant
  • Total des empreintes de pied: ~200MB RAM vs 2-4GB pour le stack complet

Voir le Guide des dépendances de développement complet pour les instructions de configuration.

Ressource-Établissement de production restreinte

Aux fins d’un budget VPS (2-4GB RAM ), Prioriter les services essentiels:

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:

Qu’est-ce qui a été supprimé et quelles sont les solutions de rechange?

  • Aucun Prometheus/Grafana: Utiliser la surveillance externe (UptimeRobotM SK2 BetterStack free tier)
  • Aucune séquence: Utiliser le fichier-logage basé sur les données + docker logs, ou Seq Cloud, niveau gratuit
  • Aucune tour d'observation: Mise à jour manuelle avec les notifications des actions de GitHub
  • Aucun service de traduction: Suivre sur-à la demande dans un contenant séparé que vous démarrezM SK2arrêter manuellement
  • Limites de ressources: Protéger tout service unique de la consommation de toute mémoire

Stratégie d'amélioration progressive

Démarrer minimale, ajouter les services au besoin:

Étape 1: Core (512MBM SK2GB VPS)

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

Étape 2: Ajouter l’observabilité (2GB VPSM SK2

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

Étape 3: Équipe complète (4GBM SK2 VPS)

# Add everything
docker-compose up -d

Techniques d'optimisation des ressources

1. Utiliser des images alpines

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

Épargnes: Les images alpines sont plus petites 50-70%

2. Instance de base de données partagée

Au lieu d’une base de données par service, utiliser une instance PostgreSQL avec plusieurs bases de données:

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

Épargnes: 400MB RAM par base de données supplémentaire que vous consolidez

3. Limite CPU pour les services de base

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

Cela empêche les tâches de fond d'être faimées par votre application Web.

4. Volumes de cache persistants

Tel qu'il est indiqué dans ImageSharp avec Docker, le montage des répertoires de cache permet d'éviter un retraitement inutile

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

Pourquoi cela est important: Sans cela, chaque nouveau démarrage du contenant régénére toutes les images miniaturesM SK2images traitées .

5. Utiliser des services externes ( Niveaux gratuits)

Service Automne
Seq MSC2MB MSC3 Cloud Seq
Prometheus + Grafana MSC2 MSC3MB SMC4 Cloud de la Grafana
Umami ~200MB SMK3 Plausible (payé) ou lui-mêmeM SK6 hôte ailleurs SSK7

Stratégie: Offload observability to free tiers, keep core application on your VPS .

6. Sur-Services à la demande

Pour les services peu utilisés comme la traduction:

# 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

Épargnes: 1-2GB RAM lorsque le service de traduction n’est pas en cours

Real-Exemple d’hébergement budgétaire mondial

Voici ce qui fonctionne sur un $6/month Hetzner VPS.

# 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

Utilisation totale des ressources:

  • RAM: M SK1MB (levées 3.2GB libres)
  • Disque: ~2GB
  • CPU: M SK1 en mode d'utilisation

Qu’est-ce qui diffère ?

  • Aucun Prometheus/Grafana (utilisez UptimeRobot + Cloudflare AnalyticsM SK3
  • Aucun Seq (utiliser docker logs + grep occasionnelM SK1
  • Aucune Umami (utiliser Cloudflare Web Analytics - gratuitM SK2
  • Contrôles horaires au lieu de tous les 5 minutes
  • Aucun service de traduction (suivre manuellement lorsque nécessaire)

Surveillance du budget

Sans Prometheus/Grafana, utilisez ces solutions de rechange gratuitesM SK2

Surveillance de la santé:

# 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

Surveillance du log:

# 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

Utilisation des ressources:

# Quick resource check
docker stats --no-stream

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

Version Aspire: La voie .NET

Maintenant, laissez 's réimaginer l’ensemble de la pile en utilisant .NET Aspire. Ce qui vous donne tous les avantages d’orchestrement avec une meilleure

Setting Up Aspire for Mostlylucid

Premièrement, créer l’Aspire App Host:

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

Mostlylucid.AppHost/Programme .csM SK3

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

Défaults du service Aspire

Créer Utilisation des services par défaut Projet:

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

Mise à jour Mostlylucid/Program.cs

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

Avantages de l'approche Aspire

Expérience de développement:

  1. Commande unique: dotnet run --project Mostlylucid.AppHost Tout débute
  2. Tableau de bord: Beautiful UI at httpM SK1localhost:15888 showingMSC3
    • Tous les services dont le statut est vivant
    • Logs de tous les services en un seul endroit
    • Traces répartis entre les services
    • Mesures et contrôles de santé
  3. Dévoilement des services: Les services se trouvent mutuellement automatiquement par le biais de noms
  4. Configuration: Centralisé dans AppHost, pas plus .env Juggling

Déploiement de la production:

Générer des manifestes de déploiement à partir d’Aspire:

# 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

Docker généré-composition.yml (simplifiéM SK1

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 vs Traditional Docker Compose

Caractéristiques Composeur de docker MSC2 SMC3NET Aspire S
Mise en place Manuel YAML Code C# avec IntelliSense
Dévoilement des services Envelope manuelle vars
Observabilité Portez votre propre Construit-dans MSC3OpenTelemetryMSC4 SMC5
Développement docker-compose up dotnet run Tableau de bord +
Débugging Attacher au contenant FM SK2 dans l'atelier visuel MSC3
Logs docker logs Tableau de bord centralisé
Traçage Établissement manuelle Traçage distribué automatique
Production Utiliser YAML directement Gérer des manifestes
Curve d'apprentissage syntaxe YAML CM SK2 vous connaissez déjà

Quand utiliser Aspire vs Docker Compose

Utiliser Aspire lorsque:

  • Établissement de microservices .NET
  • Vous voulez un débugging intégré
  • L’équipe est confortable avec C#
  • Vous avez besoin d'un traçage distribué de la boîte
  • Vous'vous déployez des applications de conteneurs Azure (appui natifM SK2

Utiliser Docker Compose lorsque:

  • Services de polyglot (Node.js , PythonM SK3 GoMST4 etcMSC5
  • L’équipe privilégie l’infrastructure-as-code YAML
  • Déploiement sur un hôte compatible Docker-
  • Vous avez besoin d'un contrôle maximal sur la configuration du contenant
  • Simples déploiements de service unique-

Utiliser les deux :

  • Aspirer au développement local
  • Generate Docker Compose pour le déploiement de la production
  • Le meilleur des deux mondes!

Évolution de ce blog's Docker Setup

Ce blog's Voyage du Docker montre une progression typique:

  1. Juillet 2024 - Début simple: Juste l’applicationM SK1 Cloudflared, et Watchtower
  2. Août 2024 - Dépendances en matière de développement: Développement additionnel -seulement services
  3. Août 2024 - Fixation de la compression d'image: Permis de volume résolus pour le caching
  4. Novembre 2024 - Stock complet: Observabilité complète avec Prometheus, GrafanaM SK2 Seq
  5. Aujourd'hui: Option Aspire pour le premier développement

Les leçons apprises:

  • Démarrer simple, ajouter la complexité seulement lorsque nécessaire
  • Séparer les configurations dev et de production
  • Les limites des ressources empêchent un service de tuer d'autres
  • Les montages de volume pour les caches permettent d'économiser un temps considérable de retraitement.
  • La tour d'observation permet des mises à jour zéro-downtime auto-
  • L’observabilité vaut le coût des ressources en production

Résumé des meilleures pratiques

Les meilleures pratiques en matière de dockerfile

  1. Utiliser des constructions multi-stage
  2. Run as non-root user
  3. Instructions d'ordre pour le caching optimal
  4. Utiliser des balises d'image de base spécifiques (non latest)
  5. Inclure les contrôles de santé
  6. Utilisation .dockerignore pour exclure les fichiers inutiles
  7. Réduire au minimum les couches (combiner les commandes RUN)
  8. Utiliser les arguments de construction pour la souplesse

Docker Compose Best Practices

  1. Utiliser des volumes désignés pour les données
  2. Mettre en œuvre des contrôles de santé
  3. Utilisation .env pour les secrets (never commit)
  4. Définir les réseaux explicites
  5. Utilisation depends_on avec des conditions de santé
  6. Établir les politiques de remarrage
  7. Séparer les configurations dev/prod
  8. Limites de ressources dans la production

Pratiques exemplaires en matière de sécurité

  1. Run as non-root user
  2. Utiliser les images de base minimales chiseled/
  3. Scanner les images pour des vulnérabilités
  4. Garder les images de base à jour
  5. Don' ne contiennent pas de secrets dans les images
  6. Utiliser read-seulement les systèmes de fichier lorsque possible
  7. Limiter les capacités des conteneurs
  8. Utiliser les secrets de Docker ou des gestionnaires externes

Pratiques exemplaires en matière de rendement

  1. Utiliser BuildKit pour des constructions plus rapides
  2. Cache de la couche d'élévation
  3. Multi-stage construit pour réduire la taille de l'image
  4. Utilisation .dockerignore généreusement
  5. Alpine/images chiselées pour plus petite taille
  6. Utiliser les montages de volume pour le développement
  7. Configurer les limites de ressources appropriées
  8. Utiliser des vérifications de santé pour l'orchestrement

Troubleshooting Common Issues

Contenant Won't Démarrage

# 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

Fabrications lentes

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

Questions relatives aux réseaux

# 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

Questions relatives aux permis de volume

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

Conclusion

Docker a évolué d’un outil de contenant simple à une plate-forme globale pour la construction, transports, et l’exploitation des applications modernes . Ce guide vous a conduit des fondations à la productionM SK3 déploiements déjà effectuésSSK4 avec des exemples réels-world depuis la mise en oeuvre principalementlucidMST6netSST7

Key Takeaways

Pour les débutants:

Pour les Autochtones-Présidents sur le budget VPS:

  • Démarrer minimale: App + Base de données + Proxy inverse (~800 RAMMBM SK4
  • Utiliser des images alpines et les limites de ressources
  • Offload observability to free tiers (Seq CloudM SK1 Grafana Cloud , UptimeRobot)
  • Exécuter des services coûteux (traduction, MLM SK2 sur -demand seulement
  • Un VPS mensuel $6/ peut gérer un blog de production avec des espaces à consacrer

Pour les déploiements de production:

  • Les vérifications de santé permettent la mise à jour du temps d’arrêt zero- avec Watchtower
  • Réseaux séparés assurent la sécurité (frontend/backend isolationM SK2
  • Montants de joints pour les données que vous devez sauvegarder/access
  • Volumes désignés pour le stockage géré par Docker-
  • CPU/limites de mémoire empêchent la faim des ressources
  • Cloudflare Tunnel élimine le besoin d'adresses IP publiques

Pour .Developpereurs NET:

  • Élaboré - dans l’édition de conteneurs en .NET 9 élimine les Dockerfiles pour des applications simples
  • Les images Chiseled Ubuntu fournissent une surface d'attaque minimale
  • .NET Aspire offre les meilleures expériences de développement local de la classe
  • Aspire peut générer Docker Compose pour le déploiement de la production
  • L'intégration de OpenTelemetry est gratuite avec Aspire

Cases d'utilisation avancée:

  • Les contenants GPU permettent la charge de travail ML/AI avec une vitesse accrue 10-15x
  • Multi-architecture construit des supports ARM64 et AMDM SK2 à partir d'une source unique
  • GitHub Actions automatise les constructions multi-platform
  • Le caching des couches accélère considérablement les constructions répétées

Le voyage

Ce blog's L’évolution du Docker témoigne de la progression typique:

Étape Services MSC2 Usage du RAM MSC3 Complexité SMC4
Étape 1 (juillet MSC1 MSC2 App SMC3 Tunnel ESC4 \MSC5MB \ESC6 Simple ESC7
Étape 2 (Août 2024) 3 Dépendances de développement 4 5 MB 6 Apprentissage
Étape 3 (Août 2024) SMK2 \ + Révisions de volume ~500MB Débugging
Étape 4 (Nouveau 2024)
Étape 5 (À l'heure actuelle) ≥+ Option d'aspiration Variable Moderne S

Le modèle: Commencer par simpler, résoudre les problèmes à mesure qu’ils se posentM SK2 ajouter la complexité seulement lorsque nécessaireMSC3

Qu’est-ce que's Suivant?

Selon votre chemin:

  • Docker d'apprentissage: Commencez par Docker Compose basics
  • Self-Hosting: Essayez la configuration minimale de VPS à partir de cet article
  • Établissement de microservices: Explore .NET Aspire
  • Exécuter les charges de travail du ML: Consultez le mostlylucid-nmt service de traduction
  • Multi-platform: Établir un buildx pour les builds ARM64 + AMDM SK3

Ressources

Documentation officielle:

Ce blog's Docker Series:

  1. Docker Compose - Début
  2. Dépendances en matière de développement - Établissement du dév local
  3. ImageSharp avec Docker - Permis de volume
  4. Cet article - plongée profonde dans la production

Projets connexes:

Rappelez-vous: La meilleure architecture est celle qui répond à vos besoins sans trop de complexité.

Contenants heureux! 🐳

logo

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