ASP.NET Core  

ASP.NET Core Docker Kubernetes Deployment Guide | Cloud-Native DevOps (Part-32 of 40)

Deployment-domination

Previous article:   Real-Time Magic: SignalR and gRPC - Bring Apps Alive with Interactive Wonders! (Part - 31 of 40)

Table of Contents

  1. Introduction to Containerization

  2. Docker Fundamentals

  3. ASP.NET Core Dockerization

  4. Multi-Stage Docker Builds

  5. Docker Compose for Development

  6. Kubernetes Core Concepts

  7. Kubernetes Deployment Strategies

  8. Production-Grade Configuration

  9. CI/CD Pipeline Implementation

  10. Monitoring and Logging

  11. Security Best Practices

  12. Real-World Case Study

1. Introduction to Containerization

The Evolution of Application Deployment

Traditional deployment methodologies often led to the infamous "it works on my machine" syndrome. Containerization revolutionizes this by packaging applications with their dependencies, ensuring consistency across environments.

Real-World Analogy: Think of containers as shipping containers in the logistics industry. Just as standardized containers can be transported via ship, train, or truck without opening, software containers run consistently regardless of the underlying infrastructure.

Why Containerization Matters for  ASP.NET  Core?

  
    // Traditional deployment challenges
public class DeploymentPainPoints
{
    public List<string> CommonIssues = new()
    {
        "Dependency version conflicts",
        "Environment configuration mismatches",
        "Inconsistent runtime behavior",
        "Difficult scaling operations",
        "Long deployment cycles"
    };
}
  

Benefits Overview

  • Consistency: Identical environments from development to production

  • Isolation: Applications run in isolated environments

  • Portability: Run anywhere Docker is supported

  • Scalability: Easy horizontal scaling

  • Resource Efficiency: Better utilization than virtual machines

2. Docker Fundamentals

Docker Architecture Deep Dive

  
    # Understanding Docker components
Docker Ecosystem:
  Docker Engine:
    - Docker Daemon
    - Docker Client
    - REST API
  Docker Images: Immutable templates
  Docker Containers: Runnable instances
  Docker Registry: Image storage (Docker Hub, Azure Container Registry)
  Docker Compose: Multi-container applications
  

Essential Docker Commands

  
    # Image management
docker build -t myapp:latest .
docker images
docker rmi <image_id>

# Container operations
docker run -d -p 8080:80 --name myapp myapp:latest
docker ps
docker stop <container_id>
docker logs <container_id>

# System management
docker system prune
docker stats
  

Dockerfile Anatomy

  
    # Base image
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

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

# Publish stage
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish

# Final stage
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
  

3.  ASP.NET  Core Dockerization

Basic Docker Configuration

Let's create a real-world e-commerce application and containerize it step by step.

Project Structure

  
    ECommerceApp/
├── src/
│   ├── ECommerce.API/
│   ├── ECommerce.Services/
│   └── ECommerce.Data/
├── tests/
├── docker-compose.yml
└── Dockerfile
  

Complete Docker Implementation

  
    // Program.cs - Modern minimal API approach
using ECommerce.API;
using ECommerce.Data;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Database configuration with environment flexibility
builder.Services.AddDbContext<ECommerceContext>(options =>
{
    var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
    if (builder.Environment.IsProduction())
    {
        options.UseSqlServer(connectionString, 
            sqlOptions => sqlOptions.EnableRetryOnFailure());
    }
    else
    {
        options.UseSqlServer(connectionString);
    }
});

// Health checks
builder.Services.AddHealthChecks()
    .AddDbContextCheck<ECommerceContext>();

var app = builder.Build();

// Configure pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks("/health");

app.Run();
  

dockerfile

  
    # Multi-stage Dockerfile for ASP.NET Core
# Stage 1: Base
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

# Install curl for health checks
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

