Skip to content

senapatisantosh/DotnetNginx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

K3s + NGINX Ingress + Dapr + .NET API Learning Project

A complete, runnable learning project that demonstrates NGINX as an Ingress Controller and API gateway in a Kubernetes (K3s) environment, with Dapr sidecar for distributed application capabilities, TLS certificate automation via cert-manager and Let's Encrypt, and Redis as the backing store.

Architecture

User Browser
    |
    v
DNS Resolution (*.localtest.me -> 127.0.0.1)
    |
    v
NGINX Ingress Controller (TLS termination)
    |
    v
Kubernetes Service (ClusterIP)
    |
    v
Load Balanced .NET API Pods (3 replicas, each with Dapr sidecar)
    |                                    |
    v                                    v
Dapr State Store (Redis)         Dapr Pub/Sub (Redis Streams)
                                         |
                                         v
                                 Zipkin (Distributed Tracing)
graph TB
    B["Browser"] -->|"HTTPS"| NIC["NGINX Ingress Controller<br/>:30443"]
    NIC -->|"TLS Termination"| SVC["Service: sample-api"]
    SVC -->|"Round Robin"| P1["Pod 1<br/>.NET 8 + daprd"]
    SVC -->|"Round Robin"| P2["Pod 2<br/>.NET 8 + daprd"]
    SVC -->|"Round Robin"| P3["Pod 3<br/>.NET 8 + daprd"]
    CM["cert-manager"] -->|"Provisions TLS"| NIC
    LE["Let's Encrypt"] -->|"ACME"| CM

    P1 -->|"state/pubsub"| REDIS["Redis<br/>(Bitnami)"]
    P1 -->|"tracing"| ZIP["Zipkin"]
    SENTRY["dapr-sentry"] -->|"mTLS certs"| P1

    style NIC fill:#326CE5,color:#fff
    style CM fill:#E6522C,color:#fff
    style P1 fill:#512BD4,color:#fff
    style P2 fill:#512BD4,color:#fff
    style P3 fill:#512BD4,color:#fff
    style REDIS fill:#D82C20,color:#fff
    style ZIP fill:#F5A623,color:#000
    style SENTRY fill:#E6522C,color:#fff
Loading

What You'll Learn

Topic Concepts
NGINX Reverse proxy, load balancing, upstreams, path/host routing, rate limiting, header manipulation, TLS termination, connection reuse, buffering, timeouts
Kubernetes Networking Services (ClusterIP), Ingress, NGINX Ingress Controller, traffic flow, CoreDNS
TLS & Certificates TLS termination, Let's Encrypt ACME, HTTP-01 challenge, cert-manager, Issuer vs ClusterIssuer, automatic renewal
Dapr Sidecar architecture, state management, pub/sub messaging, distributed tracing, mTLS, sentry, placement, operator, dashboard
Redis Bitnami Helm chart, state backing store, pub/sub via Redis Streams, consumer groups
Container Runtime containerd vs Docker, OCI images, nerdctl
K3s K3s components, embedded containerd, replacing Traefik with NGINX
Deployment Helmfile, multi-chart management, environment configuration

Repository Structure

.
├── src/SampleApi/                  # .NET 8 Web API
│   ├── Controllers/
│   │   ├── HelloController.cs      # Hello + load balancing demo
│   │   ├── PodInfoController.cs    # Pod metadata + headers
│   │   ├── StateController.cs      # Dapr state management (Redis)
│   │   ├── PubSubController.cs     # Dapr pub/sub (Redis Streams)
│   │   └── DaprInfoController.cs   # Dapr sidecar health/metadata
│   ├── Program.cs
│   └── SampleApi.csproj
│
├── docker/
│   └── Dockerfile                  # Multi-stage .NET build
│
├── k8s/
│   ├── base/
│   │   └── namespace.yaml
│   ├── api/
│   │   ├── deployment.yaml         # 3 replicas + Dapr sidecar annotations
│   │   └── service.yaml            # ClusterIP service
│   ├── ingress/
│   │   ├── ingress.yaml            # Primary domain (TLS)
│   │   ├── ingress-second-domain.yaml
│   │   └── rate-limit-ingress.yaml
│   ├── cert-manager/
│   │   ├── cluster-issuer.yaml     # Let's Encrypt + self-signed
│   │   └── certificate.yaml
│   ├── nginx/
│   │   └── nginx-config.yaml       # NGINX global config
│   └── dapr/
│       ├── dapr-config.yaml        # Tracing + mTLS configuration
│       ├── dapr-components.yaml    # State store + pub/sub (Redis)
│       ├── dapr-subscription.yaml  # Declarative pub/sub subscription
│       └── zipkin.yaml             # Zipkin deployment + service
│
├── helmfile/
│   └── helmfile.yaml               # NGINX + cert-manager + Dapr + Redis
│
├── scripts/
│   ├── mac/
│   │   ├── setup.sh                # Full macOS setup
│   │   └── teardown.sh
│   └── wsl/
│       ├── setup.sh                # Full WSL setup
│       └── teardown.sh
│
└── docs/
    ├── architecture.md
    ├── nginx-ingress-deep-dive.md
    ├── cert-manager-explained.md
    ├── tls-handshake-flow.md
    ├── dns-resolution-flow.md
    ├── helmfile-deployment.md
    ├── k3s-setup.md
    ├── nerdctl-containerd.md
    ├── dapr-deep-dive.md           # Dapr architecture + building blocks
    └── redis-bitnami.md            # Redis as Dapr backing store

