Skip to main content

Command Palette

Search for a command to run...

Kubernetes — From Beginner to Advanced

Updated
10 min read
K
Backend-focused web developer and engineering student.

🟢 CHAPTER 1: What is Kubernetes? (Absolute Beginner)

The Problem First

Imagine you built a Node.js app. You dockerized it. Now:

  • Your app crashes → no one restarts it automatically

  • Traffic increases → you need 10 copies of your app running

  • One server dies → your app goes down

You need something to manage all this automatically.

That's Kubernetes.


What Kubernetes Does

Kubernetes is a system that automatically manages your Docker containers — starts them, restarts them, scales them, and balances traffic between them.

Think of it like this:

YOU         →  tell Kubernetes: "I want 3 copies of my Node.js app running always"
KUBERNETES  →  does it, watches it, fixes it if anything breaks

Core Vocabulary (memorize these)

Term Simple Meaning
Cluster A group of machines (servers) Kubernetes manages
Node One machine (server) inside the cluster
Pod The smallest unit — holds 1 or more containers
Deployment Tells Kubernetes: run this Pod, keep N copies alive
Service Gives your Pod a stable address (like a door to your app)
Namespace A way to group/separate resources (like folders)
kubectl The command-line tool you use to talk to Kubernetes

🟢 CHAPTER 2: How Kubernetes Works Internally

[ Your Laptop / CI ]
        |
        |  kubectl apply -f file.yaml
        ↓
[ Control Plane (Master) ]
    - API Server      ← receives all commands
    - Scheduler       ← decides which Node to put Pod on
    - etcd            ← stores the entire cluster state
    - Controller      ← watches and fixes things

[ Worker Nodes ]
    - kubelet         ← runs Pods on this machine
    - kube-proxy      ← handles networking
    - Container Runtime (Docker/containerd)

You never talk to Nodes directly. You talk to the API Server using kubectl.


🟡 CHAPTER 3: Your First YAML — The Pod

Every Kubernetes resource is described in a YAML file. You write what you want, Kubernetes makes it happen.

# pod.yaml
apiVersion: v1          # Which Kubernetes API version to use
kind: Pod               # What type of resource this is
metadata:
  name: my-node-app     # Name of this Pod
  labels:
    app: node-app       # A tag — used later by Services to find this Pod
spec:
  containers:
    - name: node-container        # Name of the container inside the Pod
      image: my-node-app:1.0      # Docker image to use
      ports:
        - containerPort: 3000     # Port your Node.js app listens on

Apply it:

kubectl apply -f pod.yaml
kubectl get pods          # see running pods
kubectl describe pod my-node-app   # detailed info
kubectl logs my-node-app           # see app logs

⚠️ Important Rule:

You almost never create Pods directly. You use Deployments instead. A Pod alone has no self-healing.


🟡 CHAPTER 4: Deployment YAML — The Real Way

A Deployment wraps your Pod and adds:

  • Auto-restart if Pod crashes

  • Rolling updates (zero downtime)

  • Easy scaling

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-app-deployment    # Name of the Deployment
spec:
  replicas: 3                  # Keep 3 copies of this Pod running always
  selector:
    matchLabels:
      app: node-app            # Manage Pods that have this label
  template:                    # This is the Pod blueprint
    metadata:
      labels:
        app: node-app          # Label on each Pod — must match selector above
    spec:
      containers:
        - name: node-container
          image: my-node-app:1.0
          ports:
            - containerPort: 3000
          resources:
            requests:
              memory: "128Mi"   # Minimum memory needed
              cpu: "250m"       # 250 millicores = 0.25 CPU
            limits:
              memory: "256Mi"   # Max memory allowed
              cpu: "500m"       # Max CPU allowed

Key Concepts in this YAML:

  • replicas: 3 → Kubernetes always ensures exactly 3 Pods are running. If one dies, it creates a new one immediately.

  • selector.matchLabels → This links the Deployment to its Pods using labels.

  • template → The Pod definition. Every Pod created will look like this.

  • resources → Always set this. Without it, one app can eat all server memory and kill everything.

kubectl apply -f deployment.yaml
kubectl get deployments
kubectl get pods              # you'll see 3 pods
kubectl rollout status deployment/node-app-deployment

🟡 CHAPTER 5: Service YAML — Expose Your App

A Pod's IP changes every time it restarts. A Service gives a stable endpoint.

There are 3 main Service types:

Type Use Case
ClusterIP Internal only — Pods talk to each other
NodePort Expose on a port of the Node (for testing)
LoadBalancer Cloud providers give a public IP
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: node-app-service
spec:
  type: ClusterIP              # Only accessible inside the cluster
  selector:
    app: node-app              # Find Pods with this label (matches Deployment)
  ports:
    - protocol: TCP
      port: 80                 # Port the Service exposes
      targetPort: 3000         # Port the Pod/container listens on

How traffic flows:

Request → Service (port 80) → Pod (port 3000)

The Service load-balances automatically between all 3 Pods.


🟡 CHAPTER 6: ConfigMap and Secret YAML

Your Node.js app needs environment variables (DB_HOST, API_KEY, etc.). Don't hardcode them in the image.

ConfigMap — for non-sensitive config:

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: node-app-config
data:
  NODE_ENV: "production"
  PORT: "3000"
  DB_HOST: "postgres-service"

