سالها توسعهدهندگان داتنت به محیطهای Windows Server و وبسرور IIS وابسته بودند. اما با معرفی .NET Core (و نسخههای مدرن .NET 6/7/8/9)، داتنت به یک فریمورک کاملاً Cross-platform و سبک تبدیل شد که شانه به شانه Node.js و Go در محیطهای لینوکسی میتازد.
کانتینرسازی به زبان ساده یعنی بستهبندی کد، Runtime، Dependencyها و تنظیمات اپلیکیشن در یک واحد مستقل به نام Image.
مزایای کلیدی برای پروژههای داتنت:
حذف مشکل "روی سیستم من کار میکرد!": رفتار اپلیکیشن در محیط Dev، Staging و Production کاملاً یکسان خواهد بود.
سبکسازی و کارایی عالی: اجرای اپلیکیشنهای .NET روی لینوکس Alpine درون Docker هزینههای RAM و CPU را به شدت کاهش میدهد.
مقیاسپذیری (Scalability) خطی: افزایش یا کاهش نمونههای (Instances) برنامه در چند ثانیه.
ساخت یک Dockerfile معمولی کار سختی نیست، اما نوشتن یک Dockerfile بهینه، امن و سبک برای محیط Production نیازمند رعایت چند اصل اساسی است.
الگو: Multi-Stage Build
در داتنت، برای ساخت برنامههای Dockerized حتماً باید از Multi-Stage Build استفاده کنیم. چرا؟
چون SDK داتنت (که شامل کامپایلر و ابزارهای Build است) حجمی حدود ۷۰۰ تا ۹۰۰ مگابایت دارد، در حالی که جهت اجرای برنامه فقط به Runtime (با حجم حدود ۱۰۰-۱۵۰ مگابایت) نیاز داریم.
نمونه Dockerfile استاندارد برای یک Web API در .NET 8/9:
# Stage 1: Build Environment
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
# کپی فایلهای csproj جهت استفاده بهینه از Docker Layer Caching
COPY ["MyApi/MyApi.csproj", "MyApi/"]
COPY ["MyApi.Core/MyApi.Core.csproj", "MyApi.Core/"]
RUN dotnet restore "MyApi/MyApi.csproj"
# کپی کل سورسکد و کامپایل برنامه
COPY . .
WORKDIR "/src/MyApi"
RUN dotnet build "MyApi.csproj" -c Release -o /app/build
# Stage 2: Publish Environment
FROM build AS publish
RUN dotnet publish "MyApi.csproj" -c Release -o /app/publish /p:UseAppHost=false
# Stage 3: Final Runtime Environment (Ultra Lightweight)
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
WORKDIR /app
# ایجاد یک غیر-ریشه (Non-root user) جهت امنیت بیشتر
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY --from=publish /app/publish .
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApi.dll"]
چند نکته باریکتر از مو در Dockerfile بالا:
استفاده از Alpine: توزیع aspnet:9.0-alpine حجم Image نهایی شما را زیر ۲۰۰ مگابایت نگه میدارد.
Layer Caching: کپی کردن csproj و اجرای dotnet restore قبل از کپی کامل سورسکد باعث میشود تا زمانی که Dependency جدیدی اضافه نکردهاید، لایه Restore از Cache خوانده شود و سرعت Build خیرهکننده باشد.
امنیت (Non-Root User): بهصورت پیشفرض داکر برنامهها را با دسترسی Root اجرا میکند. با تعریف appuser اگر کانتینر آسیبپذیر شود، مهاجم دسترسی کامل به سرور نخواهد داشت.
۳. مدیریت کانتینرها در توسعه محلی با Docker Compose
معمولاً یک اپلیکیشن داتنت بهتنهایی کار نمیکند و به دیتابیس (مثل SQL Server یا PostgreSQL)، Cache (مثل Redis) و غیره نیاز دارد. برای شبیهسازی این محیط در سیستم خود از docker-compose.yml استفاده میکنیم:
version: '3.8'
services:
myapi:
build:
context: .
dockerfile: MyApi/Dockerfile
ports:
- "5000:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__DefaultConnection=Server=sqlserver;Database=AppDb;User Id=sa;Password=YourStrong@Password123;TrustServerCertificate=True;
- Redis__ConnectionString=redis:6379
depends_on:
- sqlserver
- redis
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- MSSQL_SA_PASSWORD=YourStrong@Password123
ports:
- "1433:1433"
redis:
image: redis:alpine
ports:
- "6379:6379"
نکته معماری: توجه کنید که در رشته اتصال (Connection String) داتنت، نام سرویس داکر یعنی sqlserver و redis بهعنوان Hostname استفاده شده است. داکر بهصورت داخلی DNS Resolution را بین کانتینرها انجام میدهد.
وقتی تعداد کانتینرها بالا میرود و سیستم زیر بار ترافیک شدید قرار میگیرد، Docker Compose کافی نیست. شما به یک Orchestrator نیاز دارید که وظایف زیر را به عهده بگیرد:
Self-healing (اگر کانتینری کرش کرد، سریعاً یکی دیگر جایگزین کند).
Auto-scaling (افزایش تعداد کانتینرها با افزایش مصرف CPU/RAM).
Zero-downtime Deployments (آپدیت برنامه بدون قطعی).
کوبرنتیز (Kubernetes) اینجاست تا این نقش را بازی کند.
۵. آبجکتهای کلیدی Kubernetes برای یک سرویس .NET
برای استقرار (Deploy) یک اپلیکیشن داتنت روی کوبرنتیز حداقل به ۴ آبجکت اصلی نیاز داریم:
۱. ConfigMap و Secret
جایگزین فایل appsettings.json و کلیدهای حساس.
apiVersion: v1
kind: ConfigMap
metadata:
name: myapi-config
data:
ASPNETCORE_ENVIRONMENT: "Production"
Serilog__MinimumLevel: "Warning"
---
apiVersion: v1
kind: Secret
metadata:
name: myapi-secrets
type: Opaque
stringData:
DbConnectionString: "Server=prod-db.example.com;Database=AppDb;User Id=appuser;Password=SuperSecretPassword;"
۲. Deployment
مدیریت چرخه حیات پادها (Pod) و تعداد نسخه اجرا شده (Replicas).
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapi-deployment
labels:
app: myapi
spec:
replicas: 3
selector:
matchLabels:
app: myapi
template:
metadata:
labels:
app: myapi
spec:
containers:
- name: myapi
image: myregistry.azurecr.io/myapi:v1.0.0
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: myapi-config
env:
- name: ConnectionStrings__DefaultConnection
valueFrom:
secretKeyRef:
name: myapi-secrets
key: DbConnectionString
# تعیین منابع دقیق جهت جلوگیری از Starvation
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
# Liveness & Readiness Probes (سلامتسنجی)
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
۳. Service
ایجاد یک Cluster IP ثابت برای لود بالانسینگ بین پادهای داتنت.
apiVersion: v1
kind: Service
metadata:
name: myapi-service
spec:
type: ClusterIP
selector:
app: myapi
ports:
- port: 80
targetPort: 8080
۶. تنظیم Health Checks تخصصی در .NET برای Kubernetes
کوبرنتیز برای اینکه بفهمد کانتینر شما آماده دریافت ترافیک است یا اینکه کرش کرده و باید ریستارت شود، از Probes استفاده میکند. داتنت بهصورت نیتیو پکیج Microsoft.Extensions.Diagnostics.HealthChecks را دارد.
کد نمونه در Program.cs:
var builder = WebApplication.CreateBuilder(args);
// افزودن Health Checks برای دیتابیس و ردیس
builder.Services.AddHealthChecks()
.AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")!, name: "db_check")
.AddRedis(builder.Configuration["Redis:ConnectionString"]!, name: "redis_check");
var app = builder.Build();
// Endpoint برای Liveness (فقط چک میکند که برنامه زنده است)
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false
});
// Endpoint برای Readiness (چک میکند که تمام وابستگیها مثل دیتابیس وصل هستند)
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("db") || check.Tags.Contains("redis")
});
app.Run();
۷. ملاحضات عملکرد و مدیریت حافظه (Memory Management)
یکی از چالشهای معروف توسعهدهندگان داتنت در داکر، مدیریت رم و Garbage Collector (GC) است.
در نسخه .NET Core 3.0 به بعد، داتنت بهصورت هوشمند محدودیتهای (Limits) تعریفشده در Docker و Kubernetes را تشخیص میدهد. با این حال، چند نکته طلایی برای بهینهسازی وجود دارد:
Server GC vs Workstation GC:
بهطور پیشفرض در برنامههای ASP.NET Core حالت Server GC فعال است که چند نخ (Thread) برای پاکسازی حافظه میسازد و حافظه بیشتری رزرو میکند. اگر کانتینر شما منابع کمی دارد (مثلاً زیر ۱ گیگابایت رم)، بهتر است حالت Workstation GC یا DATAS (Dynamic Adaptation To Application Sizes) در .NET 8/9 را تست کنید.
در فایل .csproj:
<PropertyGroup>
<ServerGarbageCollection>false</ServerGarbageCollection>
</PropertyGroup>
Graceful Shutdown (خروج نرم):
وقتی کوبرنتیز میخواهد یک پاد داتنت را بکشد، سیگنال SIGTERM ارسال میکند. داتنت بهصورت پیشفرض چند ثانیه مهلت میدهد تا ریکوئستهای جاری تمام شوند. این زمان را میتوانید تنظیم کنید تا هیچ ریکوئستی با خطای 503 مواجه نشود:
builder.Services.Configure<HostOptions>(options =>
{
options.ShutdownTimeout = TimeSpan.FromSeconds(30);
});
ترکیب .NET مدرن با Docker و Kubernetes، زیرساختی بسار پایدار، فوقالعاده سریع و آماده برای مقیاسهای بزرگ (Enterprise Level) ایجاد میکند.
خلاصه چکلیست برای شروع:
استفاده از Multi-Stage Build با Base Imageهای لینوکسی و سبک Alpine.
رعایت اصول امنیتی و عدم اجرای کانتینر با دسترسی Root.
پیادهسازی کامل Health Checks (liveness و readiness) درون برنامه.
تنظیم دقیق requests و limits در کوبرنتیز برای جلوگیری از OOMKill (Out Of Memory Kill).
0 نظر
هنوز نظری برای این مقاله ثبت نشده است.