Prerequisites

  • macOS: Homebrew, 4GB RAM, 20GB disk
  • Windows: WSL2 with Ubuntu 22.04+, systemd enabled, 4GB RAM, 20GB disk

Quick Start

macOS

git clone <repo-url> && cd DotnetNginx

chmod +x scripts/mac/setup.sh
./scripts/mac/setup.sh

export KUBECONFIG=~/.kube/config-k3s-demo

Windows WSL

git clone <repo-url> && cd DotnetNginx

# Ensure systemd is enabled in /etc/wsl.conf:
# [boot]
# systemd=true

chmod +x scripts/wsl/setup.sh
./scripts/wsl/setup.sh

Step-by-Step Manual Setup

1. Install K3s (without Traefik)

# WSL / Linux
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik --write-kubeconfig-mode=644" sh -

mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config

2. Build the Container Image

sudo nerdctl --address /run/k3s/containerd/containerd.sock --namespace k8s.io \
  build -t demo-api:local -f docker/Dockerfile .

sudo nerdctl --address /run/k3s/containerd/containerd.sock --namespace k8s.io \
  tag demo-api:local localhost:5000/demo-api:latest

sudo nerdctl --address /run/k3s/containerd/containerd.sock --namespace k8s.io \
  push localhost:5000/demo-api:latest

3. Deploy Infrastructure with Helmfile

cd helmfile/
helmfile sync

This installs:

  1. NGINX Ingress Controller (NodePort on 30080/30443)
  2. cert-manager (with CRDs)
  3. Redis (Bitnami, standalone mode)
  4. Dapr runtime (sentry, placement, operator, dashboard, sidecar-injector)

4. Deploy Zipkin and Dapr Components

# Zipkin for distributed tracing
kubectl apply -f k8s/dapr/zipkin.yaml

# Namespace (if not already created)
kubectl apply -f k8s/base/namespace.yaml

# Dapr configuration and components
kubectl apply -f k8s/dapr/dapr-config.yaml
kubectl apply -f k8s/dapr/dapr-components.yaml
kubectl apply -f k8s/dapr/dapr-subscription.yaml

5. Deploy the Application

kubectl apply -f k8s/base/namespace.yaml
kubectl apply -f k8s/api/deployment.yaml
kubectl apply -f k8s/api/service.yaml

kubectl apply -f k8s/cert-manager/cluster-issuer.yaml
kubectl apply -f k8s/cert-manager/certificate.yaml

kubectl apply -f k8s/ingress/ingress.yaml
kubectl apply -f k8s/ingress/ingress-second-domain.yaml

6. Configure DNS

*.localtest.me automatically resolves to 127.0.0.1. No configuration needed.

For WSL, map to the node IP:

NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
echo "${NODE_IP} api.demo.localtest.me api-v2.demo.localtest.me" | sudo tee -a /etc/hosts

7. Test the API

# Health check
curl http://api.demo.localtest.me:30080/health -H "Host: api.demo.localtest.me"

# Hello endpoint
curl http://api.demo.localtest.me:30080/api/hello -H "Host: api.demo.localtest.me"

# Pod info (shows which pod handled the request)
curl http://api.demo.localtest.me:30080/api/podinfo -H "Host: api.demo.localtest.me"

# Dapr sidecar status
curl http://api.demo.localtest.me:30080/api/daprinfo -H "Host: api.demo.localtest.me"

8. Test Dapr State Management

# Save state
curl -X POST http://api.demo.localtest.me:30080/api/state/mykey \
  -H "Host: api.demo.localtest.me" \
  -H "Content-Type: application/json" \
  -d '{"value":"hello from Dapr!"}'

# Read state (may return from a different pod — state is shared via Redis)
curl http://api.demo.localtest.me:30080/api/state/mykey \
  -H "Host: api.demo.localtest.me"

# Delete state
curl -X DELETE http://api.demo.localtest.me:30080/api/state/mykey \
  -H "Host: api.demo.localtest.me"

9. Test Dapr Pub/Sub

# Publish a message to the "orders" topic
curl -X POST http://api.demo.localtest.me:30080/api/pubsub/publish/orders \
  -H "Host: api.demo.localtest.me" \
  -H "Content-Type: application/json" \
  -d '{"orderId":"123","item":"Widget","quantity":5}'

# Check logs to see which pod received the message
kubectl logs -n demo-api -l app=sample-api -c sample-api --tail=5

10. Observe Load Balancing

for i in {1..6}; do
  echo "--- Request $i ---"
  curl -s http://api.demo.localtest.me:30080/api/hello \
    -H "Host: api.demo.localtest.me" | jq .pod
