Docker & Kubernetes Fundamentals for .NET Developers
Docker & Kubernetes Fundamentals for .NET Developers
Section titled “Docker & Kubernetes Fundamentals for .NET Developers”If you’re building .NET APIs and deploying to the cloud, containers aren’t optional anymore. This guide covers the Docker and Kubernetes concepts you’ll use every week — no fluff, just the bits that matter.
Docker: The Essentials
Section titled “Docker: The Essentials”Why Containers?
Section titled “Why Containers?”- Consistency: “Works on my machine” is dead. The container is the machine.
- Isolation: Each service gets its own dependencies, runtime, and config.
- Speed: Containers start in seconds, not minutes. CI/CD loves them.
Dockerfile for .NET (Multi-Stage Build)
Section titled “Dockerfile for .NET (Multi-Stage Build)”# Build stageFROM mcr.microsoft.com/dotnet/sdk:9.0 AS buildWORKDIR /srcCOPY *.csproj .RUN dotnet restoreCOPY . .RUN dotnet publish -c Release -o /app
# Runtime stageFROM mcr.microsoft.com/dotnet/aspnet:9.0WORKDIR /appCOPY --from=build /app .EXPOSE 8080ENTRYPOINT ["dotnet", "MyApi.dll"]Why multi-stage? The SDK image is ~900MB. The runtime image is ~220MB. Your final image should only carry what it needs to run.
Key Docker Commands
Section titled “Key Docker Commands”docker build -t myapi:1.0 .docker run -d -p 8080:8080 --name myapi myapi:1.0docker logs myapidocker exec -it myapi /bin/shdocker compose up -d # multi-container setupsDocker Compose for Local Dev
Section titled “Docker Compose for Local Dev”services: api: build: . ports: - "8080:8080" environment: - ConnectionStrings__Default=Server=db;Database=mydb;User=sa;Password=YourStrong!Passw0rd depends_on: - db db: image: mcr.microsoft.com/mssql/server:2022-latest environment: - ACCEPT_EULA=Y - SA_PASSWORD=YourStrong!Passw0rd ports: - "1433:1433"Docker vs Kubernetes — What Does Each Actually Do?
Section titled “Docker vs Kubernetes — What Does Each Actually Do?”A common confusion: Docker and Kubernetes solve different problems and are typically used together.
| Concern | Docker | Kubernetes |
|---|---|---|
| What it does | Builds and runs containers | Orchestrates containers across nodes |
| Scope | Single host | Cluster of hosts |
| Scaling | Manual | Automatic (HPA, replica sets) |
| Self-healing | ❌ No | ✅ Restarts failed pods |
| Service discovery | Manual / Compose networks | Built-in DNS (svc.cluster.local) |
| Load balancing | Manual | Built-in (Services) |
| Best for | Local dev, simple deploys | Production, multi-service workloads |
Kubernetes: The Essentials
Section titled “Kubernetes: The Essentials”Core Concepts
Section titled “Core Concepts”| Concept | What It Does |
|---|---|
| Pod | Smallest deployable unit — one or more containers sharing network/storage |
| Deployment | Manages pod replicas, rolling updates, rollbacks |
| Service | Stable network endpoint for a set of pods (ClusterIP, LoadBalancer, NodePort) |
| ConfigMap | Externalised configuration (non-sensitive) |
| Secret | Externalised sensitive data (base64 encoded, not encrypted by default) |
| Ingress | HTTP routing rules — host/path-based routing to services |
Minimal Deployment + Service
Section titled “Minimal Deployment + Service”apiVersion: apps/v1kind: Deploymentmetadata: name: myapispec: replicas: 3 selector: matchLabels: app: myapi template: metadata: labels: app: myapi spec: containers: - name: myapi image: myregistry.azurecr.io/myapi:1.0 ports: - containerPort: 8080 resources: requests: memory: "128Mi" cpu: "250m" limits: memory: "256Mi" cpu: "500m"---apiVersion: v1kind: Servicemetadata: name: myapi-svcspec: selector: app: myapi ports: - port: 80 targetPort: 8080 type: ClusterIPkubectl Commands You’ll Use Daily
Section titled “kubectl Commands You’ll Use Daily”kubectl get podskubectl describe pod <name>kubectl logs <pod-name> -fkubectl apply -f deployment.yamlkubectl rollout status deployment/myapikubectl rollout undo deployment/myapi # instant rollbackkubectl port-forward svc/myapi-svc 8080:80Health Checks Matter
Section titled “Health Checks Matter”Always define liveness and readiness probes. Without them, Kubernetes can’t tell if your app is healthy:
livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 10 periodSeconds: 15readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 10Common Gotchas
Section titled “Common Gotchas”Docker:
- Running as root — containers run as root by default. Add a
USERinstruction to your Dockerfile to drop privileges - Forgetting
.dockerignore— without it, your entirebin/,obj/, and.git/folder is sent as build context, bloating build time significantly latesttag in production —latestis mutable and unpredictable. Always tag images with a specific version or Git SHA
Kubernetes:
- Secrets are not encrypted — base64 encoding is not encryption. Use Azure Key Vault with the CSI Secrets Store driver for production credentials
- No resource limits — one unconstrained pod can starve all others on the same node. Always set
resources.requestsandresources.limits ImagePullPolicy: IfNotPresent— Kubernetes won’t pull a newer image if a local copy exists. UseAlwaysin CI/CD or pin to specific image versions- StatefulSets for stateful apps — if your pod needs persistent identity or ordered deployment (databases, queues), use StatefulSets, not Deployments
.NET-Specific Tips
Section titled “.NET-Specific Tips”- Use
.dockerignore— Excludebin/,obj/,.vs/, andnode_modules/to keep build context small. - Health check endpoints — Use
Microsoft.Extensions.Diagnostics.HealthChecksand map/healthzand/ready. - Configuration — Kubernetes ConfigMaps and Secrets map naturally to .NET’s
IConfigurationvia environment variables. - Graceful shutdown — Handle
IHostApplicationLifetime.ApplicationStoppingso in-flight requests complete during pod termination.
These fundamentals cover ~80% of what you need for day-to-day container work. For production hardening — network policies, RBAC, service mesh — that’s a deeper dive.