# Stage 2: Build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Copy project files
COPY ["src/ECommerce.API/ECommerce.API.csproj", "src/ECommerce.API/"]
COPY ["src/ECommerce.Services/ECommerce.Services.csproj", "src/ECommerce.Services/"]
COPY ["src/ECommerce.Data/ECommerce.Data.csproj", "src/ECommerce.Data/"]

# Restore dependencies
RUN dotnet restore "src/ECommerce.API/ECommerce.API.csproj"

# Copy everything else
COPY . .

# Build
WORKDIR "/src/src/ECommerce.API"
RUN dotnet build "ECommerce.API.csproj" -c Release -o /app/build

# Stage 3: Publish
FROM build AS publish
RUN dotnet publish "ECommerce.API.csproj" -c Release -o /app/publish

# Stage 4: Final
FROM base AS final
WORKDIR /app

# Create a non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
RUN chown -R appuser:appuser /app
USER appuser

COPY --from=publish /app/publish .

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

ENTRYPOINT ["dotnet", "ECommerce.API.dll"]
  

Environment-Specific Configurations

  
    // appsettings.Production.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=sql-server;Database=ECommerce;User Id=sa;Password=${SA_PASSWORD};TrustServerCertificate=true;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://+:80"
      }
    }
  }
}
  

4. Multi-Stage Docker Builds

Advanced Optimization Techniques

  
    # Ultra-optimized multi-stage build
# Stage 1: Base with security scanning
FROM mcr.microsoft.com/dotnet/aspnet:8.0@sha256:abc123... AS base
USER root
WORKDIR /app

# Security hardening
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# Stage 2: Build with caching optimization
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Copy only project files for better layer caching
COPY ["Directory.Build.props", "./"]
COPY ["src/ECommerce.API/ECommerce.API.csproj", "src/ECommerce.API/"]
COPY ["src/ECommerce.Services/ECommerce.Services.csproj", "src/ECommerce.Services/"]
COPY ["src/ECommerce.Data/ECommerce.Data.csproj", "src/ECommerce.Data/"]

# Restore with no cache for deterministic builds
RUN dotnet restore "src/ECommerce.API/ECommerce.API.csproj" --no-cache

# Copy source code
COPY . .

# Build with optimization
WORKDIR "/src/src/ECommerce.API"
RUN dotnet build "ECommerce.API.csproj" -c Release -o /app/build \
    --no-restore \
    -p:ContinuousIntegrationBuild=true

# Stage 3: Test
FROM build AS test
WORKDIR "/src/tests"
RUN dotnet test --logger "trx" --results-directory /testresults

# Stage 4: Publish with trimming
FROM build AS publish
RUN dotnet publish "ECommerce.API.csproj" -c Release -o /app/publish \
    --no-build \
    -p:PublishReadyToRun=true \
    -p:PublishTrimmed=true \
    -p:TrimMode=link

# Stage 5: Final optimized image
FROM base AS final
WORKDIR /app

# Non-root user for security
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
RUN chown -R appuser:appgroup /app
USER appuser

COPY --from=publish /app/publish .

# Enhanced health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:80/health || exit 1

ENTRYPOINT ["dotnet", "ECommerce.API.dll"]
  

Build Arguments and Environment Variables

  
    # Parameterized Dockerfile
ARG RUNTIME_IMAGE=mcr.microsoft.com/dotnet/aspnet:8.0
ARG SDK_IMAGE=mcr.microsoft.com/dotnet/sdk:8.0
ARG CONFIGURATION=Release

FROM ${RUNTIME_IMAGE} AS base
# ... base layer setup

FROM ${SDK_IMAGE} AS build
ARG CONFIGURATION
# ... build with ${CONFIGURATION}

# Build command with arguments
# docker build --build-arg CONFIGURATION=Debug -t myapp:debug .
  

5. Docker Compose for Development

Complete Development Environment

  
    # docker-compose.yml - Full development stack
version: '3.8'