Secret — for sensitive data:

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: node-app-secret
type: Opaque
data:
  DB_PASSWORD: cGFzc3dvcmQxMjM=    # base64 encoded — never plain text
  JWT_SECRET: bXlzZWNyZXRrZXk=

Encode your values:

echo -n "password123" | base64
# output: cGFzc3dvcmQxMjM=

Use them inside your Deployment:

# inside deployment.yaml → spec.containers
env:
  - name: NODE_ENV
    valueFrom:
      configMapKeyRef:
        name: node-app-config    # ConfigMap name
        key: NODE_ENV            # Key inside ConfigMap

  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: node-app-secret    # Secret name
        key: DB_PASSWORD         # Key inside Secret

🟠 CHAPTER 7: Ingress + Nginx — The Real Production Setup

The goal: User hits yourapp.com → Nginx Ingress Controller → routes to your Node.js Service

Internet
   |
[ Ingress Controller (Nginx) ]  ← one entry point
   |           |
   |           |
[node-app-service]   [another-service]
   |
[3 Node.js Pods]

Step 1 — Install Nginx Ingress Controller

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.4/deploy/static/provider/cloud/deploy.yaml

Step 2 — Ingress YAML

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: node-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /    # Rewrite URL path
spec:
  ingressClassName: nginx                            # Use nginx ingress class
  rules:
    - host: yourapp.com                              # Your domain
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: node-app-service              # Your Service name
                port:
                  number: 80                        # Service port

Multiple apps on same domain:

rules:
  - host: yourapp.com
    http:
      paths:
        - path: /api
          pathType: Prefix
          backend:
            service:
              name: api-service
              port:
                number: 80

        - path: /
          pathType: Prefix
          backend:
            service:
              name: frontend-service
              port:
                number: 80

🟠 CHAPTER 8: Namespace YAML

Namespaces separate your environments inside one cluster.

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: production
kubectl apply -f namespace.yaml
kubectl apply -f deployment.yaml -n production    # deploy into this namespace
kubectl get pods -n production

🔴 CHAPTER 9: Full Real-World Node.js + Nginx Setup

Here is the complete folder structure:

k8s/
├── namespace.yaml
├── configmap.yaml
├── secret.yaml
├── deployment.yaml
├── service.yaml
└── ingress.yaml

namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: production

configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: node-config
  namespace: production
data:
  NODE_ENV: "production"
  PORT: "3000"

secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: node-secret
  namespace: production
type: Opaque
data:
  DB_PASSWORD: cGFzc3dvcmQxMjM=

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-deployment
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: node-app
  strategy:
    type: RollingUpdate          # Zero downtime deployment
    rollingUpdate:
      maxSurge: 1                # Create 1 extra Pod during update
      maxUnavailable: 0          # Never take Pods down before new ones are ready
  template:
    metadata:
      labels:
        app: node-app
    spec:
      containers:
        - name: node-app
          image: your-dockerhub/node-app:1.0
          ports:
            - containerPort: 3000
          env:
            - name: NODE_ENV
              valueFrom:
                configMapKeyRef:
                  name: node-config
                  key: NODE_ENV
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: node-secret
                  key: DB_PASSWORD
          resources:
            requests:
              memory: "128Mi"
              cpu: "250m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          livenessProbe:         # Kubernetes checks if app is alive
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 10   # Wait 10s before first check
            periodSeconds: 5          # Check every 5 seconds
          readinessProbe:        # Kubernetes checks if app is ready for traffic
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 3

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: node-service
  namespace: production
spec:
  type: ClusterIP
  selector:
    app: node-app
  ports:
    - port: 80
      targetPort: 3000

ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: node-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - yourapp.com
      secretName: tls-secret     # Your SSL certificate stored as a Secret
  rules:
    - host: yourapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: node-service
                port:
                  number: 80

Deploy everything:

kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml

🔴 CHAPTER 10: Scaling & Updates (Advanced)

Scale manually:

kubectl scale deployment node-deployment --replicas=10 -n production

Auto-scaling (HPA):

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: node-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: node-deployment
  minReplicas: 2          # Never go below 2 Pods
  maxReplicas: 20         # Never go above 20 Pods
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70    # Scale up when CPU hits 70%
kubectl apply -f hpa.yaml
kubectl get hpa -n production

Update your app (zero downtime):

# Build and push new Docker image
docker build -t your-dockerhub/node-app:2.0 .
docker push your-dockerhub/node-app:2.0

# Update the image in Kubernetes
kubectl set image deployment/node-deployment node-app=your-dockerhub/node-app:2.0 -n production

# Watch the rolling update happen live
kubectl rollout status deployment/node-deployment -n production

# Rollback if something goes wrong
kubectl rollout undo deployment/node-deployment -n production

📋 Essential kubectl Commands Cheatsheet

# Get everything
kubectl get all -n production

# Watch pods live
kubectl get pods -n production -w

# See logs
kubectl logs <pod-name> -n production
kubectl logs <pod-name> -n production -f    # follow live logs

# Go inside a Pod (like docker exec)
kubectl exec -it <pod-name> -n production -- /bin/sh

# Delete and re-apply
kubectl delete -f deployment.yaml
kubectl apply -f deployment.yaml

# See resource usage
kubectl top pods -n production
kubectl top nodes

🗺️ Your Learning Path Summary

Beginner:    Pod → Deployment → Service
Intermediate: ConfigMap → Secret → Namespace → Ingress + Nginx
Advanced:     HPA → Rolling Updates → Probes → TLS → Multi-service routing

More from this blog