پادشاهِ کُدنویسا شو!
کینگتو - آموزش برنامه نویسی تخصصصی - دات نت - سی شارپ - بانک اطلاعاتی و امنیت

Kubernetes و Docker در دات نت

15 بازدید 0 نظر ۱۴۰۵/۰۵/۰۵
به‌عنوان یک مهندس نرم‌افزار که سال‌ها روی معماری سیستم‌های پرترافیک و توزیع‌شده با اکوسیستم دات‌نت کار کرده‌ام، کاملاً درکت می‌کنم که انتقال از سیستم‌های سنتی (Self-Hosted یا IIS) به سمت معماری مبتنی بر کانتینر چقدر می‌تواند چالش‌برانگیز و در عین حال نجات‌بخش باشد. در این مقاله، صفر تا صد کانتینرسازی (Containerization) اپلیکیشن‌های .NET را با Docker و مدیریت آن‌ها در Kubernetes به‌صورت تخصصی و کاربردی بررسی می‌کنیم.

چرا کانتینرسازی و اکوسیستم .NET؟

سال‌ها توسعه‌دهندگان دات‌نت به محیط‌های 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) برنامه در چند ثانیه.

 

کانتینرسازی حرفه‌ای اپلیکیشن .NET با Docker

ساخت یک 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 بالا:

  1. استفاده از Alpine: توزیع aspnet:9.0-alpine حجم Image نهایی شما را زیر ۲۰۰ مگابایت نگه می‌دارد.

  2. Layer Caching: کپی کردن csproj و اجرای dotnet restore قبل از کپی کامل سورس‌کد باعث می‌شود تا زمانی که Dependency جدیدی اضافه نکرده‌اید، لایه Restore از Cache خوانده شود و سرعت Build خیره‌کننده باشد.

  3. امنیت (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 را بین کانتینرها انجام می‌دهد.

ورود به دنیای Kubernetes (K8s)

وقتی تعداد کانتینرها بالا می‌رود و سیستم زیر بار ترافیک شدید قرار می‌گیرد، 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 را تشخیص می‌دهد. با این حال، چند نکته طلایی برای بهینه‌سازی وجود دارد:

  1. 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>
    
  2. Graceful Shutdown (خروج نرم):

    وقتی کوبرنتیز می‌خواهد یک پاد دات‌نت را بکشد، سیگنال SIGTERM ارسال می‌کند. دات‌نت به‌صورت پیش‌فرض چند ثانیه مهلت می‌دهد تا ریکوئست‌های جاری تمام شوند. این زمان را می‌توانید تنظیم کنید تا هیچ ریکوئستی با خطای 503 مواجه نشود:

    builder.Services.Configure<HostOptions>(options =>
    {
        options.ShutdownTimeout = TimeSpan.FromSeconds(30);
    });
    

 

 

ترکیب .NET مدرن با Docker و Kubernetes، زیرساختی بسار پایدار، فوق‌العاده سریع و آماده برای مقیاس‌های بزرگ (Enterprise Level) ایجاد می‌کند.

خلاصه چک‌لیست برای شروع:

  1. استفاده از Multi-Stage Build با Base Imageهای لینوکسی و سبک Alpine.

  2. رعایت اصول امنیتی و عدم اجرای کانتینر با دسترسی Root.

  3. پیاده‌سازی کامل Health Checks (liveness و readiness) درون برنامه.

  4. تنظیم دقیق requests و limits در کوبرنتیز برای جلوگیری از OOMKill (Out Of Memory Kill).

 

لینک استاندارد شده: gr124NLUqH

0 نظر

    هنوز نظری برای این مقاله ثبت نشده است.
جستجوی مقاله و آموزش
دوره‌ها با تخفیفات ویژه