services:
  ecommerce.api:
    image: ecommerce-api:latest
    build:
      context: .
      dockerfile: Dockerfile
      target: build  # Use build stage for development
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ConnectionStrings__DefaultConnection=Server=sql-server;Database=ECommerce;User Id=sa;Password=YourPassword123!;TrustServerCertificate=true;
    ports:
      - "5000:80"
    volumes:
      - .:/src
      - ~/.nuget/packages:/root/.nuget/packages:ro
    depends_on:
      - sql-server
      - redis
    networks:
      - ecommerce-network

  sql-server:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      SA_PASSWORD: "YourPassword123!"
      ACCEPT_EULA: "Y"
      MSSQL_PID: "Express"
    ports:
      - "1433:1433"
    volumes:
      - sql-data:/var/opt/mssql
    networks:
      - ecommerce-network

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    networks:
      - ecommerce-network

  seq:
    image: datalust/seq:latest
    environment:
      - ACCEPT_EULA=Y
    ports:
      - "5341:5341"
      - "8081:80"
    volumes:
      - seq-data:/data
    networks:
      - ecommerce-network

volumes:
  sql-data:
  redis-data:
  seq-data:

networks:
  ecommerce-network:
    driver: bridge
  

Development-Specific Dockerfile

  
    # Dockerfile.dev - Development optimized
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS development

# Install tools for development
RUN dotnet tool install -g dotnet-ef
RUN dotnet tool install -g dotnet-watch

ENV PATH="$PATH:/root/.dotnet/tools"

WORKDIR /app

# Copy project files
COPY . .

# Expose ports
EXPOSE 80
EXPOSE 443

# Development entry point
CMD ["dotnet", "watch", "run", "--urls", "http://0.0.0.0:80"]
  

Database Migration Strategy

  
    // Database migrator service
public static class DatabaseMigrator
{
    public static async Task MigrateDatabaseAsync(this WebApplication app)
    {
        using var scope = app.Services.CreateScope();
        var services = scope.ServiceProvider;
        
        try
        {
            var context = services.GetRequiredService<ECommerceContext>();
            await context.Database.MigrateAsync();
            
            app.Logger.LogInformation("Database migrated successfully");
        }
        catch (Exception ex)
        {
            app.Logger.LogError(ex, "An error occurred while migrating the database");
            throw;
        }
    }
}

// In Program.cs
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    await app.MigrateDatabaseAsync();
}
  

6. Kubernetes Core Concepts

Kubernetes Architecture Overview

  
    # Understanding Kubernetes components
Kubernetes Cluster:
  Control Plane:
    - API Server: Frontend for Kubernetes
    - etcd: Key-value store for cluster data
    - Scheduler: Assigns pods to nodes
    - Controller Manager: Regulates cluster state
  Worker Nodes:
    - Kubelet: Agent running on each node
    - Container Runtime: Docker, containerd
    - Kube-proxy: Network proxy
  

Essential Kubernetes Objects

  
    # Pod - Smallest deployable unit
apiVersion: v1
kind: Pod
metadata:
  name: ecommerce-api-pod
  labels:
    app: ecommerce-api
    tier: backend
spec:
  containers:
  - name: ecommerce-api
    image: ecommerce-api:latest
    ports:
    - containerPort: 80
    env:
    - name: ASPNETCORE_ENVIRONMENT
      value: "Production"

# Service - Network abstraction
apiVersion: v1
kind: Service
metadata:
  name: ecommerce-api-service
spec:
  selector:
    app: ecommerce-api
  ports:
  - port: 80
    targetPort: 80
  type: LoadBalancer

# Deployment - Declarative updates
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ecommerce-api-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ecommerce-api
  template:
    metadata:
      labels:
        app: ecommerce-api
    spec:
      containers:
      - name: ecommerce-api
        image: ecommerce-api:latest
        ports:
        - containerPort: 80
  

Real-World E-Commerce Kubernetes Setup

  
    # k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: ecommerce-production
  labels:
    name: ecommerce-production
    environment: production

# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: ecommerce-config
  namespace: ecommerce-production
data:
  appsettings.json: |
    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*"
    }

# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: ecommerce-secrets
  namespace: ecommerce-production
type: Opaque
data:
  connection-string: <base64-encoded-connection-string>
  api-key: <base64-encoded-api-key>
  

7. Kubernetes Deployment Strategies

Complete Deployment Configuration

  
    # k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ecommerce-api
  namespace: ecommerce-production
  labels:
    app: ecommerce-api
    version: v1.0.0
spec:
  replicas: 3
  minReadySeconds: 30
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: ecommerce-api
  template:
    metadata:
      labels:
        app: ecommerce-api
        version: v1.0.0
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "80"
        prometheus.io/path: "/metrics"
    spec:
      containers:
      - name: ecommerce-api
        image: ecommerce-api:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: http
        - containerPort: 443
          name: https
        env:
        - name: ASPNETCORE_ENVIRONMENT
          value: "Production"
        - name: ConnectionStrings__DefaultConnection
          valueFrom:
            secretKeyRef:
              name: ecommerce-secrets
              key: connection-string
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 1
        startupProbe:
          httpGet:
            path: /health/startup
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 10
          failureThreshold: 3
        securityContext:
          allowPrivilegeEscalation: false
          runAsNonRoot: true
          runAsUser: 1000
          capabilities:
            drop:
            - ALL
      restartPolicy: Always
      terminationGracePeriodSeconds: 60
  

Service and Ingress Configuration

  
    # k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ecommerce-api-service
  namespace: ecommerce-production
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "false"
spec:
  selector:
    app: ecommerce-api
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
  - name: https
    port: 443
    targetPort: 443
    protocol: TCP
  type: LoadBalancer

# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ecommerce-ingress
  namespace: ecommerce-production
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - api.ecommerce.com
    secretName: ecommerce-tls
  rules:
  - host: api.ecommerce.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: ecommerce-api-service
            port:
              number: 80
  

Horizontal Pod Autoscaler

  
    # k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ecommerce-api-hpa
  namespace: ecommerce-production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ecommerce-api
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 100
        periodSeconds: 60
  

8. Production-Grade Configuration

Advanced Health Checks

  
    // Advanced health monitoring
public static class HealthCheckExtensions
{
    public static IHealthChecksBuilder AddProductionHealthChecks(
        this IServiceCollection services, 
        IConfiguration configuration)
    {
        return services.AddHealthChecks()
            .AddDbContextCheck<ECommerceContext>(
                name: "database",
                tags: new[] { "ready", "live" })
            .AddRedis(
                redisConnectionString: configuration.GetConnectionString("Redis"),
                name: "redis",
                tags: new[] { "ready", "live" })
            .AddUrlGroup(
                new Uri("https://api.paymentgateway.com/health"),
                name: "payment-gateway",
                tags: new[] { "ready" })
            .AddDiskStorageHealthCheck(s => 
                s.AddDrive("C:\\", 1024), 
                name: "storage",
                tags: new[] { "live" })
            .AddApplicationInsightsPublisher();
    }
}

// Custom health check for business logic
public class OrderProcessingHealthCheck : IHealthCheck
{
    private readonly ECommerceContext _context;

    public OrderProcessingHealthCheck(ECommerceContext context)
    {
        _context = context;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, 
        CancellationToken cancellationToken = default)
    {
        try
        {
            // Check if orders can be processed
            var recentOrders = await _context.Orders
                .Where(o => o.CreatedAt > DateTime.UtcNow.AddHours(-1))
                .CountAsync(cancellationToken);

            return recentOrders >= 0 
                ? HealthCheckResult.Healthy("Order processing is operational")
                : HealthCheckResult.Degraded("Order processing is experiencing issues");
        }
        catch (Exception ex)
        {
            return HealthCheckResult.Unhealthy("Order processing health check failed", ex);
        }
    }
}
  