done

11. Verify TLS Certificate

kubectl describe certificate api-demo-tls -n demo-api
kubectl get secret api-demo-tls -n demo-api

curl -k https://api.demo.localtest.me:30443/api/hello \
  -H "Host: api.demo.localtest.me"

12. Access Dapr Dashboard and Zipkin

# Dapr Dashboard — inspect apps, components, configurations
kubectl port-forward svc/dapr-dashboard -n dapr-system 8080:8080
# Open http://localhost:8080

# Zipkin — visualize distributed traces
kubectl port-forward svc/zipkin -n dapr-monitoring 9411:9411
# Open http://localhost:9411

API Endpoints

Endpoint Method Description
/health GET Kubernetes health probe
/api/hello GET Returns greeting + pod hostname
/api/hello/{name} GET Personalized greeting
/api/podinfo GET Detailed pod metadata + request headers
/api/daprinfo GET Dapr sidecar health + metadata
/api/state/{key} GET Read from Dapr state store (Redis)
/api/state/{key} POST Save to Dapr state store (Redis)
/api/state/{key} DELETE Delete from Dapr state store
/api/pubsub/publish/{topic} POST Publish message via Dapr pub/sub
/api/events/orders POST Receive messages from "orders" topic (Dapr subscription)

Helmfile Releases

Release Chart Namespace Purpose
ingress-nginx ingress-nginx/ingress-nginx ingress-nginx NGINX Ingress Controller
cert-manager jetstack/cert-manager cert-manager TLS certificate automation
redis bitnami/redis redis State store + pub/sub backing
dapr dapr/dapr dapr-system Dapr runtime (sentry, placement, operator, dashboard, injector)

Certificate Provisioning Flow

sequenceDiagram
    participant U as User
    participant CM as cert-manager
    participant LE as Let's Encrypt
    participant NIC as NGINX Ingress

    U->>U: 1. Apply Ingress with TLS + cert-manager annotation
    CM->>CM: 2. Detect Ingress, create Certificate resource
    CM->>LE: 3. Request certificate for api.demo.localtest.me
    LE->>CM: 4. Challenge: serve token at /.well-known/acme-challenge/
    CM->>CM: 5. Create temp Ingress + Pod to serve token
    LE->>NIC: 6. GET /.well-known/acme-challenge/{token}
    NIC->>LE: 7. Token response
    LE->>CM: 8. Verified! Here's your certificate
    CM->>CM: 9. Store cert in Secret "api-demo-tls"
    NIC->>NIC: 10. Load TLS Secret, serve HTTPS
Loading

Documentation

Document Topics Covered
architecture.md System architecture, component details, resource relationships
nginx-ingress-deep-dive.md Reverse proxy, load balancing, upstreams, routing, rate limiting, headers, TLS, health checks, buffering, timeouts
cert-manager-explained.md cert-manager architecture, HTTP-01 challenges, certificate renewal, staging vs production
tls-handshake-flow.md TLS 1.3 handshake, SNI, certificate chains, TLS termination
dns-resolution-flow.md External DNS, CoreDNS internals, service discovery, DNS search domains
helmfile-deployment.md Helmfile concepts, release ordering, environments, commands
k3s-setup.md K3s vs full K8s, architecture, embedded containerd, setup
nerdctl-containerd.md containerd vs Docker, OCI images, nerdctl commands, multi-stage builds
dapr-deep-dive.md Dapr control plane (sentry, placement, operator, injector, dashboard), building blocks (state, pub/sub, invocation), tracing, mTLS
redis-bitnami.md Bitnami Redis chart, state store internals, pub/sub via Redis Streams, consumer groups, monitoring

Troubleshooting

Pods not starting

kubectl describe pod -n demo-api -l app=sample-api
kubectl logs -n demo-api -l app=sample-api -c sample-api
kubectl logs -n demo-api -l app=sample-api -c daprd

Dapr sidecar not injected

# Verify annotations
kubectl get deployment sample-api -n demo-api -o jsonpath='{.spec.template.metadata.annotations}'

# Check sidecar injector
kubectl logs -n dapr-system -l app=dapr-sidecar-injector

# Verify 2 containers (app + daprd)
kubectl get pods -n demo-api -o jsonpath='{.items[0].spec.containers[*].name}'

Dapr components not working

kubectl get components -n demo-api
kubectl describe component statestore -n demo-api
kubectl logs -n dapr-system -l app=dapr-operator

# Test Redis
kubectl exec -it -n redis redis-master-0 -- redis-cli -a demo-redis-password PING

Ingress not routing

kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller

Certificate not provisioning

kubectl describe certificate -n demo-api
kubectl get challenges --all-namespaces
kubectl logs -n cert-manager deployment/cert-manager

DNS not resolving

nslookup api.demo.localtest.me
kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- \
  nslookup sample-api.demo-api.svc.cluster.local

Teardown

# macOS
./scripts/mac/teardown.sh

# WSL
./scripts/wsl/teardown.sh

Further Reading

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors