.NET Core Success: Implement Powerful Cloud-Native Microservices with Kubernetes

Implementing Cloud-Native Microservices with ASP.NET Core and Kubernetes

Executive Summary

In modern .NET core enterprise applications, monolithic architectures struggle with scaling, deployment speed, and team velocity. This guide solves that by showing you how to build, containerize, and deploy independent ASP.NET Core microservices to Kubernetes. You’ll create a production-ready catalog service that scales horizontally, handles health checks, and communicates reliably—essential for cloud-native apps that must run 24/7 with zero-downtime updates and automatic scaling.

Prerequisites

  • .NET 10 SDK (latest stable release)
  • Docker Desktop with Kubernetes enabled (for local cluster)
  • kubectl CLI (install via winget install Kubernetes.kubectl on Windows or brew on macOS)
  • Visual Studio 2022 or VS Code with C# Dev Kit extension
  • Minikube (optional fallback: minikube start)
  • Basic folders: Create a solution root with services/catalog subfolder

Step-by-Step Implementation

Step 1: Create the Catalog Microservice with Minimal APIs

Let’s build our first microservice—a catalog API exposing products. Start in services/catalog.

dotnet new webapi -n CatalogService --no-https -f net10.0
cd CatalogService
dotnet add package Microsoft.AspNetCore.OpenApi

Replace Program.cs with this modern minimal API using primary constructors and records:

using CatalogService.Models;

var builder = WebApplication.CreateSlimBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHealthChecks();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

var products = new[]
{
    new Product(1, "Laptop", 999.99m),
    new Product(2, "Mouse", 29.99m)
};

app.MapGet("/products", () => products)
   .WithTags("Products")
   .WithOpenApi();

app.MapHealthChecks("/health");
app.MapHealthChecks("/ready", HealthCheckOptions);

app.Run();

static void HealthCheckOptions(HealthCheckOptions options)
{
    options.AddCheck("self", () => HealthCheckResult.Healthy());
}

Create Models/Product.cs:

namespace CatalogService.Models;

public record Product(int Id, string Name, decimal Price);

Step 2: Add Docker Multi-Stage Build

Create Dockerfile in services/catalog for optimized, production-ready images:

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY ["CatalogService.csproj", "."]
RUN dotnet restore "CatalogService.csproj"
COPY . .
RUN dotnet publish "CatalogService.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl --fail http://localhost:8080/health || exit 1
ENTRYPOINT ["dotnet", "CatalogService.dll"]

Build and test locally:

docker build -t catalog-service:dev .
docker run -p 8080:8080 catalog-service:dev

Hit http://localhost:8080/swagger—your API is live!

Step 3: Deploy to Kubernetes with Manifests

Enable Kubernetes in Docker Desktop. Create k8s/ folder with these YAML files.

deployment.yaml (with probes and resource limits):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: catalog-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: catalog
  template:
    metadata:
      labels:
        app: catalog
    spec:
      containers:
      - name: catalog
        image: catalog-service:dev
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: catalog-service
spec:
  selector:
    app: catalog
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

Deploy:

kubectl apply -f k8s/deployment.yaml
kubectl get pods
kubectl port-forward service/catalog-service 8080:80

Access at http://localhost:8080/swagger. Scale with kubectl scale deployment catalog-deployment --replicas=3.

Step 4: Add ConfigMaps and Secrets

For environment-specific config, create configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: catalog-config
data:
  Logging__LogLevel__Default: "Information"
  Products__MinPrice: "10.0"
---
apiVersion: v1
kind: Secret
metadata:
  name: catalog-secret
type: Opaque
data:
  ConnectionStrings__Db: c29tZS1iYXNlNjQtZGF0YQo= # "some-base64-data"

Mount in deployment under envFrom and volumeMounts.

Production-Ready C# Examples

Enhance with gRPC for inter-service calls and async events. Add to Program.cs:

// gRPC example for Product service
builder.Services.AddGrpc();

app.MapGrpcService<ProductService>();

// Event publishing with IMessageBroker (inject MassTransit or custom)
app.MapPost("/products", async (Product product, IMessageBroker broker) =>
{
    await broker.PublishAsync(new ProductCreated(product.Id, product.Name));
    return Results.Created($"/products/{product.Id}", product);
});

Use primary constructors for lean services:

public class ProductService(IMessageBroker broker) : ProductServiceBase
{
    public override async Task<GetProductsResponse> GetProducts(GetProductsRequest request, ServerCallContext context)
    {
        // Fetch from DB or cache
        await broker.PublishAsync(new ProductsQueried());
        return new() { Products = { /* products */ } };
    }
}

