Hands-On Project: Set Up a Production-Like Kubernetes Cluster¶
Build a Kubernetes cluster with essential add-ons and deploy a sample application.
Estimated Time: 4-5 hours Difficulty: Intermediate Prerequisites: kubectl and Helm installed, cloud account or local VM setup
Architecture Overview¶
Kubernetes Cluster
|
+-- Control Plane (managed or self-hosted)
|
+-- Node Pool (worker nodes)
| +-- CNI Plugin (Calico/Cilium)
| +-- kube-proxy or eBPF
|
+-- Add-ons
| +-- Ingress Controller (NGINX)
| +-- cert-manager (TLS certificates)
| +-- Monitoring (Prometheus + Grafana)
| +-- Logging (optional)
|
+-- Sample Application
+-- Deployment + Service + Ingress
+-- HPA (Horizontal Pod Autoscaler)
Option 1: Managed Kubernetes (EKS / AKS / GKE)¶
AWS EKS¶
# Install eksctl
# https://eksctl.io/installation/
# Create cluster
eksctl create cluster \
--name my-cluster \
--region us-east-1 \
--version 1.29 \
--nodegroup-name standard-workers \
--node-type t3.medium \
--nodes 3 \
--nodes-min 2 \
--nodes-max 5 \
--managed
# Verify
kubectl get nodes
Docs: https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html
Azure AKS¶
# Create resource group
az group create --name myRG --location eastus
# Create cluster
az aks create \
--resource-group myRG \
--name my-cluster \
--node-count 3 \
--node-vm-size Standard_DS2_v2 \
--kubernetes-version 1.29 \
--enable-managed-identity \
--generate-ssh-keys
# Get credentials
az aks get-credentials --resource-group myRG --name my-cluster
kubectl get nodes
Docs: https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-cli
GCP GKE¶
# Create cluster
gcloud container clusters create my-cluster \
--zone us-central1-a \
--num-nodes 3 \
--machine-type e2-medium \
--cluster-version 1.29 \
--enable-ip-alias
# Get credentials
gcloud container clusters get-credentials my-cluster --zone us-central1-a
kubectl get nodes
Docs: https://cloud.google.com/kubernetes-engine/docs/deploy-app-cluster
Option 2: Self-Managed with kubeadm¶
Prerequisites (on each node)¶
# Disable swap
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab
# Load kernel modules
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# Set sysctl parameters
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
# Install containerd
sudo apt-get update
sudo apt-get install -y containerd
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo systemctl restart containerd
# Install kubeadm, kubelet, kubectl
sudo apt-get install -y apt-transport-https ca-certificates curl
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
Initialize Control Plane¶
# On the control plane node
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
# Set up kubectl access
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Join Worker Nodes¶
# On each worker node (use the command from kubeadm init output)
sudo kubeadm join CONTROL_PLANE_IP:6443 --token TOKEN --discovery-token-ca-cert-hash sha256:HASH
Docs: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
Step 1: Install CNI Plugin¶
Calico¶
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
# Verify
kubectl get pods -n kube-system -l k8s-app=calico-node
Docs: https://docs.tigera.io/calico/latest/getting-started/kubernetes/
Cilium (alternative)¶
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium --namespace kube-system
# Verify
kubectl get pods -n kube-system -l app.kubernetes.io/name=cilium-agent
Docs: https://docs.cilium.io/en/stable/gettingstarted/
Step 2: Install Ingress Controller¶
NGINX Ingress Controller¶
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.replicaCount=2
# Verify
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
Get the external IP/hostname:
kubectl get svc ingress-nginx-controller -n ingress-nginx \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
Docs: https://kubernetes.github.io/ingress-nginx/deploy/
Step 3: Install cert-manager¶
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true
# Verify
kubectl get pods -n cert-manager
Configure Let's Encrypt Issuer¶
# cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
kubectl apply -f cluster-issuer.yaml
Docs: https://cert-manager.io/docs/installation/
Step 4: Install Monitoring¶
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set grafana.adminPassword=admin
# Verify
kubectl get pods -n monitoring
See the monitoring project guide for detailed dashboard and alerting setup.
Step 5: Deploy Sample Application¶
Deployment¶
# app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-app
namespace: default
labels:
app: sample-app
spec:
replicas: 3
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: app
image: nginx:1.25-alpine
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 30
Service¶
# app-service.yaml
apiVersion: v1
kind: Service
metadata:
name: sample-app
namespace: default
spec:
selector:
app: sample-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
Ingress¶
# app-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sample-app
namespace: default
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: sample-app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sample-app
port:
number: 80
Horizontal Pod Autoscaler¶
# app-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: sample-app
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: sample-app
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
Apply All Resources¶
kubectl apply -f app-deployment.yaml
kubectl apply -f app-service.yaml
kubectl apply -f app-ingress.yaml
kubectl apply -f app-hpa.yaml
# Verify
kubectl get deployments
kubectl get pods
kubectl get svc
kubectl get ingress
kubectl get hpa
Step 6: Verify the Setup¶
# Check all pods are running
kubectl get pods -A
# Test internal DNS
kubectl run test --rm -it --image=busybox -- nslookup sample-app.default.svc.cluster.local
# Test the application via port-forward
kubectl port-forward svc/sample-app 8080:80
# Open http://localhost:8080
# Check ingress endpoint
curl -v https://app.example.com
# Verify HPA is collecting metrics
kubectl get hpa sample-app
Verification Checklist¶
- All nodes are in Ready state
- CNI plugin pods are running on every node
- Ingress controller is running with an external IP/hostname
- cert-manager is running and ClusterIssuer is ready
- Monitoring stack is deployed (Prometheus + Grafana accessible)
- Sample application deployment has desired replicas running
- Service endpoints are populated
- Ingress routes traffic to the application
- TLS certificate is issued (or pending for real domains)
- HPA is active and shows current metrics
Cleanup¶
Managed Clusters¶
# EKS
eksctl delete cluster --name my-cluster --region us-east-1
# AKS
az aks delete --resource-group myRG --name my-cluster --yes
# GKE
gcloud container clusters delete my-cluster --zone us-central1-a
kubeadm Clusters¶
# On each node
sudo kubeadm reset
sudo rm -rf /etc/cni/net.d