# Immersione profonda di sviluppo del docker: dalle basi alla Containerizzazione avanzata .NET

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

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

Docker ha fondamentalmente trasformato il modo in cui costruiamo, navighiamo ed eseguiamo le applicazioni.

Ciò che è iniziato come un semplice strumento di containerizzazione si è evoluto in un ecosistema completo per lo sviluppo e l'implementazione di applicazioni moderne.

1. **[Questo è il quarto e più completo articolo della mia serie Docker per gli sviluppatori .NET.](/dockercompose)**Se sei nuovo a Docker, potresti iniziare con:
2. **[Docker Componi](/dockercomposedevdeps)**- Iniziare con le configurazioni di base multi-container (luglio 2024)
3. **[Dipendenze di sviluppo con Docker Compose](/imagesharpwithdocker)**- Configurazione di ambienti dev locali (agosto 2024)

Condivisione immagini con Docker

- **- Risoluzione dei problemi di autorizzazione del volume (agosto 2024)**In questa immersione profonda, costruiremo su quelle fondamenta per esplorare:
- **Componi Docker pronto per la produzione**: Lo stack reale che esegue questo blog
- **Auto-ospitalità su risorse limitate**: Tecniche pratiche di ottimizzazione per le implementazioni VPS di bilancio
- **Supporto GPU**: Esecuzione di carichi di lavoro ML in contenitori
- **Costruzioni multi-architettura**: Supporto ARM64 e AMD64
- **.NET 9 caratteristiche del contenitore**: Pubblicazione integrata di container senza Dockerfiles

> .NET Aspire

: L'approccio moderno .NET all'orchestrazione dei container

[TOC]

## NOTA: Questo fa parte dei miei esperimenti con AI / un modo per spendere $1000 Claude Code Crediti Web.

### Ho dato a questo un sacco di documenti, la mia comprensione, le domande che ho dovuto generare questo articolo.

E' divertente e riempie un vuoto che non ho visto riempito da nessun'altra parte.

Sia che stiate implementando una semplice web app o orchestrando un'architettura complessa di microservizi con modelli di apprendimento automatico accelerati dalla GPU, questa guida vi porta dalle basi di Docker alle applicazioni containerizzate pronte per la produzione - con esempi reali dall'esecuzione principalmentelucid.com.

- **Docker Fondamentals: Capire i contenitori**Cos'e' Docker, davvero?
- **Al suo centro, Docker è una piattaforma di containerizzazione che confeziona l'applicazione e tutte le sue dipendenze in un'unità standardizzata chiamata contenitore.**A differenza delle macchine virtuali che virtualizzano interi sistemi operativi, i container condividono il kernel host OS mantenendo spazi utente isolati.

### Pensala in questo modo:

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

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

Macchine virtuali

1. **: Ogni VM esegue uno stack completo del sistema operativo (Kernel Linux, librerie di sistema, ecc.) - pesante e lento all'avvio**Contenitori
2. **: Condividere il kernel host, pacchetto solo codice dell'applicazione e dipendenze - leggero e veloce**Perché Containers Materia per gli sviluppatori
3. **I contenitori risolvono diversi problemi critici:**Coerenza ambientale
4. **: Lo sviluppo, i test e gli ambienti di produzione sono identici**Isolamento della dipendenza
5. **: Niente più "DLL hell" o versioni della libreria in conflitto**Build riproducibili

### : Stesso input = stesso output, ogni volta

#### Distribuzione rapida

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

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

**: Avviare contenitori in pochi secondi, non minuti**Efficienza delle risorse

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

: Eseguire dozzine di contenitori su un singolo host

- **Concetti Docker essenziali**Immagini vs Contenitori
- **Immagini**sono immutabili, filesystem a livelli.
- **Ogni istruzione in un file Docker crea un nuovo livello:**Questo strato è potente:

#### CachingCity name (optional, probably does not need a translation)

: I livelli immutati vengono riutilizzati, accelerando i build**Condivisione**