Configuration Management

  
    // Configuration builder with multiple sources
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                  .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", 
                              optional: true, reloadOnChange: true)
                  .AddEnvironmentVariables()
                  .AddUserSecrets<Program>(optional: true);

            if (context.HostingEnvironment.IsProduction())
            {
                config.AddAzureKeyVault(
                    "https://mykeyvault.vault.azure.net/",
                    new DefaultAzureCredential());
            }
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        })
        .UseSerilog((context, configuration) =>
        {
            configuration.ReadFrom.Configuration(context.Configuration);
        });
  

Resilience and Circuit Breaker Patterns

  
    // Polly resilience policies
public static class ResiliencePolicies
{
    public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg => !msg.IsSuccessStatusCode)
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: retryAttempt => 
                    TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetry: (outcome, timespan, retryCount, context) =>
                {
                    var logger = context.GetLogger();
                    logger?.LogWarning(
                        "Retry {RetryCount} after {Delay}ms for {OperationKey}", 
                        retryCount, timespan.TotalMilliseconds, context.OperationKey);
                });
    }

    public static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .CircuitBreakerAsync(
                handledEventsAllowedBeforeBreaking: 3,
                durationOfBreak: TimeSpan.FromSeconds(30),
                onBreak: (outcome, breakDelay, context) =>
                {
                    var logger = context.GetLogger();
                    logger?.LogError(
                        "Circuit breaker opened for {BreakDelay}ms", 
                        breakDelay.TotalMilliseconds);
                },
                onReset: (context) =>
                {
                    var logger = context.GetLogger();
                    logger?.LogInformation("Circuit breaker reset");
                });
    }
}

// Usage in service registration
services.AddHttpClient<IPaymentService, PaymentService>()
    .AddPolicyHandler(ResiliencePolicies.GetRetryPolicy())
    .AddPolicyHandler(ResiliencePolicies.GetCircuitBreakerPolicy());
  

9. CI/CD Pipeline Implementation

GitHub Actions Pipeline

  
    # .github/workflows/deploy.yml
name: Deploy to Kubernetes

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: 8.0.x
        
    - name: Restore dependencies
      run: dotnet restore
      
    - name: Build
      run: dotnet build --no-restore --configuration Release
      
    - name: Test
      run: dotnet test --no-build --verbosity normal --logger trx

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Build Docker image
      run: |
        docker build . \
          --file Dockerfile \
          --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
          --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          
    - name: Log into registry
      run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin
      
    - name: Push Docker image
      run: |
        docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
        docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Deploy to Kubernetes
      uses: azure/k8s-deploy@v4
      with:
        namespace: ecommerce-production
        manifests: |
          k8s/namespace.yaml
          k8s/configmap.yaml
          k8s/secret.yaml
          k8s/deployment.yaml
          k8s/service.yaml
          k8s/ingress.yaml
          k8s/hpa.yaml
        images: |
          ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
        kubectl-version: 'latest'
        
    - name: Verify deployment
      run: |
        kubectl rollout status deployment/ecommerce-api -n ecommerce-production
        kubectl get pods -n ecommerce-production
  

Azure DevOps Pipeline

  
    # azure-pipelines.yml
trigger:
  branches:
    include:
    - main
    - develop

variables:
  dockerRegistryServiceConnection: 'AzureContainerRegistry'
  imageRepository: 'ecommerceapi'
  containerRegistry: 'myacr.azurecr.io'
  dockerfilePath: '$(Build.SourcesDirectory)/Dockerfile'
  tag: '$(Build.BuildId)'
  
stages:
- stage: Build
  displayName: Build and test
  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: 'ubuntu-latest'
      
    steps:
    - task: DotNetCoreCLI@2
      displayName: 'Restore dependencies'
      inputs:
        command: 'restore'
        
    - task: DotNetCoreCLI@2
      displayName: 'Build solution'
      inputs:
        command: 'build'
        arguments: '--no-restore --configuration Release'
        
    - task: DotNetCoreCLI@2
      displayName: 'Run tests'
      inputs:
        command: 'test'
        arguments: '--no-build --verbosity normal --logger trx'
        
    - task: Docker@2
      displayName: 'Build Docker image'
      inputs:
        command: 'build'
        repository: '$(imageRepository)'
        dockerfile: '$(dockerfilePath)'
        tags: |
          $(tag)
          latest
          
    - task: Docker@2
      displayName: 'Push Docker image'
      inputs:
        command: 'push'
        repository: '$(imageRepository)'
        tags: |
          $(tag)
          latest

- stage: DeployToStaging
  displayName: Deploy to staging
  dependsOn: Build
  condition: succeeded()
  
  jobs:
  - deployment: Deploy
    displayName: Deploy
    environment: 'staging'
    pool:
      vmImage: 'ubuntu-latest'
      
    strategy:
      runOnce:
        deploy:
          steps:
          - task: KubernetesManifest@0
            displayName: 'Deploy to Kubernetes'
            inputs:
              action: 'deploy'
              namespace: 'ecommerce-staging'
              manifests: |
                $(Build.SourcesDirectory)/k8s/**/*.yaml
              containers: |
                $(containerRegistry)/$(imageRepository):$(tag)
                
          - task: Kubernetes@1
            displayName: 'Verify deployment'
            inputs:
              command: 'rollout'
              arguments: 'status deployment/ecommerce-api -n ecommerce-staging'
  

Database Migration in CI/CD

  
    // Database migration job in Kubernetes
apiVersion: batch/v1
kind: Job
metadata:
  name: database-migration
  namespace: ecommerce-production
spec:
  template:
    spec:
      containers:
      - name: migrator
        image: ecommerce-api:latest
        command: ["dotnet", "ECommerce.API.dll", "migrate"]
        env:
        - name: ASPNETCORE_ENVIRONMENT
          value: "Production"
        - name: ConnectionStrings__DefaultConnection
          valueFrom:
            secretKeyRef:
              name: ecommerce-secrets
              key: connection-string
      restartPolicy: Never
  backoffLimit: 2
  

10. Monitoring and Logging

Structured Logging with Serilog

  
    // Program.cs with advanced logging
using Serilog;
using Serilog.Events;
using Serilog.Sinks.Elasticsearch;

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .MinimumLevel.Override("System", LogEventLevel.Warning)
    .Enrich.FromLogContext()
    .Enrich.WithProperty("Application", "ECommerce.API")
    .Enrich.WithMachineName()
    .Enrich.WithEnvironmentName()
    .WriteTo.Console(
        outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}")
    .WriteTo.File(
        "logs/ecommerce-.log",
        rollingInterval: RollingInterval.Day,
        retainedFileCountLimit: 7,
        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
    .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://elasticsearch:9200"))
    {
        AutoRegisterTemplate = true,
        AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
        IndexFormat = "ecommerce-logs-{0:yyyy.MM}",
        NumberOfShards = 2,
        NumberOfReplicas = 1
    })
    .CreateLogger();

try
{
    Log.Information("Starting web application");
    
    var builder = WebApplication.CreateBuilder(args);
    
    builder.Host.UseSerilog();
    
    // ... rest of configuration
    
    var app = builder.Build();
    
    // ... app configuration
    
    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
    Log.CloseAndFlush();
}
  

Application Insights Integration

  
    // Advanced telemetry configuration
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
    services.AddApplicationInsightsTelemetry(options =>
    {
        options.ConnectionString = configuration["ApplicationInsights:ConnectionString"];
        options.EnableAdaptiveSampling = false;
    });
    
    services.AddApplicationInsightsKubernetesEnricher();
    
    // Custom telemetry initializer
    services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();
}

public class CustomTelemetryInitializer : ITelemetryInitializer
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CustomTelemetryInitializer(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        var requestTelemetry = telemetry as RequestTelemetry;
        if (requestTelemetry != null)
        {
            var context = _httpContextAccessor.HttpContext;
            if (context != null)
            {
                requestTelemetry.Properties["User"] = context.User.Identity?.Name;
                requestTelemetry.Properties["ClientIP"] = context.Connection.RemoteIpAddress?.ToString();
            }
        }
    }
}
  

