1. Introduction to Container Orchestration
What is Container Orchestration?
Container orchestration is the automated process of managing, deploying, scaling, and networking containers. Think of it as a conductor orchestrating an entire symphony of containers.
Why Do We Need Orchestration Tools?
Modern applications are increasingly built using microservices architecture - breaking down monolithic applications into smaller, independent services. While containers are perfect hosts for these microservices, managing hundreds or thousands of containers manually becomes impossible.
Key Problems Orchestration Solves:
-
High Availability (No Downtime)
- Automatically restarts failed containers
- Distributes workload across multiple machines
- Ensures your application is always accessible
-
Scalability
- Automatically scales applications up or down based on demand
- Handles load balancing across container replicas
- Manages resource allocation efficiently
-
Disaster Recovery
- Automatic backup and restore capabilities
- Self-healing mechanisms
- Data replication across nodes
The Journey: Monolith → Microservices
┌─────────────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ │ │ Service │ │ Service │ │ Service │
│ MONOLITHIC APP │ ───► │ A │ │ B │ │ C │
│ │ │ │ │ │ │ │
└─────────────────────┘ └──────────┘ └──────────┘ └──────────┘
Single Deployment Independent Deployments
Tight Coupling Loose Coupling
Hard to Scale Easy to Scale
2. What is Kubernetes?
Kubernetes (K8s) is an open-source container orchestration platform developed by Google. The name comes from the Greek word for “helmsman” or “pilot” - fitting, as it steers your containerized applications.
Why “K8s”?
The “8” represents the eight letters between “K” and “s” in “Kubernetes”.
Official Definition
Kubernetes automates many processes involved in deploying, managing, and scaling containerized applications. It has become the most widely used container orchestration platform in the industry.
What Kubernetes Provides:
- ✅ High Availability: Zero downtime deployments
- ✅ Automatic Scaling: Responds to load changes
- ✅ Self-Healing: Restarts failed containers automatically
- ✅ Load Balancing: Distributes network traffic
- ✅ Rollback: Easy revert to previous versions
- ✅ Storage Orchestration: Manages storage systems
3. Core Kubernetes Components
Kubernetes has many components, but let’s focus on the essential ones you need to understand.
3.1 Pod
What is a Pod?
- The smallest deployable unit in Kubernetes
- An abstraction over a container (you don’t work with containers directly)
- Usually contains one application per pod (though it can have multiple containers)
- Each pod gets its own IP address for communication
Why Pods are Important:
Pods provide an abstraction layer over Docker/containerd. This means:
- You’re not tied to a specific container technology
- Kubernetes manages the container lifecycle, not you
- Easier to replace container runtimes without changing your apps
Pods are Ephemeral:
Pod Created → Running → Crashes/Dies
↓ ↓
IP: 10.0.0.1 New Pod Created
NEW IP: 10.0.0.2 ❌
When a pod dies and is recreated, it gets a new IP address. This makes direct pod-to-pod communication unreliable. That’s where Services come in.
3.2 Service
What is a Service?
- A permanent IP address attached to pods
- Acts as a load balancer for pod replicas
- Lifecycle is independent of pods
Why Services Solve the IP Problem:
┌─────────────┐
│ Service │ ← Permanent IP: 10.0.0.100
│ 10.0.0.100 │
└──────┬──────┘
│
┌───┴────┐
│ │
┌──▼──┐ ┌──▼──┐
│ Pod │ │ Pod │ ← Pods can die and be recreated
│ #1 │ │ #2 │ with new IPs, but service IP
└─────┘ └─────┘ remains the same!
Internal vs External Services:
- Internal Service: Default type for services that shouldn’t be accessible from outside (e.g., databases)
- External Service: Makes your application accessible via a browser (but uses an ugly URL like
http://124.89.101.2:8080)
3.3 Ingress
The Problem with External Services: External services give you URLs like http://124.89.101.2:8080 - not very user-friendly!
Ingress to the Rescue: Ingress is the entrypoint to your Kubernetes cluster. It:
- Provides a human-readable URL:
https://my-app.com - Routes requests to the appropriate internal services
- Handles SSL/TLS termination
Request Flow:
User Browser → Ingress (https://my-app.com) → Service → Pods
3.4 ConfigMap & Secret
Applications need configuration: database URLs, feature flags, etc. Hard-coding these into your application is bad practice.
ConfigMap:
- Stores non-confidential data in key-value pairs
- Example: database URLs, application settings
- Easy to update without rebuilding images
Secret:
- Similar to ConfigMap but for sensitive data
- Example: passwords, API tokens, certificates
- Values are base64-encoded (NOT encrypted by default!)
Important Security Note: Storing data in Secrets doesn’t automatically make it secure. You need to:
- Enable encryption at rest
- Define authorization policies
- Consider third-party secret management tools (HashiCorp Vault, AWS Secrets Manager)
How Pods Use Them:
# Pods can consume ConfigMaps and Secrets as:
- Environment variables
- Command-line arguments
- Configuration files in a Volume
3.5 Volumes
The Problem: When a container crashes, Kubernetes restarts it with a clean state - all data is lost! 💥
The Solution: Volumes attach physical storage to your pods, persisting data beyond container/pod lifecycles.
Important: Kubernetes does NOT manage data persistence. You are responsible for:
- Backing up data
- Replicating data
- Choosing storage type (local vs remote)
Storage Requirements for Production:
- Must be available on all nodes (pods can start anywhere)
- Must survive pod lifecycle
- Must survive cluster crashes
Think of it as: An external hard drive plugged into your Kubernetes cluster.
3.6 Deployment
What is a Deployment?
- A blueprint for creating pods
- Specifies how many replicas to run
- Manages updates and rollbacks
- For stateless applications
Why Not Work Directly with Pods? You never create pods directly. Instead:
You Define → Deployment → Creates → Pods
Example Benefits:
Deployment: "I want 3 replicas of nginx"
↓
Kubernetes Creates:
┌──────┐ ┌──────┐ ┌──────┐
│ Pod1 │ │ Pod2 │ │ Pod3 │
└──────┘ └──────┘ └──────┘
If Pod2 crashes:
┌──────┐ ┌──────┐ ┌──────┐
│ Pod1 │ │ NEW │ │ Pod3 │
└──────┘ │ Pod2 │ └──────┘
└──────┘
3.7 StatefulSet
For Stateful Applications: While Deployments are great for stateless apps (web servers, APIs), databases and stateful apps need special handling.
StatefulSet provides:
- Sticky identity: Each pod gets a persistent identifier (e.g.,
mysql-0,mysql-1,mysql-2) - Ordered creation/deletion: Pods are created and deleted in order
- Stable network identity: Each pod maintains its identity across restarts
- Data synchronization: Ensures database reads/writes are coordinated
Deployment vs StatefulSet:
| Feature | Deployment | StatefulSet |
|---|---|---|
| Pod Names | Random hash (nginx-d98b7f5c-qr9zx) | Ordered (mysql-0, mysql-1) |
| Creation Order | Random, parallel | Sequential |
| Storage | Shared or none | Individual persistent storage |
| Use Case | Stateless apps | Databases, message queues |
Why StatefulSets are Complex:
MySQL StatefulSet with 3 replicas:
mysql-0 (Master) ← Only this can write
↓
mysql-1 (Slave) ← Read replicas
↓
mysql-2 (Slave)
Each has its own storage, and data must be
synchronized to avoid inconsistencies!
Note: Deploying stateful applications is significantly more complex than stateless ones. Many teams choose managed databases to avoid this complexity.
4. Kubernetes Architecture
A Kubernetes cluster consists of two types of nodes:
4.1 Worker Nodes
Worker nodes are where your actual applications run. Think of them as the workhorses of your cluster.
Each Worker Node Requires 3 Processes:
1. Container Runtime
- Software responsible for running containers
- Examples: containerd, CRI-O, Docker
- Kubernetes is container-agnostic
2. Kubelet
- The agent that runs on every worker node
- Communicates with the control plane
- Manages pod and container lifecycle
- Talks to:
- Underlying server: To get CPU, memory, storage resources
- Container runtime: To start/stop containers
3. Kube-proxy
- A network proxy on every node
- Maintains network rules
- Intelligently forwards requests to pods
- Provides load balancing
Worker Node Visualization:
┌─────────────────────────────────────┐
│ Worker Node │
│ │
│ ┌──────────────────────────────┐ │
│ │ Container Runtime │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ Kubelet │ │
│ │ - Manages Pods │ │
│ │ - Reports Status │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ Kube-proxy │ │
│ │ - Network Rules │ │
│ │ - Load Balancing │ │
│ └──────────────────────────────┘ │
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ Pod │ │ Pod │ │ Pod │ ← Apps │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────────────┘
4.2 Control Plane (Master Node)
The Control Plane manages the worker nodes and pods in the cluster. It’s the brain of Kubernetes.
Each Control Plane Requires 4 Processes:
1. API Server
- The cluster gateway and single entrypoint
- All communication goes through the API server
- Acts as a gatekeeper for authentication
- Validates requests before processing
Clients that interact with API Server:
- UI (Kubernetes Dashboard)
- API (REST calls)
- CLI (kubectl)
2. Scheduler
- Decides on which node to schedule new pods
- Considers factors:
- Resource requirements (CPU, memory)
- Hardware/software constraints
- Node availability
- Data locality
- Affinity rules
Note: The Scheduler only decides where to place the pod. The Kubelet actually starts it.
3. Controller Manager
- Detects state changes in the cluster
- Examples: pod crashes, node failures
- Makes requests to Scheduler to recover cluster state
- Different controllers handle different resources:
- Node Controller: Monitors node health
- Replication Controller: Maintains correct number of pods
- Endpoints Controller: Populates endpoint objects
Self-Healing in Action:
Pod Crashes → Controller Manager Detects
↓
Requests Scheduler
↓
Scheduler Decides Node
↓
Kubelet Starts New Pod
4. etcd
- Kubernetes’ backing store for all cluster data
- A consistent, distributed key-value store
- Think of it as the cluster brain
- Every change in the cluster is saved here
What etcd Stores:
- Cluster state
- Configuration data
- Metadata about resources
- NOT application data (that goes in volumes)
How Other Components Use etcd:
- Scheduler reads available resources from etcd
- Controller Manager reads current state from etcd
- API Server is the only component that talks directly to etcd
Control Plane Visualization:
┌────────────────────────────────────────────┐
│ Control Plane Node │
│ │
│ ┌────────────────────────────────────┐ │
│ │ API Server │ │
│ │ - Cluster Gateway │ │
│ │ - Authentication │ │
│ └─────┬──────────────────────────────┘ │
│ │ │
│ ┌─────▼──────┐ ┌────────────────┐ │
│ │ Scheduler │ │ Controller Mgr │ │
│ │ │ │ │ │
│ └─────┬──────┘ └───────┬────────┘ │
│ │ │ │
│ ┌─────▼─────────────────▼────────┐ │
│ │ etcd │ │
│ │ (Cluster Data Store) │ │
│ └────────────────────────────────┘ │
└────────────────────────────────────────────┘
4.3 How Components Interact
Example: Creating a Deployment
1. You run: kubectl apply -f deployment.yaml
↓
2. kubectl → API Server (authenticates & validates)
↓
3. API Server → etcd (saves desired state)
↓
4. Controller Manager notices change (watching etcd)
↓
5. Controller Manager → API Server → Scheduler
↓
6. Scheduler decides which node to use
↓
7. API Server → Kubelet on selected node
↓
8. Kubelet → Container Runtime (starts container)
↓
9. Kubelet reports status → API Server → etcd
4.4 Scaling Your Cluster
Adding a Worker Node:
1. Get a new server
2. Install: Container Runtime, Kubelet, Kube-proxy
3. Join to cluster: kubeadm join <control-plane-endpoint>
Adding a Control Plane Node (for high availability):
1. Get a new server
2. Install: API Server, Scheduler, Controller Manager, etcd
3. Join to cluster using kubeadm
Production Recommendation:
- Multiple control plane nodes for high availability
- Multiple worker nodes for workload distribution
- Control planes often run on separate, smaller machines
- Worker nodes need more compute resources (CPU, RAM)
5. Getting Started - Local Setup
5.1 Minikube - Local Kubernetes Cluster
What is Minikube?
- A tool that implements a local Kubernetes cluster
- Runs both Control Plane and Worker processes on one machine
- Perfect for local development and testing
Why Minikube? Running a full Kubernetes cluster for testing would require:
- Multiple servers
- Complex networking setup
- Significant resource overhead
Minikube makes it simple by running everything in a single VM or container on your laptop.
Installation:
# macOS
brew install minikube
# Linux
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
# Windows
choco install minikube
Start Minikube:
# Start with Docker driver (recommended)
minikube start --driver=docker
# Start with VirtualBox
minikube start --driver=virtualbox
# Check status
minikube status
Useful Minikube Commands:
minikube dashboard # Open Kubernetes dashboard
minikube service <name> # Expose a service
minikube stop # Stop the cluster
minikube delete # Delete the cluster
minikube ssh # SSH into the node
5.2 kubectl - Kubernetes Command Line Tool
What is kubectl?
- The CLI tool for interacting with Kubernetes clusters
- Pronounced “kube-control” or “kube-c-t-l”
- Communicates with the API Server
How kubectl Connects: kubectl needs a kubeconfig file to access a cluster. This file contains:
- Cluster connection details
- Authentication credentials
- Context information
Default Location: ~/.kube/config
When you start Minikube, this config file is created automatically.
5.3 Essential kubectl Commands
Basic Information Commands
# Get cluster information
kubectl cluster-info
# Get all nodes in the cluster
kubectl get nodes
# Get all pods in current namespace
kubectl get pods
kubectl get pod # alternative
# Get all services
kubectl get services
kubectl get svc # shorthand
# Get all deployments
kubectl get deployments
kubectl get deploy # shorthand
# Get all replicasets
kubectl get replicaset
kubectl get rs # shorthand
# Get everything
kubectl get all
# Filter with grep
kubectl get all | grep "nginx"
Create Resources
# Create a deployment imperatively
kubectl create deployment nginx-depl --image=nginx
# Create from YAML file
kubectl apply -f deployment.yaml
# Create multiple resources
kubectl apply -f . # Apply all YAML files in current directory
Update Resources
# Edit a deployment (opens in default editor)
kubectl edit deployment nginx-depl
# Scale a deployment
kubectl scale deployment nginx-depl --replicas=3
Debugging Commands
# Get detailed information about a pod
kubectl describe pod <pod-name>
# Get logs from a pod
kubectl logs <pod-name>
# Follow logs in real-time
kubectl logs -f <pod-name>
# Get logs from a specific container in a pod
kubectl logs <pod-name> -c <container-name>
# Execute command in a pod
kubectl exec -it <pod-name> -- /bin/bash
kubectl exec -it <pod-name> -- /bin/sh # if bash not available
# Get YAML definition of a resource
kubectl get deployment <depl-name> -o yaml
Delete Resources
# Delete a deployment
kubectl delete deployment <depl-name>
# Delete from file
kubectl delete -f deployment.yaml
# Delete all resources of a type
kubectl delete pods --all
Namespace Commands
# Get all namespaces
kubectl get namespace
kubectl get ns # shorthand
# Get resources in a specific namespace
kubectl get pods -n <namespace-name>
# Get resources from all namespaces
kubectl get pods --all-namespaces
kubectl get pods -A # shorthand
6. Configuration Files Deep Dive
Kubernetes uses YAML files (also called Kubernetes manifests) to define resources. These files are declarative - you specify the desired state, and Kubernetes makes it happen.
6.1 The Three Parts of Every Config File
Every Kubernetes YAML file has three main sections:
1. metadata
- Information about the resource
- Contains
name,labels,namespace, etc.
2. specification (spec)
- The desired state you want
- Attributes are specific to the resource kind
- Most important section
3. status
- Automatically generated by Kubernetes
- Shows the current actual state
- Kubernetes gets this from etcd
- Used for self-healing
How Status Works:
Desired State (spec): 2 replicas
Current State (status): 1 replica
Kubernetes sees mismatch → Creates new pod
6.2 Basic Deployment Example
Let’s create a simple nginx deployment:
apiVersion: apps/v1 # API version for Deployment
kind: Deployment # Type of resource
metadata:
name: nginx-depl # Name of the deployment
labels:
app: nginx # Labels for organization
spec:
replicas: 1 # Number of pod replicas
selector:
matchLabels:
app: nginx # Which pods this deployment manages
template: # Pod template
metadata:
labels:
app: nginx # Labels for the pods
spec:
containers:
- name: nginx # Container name
image: nginx:1.25 # Container image
ports:
- containerPort: 80 # Port the container listens on
Apply this configuration:
kubectl apply -f nginx-deployment.yaml
What happens:
- Deployment resource is created
- ReplicaSet is automatically created
- Pod(s) are created based on replica count
Check what was created:
kubectl get deployment
kubectl get replicaset
kubectl get pod
6.3 Understanding the Template
Notice the template section inside the Deployment spec? This is actually a Pod configuration nested inside the Deployment.
template:
metadata: # Pod's metadata
labels:
app: nginx
spec: # Pod's specification
containers:
- name: nginx
image: nginx:1.25
This is why Deployment is called an abstraction over Pods. The Deployment contains the blueprint for creating Pods.
6.4 Service Configuration
Now let’s create a Service to expose our nginx deployment:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx # Matches pods with this label
ports:
- protocol: TCP
port: 80 # Port the service exposes
targetPort: 80 # Port on the pod
Multiple Resources in One File:
You can define multiple resources in one YAML file using --- as a separator:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-depl
# ... deployment spec ...
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
# ... service spec ...
Apply both:
kubectl apply -f nginx-config.yaml
6.5 Labels and Selectors
Labels and Selectors are how Kubernetes resources find and connect to each other.
Labels:
- Key-value pairs attached to resources
- Used for identification and organization
- Don’t provide uniqueness (multiple resources can have the same label)
Selectors:
- Used to filter and select resources by labels
- Connect Services to Pods, Deployments to Pods, etc.
Example Connection:
# Deployment creates pods with label app: nginx
spec:
template:
metadata:
labels:
app: nginx
---
# Service finds those pods using selector
spec:
selector:
app: nginx # "Find all pods with label app: nginx"
Verify the connection:
# Get the service and check its endpoints
kubectl describe service nginx-service
# Look for the "Endpoints" field - it should show pod IPs
# Get pod IPs to verify
kubectl get pod -o wide
6.6 Understanding Ports
Three types of ports can be confusing. Let’s clarify:
In Service:
- port: Port where the service itself is accessible
- targetPort: Port where the container accepts traffic
In Deployment (Pod spec):
- containerPort: Port where the container listens
Visual Example:
External Request
↓
[Service: port 80]
↓
Routes to
↓
[Service: targetPort 8080]
↓
Forwards to
↓
[Pod Container: containerPort 8080]
Example Configuration:
# Service
spec:
ports:
- port: 80 # Clients connect to service on port 80
targetPort: 8080 # Service forwards to pod on port 8080
---
# Deployment
spec:
template:
spec:
containers:
- name: app
ports:
- containerPort: 8080 # Container listens on port 8080
6.7 Getting YAML Output
You can get the YAML definition of any resource:
# Get deployment YAML
kubectl get deployment nginx-depl -o yaml
# Get pod YAML
kubectl get pod <pod-name> -o yaml
# See the auto-generated status section
kubectl get deployment nginx-depl -o yaml | grep -A 5 "status:"
6.8 Best Practices for Configuration Files
- Store in version control (Git)
- Use meaningful names for resources
- Add labels for organization
- Pin image versions (avoid
latest) - Define resource limits (coming in deployment spec)
- Use separate files for different environments (dev, staging, prod)
7. Practical Example: MongoDB + Mongo Express
Let’s build a complete application stack to see how all components work together. We’ll deploy:
- MongoDB: Database (internal service)
- Mongo Express: Web-based UI (external service)
7.1 Architecture Overview
┌─────────────────────────────────────────────────────────┐
│ Browser │
│ http://node-ip:30000 │
└────────────────────┬────────────────────────────────────┘
│
▼
┌────────────────────────┐
│ Mongo Express Service │ (External/LoadBalancer)
│ Type: LoadBalancer │
└────────┬───────────────┘
│
▼
┌─────────────────┐
│ Mongo Express │
│ Pod │ ← Uses Secret for credentials
└────────┬────────┘ ← Uses ConfigMap for DB URL
│
▼
┌──────────────────┐
│ MongoDB Service │ (Internal/ClusterIP)
└────────┬─────────┘
│
▼
┌─────────────────┐
│ MongoDB Pod │ ← Uses Secret for root credentials
└─────────────────┘
7.2 Step 1: Create Secret for MongoDB Credentials
Secrets must be created before the Deployments that use them.
# mongodb-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mongodb-secret
type: Opaque
data:
mongo-root-username: dXNlcm5hbWU= # base64 encoded "username"
mongo-root-password: cGFzc3dvcmQ= # base64 encoded "password"
How to encode values:
echo -n 'username' | base64 # dXNlcm5hbWU=
echo -n 'password' | base64 # cGFzc3dvcmQ=
Apply the secret:
kubectl apply -f mongodb-secret.yaml
7.3 Step 2: Create ConfigMap for MongoDB URL
# mongodb-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mongodb-configmap
data:
database_url: mongodb-service:27017
Important: The database_url uses the Service name (mongodb-service) as the hostname. Kubernetes’ internal DNS resolves this to the service IP.
Apply the ConfigMap:
kubectl apply -f mongodb-configmap.yaml
7.4 Step 3: Deploy MongoDB with Internal Service
# mongodb.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb-deployment
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
---
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017
Key Points:
- Environment variables are injected from the Secret
- Service type is ClusterIP (default) - only accessible within cluster
- Service port and targetPort are both 27017
Apply:
kubectl apply -f mongodb.yaml
Verify:
kubectl get pod
kubectl get service
kubectl describe service mongodb-service # Check endpoints
7.5 Step 4: Deploy Mongo Express with External Service
# mongo-express.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongo-express
labels:
app: mongo-express
spec:
replicas: 1
selector:
matchLabels:
app: mongo-express
template:
metadata:
labels:
app: mongo-express
spec:
containers:
- name: mongo-express
image: mongo-express
ports:
- containerPort: 8081
env:
- name: ME_CONFIG_MONGODB_ADMINUSERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: ME_CONFIG_MONGODB_ADMINPASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: mongodb-configmap
key: database_url
- name: ME_CONFIG_MONGODB_URL
value: "mongodb://$(ME_CONFIG_MONGODB_ADMINUSERNAME):$(ME_CONFIG_MONGODB_ADMINPASSWORD)@$(DATABASE_URL)"
---
apiVersion: v1
kind: Service
metadata:
name: mongo-express-service
spec:
selector:
app: mongo-express
type: LoadBalancer
ports:
- protocol: TCP
port: 8081
targetPort: 8081
nodePort: 30000
Key Points:
- Uses both Secret and ConfigMap
- Constructs MongoDB connection URL using environment variables
- Service type is LoadBalancer (creates external access)
- nodePort: 30000 makes it accessible on that port
Apply:
kubectl apply -f mongo-express.yaml
7.6 Access the Application
In Minikube:
minikube service mongo-express-service
This automatically opens the Mongo Express UI in your browser.
In a real cluster:
# Get the external IP
kubectl get service mongo-express-service
# Access via: http://<external-ip>:30000
7.7 Complete Request Flow
1. User → http://node-ip:30000
2. LoadBalancer Service (mongo-express-service)
3. Routes to Mongo Express Pod
4. Mongo Express needs to connect to MongoDB
5. Uses DATABASE_URL from ConfigMap: "mongodb-service:27017"
6. Kubernetes DNS resolves "mongodb-service" to ClusterIP
7. ClusterIP Service (mongodb-service)
8. Routes to MongoDB Pod
9. MongoDB authenticates using credentials from Secret
10. Data is returned back through the chain
7.8 Verify Everything Works
# Check all resources
kubectl get all
# Check secrets
kubectl get secret
# Check configmap
kubectl get configmap
# Check if services are properly connected
kubectl describe service mongodb-service
kubectl describe service mongo-express-service
# Check pod logs
kubectl logs <mongo-express-pod-name>
kubectl logs <mongodb-pod-name>
# Verify endpoints
kubectl get endpoints
7.9 Cleanup
kubectl delete -f mongo-express.yaml
kubectl delete -f mongodb.yaml
kubectl delete -f mongodb-configmap.yaml
kubectl delete -f mongodb-secret.yaml
8. Namespaces
8.1 What are Namespaces?
Namespaces provide a mechanism for isolating groups of resources within a single cluster. Think of them as virtual clusters inside a cluster.
Key Characteristics:
- Resource names must be unique within a namespace, but not across namespaces
- Provides a scope for names
- Way to divide cluster resources between multiple users or teams
8.2 Default Namespaces
When you install Kubernetes, you get 4 default namespaces:
1. default
- Where resources are created by default
- Used when you don’t specify a namespace
- Your applications typically go here
2. kube-system
- Contains Control Plane processes
- System components managed by Kubernetes
- DO NOT modify or delete anything here!
3. kube-public
- Publicly accessible data
- Contains cluster information
- Readable by all users (including unauthenticated)
4. kube-node-lease
- Contains lease objects associated with each node
- Used for node heartbeat data
- Improves performance of node heartbeats
View namespaces:
kubectl get namespaces
kubectl get ns # shorthand
8.3 Why Use Namespaces?
Use Case 1: Group Resources Logically
Instead of having everything in the default namespace, organize by function:
├── database namespace
│ ├── mongodb
│ ├── mysql
│ └── redis
│
├── monitoring namespace
│ ├── prometheus
│ ├── grafana
│ └── alertmanager
│
└── default namespace
├── nginx
└── api-service
Use Case 2: Isolate Team Resources
Avoid conflicts when multiple teams use the same cluster:
├── team-alpha namespace
│ ├── alpha's deployments
│ └── alpha's services
│
└── team-beta namespace
├── beta's deployments
└── beta's services
Both teams can have a deployment named api-service without conflicts!
Use Case 3: Share Resources Between Environments
Run multiple environments in the same cluster:
├── production namespace
│ └── production workloads
│
├── staging namespace
│ └── staging workloads
│
└── development namespace
└── development workloads
Or implement Blue-Green deployments:
├── blue namespace (active)
│ └── version 1.0
│
└── green namespace (preparing)
└── version 2.0
Use Case 4: Limit Permissions and Resources
# Resource limits per namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: development
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
pods: "10"
You can also use RBAC to limit what users can do in each namespace.
8.4 Creating Namespaces
Using kubectl:
kubectl create namespace my-namespace
Using YAML:
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: my-namespace
kubectl apply -f namespace.yaml
8.5 Using Namespaces
Specify namespace in kubectl:
# Get resources in a specific namespace
kubectl get pods -n my-namespace
# Get resources from all namespaces
kubectl get pods --all-namespaces
kubectl get pods -A # shorthand
Specify namespace in YAML:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
namespace: my-namespace # Specify here
spec:
containers:
- name: nginx
image: nginx
Apply to a namespace:
kubectl apply -f pod.yaml -n my-namespace
8.6 Changing Default Namespace
Instead of adding -n namespace every time, change your default context:
# Set default namespace for current context
kubectl config set-context --current --namespace=my-namespace
# Verify
kubectl config view --minify | grep namespace:
Better Tool: kubens
Install kubectx which includes kubens:
# Install on macOS
brew install kubectx
# List all namespaces
kubens
# Switch namespace
kubens my-namespace
# Switch back to previous namespace
kubens -
8.7 Namespaced vs Cluster-Wide Resources
Most resources are namespaced:
# List namespaced resources
kubectl api-resources --namespaced=true
# Examples:
- pods
- services
- deployments
- configmaps
- secrets
Some resources are cluster-wide:
# List cluster-wide resources
kubectl api-resources --namespaced=false
# Examples:
- nodes
- persistentvolumes
- clusterroles
- namespaces
Cluster-wide resources cannot be created within a namespace.
8.8 Accessing Resources Across Namespaces
Services can be accessed across namespaces:
# Service in "database" namespace
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
namespace: database
Access from another namespace:
# Pod in "default" namespace
env:
- name: DATABASE_URL
value: "mongodb-service.database:27017"
# <service-name>.<namespace>:<port>
Full DNS format:
<service-name>.<namespace>.svc.cluster.local:<port>
Most resources CANNOT be accessed across namespaces:
- Secrets
- ConfigMaps
- PersistentVolumeClaims
Each namespace needs its own copies of these.
9. Kubernetes Services In-Depth
9.1 Why Do We Need Services?
Recap of the problem:
- Pods are ephemeral (short-lived)
- When a pod restarts, it gets a new IP address
- Direct pod-to-pod communication is unreliable
Services provide:
- Stable IP address and DNS name
- Load balancing across pod replicas
- Service discovery within the cluster
9.2 How Services Work
Services use labels and selectors to find pods:
# Service
spec:
selector:
app: nginx # "Find all pods with label app: nginx"
# Deployment creates pods with matching label
spec:
template:
metadata:
labels:
app: nginx # This matches!
Service tracks pod endpoints:
kubectl get endpoints
# You'll see pod IPs listed as endpoints
9.3 Service Types
Kubernetes supports 4 types of services:
| Type | Description | Use Case |
|---|---|---|
| ClusterIP | Internal, only accessible within cluster | Default, databases, internal APIs |
| NodePort | Accessible on each node’s IP at a static port | Development, testing |
| LoadBalancer | Exposes service via cloud provider’s load balancer | Production, external access |
| ExternalName | Maps service to external DNS name | External database, third-party API |
9.4 ClusterIP Service (Default)
Most common type, used for internal communication.
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP # Default, can be omitted
selector:
app: backend
ports:
- protocol: TCP
port: 80
targetPort: 8080
Characteristics:
- Only accessible from within the cluster
- Gets a virtual IP (cluster IP)
- Kubernetes’ internal DNS creates a record
Access within cluster:
# From another pod, you can access via:
http://backend-service:80
# or
http://backend-service.default.svc.cluster.local:80
Check ClusterIP:
kubectl get service backend-service
# Output:
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# backend-service ClusterIP 10.96.45.123 <none> 80/TCP 5m
Multi-Port Services
When you need to expose multiple ports:
apiVersion: v1
kind: Service
metadata:
name: multi-port-service
spec:
selector:
app: my-app
ports:
- name: http # Names are REQUIRED
protocol: TCP
port: 80
targetPort: 8080
- name: metrics # for multi-port services
protocol: TCP
port: 9090
targetPort: 9090
Important: Port names are mandatory for multi-port services to avoid ambiguity.
Headless Service
Sometimes you need to talk to a specific pod instead of load balancing:
Use Cases:
- Stateful applications (databases)
- Master-slave architecture (only master can write)
- Client-side load balancing
Create a headless service:
apiVersion: v1
kind: Service
metadata:
name: mongodb-headless
spec:
clusterIP: None # This makes it headless!
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017
How it works:
- No cluster IP is assigned
- DNS returns pod IPs directly
- Client decides which pod to connect to
Example with StatefulSet:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
spec:
serviceName: mongodb-headless # Links to headless service
replicas: 3
# ...
Accessing individual pods:
# Pod names in StatefulSet are predictable:
mongodb-0.mongodb-headless.default.svc.cluster.local
mongodb-1.mongodb-headless.default.svc.cluster.local
mongodb-2.mongodb-headless.default.svc.cluster.local
9.5 NodePort Service
Makes a service accessible from outside the cluster by opening a port on all nodes.
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: NodePort
selector:
app: frontend
ports:
- protocol: TCP
port: 80 # ClusterIP port
targetPort: 8080
nodePort: 30000 # Port on each node (30000-32767)
What happens:
- Kubernetes automatically creates a ClusterIP service
- Opens port 30000 on every node
- Routes traffic:
NodeIP:30000→ClusterIP:80→Pod:8080
Access the service:
# Get node IP
kubectl get nodes -o wide
# Access via any node IP
curl http://<node-ip>:30000
In Minikube:
minikube service frontend-service
Visual Flow:
External Client
↓
Node IP:30000 (NodePort)
↓
ClusterIP Service :80
↓
Pod :8080
Port Ranges:
- NodePort range: 30000-32767
- If you don’t specify, Kubernetes assigns one randomly
Why Not Use in Production?
- ❌ Exposes worker nodes directly (security risk)
- ❌ Multiple entry points (one per node)
- ❌ Port management becomes difficult
- ✅ Better alternatives: LoadBalancer or Ingress
9.6 LoadBalancer Service
Best option for production when running on a cloud provider.
apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
What happens:
- Kubernetes automatically creates NodePort service
- Kubernetes automatically creates ClusterIP service
- Cloud provider provisions a real load balancer
- Load balancer gets a public IP address
Request Flow:
External Client
↓
Cloud Load Balancer (Public IP)
↓
Node IP:NodePort (random)
↓
ClusterIP Service :80
↓
Pods :8080 (Load balanced)
Check external IP:
kubectl get service app-service
# Output:
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# app-service LoadBalancer 10.96.45.12 35.123.45.67 80:31234/TCP 5m
# ↑ Public IP ↑ NodePort
Cloud Provider Support:
- ✅ AWS: Creates ELB (Elastic Load Balancer)
- ✅ GCP: Creates Google Cloud Load Balancer
- ✅ Azure: Creates Azure Load Balancer
- ❌ Bare metal: Needs manual configuration (MetalLB)
Advantages:
- ✅ Single entry point with public IP
- ✅ Cloud-managed (handles health checks, SSL)
- ✅ More secure than NodePort
- ✅ Automatic scaling with cloud provider