: Le immagini multiple possono condividere i livelli di base

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

**Efficienza**

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

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

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

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

**: Solo i livelli modificati devono essere scaricati/caricati**

1. **Comprendere l'esecuzione di Dockerfile: Non la tua macchina!**Una fonte comune di confusione per i principianti Docker:`COPY`I comandi in un file Docker non girano sulla vostra macchina - girano all'interno del sistema operativo del contenitore build.`ADD`Ecco cosa succede veramente:
2. **Perche' questo e' importante:**Intuizioni chiave:`RUN`Filesystem locale
3. **: Il tuo**e
4. **comandi letti dalla tua macchina**Genera immagine

: Il tuo`apt-get`comandi eseguiti nel sistema operativo del contenitore (non nella tua macchina)

**Immagine di output**

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

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

: L'immagine finale contiene tutti i livelli creati durante la generazione

#### Piattaforma trasversale

: È possibile essere su Windows, la costruzione di un'immagine Linux, utilizzando i comandi Linux

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

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

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

# Copy everything else
COPY . .

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

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

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

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

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

# Switch to non-root user
USER appuser

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

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

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

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

#### Questo è il motivo per cui puoi scrivere

comandi in un file Docker su Windows - non sono in esecuzione su Windows!

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

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

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

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

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

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

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

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

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

**Stanno correndo all'interno del contenitore build basato su Linux.**

1. **Scenario di confusione di esempio:**Il demone Docker gestisce la traduzione tra il filesystem locale e l'ambiente di generazione containerizzato.
2. **Migliori pratiche Dockerfile**Ecco un .NET Dockerfile pronto alla produzione con commento:`.csproj`Comprendere il flusso di generazione
3. **Ecco cosa succede in realtà durante una generazione Docker multi-stage - dove i file vengono da e dove finiscono:**Intuizioni chiave di questo flusso:`npm run build`Immagine SDK scartata
4. **: Il contenitore SDK da 1,5GB viene buttato via dopo la pubblicazione - solo ~50MB di output compilato si muove in avanti**: `appsettings.json`Caching dei livelli`wwwroot/`: Copia
5. **prima del codice sorgente significa che il ripristino della dipendenza è in cache a meno che le dipendenze non cambino**Attività di frontend
6. **: Costruito separatamente (spesso via**) e copiata nell'immagine finale

**File di configurazione**

1. **e**contenuto copiato dalla macchina, non costruito
2. **Immagine finale**: Avvia dall'immagine runtime minimale (~220MB) + la tua app (~30MB) + le attività (~5MB) = ~255MB totale
3. **Solo file di produzione**: Codice sorgente, obj/, bin/, nodo_modules/ mai arrivare all'immagine finale
4. **Principi fondamentali illustrati:**Build multistadio
5. **: Gli stadi separati di compilazione/pubblicazione riducono drasticamente la dimensione finale dell'immagine**Ottimizzazione dei livelli

#### : Copiare i file di dipendenza prima del codice sorgente per una migliore cache

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

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

# View logs
docker logs -f myapp

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

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

## Sicurezza

: Esegui come utente non root

### Controlli sanitari

: Container orchestratori possono monitorare la salute delle applicazioni

- Ambiente esplicito
- : Imposta impostazioni predefinite di produzione
- Edificio e funzionamento
- Docker Compose: Orchestra multi-container
- Docker Compose consente di definire ed eseguire applicazioni multi-container.

Invece di gestire i container singolarmente, descrivi l'intero stack dell'applicazione in un file YAML.`docker run`Perché Docker Compose?

### Considerare una tipica applicazione web .NET:

ASP.NET Core web app**Database PostgreSQL`docker-compose.yml`**Cache Redis

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

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

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

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

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

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

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

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

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

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

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

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

networks:
  app_network:
    driver: bridge