Kubernetes Monitoring Stack

  
    # k8s/monitoring.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: monitoring
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s
      evaluation_interval: 15s
    
    scrape_configs:
    - job_name: 'ecommerce-api'
      kubernetes_sd_configs:
      - role: endpoints
        namespaces:
          names:
          - ecommerce-production
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)
      - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
        action: replace
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        target_label: __address__
      - action: labelmap
        regex: __meta_kubernetes_service_label_(.+)
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: kubernetes_namespace
      - source_labels: [__meta_kubernetes_service_name]
        action: replace
        target_label: kubernetes_name

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      containers:
      - name: prometheus
        image: prom/prometheus:latest
        ports:
        - containerPort: 9090
        volumeMounts:
        - name: prometheus-config
          mountPath: /etc/prometheus/
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
      volumes:
      - name: prometheus-config
        configMap:
          name: prometheus-config
  

11. Security Best Practices

Security Context and Policies

  
    # k8s/security.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ecommerce-api-sa
  namespace: ecommerce-production
automountServiceAccountToken: false

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: ecommerce-production
  name: ecommerce-api-role
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["configmaps", "secrets"]
  verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ecommerce-api-rolebinding
  namespace: ecommerce-production
subjects:
- kind: ServiceAccount
  name: ecommerce-api-sa
  namespace: ecommerce-production
roleRef:
  kind: Role
  name: ecommerce-api-role
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ecommerce-api-network-policy
  namespace: ecommerce-production
spec:
  podSelector:
    matchLabels:
      app: ecommerce-api
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 80
    - protocol: TCP
      port: 443
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          name: ecommerce-production
    ports:
    - protocol: TCP
      port: 1433
    - protocol: TCP
      port: 6379
  

Container Security

  
    # Security-hardened Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base

# Security updates
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# Create non-root user
RUN groupadd -r appgroup && \
    useradd -r -g appgroup -s /bin/false appuser

WORKDIR /app

# Set secure permissions
RUN chown -R appuser:appgroup /app && \
    chmod -R 755 /app

USER appuser

# Copy application
COPY --from=publish /app/publish .

# Security headers via environment
ENV ASPNETCORE_URLS=http://+:80
ENV DOTNET_RUNNING_IN_CONTAINER=true
ENV COMPlus_EnableDiagnostics=0

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

ENTRYPOINT ["dotnet", "ECommerce.API.dll"]
  

Secret Management

  
    // Secure configuration management
public static class SecurityExtensions
{
    public static IServiceCollection AddSecureConfiguration(
        this IServiceCollection services, 
        IConfiguration configuration)
    {
        // Azure Key Vault integration
        if (configuration.GetValue<bool>("UseKeyVault"))
        {
            var keyVaultEndpoint = configuration["KeyVault:Endpoint"];
            services.AddAzureKeyVault(keyVaultEndpoint);
        }

        // Database encryption
        services.AddDbContext<ECommerceContext>(options =>
        {
            options.UseSqlServer(
                configuration.GetConnectionString("DefaultConnection"),
                sqlOptions =>
                {
                    sqlOptions.EnableRetryOnFailure();
                    sqlOptions.CommandTimeout(30);
                });
        });

        // Secure HTTP clients
        services.AddHttpClient("SecureClient")
            .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
                {
                    // Custom certificate validation
                    return errors == System.Net.Security.SslPolicyErrors.None;
                }
            });

        return services;
    }
}
  

12. Real-World Case Study

E-Commerce Platform Deployment

Scenario: Deploying a high-traffic e-commerce platform with 1 million+ products and 10,000+ concurrent users.

  
    Production Stack:
  Frontend: 
    - React SPA (CDN hosted)
    - Azure Front Door for global distribution
  Backend:
    - ASP.NET Core API (Kubernetes)
    - Redis Cluster for caching
    - SQL Server Always On Availability Groups
  Infrastructure:
    - Azure Kubernetes Service (AKS)
    - Azure Application Gateway
    - Azure Monitor + Application Insights
  

