Ο Ντόκερ έχει μεταμορφώσει ριζικά τον τρόπο που φτιάχνουμε, στέλνουμε και εκτελούμε εφαρμογές.
Αυτό που ξεκίνησε ως ένα απλό εργαλείο εμπορευματοκιβωτίων έχει εξελιχθεί σε ένα πλήρες οικοσύστημα για τη σύγχρονη ανάπτυξη και ανάπτυξη εφαρμογών.
ImageSharp με Docker
.NET φιλοδοξεί
: Η σύγχρονη προσέγγιση .NET στην ενορχήστρωση εμπορευματοκιβωτίων
Έχει πλάκα και γεμίζει ένα κενό που δεν έχω δει να γεμίζει πουθενά αλλού.
Είτε αναπτύσσετε μια απλή εφαρμογή web ή ενορχηστρώνοντας μια πολύπλοκη αρχιτεκτονική μικρουπηρεσιών με GPU-επιταχυνόμενα μοντέλα εκμάθησης μηχανών, αυτός ο οδηγός σας μεταφέρει από τα βασικά Docker μέχρι τις εφαρμογές συσκευασίας παραγωγής-έτοιμες - με πραγματικά παραδείγματα από το τρέξιμο ως επί το πλείστον διαυγή.com.
# The classic developer problem
"It works on my machine!"
# The container solution
"Ship your machine!"
Εικονικές μηχανές
# 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
: Ξεκινήστε τα δοχεία σε δευτερόλεπτα, όχι λεπτάΑποδοτικότητα πόρων
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
: Εκτέλεση δεκάδων εμπορευματοκιβωτίων σε έναν και μόνο ξενιστή
: Τα αμετάβλητα στρώματα ξαναχρησιμοποιούνται, επιταχύνοντας τις κατασκευές
# 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"]
Κοινή χρήση
# 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 runΕξετάστε μια τυπική εφαρμογή NET web:
Βάση δεδομένων PostgreSQL**Redis cachedocker-compose.yml**Seq για την καταγραφή
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
Ίσως μια υπηρεσία εργασίας υποβάθρου
app_networkΟ Ντόκερ Κόμπος λύνει αυτό./mnt/*πραγματική παραγωγή.envΕτικέτες Σκοπιάςservices:
web:
depends_on:
db:
condition: service_healthy # Wait for health check
redis:
condition: service_started # Just wait for start
: Οι περιορισμοί CPU στη μεταφραστική υπηρεσία εμποδίζουν την πείνα των πόρωνcondition: service_healthyΕξωτερική χαρτογράφηση θύρας
# .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
Αρχεία περιβάλλοντος
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
αρχείο (ποτέ δεν δεσμεύεται να git):
απαιτεί τον έλεγχο υγείας της βάσης δεδομένων να περάσει πριν από την έναρξη της εφαρμογής web.:
networks:
frontend:
driver: bridge
backend:
driver: bridge
services:
web:
networks:
- frontend
- backend
db:
networks:
- backend # Not exposed to frontend
Διοικούμενος από τον Docker
services:
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
Μπορεί να υποστηρίζεται/αποκατασταθεί με εντολές 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
Δικτύωση
**Τα δίκτυα παρέχουν απομόνωση.**Εδώ, η βάση δεδομένων είναι προσβάσιμη μόνο σε υπηρεσίες υποστήριξης, όχι άμεσα εκτεθειμένη.
services:
web:
build: .
environment:
- ASPNETCORE_ENVIRONMENT=Development
Υγειονομικοί έλεγχοιΟι έλεγχοι υγείας επιτρέπουν στον Ντόκερ να:
services:
web:
volumes:
- .:/app # Live code reloading
ports:
- "5000:8080"
**Καθορίστε αν ένα δοχείο είναι πραγματικά έτοιμο (δεν μόλις ξεκίνησε)**Επανεκκίνηση ανθυγιεινών εμπορευματοκιβωτίων
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
Κοινή Docker Compose Εντολές
# 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
# 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"]
# 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
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
(ανάπτυξη - αυτόματη επεξεργασία):Docker-compose. prod.yml(παραγωγή):
Υποστήριξη GPU σε Docker: Επιτάχυνση ML Lettings
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:
Το έργο καταδεικνύει:
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:
Παραλλαγές GPU και CPU
scottgal/mostlylucid-nmt:gpu- Ίδια βάση κώδικα, διαφορετικές βασικές εικόνεςscottgal/mostlylucid-nmt:cpuΠολυ-αρχιτεκτονική κατασκευήscottgal/mostlylucid-nmt:gpu-min- Υποστηρίζει AMD64 και ARM64scottgal/mostlylucid-nmt:cpu-minΒελτιστοποιημένες εικόνες Docker- Τόσο πλήρεις όσο και ελάχιστες παραλλαγές
/health- Minimal CPU build (~1.5GB)/readyΒασικά χαρακτηριστικά:Μοντέλο Auto-Download: Downloads translation models on-demandΥποστήριξη FallbackName
Επιμονή σε Όγκο
# 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 .
και
# 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
# 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
# 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 \
.
πλήρες έργο στο GitHub
για παραδείγματα Dockerfile, κατασκευάστε σενάρια, και API τεκμηρίωση.
# 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
Multi-Architecture Κατασκευάζει με Docker Buildx
#!/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
Γιατί Θέματα Πολυ-Αρχειτεκτονικής
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
Ρύθμιση Buildx
Δυστυχώς, ο Ντόκερ Κόμπος δεν υποστηρίζει άμεσα την κατασκευή.
Επιλογή 1: Προ-κατασκευή εικόνων
# 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
Ρεάλ-Παγκόσμιο Παράδειγμα: CI/CD αγωγός.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>
GitHub Δράσεις ροή εργασίας για πολυ-αρχιτεκτονική κατασκευή:
dotnet publish -p:PublishProfile=DefaultContainer
<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>
Στη συνέχεια, δημοσιεύστε:
Ασφάλεια
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"]
: Αυτοματοποιημένες ενημερώσεις εικόνας βάσης
# Just this!
dotnet publish -p:PublishProfile=DefaultContainer
Μικρότερες εικόνες
Κοπή και AOT για μικρότερες εικόνες
~110MB
Και οι δύο παράγουν παρόμοιες εικόνες, αλλά η ενσωματωμένη προσέγγιση είναι απλούστερη και μπορεί να διατηρηθεί.
<PropertyGroup>
<ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:9.0-noble-chiseled</ContainerBaseImage>
</PropertyGroup>
Πότε να χρησιμοποιήσετε Dockerfile vs Ενσωματωμένο
Standard .NET web/console εφαρμογές
Γρήγορη πρωτοτυποποίηση
NET 9 υποστηρίζει "chiseled" Ubuntu εικόνες - υπερ-μίνι εικόνες βάσης:
Μικροσκοπικό
Σκεφτείτε το ως Docker Compose σε στεροειδή, ειδικά σχεδιασμένο για .NET μικρουπηρεσίες.
: Ενσωματωμένη καταγραφή, μετρήσεις, εντοπισμός
dotnet new aspire-starter -n MyDistributedApp
cd MyDistributedApp
Συστατικά
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();
: Προρυθμισμένες ενοποιήσεις (Redis, PostgreSQL, RabbitMQ, κ.λπ.)
// MyApi/Program.cs
var builder = WebApplication.CreateBuilder(args);
// Aspireing
3. Multi-stage builds to reduce image size
4. Use `.dockerignore` generously
5. Alpine/chiseled images for smaller size
6. Use volume mounts for development
7. Configure appropriate resource limits
8. Use health checks for orchestration
## Troubleshooting Common Issues
### Container Won't Start
```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
# 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 .
# 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
# Permission denied on volume
# Solution: Match user IDs
FROM ubuntu
RUN useradd -u 1000 appuser # Match host user ID
USER appuser
App Host
Αργά Κατασκευές
Happy containerizing! 🐳 Εφαρμογές Οικοδόμησης
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
: Πόσιμο Docker Compose για πολυ-service ενορχήστρωση
Χτίζοντας μικροϋπηρεσίες: Εξερευνήστε .NET Επιδιώκουν για κατανεμημένες εφαρμογέςΤο οικοσύστημα εμπορευματοκιβωτίων παρέχει εργαλεία για κάθε σενάριο.
Συμπληρωματικοί πόροι
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:
Τεκμηρίωση Docker
docker logsΝτόκερ ΧιούτξCity name (optional, probably does not need a translation)~200MB RAM vs 2-4GB για πλήρη στοίβα
Δείτε το
# Just app + database + reverse proxy
docker-compose up -d mostlylucid db caddy
Οδηγός εξυπηρέτησης πλήρους ανάπτυξης
# Add lightweight monitoring
docker-compose up -d mostlylucid db caddy seq
# Use Seq free 10GB/month
για οδηγίες εγκατάστασης.
# Add everything
docker-compose up -d
services:
myapp:
image: postgres:16-alpine # 50% smaller than postgres:16
# vs
image: postgres:16 # Full Debian base
**Τι έχει αφαιρεθεί και εναλλακτικές λύσεις:**Αριθ. Prometheus/Grafana
Αριθ. Seq
db:
image: postgres:16-alpine
# One instance, multiple databases
# Umami, Mostlylucid, etc. all share this PostgreSQL
: Χρήση καταγραφής με βάση το αρχείο +, ή Seq Cloud ελεύθερη βαθμίδα
easynmt:
deploy:
resources:
limits:
cpus: "2.0" # Don't let translation consume all CPU
reservations:
cpus: "0.5" # Guarantee minimum
: Χειροκίνητες ενημερώσεις με τις κοινοποιήσεις GitHub Activities
: Εκτέλεση κατά παραγγελία σε ξεχωριστό δοχείο που ξεκινάτε/σταματάτε χειροκίνηταΌρια πόρων: Αποτρέψτε κάθε ενιαία υπηρεσία από την κατανάλωση όλης της μνήμης
mostlylucid:
volumes:
- /mnt/imagecache:/app/wwwroot/cache # ImageSharp cache persists across restarts
Στρατηγική Προοδευτικής ΕνίσχυσηςΞεκινήστε minimal, προσθέστε υπηρεσίες όπως απαιτείται:
Στάδιο 2: Προσθήκη παρατηρησιμότητας (2GB VPS) |---------|-----------------|---------------------| Στάδιο 3: Πλήρης Στοίβα (4GB+ VPS) Τεχνικές βελτιστοποίησης πόρων 1.
Χρήση αλπικών εικόνωνΑποταμιεύσεις
# 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
Κοινόχρηστο piλαίσιο δεδοένωνΑντί για μία βάση δεδομένων ανά υπηρεσία, χρησιμοποιήστε ένα παράδειγμα PostgreSQL με πολλαπλές βάσεις δεδομένων:
: 400MB RAM ανά πρόσθετη βάση δεδομένων που εδραιώνετε
# 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
3.
Συνεχείς όγκοι Cache
docker logs, η τοποθέτηση καταλόγων cache εμποδίζει την περιττή επεξεργασία:Εξυπηρέτηση Αυτό-Hosted RAM & Εξωτερική Εναλλακτική
~500MB ~ Seq Cloud (10GB/μήνα δωρεάν) ~
# 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
Προμηθέας + Grafana ~600MB ~ Grafana Cloud (δωρεάν βαθμίδα)
# 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
Ουμάμι ~200MB Πιθανό (πληρωμένο) ή αυτο-ξενώνας κάπου αλλού
# Quick resource check
docker stats --no-stream
# Pretty output
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
: Αποφορτίστε την παρατηρητικότητα σε ελεύθερες βαθμίδες, κρατήστε την εφαρμογή πυρήνα στο VPS σας.
Υπηρεσίες On-Demand
# Add Aspire to your solution
dotnet new aspire-apphost -n Mostlylucid.AppHost
cd Mostlylucid.AppHost
Για σπάνια χρησιμοποιούμενες υπηρεσίες όπως η μετάφραση:
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();
: 1-2GB RAM όταν η μεταφραστική υπηρεσία δεν λειτουργείΠαράδειγμα φιλοξενίας προϋπολογισμού πραγματικού κόσμουΕδώ είναι τι τρέχει σε ένα $ 6 μήνες Hetzner VPS (2 vCPU, 4GB RAM):
// 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;
}
}
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();
Δίσκος: ~2GB
dotnet run --project Mostlylucid.AppHostΤι είναι διαφορετικό:.envΧρήση πόρων:Aspire Version: The .NET Way
Τώρα ας ξαναφτιάξουμε ολόκληρη τη στοίβα χρησιμοποιώντας .NET 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
**Αυτό σας δίνει όλα τα οφέλη ενορχήστρωσης με καλύτερη ενσωμάτωση .NET και μια καταπληκτική εμπειρία προγραμματιστή.**Η Ρύθμιση Επιδιώκει την Κυρίως Διαύγεια
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
Κυρίως διαυγής. AppHost/Program.cs:
|---------|---------------|-------------|
| Προκαθορισμένα προγράμματα υπηρεσιών έμπνευσηςNameΔημιουργία
| Κυρίως διαυγής.ΥπηρεσίεςΠροκαταβολέςΈργο:
| Ενημέρωση ως επί το πλείστον διαυγής/πρόγραμμα.csΟφέλη της Εμπνευσμένης Προσέγγισης
| Εμπειρία στην ανάπτυξη: | docker-compose up | dotnet runΕνιαία εντολή
| **Ξεκινάει τα πάντα.**Ταχυδρόμος
| : Beautiful UI at http://localhost:15888 showing: | docker logsΌλες οι υπηρεσίες με ζωντανή κατάσταση
| Καταχωρήσεις από όλες τις υπηρεσίες σε ένα μέροςΔιανεμημένα ίχνη σε όλες τις υπηρεσίες
| Μετρικός και υγειονομικός έλεγχοςΑνακάλυψη υπηρεσίας
| : Οι υπηρεσίες βρίσκουν ο ένας τον άλλον αυτόματα μέσω ονομάτωνΡύθμιση
juggling
Χαρακτηριστικό ~ Docker Compose ~ .NET Aspire ~
Φέρτε το δικό σας Ενσωματωμένο (OpenTelemetry)
Ημερολόγια
Θέλετε ολοκληρωμένη αποσφαλμάτωση
latest).dockerignoreΑυτό το ταξίδι Docker του blog δείχνει τυπική εξέλιξη:.envΝοέμβριος 2024 - Πλήρης Στοίβαdepends_on: Επιδίωξη επιλογής για .NET-πρώτη ανάπτυξη.dockerignoreΧρήση επιχειρημάτων για ευελιξία# 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
# 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 .
# 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
# Permission denied on volume
# Solution: Match user IDs
FROM ubuntu
RUN useradd -u 1000 appuser # Match host user ID
USER appuser
Όρια πόρων στην παραγωγή
Εκτελέστε ως μη-root χρήστης
Κρυπτογράφηση στρώματος
Ρύθμιση κατάλληλων ορίων πόρων
Συμπέρασμα
βασική ρύθμιση Docker Compose
άδειες έντασης
νωρίς |-------|----------|-----------|------------| | **Για Self-Hosters για τον προϋπολογισμό VPS:**Μίνιμουμ εκκίνησης: App + Βάση δεδομένων + Αντιστροφή Proxy (~800MB RAM) | Χρήση αλπικών εικόνων και ορίων πόρωνOffload παρατηρησιμότητα σε ελεύθερες βαθμίδες (Seq Cloud, Grafana Cloud, UptimeRobot) | Εκτελέστε δαπανηρές υπηρεσίες (μετάφραση, ML) μόνο κατά παραγγελίαΈνα VPS $ 6 μηνών μπορεί να τρέξει ένα blog παραγωγής με χώρο για να διαθέσει | **Για την ανάπτυξη της παραγωγής:**Οι έλεγχοι υγείας καθιστούν δυνατή την ενημέρωση του χρόνου μηδενισμού με τη Σκοπιά | **Τα ξεχωριστά δίκτυα παρέχουν ασφάλεια (απομόνωση πρόσοψης/υποστήριξης)**Δέστε βάσεις για τα δεδομένα που χρειάζεστε για την υποστήριξη/πρόσβαση
Ονόματα τόμων για την αποθήκευση Docker-managedΤα όρια CPU/μνήμης εμποδίζουν την πείνα των πόρων
Για τους προγραμματιστές NET:
Στάδιο 1
(Νοε 2024) Πλήρες παρατηρητήριο ~3.5GB ~ Παραγωγή
: Ξεκινήστε με
: Εξερευνήστε .NET Aspire
Happy containerizing! 🐳
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.