```

**Seq per la registrazione**

1. **Forse un servizio operaio di background**Gestire questi singoli con`app_network`I comandi diventano ingombranti.
2. **Docker Compose risolve questo problema.**Esempio completo di Real-World: Piattaforma di produzione del blog`/mnt/*`Ecco il
3. **Produzione effettiva**che gestisce proprio questo sito (per lo più Lucid.com):
4. **Modelli di produzione chiave:**Rete unica
5. **: Tutti i servizi su**per semplicità
6. **Montature oblique**: Percorsi di host (
7. **) per i dati persistenti è necessario eseguire il backup/accesso**Volumi con nome`.env`: Docker-managed storage per i dati non è necessario l'accesso diretto a

### Etichette della torre di avvistamento

#### : Solo i servizi di aggiornamento esplicitamente etichettati per l'aggiornamento automatico

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

Limiti delle risorse`condition: service_healthy`: I vincoli della CPU sul servizio di traduzione impediscono la fame delle risorse

#### Mappatura esterna dei porti

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

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

: PostgreSQL su 5266 invece di 5432 per evitare conflitti con altre istanze

#### File ambiente

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

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

**: Segreti in**:

- file (mai impegnato in git)
- Docker Componi funzionalità chiave
- Dipendenze dal servizio
- Docker Componi l'ordine di avvio degli orchestrati.

**La**:

- richiede il controllo dello stato di salute del database per passare prima di avviare l'applicazione web.
- Variabili e segreti dell'ambiente
- Per i segreti di produzione, utilizzare Docker Secrets o manager segreti esterni (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault).

#### Volumi con nome contro montanti Bind

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

services:
  web:
    networks:
      - frontend
      - backend

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

Volumi nominati

#### Gestito da Docker

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

Persistere i dati tra i riavvii del contenitore

- Può essere eseguito il backup/ripristino con i comandi Docker
- Cross-platform compatibile
- Montanti obliqui

### Mappatura diretta al filesystem host

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

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

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

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

# Stop services (containers remain)
docker-compose stop

# Stop and remove containers
docker-compose down

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

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

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

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

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

### Utile per lo sviluppo (ricaricamento del codice live)

File di configurazione, log, upload

**Networking**Le reti forniscono isolamento.

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

**Qui, il database è accessibile solo ai servizi di backend, non direttamente esposti.**Controlli sanitari

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

**I controlli sanitari consentono a Docker di:**Determinare se un contenitore è effettivamente pronto (non appena iniziato)

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

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

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

## Riavviare contenitori malsani

Fornire lo status agli orchestratori (Kubernetes, Docker Swarm)

### Comandi di composizione Docker comuni

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

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

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

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

### Sviluppo vs produzione Componi file

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

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

WORKDIR /app

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

# Copy application
COPY . .

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

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

### Problemi separati con più file di composizione:

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

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

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

### docker-compose.yml

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

### (base):

docker-compose.override.yml[(sviluppo - autocombustione):](https://github.com/scottgal/mostlylucid-nmt)docker-compose.prod.yml

(produzione):

- **Supporto GPU in Docker: Accelerare i carichi di lavoro ML**L'apprendimento automatico, l'informatica scientifica e le applicazioni di elaborazione video richiedono spesso l'accelerazione della GPU.
- **Docker supporta le GPU NVIDIA tramite NVIDIA Container Toolkit.**Impostazione di NVIDIA Container Toolkit
- **Dockerfile per l'applicazione Python/PyTorch accelerata GPU**Eseguire contenitori GPU
- **GPU in Docker Componi**Esempio di Real-World: servizio di traduzione con GPU e CPU Build

#### Ecco un esempio di produzione reale da

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

volumes:
  model_cache:
```

#### per lo più Lucid-nmt

, un servizio di traduzione automatica neurale ho costruito che poteri auto-traduzione su questo blog.

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

volumes:
  model_cache:
```

**Il progetto dimostra:**

- `scottgal/mostlylucid-nmt:gpu`Varianti GPU e CPU
- `scottgal/mostlylucid-nmt:cpu`- Stessa base di codice, diverse immagini di base
- `scottgal/mostlylucid-nmt:gpu-min`Costruzioni multi-architettura
- `scottgal/mostlylucid-nmt:cpu-min`- Supporta AMD64 e ARM64

**Immagini Docker ottimizzate**

- **- varianti sia complete che minimali**Pronti per la produzione
- **- Controlli sanitari, persistenza del volume, registrazione corretta**Servizio di traduzione accelerato da GPU
- **Alternativa solo per CPU**Per gli ambienti senza GPU, lo stesso servizio viene eseguito sulla CPU:
- **Varianti dell'immagine disponibili:**- CUDA 12.6 con supporto PyTorch GPU (~5GB)
- **- CPU-Solo, impronta più piccola (~2.5GB)**: `/health`- Minimale GPU build, nessun modelli precaricati (~4GB)`/ready`- Generazione minima della CPU (~1.5GB)
- **Caratteristiche principali:**Accelerazione GPU

: 10-15x traduzione più veloce con CUDA[Modello Scaricamento automatico](https://github.com/scottgal/mostlylucid-nmt): Scarica i modelli di traduzione on-demand

## Supporto di ripiegamento

: Prova Opus-MT → mBART50 → M2M100 per la massima copertura linguistica

### Persistenza del volume

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

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

### : Riavvia i modelli di cache attraverso i container

Endpoint della salute

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

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

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

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

### e

per orchestratori

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

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

RUN echo "Building on $BUILDPLATFORM for $TARGETPLATFORM"

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

### Multi-Architettura

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

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

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

### : Corre su x86_64 e ARM64 (Apple Silicon, Raspberry Pi)

Vedere il

**progetto completo su GitHub**

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

# Then use in compose
```

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

**per esempi di Dockerfile, compilazione di script e documentazione API.**

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

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

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

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

### Multi-Architettura costruisce con Docker Buildx

Le applicazioni moderne devono funzionare su più architetture: x86_64 (AMD64) per i server, ARM64 per Raspberry Pi e Apple Silicon Macs, a volte anche ARM32 per i dispositivi incorporati.

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

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

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

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

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

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

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

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

Perché questioni di multi-architettura

- Impostazione di Buildx
- Docker Buildx è incluso nel Docker Desktop.
- Per Linux:
- File Docker multi-architettura
- La maggior parte dei file Docker funzionano senza modifiche, ma ecco alcuni suggerimenti:

## Costruzione di piattaforme multiple

Multi-Architettura con Docker Compose

### Sfortunatamente, Docker Compose non supporta direttamente buildx.

Ricorsi di lavoro:

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

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

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

### Opzione 1: Pre-build immagini

Opzione 2: Genera script`.csproj`:

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

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

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

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

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

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

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

**Esempio di Real-World: CI/CD Pipeline**

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

Il flusso di lavoro delle azioni GitHub per la costruzione di multi-architettura:

- Questo flusso di lavoro:
- Trigger su push a tag principale o versione
- Imposta la QEMU per l'emulazione multipiattaforma
- Costruisce per AMD64 e ARM64

### Genera automaticamente i tag (nome del ramo, versioni semantiche, SHA)

**Usa la cache del registro per costruire più velocemente**

- .NET 9 Miglioramenti dei contenitori
- .NET 9 introduce miglioramenti significativi al supporto dei container, rendendo più facile che mai containerizzare le applicazioni .NET senza nemmeno scrivere un Dockerfile.
- Built-in Container Publishing
- Con .NET 9, è possibile pubblicare un'applicazione containerizzata direttamente:

**Configurazione delle proprietà del contenitore**

- Aggiungi al tuo
- 4.
- Esegui tutto:
- Aspire lancia:
- Cruscotto all'indirizzo http://localhost:1588

**Tutti i servizi con configurazione corretta**Redis e PostgreSQL in contenitori

### Tracciamento distribuito tra i servizi

Aspire vs Docker Compose

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

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

Docker Compose:

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

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

### Linguaggio-agnostico

Infrastructure focused

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

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

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

## Scoperta del servizio manuale

Portare la propria osservabilità

### Aspira:

.NET-specifico[Incentrato sullo sviluppo](/dockercomposedevdeps)Scoperta automatica del servizio

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

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

**Telemetria integrata**

- **Genera i manifesti di distribuzione**Utilizzare entrambi
- **: Aspire per lo sviluppo locale, genera Docker Compose/Kubernetes per la produzione.**Sfruttare le applicazioni Aspire
- **Genera i manifesti di distribuzione:**Quindi dispiegare:

Componenti di aspirazione[Le integrazioni precostruite rendono banali i servizi aggiuntivi:](/dockercomposedevdeps)Self-Hosting su risorse limitate: Ottimizzazione pratica

### Eseguire un completo stack di osservabilità come quello di cui sopra richiede risorse significative.

Se siete auto-hosting su un VPS con 4GB di RAM o un vecchio computer portatile, ecco le strategie pratiche per ridurre il consumo di risorse mantenendo la funzionalità.

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

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

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

volumes:
  db_data:
  caddy_data:

networks:
  app_network:
```

**Solo dipendenze di sviluppo**

- **Per lo sviluppo locale, non è necessario l'intero stack di produzione.**La
- **devdeps-docker-compose.yml**l'approccio funziona solo di ciò di cui hai bisogno:`docker logs`Perché questo funziona per lo sviluppo:
- **SMTP4Dev**: Funzionalità di test via email senza un vero server SMTP
- **PostgreSQL**: Corrisponde al database di produzione
- **Impronta totale**: ~200MB RAM vs 2-4GB per la pila completa

### Vedere il

guida completa sulle dipendenze per lo sviluppo

**per istruzioni di configurazione.**

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

**Configurazione di produzione basata sulle risorse**

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

**Per un VPS budget (2-4GB RAM), dare priorità ai servizi essenziali:**

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

### Cosa viene rimosso e alternative:

#### No Prometheus/Grafana

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

**: Utilizzare il monitoraggio esterno (UptimeRobot, livello gratuito di BetterStack)**No Seq

#### : Utilizzare la registrazione basata su file +

, o livello Seq Cloud free

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

**Nessuna torre di guardia**: Aggiornamenti manuali con le notifiche delle Azioni GitHub

#### Nessun servizio di traduzione

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

: Eseguire on-demand in un contenitore separato si avvia/stop manualmente

#### Limiti delle risorse

: Impedire a qualsiasi singolo servizio di consumare tutta la memoria[Strategia di miglioramento progressivo](/imagesharpwithdocker)Avviare minimal, aggiungere servizi se necessario:

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

**Fase 1: nucleo (512MB-1GB VPS)**Fase 2: aggiungere osservabilità (2GB VPS)

#### Fase 3: Stack completo (4GB+VPS)

Tecniche di ottimizzazione delle risorse
|---------|-----------------|---------------------|
1.
Usa immagini alpine
Risparmi

**: Le immagini alpine sono più piccole del 50-70%**2.

#### Caso di banca dati condivisa

Invece di un database per servizio, utilizzare un'istanza PostgreSQL con più database:

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

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

# Translate your content
# ...

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

**Risparmi**: 400MB RAM per database aggiuntivo si consolida

### 3.

Limitare la CPU per i servizi di background

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

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

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

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

**Questo impedisce ai lavori di background di morire di fame l'applicazione web.**

- 4.
- Volume cache persistente
- Coperto in

**Condivisione immagini con Docker**

- , le directory della cache di montaggio impedisce il ritrattamento inutile:
- Perche' e' importante?`docker logs`: Senza questo, ogni riavvio del contenitore rigenera tutte le miniature/immagini elaborate.
- 5.
- Utilizzare i servizi esterni (tire libere)
- | Servizio | RAM self-hosted | Alternativa esterna |

### | Seq | ~ 500MB | Seq Cloud (10GB/mese libero) |

| Prometheus + Grafana | ~ 600MB | Cloud di Grafana (livello libero) |

**| Umami | ~ 200MB | Plausibile (pagato) o auto-host altrove |**

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

**Strategia**

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

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

**: Offload osservability to free tiers, keep core application on your VPS.**

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

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

## 6.

Servizi su richiesta

### Per i servizi usati di rado come la traduzione:

Risparmi

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

**: 1-2GB RAM quando il servizio di traduzione non è in esecuzione**

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

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

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

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

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

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

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

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

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

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

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

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

### Esempio di hosting a budget reale mondiale

Ecco cosa funziona su un $6/mese Hetzner VPS (2 vCPU, 4GB di RAM):**Uso totale delle risorse:**RAM: ~800MB (lascia libero 3,2GB)

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

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

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

        return builder;
    }

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

        return app;
    }
}
```

### Disco: ~2GB

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

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

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

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

var app = builder.Build();

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

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

### CPU: <10% inattivo, <50% sotto carico

**Cosa c'e' di diverso:**

1. **Nessuna Prometheus/Grafana (usare UptimeRobot + Cloudflare Analytics)**: `dotnet run --project Mostlylucid.AppHost`No Seq (uso
2. **+ grep occasionale)**No Umami (usare Cloudflare Web Analytics - gratuito)
   - Watchtower controlla ogni ora invece di ogni 5 minuti
   - Nessun servizio di traduzione (eseguire manualmente quando necessario)
   - Monitoraggio di un bilancio
   - Senza Prometheus/Grafana, utilizzare queste alternative gratuite:
3. **Monitoraggio sanitario:**Monitoraggio dei registri:
4. **Uso delle risorse:**Aspire Version: La via .NET`.env`Ora riimmaginiamo l'intero stack usando .NET Aspire.

**Questo ti dà tutti i vantaggi di orchestrazione con una migliore integrazione .NET e una sorprendente esperienza di sviluppatore.**

Impostazione dell'aspirazione per la maggior parte deilucidi

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

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

**In primo luogo, creare l'host Aspire App:**Per lo più Lucid.AppHost/Program.cs:

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

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

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

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

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

  # ... other services
```

### Predefiniti servizi di aspirazione

Crea
|---------|---------------|-------------|
| **Predefiniti servizi**progetto:
| **Aggiorna per lo più Lucid/Program.cs**Vantaggi dell'approccio Aspire
| **Esperienza di sviluppo:**Comando singolo
| **inizia tutto** | `docker-compose up` | `dotnet run`Cruscotto
| **: Beautiful UI at http://localhost:1588 mostrando:**Tutti i servizi con stato di vita
| **Logs da tutti i servizi in un unico luogo** | `docker logs`Tracce distribuite tra i servizi
| **Metrics and health checks**Service Discovery
| **: I servizi si trovano automaticamente tramite i nomi**Configurazione
| **: Centralizzato in AppHost, non più**giocoleria

### Distribuzione della produzione:

**Genera i manifesti di distribuzione da Aspire:**

- Generato docker-compose.yml
- (semplificato):
- Aspira vs Docker tradizionale Componi
- | Caratteristica | Docker Compose | .NET Aspire |
- Configurazione

**| YaML manuale | codice C# con IntelliSense |**

- Service Discovery
- | Variatori di tensione manuali | Automatico |
- Osservabilità
- | Porta il tuo | Built-in (OpenTelemetry) |
- Politica di sviluppo

**+ Cruscotto |**

- Debug
- | Attacco al contenitore | F5 in Visual Studio |
- Registri

### | Cruscotto centralizzato |

Rintracciamento

1. **[| Setup manualmente | Automatic distributed tracing |](/dockercompose)**Produzione
2. **[| Usare direttamente YAML | Generare manifesti |](/dockercomposedevdeps)**Curve di apprendimento
3. **[| Sintax YAML | C# you already know |](/imagesharpwithdocker)**Quando usare Aspire vs Docker Compose
4. **[Usa Aspire quando:](/dockercompose)**Costruzione di microservizi .NET
5. **Vuoi il debug integrato**Il team è a suo agio con C#

**È necessario distribuito tracciando fuori-della-scatola**

- Stai distribuendo ad Azure Container Apps (supporto nativo)
- Usa Docker Compose quando:
- Servizi Polyglot (Node.js, Python, Go, ecc.)
- Il team preferisce l'infrastruttura come codice YAML
- Distribuzione a qualsiasi host compatibile con Docker
- È necessario il massimo controllo sulla configurazione del contenitore

## Semplici implementazioni in un unico servizio

### Utilizzare entrambi:

1. Aspira allo sviluppo locale
2. Genera Docker Componi per l'implementazione della produzione
3. Il meglio di entrambi i mondi!
4. Evoluzione della configurazione di Docker di questo blog`latest`)
5. Il viaggio di Docker di questo blog mostra una progressione tipica:
6. Luglio 2024 - Inizio semplice`.dockerignore`: Solo l'app, Cloudflared, e Torre di Controllo
7. Agosto 2024 - Dev Dipendenze
8. : Aggiunti solo servizi di sviluppo

### Agosto 2024 - ImageSharp Fix

1. : Risolto i permessi di volume per la cache
2. Novembre 2024 - Stack completo
3. : Completa osservabilità con Prometheus, Grafana, Seq`.env`Oggi
4. : Opzione di aspirazione per lo sviluppo .NET-first
5. Lezioni imparate:`depends_on`Avviare semplice, aggiungere complessità solo quando necessario
6. Configurazioni di dev e produzione separate
7. I limiti delle risorse impediscono ad un servizio di uccidere gli altri
8. Montaggio volume per cache risparmio significativo tempo di ritrattamento

### Watchtower consente l'aggiornamento automatico zero-downtime

1. L'osservabilità vale il costo delle risorse nella produzione
2. Sintesi delle migliori pratiche
3. Migliori pratiche Dockerfile
4. Usa build multi-stage
5. Esegui come utente non root
6. Ordinare le istruzioni per la cache ottimale
7. Usa tag immagine base specifici (non
8. Includere i controlli sanitari

### Uso

1. per escludere i file non necessari
2. Minimizza i livelli (combina i comandi RUN)
3. Usa gli argomenti build per la flessibilità
4. Docker Compose Best Practice`.dockerignore`Usa i volumi nominati per i dati
5. Attuare i controlli sanitari
6. Uso
7. per i segreti (mai commettere)
8. Definire reti esplicite

## Uso

### con condizioni di salute

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

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

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

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

### Imposta le politiche di riavvio

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

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

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

### Configurazioni separate dev/prod

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

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

### Limiti di risorse nella produzione

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

## Migliori pratiche in materia di sicurezza

Esegui come utente non root

### Usa immagini scalpellite/minimali di base

**Scansiona le immagini per le vulnerabilità**

- Mantieni aggiornate le immagini di base[Non includere segreti nelle immagini](/dockercompose)Utilizzare i filesystem di sola lettura ove possibile
- Limitare le capacità del contenitore[Utilizzare Docker segreti o manager segreti esterni](/dockercomposedevdeps)Migliori pratiche di prestazione
- Usa BuildKit per costruire più velocemente[Sfruttare la cache dei livelli](/imagesharpwithdocker)Build multi-stadio per ridurre la dimensione dell'immagine

**Uso**

- generosamente
- Immagini alpine / scalpelli per dimensioni più piccole
- Usa supporti volume per lo sviluppo
- Configurare i limiti di risorse appropriati
- Utilizzare controlli sanitari per l'orchestrazione

**Risoluzione dei problemi comuni**

- Il contenitore non parte
- Generazioni lente
- Questioni legate alla rete
- Emissioni di permessi in volume
- Conclusione
- Docker si è evoluto da un semplice strumento di containerizzazione a una piattaforma completa per la costruzione, il trasporto e l'esecuzione di applicazioni moderne.

**Questa guida vi ha portato dai fondamentali agli schieramenti pronti per la produzione, con esempi reali da eseguire principalmentelucid.com.**

- Key TakeawaysCity name (optional, probably does not need a translation)
- Per principianti:
- Inizia con il
- Docker di base Componi configurazione
- - solo 3 servizi

**Uso**

- dipendenze solo per lo sviluppo
- per il lavoro locale
- Risolvere problemi comuni come
- permessi volume

### presto

Per gli auto-hoster sul VPS di bilancio:

Start minimal: App + Database + Reverse Proxy (~800MB RAM)
|-------|----------|-----------|------------|
| **Utilizzare immagini alpine e limiti di risorse**Scarica l'osservabilità ai livelli liberi (Seq Cloud, Grafana Cloud, UptimeRobot)
| **Eseguire servizi costosi (traduzione, ML) solo su richiesta**Un VPS $6/mese può eseguire un blog di produzione con spazio per risparmiare
| **Per la distribuzione della produzione:**I controlli di salute consentono aggiornamenti zero-downtime con Watchtower
| **Reti separate forniscono sicurezza (isolamento frontend/backend)**Montaggio di bordi per i dati necessari per il backup/accesso
| **Volumi nominati per Docker-managed storage**I limiti CPU/memoria impediscono la fame delle risorse

**Cloudflare Tunnel elimina la necessità di indirizzi IP pubblici**Per gli sviluppatori .NET:

### Built-in container publishing in .NET 9 elimina Dockerfiles per applicazioni semplici

Le immagini intagliate di Ubuntu forniscono una superficie d'attacco minima

- **.NET Aspire offre la migliore esperienza di sviluppo locale di classe**Aspire può generare Docker Compose per l'implementazione della produzione[L'integrazione OpenTelemetry è gratuita con Aspire](/dockercompose)
- **Per casi di utilizzo avanzato:**I contenitori GPU consentono carichi di lavoro ML/AI con velocità di 10-15x
- **Multi-architettura costruisce il supporto ARM64 e AMD64 da singola fonte**Le azioni GitHub automatizzano i build multipiattaforma
- **La cache dei livelli accelera notevolmente le build ripetute**Il viaggio[L'evoluzione di Docker di questo blog rispecchia la progressione tipica:](https://github.com/scottgal/mostlylucid-nmt)
- **| Stadio | Servizi | Uso RAM | Complessità |**Fase 1

### (luglio 2024) | App + Tunnel | ~ 300MB | Simple |

**Fase 2**

- [(Ago 2024) | + Dev dipendenze | ~ 500MB | Learning |](https://docs.docker.com/)
- [Fase 3](https://docs.docker.com/compose/compose-file/)
- [(Ago 2024) | + Correzioni volume | ~500MB | Debugging |](https://github.com/dotnet/dotnet-docker)
- [Fase 4](https://learn.microsoft.com/en-us/dotnet/aspire/)
- [(Nov 2024) | Piena osservabilità | ~3.5GB | Produzione |](https://github.com/NVIDIA/nvidia-container-toolkit)
- [Fase 5](https://github.com/docker/buildx)

**(Oggi) | + Opzione di aspirazione | Variabile | Moderno |**

1. [Il modello](/dockercompose): Avviare semplice, risolvere i problemi come si presentano, aggiungere complessità solo quando necessario.
2. [Cosa c'e' dopo?](/dockercomposedevdeps)A seconda del percorso:
3. [Imparare Docker](/imagesharpwithdocker): Inizia con
4. Docker Componi le basi

**Auto-Hosting**

- [: Prova la configurazione VPS minimale da questo articolo](https://github.com/scottgal/mostlylucid-nmt)Microservizi per l'edilizia
- [: Esplora .NET Aspire](https://github.com/scottgal/mostlylucidweb)Esecuzione di carichi di lavoro ML

: Dai un'occhiata al

Happy containerizing! 🐳