Performance Optimization

  
    // Caching strategy
public class DistributedCacheService : ICacheService
{
    private readonly IDistributedCache _cache;
    private readonly ILogger<DistributedCacheService> _logger;

    public DistributedCacheService(IDistributedCache cache, ILogger<DistributedCacheService> logger)
    {
        _cache = cache;
        _logger = logger;
    }

    public async Task<T> GetOrCreateAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null)
    {
        var cachedData = await _cache.GetStringAsync(key);
        if (cachedData != null)
        {
            _logger.LogDebug("Cache hit for {Key}", key);
            return JsonSerializer.Deserialize<T>(cachedData);
        }

        _logger.LogDebug("Cache miss for {Key}", key);
        var data = await factory();
        
        var options = new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = expiration ?? TimeSpan.FromMinutes(30)
        };
        
        await _cache.SetStringAsync(key, JsonSerializer.Serialize(data), options);
        return data;
    }
}

// Database performance
public class OptimizedProductRepository : IProductRepository
{
    private readonly ECommerceContext _context;

    public OptimizedProductRepository(ECommerceContext context)
    {
        _context = context;
    }

    public async Task<List<Product>> GetFeaturedProductsAsync(int count)
    {
        return await _context.Products
            .Where(p => p.IsFeatured && p.IsActive)
            .OrderByDescending(p => p.CreatedAt)
            .Take(count)
            .AsNoTracking() // Read-only optimization
            .ToListAsync();
    }

    public async Task<Product> GetProductWithDetailsAsync(int productId)
    {
        return await _context.Products
            .Include(p => p.Category)
            .Include(p => p.Inventory)
            .Include(p => p.Reviews)
            .AsSplitQuery() // Performance optimization for multiple includes
            .FirstOrDefaultAsync(p => p.Id == productId);
    }
}
  

Disaster Recovery Plan

  
    # k8s/backup.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: database-backup
  namespace: ecommerce-production
spec:
  schedule: "0 2 * * *" # Daily at 2 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: mcr.microsoft.com/mssql-tools:latest
            command:
            - /bin/bash
            - -c
            - |
              /opt/mssql-tools/bin/sqlcmd -S $(DB_SERVER) -U $(DB_USER) -P $(DB_PASSWORD) \
              -Q "BACKUP DATABASE [ECommerce] TO DISK = '/backup/ecommerce_$(date +%Y%m%d_%H%M%S).bak'"
            volumeMounts:
            - name: backup-volume
              mountPath: /backup
            env:
            - name: DB_SERVER
              valueFrom:
                secretKeyRef:
                  name: ecommerce-secrets
                  key: db-server
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: ecommerce-secrets
                  key: db-user
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: ecommerce-secrets
                  key: db-password
          volumes:
          - name: backup-volume
            persistentVolumeClaim:
              claimName: backup-pvc
          restartPolicy: OnFailure

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: backup-pvc
  namespace: ecommerce-production
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi
  

Final Deployment Checklist

  
    # Production Deployment Checklist

## Pre-Deployment
- [ ] Security scanning completed
- [ ] Performance testing passed
- [ ] Database migrations tested
- [ ] Rollback plan documented
- [ ] Team communication sent

## Deployment
- [ ] Blue-green deployment configured
- [ ] Health checks passing
- [ ] Monitoring dashboards updated
- [ ] Log aggregation working
- [ ] Alert rules configured

## Post-Deployment
- [ ] Smoke tests completed
- [ ] Performance metrics verified
- [ ] Error rate monitored
- [ ] User feedback collected
- [ ] Documentation updated
  

This comprehensive guide covers the entire journey from local Docker development to production Kubernetes deployment for  ASP.NET  Core applications. The real-world examples, complete code samples, and production best practices provide a solid foundation for mastering cloud-native deployment strategies.