Common Pitfalls & Troubleshooting

  • Pod stuck in CrashLoopBackOff: Check logs with kubectl logs <pod-name>. Fix health probe paths or port mismatches.
  • Image pull errors: Tag images correctly; use docker push to registry like Docker Hub.
  • Service not reachable: Verify selector labels match deployment. Use kubectl describe service catalog-service.
  • High memory usage: Set resource limits; profile with dotnet-counters inside pod.
  • Config not loading: Use envFrom: configMapRef instead of individual env vars.

Performance & Scalability Considerations

    • Enable Horizontal Pod Autoscaler (HPA): kubectl autoscale deployment catalog-deployment --cpu-percent=50 --min=2 --max=10.
    • Use ASP.NET Core Kestrel tuning: Set Kestrel__Limits__MaxConcurrentConnections=1000 in ConfigMap.
    • Distributed caching with Redis: Add services.AddStackExchangeRedisCache().
    • Readiness gates for database migrations before traffic routing.
    • Monitor with Prometheus + Grafana; scrape /metrics endpoint.

Practical Best Practices

      • Always use multi-stage Dockerfiles to keep images under 100MB.
      • Implement OpenTelemetry for tracing: builder.Services.AddOpenTelemetryTracing().
      • Test locally with Docker Compose for multi-service setups.
      • Use Helm charts for complex deployments: helm create catalog-chart.
      • Write integration tests against Kubernetes-in-Docker (kind or minikube).
      • Prefer gRPC over REST for internal service calls—faster and typed.

Conclusion

You now have a fully functional, cloud-native catalog microservice running on Kubernetes. Next, add more services (basket, ordering), wire them with an API Gateway like Ocelot, and deploy to AKS or EKS. Experiment with Istio for service mesh and CI/CD with GitHub Actions.

FAQs

1. How do I expose my service externally in production?

Use an Ingress controller like NGINX Ingress. Create an Ingress resource pointing to your service port 80, with TLS for HTTPS.

2. What’s the difference between liveness and readiness probes?

Liveness restarts unhealthy pods; readiness stops routing traffic until the app is fully initialized (e.g., DB connected).

3. How do microservices communicate reliably?

Synchronous: gRPC or HTTP. Asynchronous: MassTransit with RabbitMQ/Kafka for events. Avoid direct DB coupling.

4. Can I use Entity Framework in microservices?

Yes, but per-service DBs only. Use dotnet ef migrations add in init containers for schema changes.

5. How to handle secrets in Kubernetes?

Store in Kubernetes Secrets or external vaults like Azure Key Vault. Mount as volumes or env vars—never hardcode.

6. Why multi-stage Dockerfiles?

They exclude build tools (SDK=500MB+), resulting in tiny runtime images (~100MB) that deploy faster and scale better.

7. How to debug pods interactively?

kubectl exec -it <pod> -- bash, then dotnet-counters collect or attach VS Code debugger.

8. Should I use StatefulSets or Deployments?

Deployments for stateless APIs like catalog. StatefulSets for databases needing stable identities.

9. How to roll out zero-downtime updates?

Kubernetes rolling updates replace pods gradually. Use strategy: type: RollingUpdate, maxUnavailable: 0.

10. What’s next after this single service?

Build a full eShopOnContainers clone: add ordering/basket services, API Gateway, and observability with Jaeger.

Headless Architecture in .NET Microservices with gRPC

AI-Driven .NET Development in 2026: How Senior Architects Master .NET 10 for Elite Performance Tuning

.NET Core Microservices and Azure Kubernetes Service

.NET Core Microservices on Azure

UnknownX

Recent Posts

Modern Authentication in 2026: How to Secure Your .NET 8 and Angular Apps with Keycloak

.NET 8 and Angular Apps with Keycloak In the rapidly evolving landscape of 2026, identity…

1 day ago

Mastering .NET 10 and C# 13: Ultimate Guide to High-Performance APIs 🚀

  Mastering .NET 10 and C# 13: Building High-Performance APIs Together Executive Summary In modern…

3 days ago

The Ultimate Guide to .NET 10 LTS and Performance Optimizations – A Critical Performance Wake-Up Call

    Implementing .NET 10 LTS Performance Optimizations: Build Faster Enterprise Apps Together Executive Summary…

5 days ago

Powerful Headless Architectures & API-First Development with .NET

  Building Production-Ready Headless Architectures with API-First .NET Executive Summary Modern applications demand flexibility across…

6 days ago

This website uses cookies.