Kubernetes: Beginner to Pro - Complete Lecture Notes
Table of Contents
1. Introduction to Kubernetes
2. Architecture and Core Components
3. Pods
4. ReplicaSets
5. Deployments
6. Services
7. Namespaces
8. Networking: Ingress & Egress
9. ConfigMaps & Secrets
10. Storage
11. Setting Up a Three-Node Cluster
1. Introduction to Kubernetes
What is Kubernetes?
Kubernetes (K8s) is an open-source container orchestration platform that automates the
deployment, scaling, and management of containerized applications.
Key Benefits
● Automated Rollouts & Rollbacks: Deploy changes safely
● Self-Healing: Restarts failed containers automatically
● Horizontal Scaling: Scale applications up/down based on demand
● Service Discovery & Load Balancing: Automatic DNS and load balancing
● Storage Orchestration: Mount storage systems automatically
● Secret & Configuration Management: Manage sensitive data securely
Core Concepts
● Cluster: A set of nodes running containerized applications
● Node: A worker machine (VM or physical) in Kubernetes
● Control Plane: Manages the cluster state
● Workload: Application running on Kubernetes
Essential Commands to Start
# Check kubectl version
kubectl version --client
# View cluster info
kubectl cluster-info
# Get all resources
kubectl get all
# Get nodes in cluster
kubectl get nodes
# Describe a resource
kubectl describe node <node-name>
2. Architecture and Core Components
Architecture Overview
Kubernetes follows a master-worker architecture with a Control Plane managing multiple
Worker Nodes.
Control Plane Components
1. API Server (kube-apiserver)
● Frontend for Kubernetes control plane
● Exposes Kubernetes API
● All communications go through API server
● RESTful interface for cluster management
2. etcd
● Consistent and highly-available key-value store
● Stores all cluster data (configuration, state, metadata)
● Source of truth for cluster state
● Distributed and replicated
3. Scheduler (kube-scheduler)
● Watches for newly created Pods with no assigned node
● Selects optimal node based on:
− Resource requirements
− Hardware/software constraints
− Affinity/anti-affinity specifications
− Data locality
4. Controller Manager (kube-controller-manager)
Runs controller processes: - Node Controller: Monitors node health - Replication
Controller: Maintains correct number of pods - Endpoints Controller: Populates
Endpoints object - Service Account Controller: Creates default accounts
5. Cloud Controller Manager
● Links cluster with cloud provider API
● Manages cloud-specific resources
Worker Node Components
1. Kubelet
● Agent running on each node
● Ensures containers are running in Pods
● Takes PodSpecs from API server
● Reports node and Pod status back
● Manages container lifecycle
2. Kube-proxy
● Network proxy on each node
● Maintains network rules for Pod communication
● Handles service abstraction and load balancing
● Implements Kubernetes Service concept
3. Container Runtime
● Software responsible for running containers
● Examples: Docker, containerd, CRI-O
● Must implement Kubernetes CRI (Container Runtime Interface)
Key Commands
# Get component status
kubectl get componentstatuses
# View API server
kubectl get pods -n kube-system | grep apiserver
# View etcd
kubectl get pods -n kube-system | grep etcd
# View scheduler
kubectl get pods -n kube-system | grep scheduler
# View controller manager
kubectl get pods -n kube-system | grep controller
# Check kubelet status (on node)
systemctl status kubelet
# View kube-proxy
kubectl get pods -n kube-system | grep kube-proxy
3. Pods
What is a Pod?
● Smallest deployable unit in Kubernetes
● Encapsulates one or more containers
● Shares network namespace (same IP, port space)
● Shares storage volumes
● Ephemeral by nature
Pod Lifecycle Phases
1. Pending: Pod accepted but not yet running
2. Running: Pod bound to node, containers created
3. Succeeded: All containers terminated successfully
4. Failed: All containers terminated, at least one failed
5. Unknown: State cannot be determined
Important Pod Spec Fields
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: myapp
tier: frontend
annotations:
description: "My application pod"
spec:
# Container specifications
containers:
- name: nginx-container
image: nginx:1.21
ports:
- containerPort: 80
name: http
protocol: TCP
# Resource requests and limits
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
# Environment variables
env:
- name: ENV_VAR
value: "production"
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: my-secret
key: password
# Volume mounts
volumeMounts:
- name: data-volume
mountPath: /data
# Liveness probe
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 3
periodSeconds: 3
# Readiness probe
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
# Init containers (run before main containers)
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'until nslookup myservice; do sleep 2; done']
# Volumes
volumes:
- name: data-volume
emptyDir: {}
# Restart policy
restartPolicy: Always # Always, OnFailure, Never
# Node selection
nodeSelector:
disktype: ssd
# Service account
serviceAccountName: my-service-account
# DNS policy
dnsPolicy: ClusterFirst
# Host network
hostNetwork: false
Essential Pod Commands
# Create pod from YAML
kubectl apply -f [Link]
kubectl create -f [Link]
# Create pod imperatively
kubectl run nginx --image=nginx --port=80
# List pods
kubectl get pods
kubectl get pods -o wide # More details
kubectl get pods --all-namespaces
kubectl get pods -n <namespace>
# Describe pod
kubectl describe pod <pod-name>
# Get pod YAML
kubectl get pod <pod-name> -o yaml
kubectl get pod <pod-name> -o json
# Execute command in pod
kubectl exec <pod-name> -- <command>
kubectl exec -it <pod-name> -- /bin/bash
# Execute in specific container (multi-container pod)
kubectl exec -it <pod-name> -c <container-name> -- /bin/bash
# View logs
kubectl logs <pod-name>
kubectl logs <pod-name> -c <container-name> # Specific container
kubectl logs <pod-name> --previous # Previous instance logs
kubectl logs <pod-name> -f # Follow logs
kubectl logs <pod-name> --tail=50 # Last 50 lines
# Port forwarding
kubectl port-forward <pod-name> 8080:80
# Copy files to/from pod
kubectl cp <pod-name>:/path/to/file ./local-file
kubectl cp ./local-file <pod-name>:/path/to/file
# Delete pod
kubectl delete pod <pod-name>
kubectl delete pod <pod-name> --grace-period=0 --force # Force delete
kubectl delete -f [Link]
# Edit pod
kubectl edit pod <pod-name>
# Get pod events
kubectl get events --field-selector [Link]=<pod-name>
# Top (resource usage)
kubectl top pod <pod-name>
Multi-Container Pod Patterns
1. Sidecar Pattern
spec:
containers:
- name: main-app
image: myapp:1.0
- name: log-agent
image: fluentd
# Shares volumes with main container
2. Ambassador Pattern
spec:
containers:
- name: main-app
image: myapp:1.0
- name: ambassador
image: proxy:1.0
# Proxies connections
3. Adapter Pattern
spec:
containers:
- name: main-app
image: myapp:1.0
- name: adapter
image: adapter:1.0
# Standardizes output
4. ReplicaSets
What is a ReplicaSet?
● Maintains a stable set of replica Pods
● Ensures specified number of pod replicas are running
● Replaces failed pods automatically
● Usually managed by Deployments
ReplicaSet Spec
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend-rs
labels:
app: frontend
spec:
# Number of replicas
replicas: 3
# Selector matches pod labels
selector:
matchLabels:
app: frontend
tier: web
# Advanced matching
matchExpressions:
- key: environment
operator: In # In, NotIn, Exists, DoesNotExist
values:
- production
- staging
# Pod template
template:
metadata:
labels:
app: frontend
tier: web
environment: production
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
Important ReplicaSet Fields
● replicas: Desired number of pods
● selector: How to identify pods to manage
− matchLabels: Simple equality-based selection
− matchExpressions: Set-based selection (more flexible)
● template: Pod template for creating pods
− Must match selector labels
− Defines pod specifications
ReplicaSet Commands
# Create ReplicaSet
kubectl apply -f [Link]
kubectl create -f [Link]
# List ReplicaSets
kubectl get rs
kubectl get replicasets
kubectl get rs -o wide
# Describe ReplicaSet
kubectl describe rs <rs-name>
# Get ReplicaSet YAML
kubectl get rs <rs-name> -o yaml
# Scale ReplicaSet
kubectl scale rs <rs-name> --replicas=5
kubectl scale --replicas=5 -f [Link]
# Edit ReplicaSet
kubectl edit rs <rs-name>
# Delete ReplicaSet
kubectl delete rs <rs-name>
kubectl delete rs <rs-name> --cascade=false # Keep pods
# View ReplicaSet events
kubectl describe rs <rs-name> | grep Events -A 10
# Auto-scale (HPA must be configured)
kubectl autoscale rs <rs-name> --min=2 --max=10 --cpu-percent=80
How ReplicaSets Work
1. Label Selection: ReplicaSet continuously monitors pods matching its selector
2. Reconciliation Loop: Compares desired vs actual state
3. Pod Creation: Creates pods if count < replicas
4. Pod Deletion: Deletes pods if count > replicas
5. Pod Adoption: Can adopt existing pods with matching labels
Key Behaviors
# If you delete a pod managed by ReplicaSet
kubectl delete pod <pod-name>
# ReplicaSet immediately creates a new pod
# Changing pod labels removes it from ReplicaSet
kubectl label pod <pod-name> app=other --overwrite
# ReplicaSet creates replacement pod
# Scaling triggers immediate action
kubectl scale rs <rs-name> --replicas=10
# ReplicaSet creates 7 new pods (if previously 3)
5. Deployments
What is a Deployment?
● Higher-level abstraction over ReplicaSets
● Provides declarative updates for Pods and ReplicaSets
● Enables rolling updates and rollbacks
● Most common way to deploy applications
Deployment Spec
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
annotations:
[Link]/change-cause: "Initial deployment"
spec:
# Number of replicas
replicas: 3
# Selector for pods
selector:
matchLabels:
app: nginx
# Update strategy
strategy:
type: RollingUpdate # RollingUpdate or Recreate
rollingUpdate:
maxSurge: 1 # Max pods above desired during update
maxUnavailable: 1 # Max pods unavailable during update
# Minimum time pod should be ready
minReadySeconds: 5
# Revision history limit
revisionHistoryLimit: 10
# Progress deadline
progressDeadlineSeconds: 600
# Pod template
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
Deployment Strategies
1. Rolling Update (Default)
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # Can be percentage or number
maxUnavailable: 25%
● Gradually replaces old pods with new ones
● Zero downtime
● Can control update speed
2. Recreate
strategy:
type: Recreate
● Kills all existing pods before creating new ones
● Causes downtime
● Useful when running multiple versions simultaneously causes issues
Essential Deployment Commands
# Create deployment
kubectl apply -f [Link]
kubectl create deployment nginx --image=nginx:1.21 --replicas=3
# List deployments
kubectl get deployments
kubectl get deploy -o wide
# Describe deployment
kubectl describe deployment <deploy-name>
# Get deployment YAML
kubectl get deployment <deploy-name> -o yaml
# Scale deployment
kubectl scale deployment <deploy-name> --replicas=5
# Update deployment image
kubectl set image deployment/<deploy-name> nginx=nginx:1.22
kubectl set image deployment/<deploy-name> container-name=new-image:tag
# Edit deployment
kubectl edit deployment <deploy-name>
# Rollout status
kubectl rollout status deployment/<deploy-name>
# Rollout history
kubectl rollout history deployment/<deploy-name>
kubectl rollout history deployment/<deploy-name> --revision=2
# Rollback to previous version
kubectl rollout undo deployment/<deploy-name>
# Rollback to specific revision
kubectl rollout undo deployment/<deploy-name> --to-revision=2
# Pause rollout
kubectl rollout pause deployment/<deploy-name>
# Resume rollout
kubectl rollout resume deployment/<deploy-name>
# Restart deployment (recreate pods)
kubectl rollout restart deployment/<deploy-name>
# Delete deployment
kubectl delete deployment <deploy-name>
# Autoscale deployment
kubectl autoscale deployment <deploy-name> --min=2 --max=10 --cpu-percent=80
# View deployment events
kubectl describe deployment <deploy-name> | grep Events -A 10
Update Workflow
# 1. Create deployment
kubectl apply -f [Link] --record
# 2. Update image
kubectl set image deployment/nginx-deployment nginx=nginx:1.22 --record
# 3. Monitor rollout
kubectl rollout status deployment/nginx-deployment
# 4. Check history
kubectl rollout history deployment/nginx-deployment
# 5. If issues, rollback
kubectl rollout undo deployment/nginx-deployment
# 6. Verify rollback
kubectl rollout status deployment/nginx-deployment
Important Deployment Behaviors
Deployment Creates ReplicaSet:
kubectl get rs
# Shows multiple ReplicaSets (one per revision)
# Only one ReplicaSet has desired replicas > 0
Progressive Rollout:
# During update, you'll see:
kubectl get pods
# Some pods with old version, some with new
# Gradually shifts from old to new
Rollback Mechanism: - Deployment keeps old ReplicaSets (limited by
revisionHistoryLimit) - Rollback reactivates old ReplicaSet - Fast and reliable
Best Practices
# Always set resource limits
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
# Use readiness probes
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
# Use liveness probes
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
# Set appropriate strategy
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # Zero downtime
6. Services
What is a Service?
● Stable network endpoint for a set of Pods
● Provides load balancing across Pods
● Enables service discovery
● Decouples frontend from backend
Service Types
1. ClusterIP (Default)
● Internal cluster IP
● Only accessible within cluster
● Most common type
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP
selector:
app: myapp
ports:
- protocol: TCP
port: 80 # Service port
targetPort: 8080 # Container port
2. NodePort
● Exposes service on each node’s IP at a static port
● Accessible from outside cluster
● Port range: 30000-32767
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 8080
nodePort: 30080 # Optional, auto-assigned if omitted
3. LoadBalancer
● Creates external load balancer (cloud provider)
● Assigns external IP
● Routes traffic to NodePort and ClusterIP
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 8080
loadBalancerIP: [Link] # Optional
4. ExternalName
● Maps service to DNS name
● No proxying, returns CNAME
apiVersion: v1
kind: Service
metadata:
name: my-external-service
spec:
type: ExternalName
externalName: [Link]
Complete Service Spec
apiVersion: v1
kind: Service
metadata:
name: my-complete-service
labels:
app: myapp
annotations:
[Link]/aws-load-balancer-type: "nlb"
spec:
# Service type
type: ClusterIP
# Selector to match pods
selector:
app: myapp
tier: backend
# Ports configuration
ports:
- name: http
protocol: TCP
port: 80 # Service port (what clients connect to)
targetPort: 8080 # Pod port (can be name or number)
- name: https
protocol: TCP
port: 443
targetPort: https-port
# Session affinity
sessionAffinity: ClientIP # None or ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
# Cluster IP (usually auto-assigned)
clusterIP: [Link]
# External IPs
externalIPs:
- [Link]
# External traffic policy (for NodePort/LoadBalancer)
externalTrafficPolicy: Local # Cluster or Local
# Health check node port
healthCheckNodePort: 30123
# IP families
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack # SingleStack, PreferDualStack,
RequireDualStack
# Load balancer source ranges
loadBalancerSourceRanges:
- [Link]/8
# Publish not ready addresses
publishNotReadyAddresses: false
Service Discovery
Via DNS (Recommended):
# Within same namespace
curl [Link]
# Cross-namespace
curl [Link]
# Fully qualified
curl [Link]
Via Environment Variables:
# Kubernetes injects these variables
MY_SERVICE_SERVICE_HOST=[Link]
MY_SERVICE_SERVICE_PORT=80
Service Commands
# Create service
kubectl apply -f [Link]
kubectl create service clusterip my-service --tcp=80:8080
# Create NodePort service
kubectl create service nodeport my-service --tcp=80:8080
# Expose deployment as service
kubectl expose deployment nginx --port=80 --target-port=8080 --type=ClusterIP
# List services
kubectl get services
kubectl get svc
kubectl get svc -o wide
# Describe service
kubectl describe service <service-name>
# Get service YAML
kubectl get service <service-name> -o yaml
# Get service endpoints
kubectl get endpoints <service-name>
kubectl describe endpoints <service-name>
# Edit service
kubectl edit service <service-name>
# Delete service
kubectl delete service <service-name>
# Port forward to service
kubectl port-forward service/<service-name> 8080:80
# Test service (from within cluster)
kubectl run test --rm -it --image=busybox -- /bin/sh
# Inside pod: wget -qO- [Link]
Headless Services
apiVersion: v1
kind: Service
metadata:
name: headless-service
spec:
clusterIP: None # Makes it headless
selector:
app: myapp
ports:
- port: 80
● No cluster IP assigned
● DNS returns all pod IPs directly
● Used for StatefulSets and service discovery
Service with Endpoints (No Selector)
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: v1
kind: Endpoints
metadata:
name: external-service
subsets:
- addresses:
- ip: [Link]
- ip: [Link]
ports:
- port: 8080
Service Troubleshooting
# Check service
kubectl get svc <service-name>
# Check endpoints
kubectl get endpoints <service-name>
# If no endpoints, check pod labels and selector
# Check if pods are ready
kubectl get pods -l app=myapp
# Test DNS resolution
kubectl run test --rm -it --image=busybox -- nslookup my-service
# Test connectivity
kubectl run test --rm -it --image=busybox -- wget -qO- [Link]
# Check service logs
kubectl logs -l app=myapp
# Describe service for events
kubectl describe service <service-name>
7. Namespaces
What is a Namespace?
● Virtual cluster within physical cluster
● Provides scope for names
● Resource isolation and organization
● Multi-tenancy support
Default Namespaces
# default: Default namespace for objects without namespace
# kube-system: System components (DNS, dashboard, etc.)
# kube-public: Publicly readable, mostly for cluster information
# kube-node-lease: Node heartbeat information
Namespace Spec
apiVersion: v1
kind: Namespace
metadata:
name: development
labels:
environment: dev
team: backend
annotations:
description: "Development environment"
Namespace with Resource Quotas
apiVersion: v1
kind: Namespace
metadata:
name: production
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: production
spec:
hard:
[Link]: "10"
[Link]: 20Gi
[Link]: "20"
[Link]: 40Gi
persistentvolumeclaims: "10"
pods: "50"
services: "20"
[Link]: "2"
---
apiVersion: v1
kind: LimitRange
metadata:
name: resource-limits
namespace: production
spec:
limits:
- max:
cpu: "2"
memory: 4Gi
min:
cpu: "100m"
memory: 128Mi
default:
cpu: "500m"
memory: 1Gi
defaultRequest:
cpu: "200m"
memory: 512Mi
type: Container
- max:
storage: 10Gi
min:
storage: 1Gi
type: PersistentVolumeClaim
Namespace Commands
# Create namespace
kubectl create namespace development
kubectl apply -f [Link]
# List namespaces
kubectl get namespaces
kubectl get ns
# Describe namespace
kubectl describe namespace development
# Get namespace YAML
kubectl get namespace development -o yaml
# Delete namespace (deletes all resources in it)
kubectl delete namespace development
# Set default namespace for context
kubectl config set-context --current --namespace=development
# View current namespace
kubectl config view --minify | grep namespace
# Create resource in specific namespace
kubectl apply -f [Link] -n development
kubectl run nginx --image=nginx -n development
# List resources in namespace
kubectl get pods -n development
kubectl get all -n development
# List resources in all namespaces
kubectl get pods --all-namespaces
kubectl get pods -A
# Switch between namespaces
kubectl config set-context --current --namespace=production
# View resource quotas
kubectl get resourcequota -n production
kubectl describe resourcequota compute-quota -n production
# View limit ranges
kubectl get limitrange -n production
kubectl describe limitrange resource-limits -n production
Cross-Namespace Communication
DNS Format:
# Service in same namespace
service-name
# Service in different namespace
[Link]-name
# Fully qualified domain name
[Link]
Example:
# Frontend pod accessing backend service in different namespace
apiVersion: v1
kind: Pod
metadata:
name: frontend
namespace: web
spec:
containers:
- name: app
image: myapp
env:
- name: BACKEND_URL
value: "[Link]
Namespace Best Practices
# Organize by environment
namespaces:
- development
- staging
- production
# Or by team
namespaces:
- team-frontend
- team-backend
- team-data
# Or by application
namespaces:
- app-ecommerce
- app-analytics
- app-billing
Network Policies with Namespaces
apiVersion: [Link]/v1
kind: NetworkPolicy
metadata:
name: allow-from-frontend
namespace: backend
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
environment: production
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
RBAC with Namespaces
# Role for namespace-specific permissions
apiVersion: [Link]/v1
kind: Role
metadata:
name: pod-reader
namespace: development
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
# RoleBinding
apiVersion: [Link]/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: development
subjects:
- kind: User
name: developer@[Link]
apiGroup: [Link]
roleRef:
kind: Role
name: pod-reader
apiGroup: [Link]
8. Networking: Ingress & Egress
Kubernetes Network Model
Core Principles: 1. All pods can communicate with each other without NAT 2. All nodes can
communicate with all pods without NAT 3. Pod’s IP address is the same as seen by itself and
by others
Pod Networking
# Each pod gets unique IP
kubectl get pods -o wide
# Pods communicate directly using pod IPs
# Example: curl [Link]
# Or using service DNS
# Example: curl [Link]
Network Policies (Egress & Ingress)
Default Deny All
apiVersion: [Link]/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # Applies to all pods
policyTypes:
- Ingress
- Egress
Allow Specific Ingress
apiVersion: [Link]/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-ingress
namespace: backend
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
# Allow from specific pods
- podSelector:
matchLabels:
app: backend-api
# Allow from specific namespaces
- namespaceSelector:
matchLabels:
environment: production
# Allow from specific IP blocks
- ipBlock:
cidr: [Link]/8
except:
- [Link]/24
ports:
- protocol: TCP
port: 5432
Allow Specific Egress
apiVersion: [Link]/v1
kind: NetworkPolicy
metadata:
name: allow-api-egress
namespace: frontend
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Egress
egress:
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
# Allow to backend API
- to:
- podSelector:
matchLabels:
app: backend-api
ports:
- protocol: TCP
port: 8080
# Allow to external API
- to:
- ipBlock:
cidr: [Link]/0
except:
- [Link]/8
- [Link]/16
ports:
- protocol: TCP
port: 443
Combined Ingress and Egress
apiVersion: [Link]/v1
kind: NetworkPolicy
metadata:
name: api-network-policy
namespace: application
spec:
podSelector:
matchLabels:
app: api
tier: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
- namespaceSelector:
matchLabels:
environment: production
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector:
matchLabels:
name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
Network Policy Commands
# Create network policy
kubectl apply -f [Link]
# List network policies
kubectl get networkpolicies
kubectl get netpol
# Describe network policy
kubectl describe networkpolicy <policy-name>
# Get network policy YAML
kubectl get networkpolicy <policy-name> -o yaml
# Delete network policy
kubectl delete networkpolicy <policy-name>
# Test network connectivity (from within pod)
kubectl exec -it <pod-name> -- curl [Link]
# Check if CNI plugin supports network policies
# (Calico, Cilium, Weave Net support them; Flannel doesn't)
kubectl get pods -n kube-system
Ingress
What is Ingress?
● API object managing external HTTP/HTTPS access to services
● Provides load balancing, SSL termination, name-based virtual hosting
● Requires Ingress Controller (nginx, traefik, haproxy, etc.)
Installing Ingress Controller (NGINX)
# Install NGINX Ingress Controller
kubectl apply -f
[Link]
deploy/static/provider/cloud/[Link]
# Verify installation
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
# Wait for external IP (LoadBalancer)
kubectl get svc ingress-nginx-controller -n ingress-nginx --watch
Basic Ingress
apiVersion: [Link]/v1
kind: Ingress
metadata:
name: simple-ingress
namespace: default
annotations:
[Link]/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: [Link]
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
Advanced Ingress with Multiple Paths
apiVersion: [Link]/v1
kind: Ingress
metadata:
name: advanced-ingress
annotations:
[Link]/ssl-redirect: "true"
[Link]/force-ssl-redirect: "true"
[Link]/rewrite-target: /$2
[Link]/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- [Link]
secretName: myapp-tls
rules:
- host: [Link]
http:
paths:
- path: /api(/|$)(.*)
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- path: /web(/|$)(.*)
pathType: Prefix
backend:
service:
name: web-service
port:
number: 3000
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
Name-Based Virtual Hosting
apiVersion: [Link]/v1
kind: Ingress
metadata:
name: virtual-host-ingress
spec:
ingressClassName: nginx
rules:
- host: [Link]
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- host: [Link]
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
- host: [Link]
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-service
port:
number: 3000
Ingress with TLS
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: default
type: [Link]/tls
data:
[Link]: <base64-encoded-cert>
[Link]: <base64-encoded-key>
---
apiVersion: [Link]/v1
kind: Ingress
metadata:
name: tls-ingress
spec:
ingressClassName: nginx
tls:
- hosts:
- [Link]
secretName: tls-secret
rules:
- host: [Link]
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-service
port:
number: 443
Path Types
# Prefix: Matches based on URL path prefix split by /
pathType: Prefix
path: /api
# Matches: /api, /api/, /api/users, /api/v1/users
# Exact: Matches exact path (case-sensitive)
pathType: Exact
path: /api
# Matches only: /api
# ImplementationSpecific: Depends on IngressClass
pathType: ImplementationSpecific
Ingress Commands
# Create ingress
kubectl apply -f [Link]
# List ingresses
kubectl get ingress
kubectl get ing
# Describe ingress
kubectl describe ingress <ingress-name>
# Get ingress YAML
kubectl get ingress <ingress-name> -o yaml
# Get ingress with address
kubectl get ingress -o wide
# Edit ingress
kubectl edit ingress <ingress-name>
# Delete ingress
kubectl delete ingress <ingress-name>
# Check ingress controller logs
kubectl logs -n ingress-nginx -l [Link]/name=ingress-nginx
# Test ingress
curl -H "Host: [Link]" [Link]
# Test with HTTPS
curl -k [Link]
Ingress Annotations (NGINX)
metadata:
annotations:
# SSL redirect
[Link]/ssl-redirect: "true"
# Rewrite target
[Link]/rewrite-target: /$2
# Rate limiting
[Link]/limit-rps: "10"
# Connection limits
[Link]/limit-connections: "10"
# Timeouts
[Link]/proxy-connect-timeout: "60"
[Link]/proxy-send-timeout: "60"
[Link]/proxy-read-timeout: "60"
# CORS
[Link]/enable-cors: "true"
[Link]/cors-allow-methods: "GET, POST, PUT"
[Link]/cors-allow-origin: "*"
# Authentication
[Link]/auth-type: basic
[Link]/auth-secret: basic-auth
# Whitelist source range
[Link]/whitelist-source-range:
"[Link]/8,[Link]/16"
# Backend protocol
[Link]/backend-protocol: "HTTPS"
# Custom error pages
[Link]/custom-http-errors: "404,503"
[Link]/default-backend: error-page-service
Service Mesh (Advanced Networking)
Popular Service Meshes: - Istio - Linkerd - Consul
Benefits: - Advanced traffic management - Security (mTLS) - Observability - Circuit
breaking - Retry logic
9. ConfigMaps & Secrets
ConfigMaps
What is a ConfigMap?
● Stores non-confidential configuration data
● Key-value pairs
● Decouples configuration from container images
● Can be consumed as environment variables, command-line arguments, or
configuration files
Creating ConfigMaps
From Literals:
kubectl create configmap app-config \
--from-literal=DATABASE_URL=postgres://db:5432/mydb \
--from-literal=CACHE_SIZE=1000 \
--from-literal=LOG_LEVEL=debug
From Files:
# Single file
kubectl create configmap nginx-config --from-file=[Link]
# Multiple files
kubectl create configmap app-configs --from-file=./config-dir/
# Specific key name
kubectl create configmap app-config --from-file=mykey=./[Link]
From Env File:
# [Link]
DATABASE_URL=postgres://db:5432/mydb
CACHE_SIZE=1000
LOG_LEVEL=debug
kubectl create configmap app-config --from-env-file=[Link]
ConfigMap YAML
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
labels:
app: myapp
data:
# Simple key-value
DATABASE_URL: "postgres://db:5432/mydb"
CACHE_SIZE: "1000"
LOG_LEVEL: "debug"
# Multi-line values
[Link]: |
[Link]=8080
[Link]=[Link]
[Link]=10
[Link]=30
# JSON configuration
[Link]: |
{
"server": {
"port": 8080,
"host": "[Link]"
},
"database": {
"url": "postgres://db:5432/mydb",
"pool": 10
}
}
# YAML configuration
[Link]: |
server:
port: 8080
host: [Link]
database:
url: postgres://db:5432/mydb
pool: 10
Using ConfigMaps in Pods
As Environment Variables:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:1.0
# Single environment variable from ConfigMap
env:
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_URL
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
# All keys as environment variables
envFrom:
- configMapRef:
name: app-config
# With prefix
- prefix: APP_
configMapRef:
name: app-config
As Volume Mounts:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
# Mount entire ConfigMap
- name: config-volume
mountPath: /etc/config
# Mount specific key
- name: nginx-config
mountPath: /etc/nginx/[Link]
subPath: [Link]
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
- name: nginx-config
configMap:
name: nginx-config
items:
- key: [Link]
path: [Link]
As Command Arguments:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:1.0
command: ["/app/[Link]"]
args:
- "--port=$(PORT)"
- "--host=$(HOST)"
env:
- name: PORT
valueFrom:
configMapKeyRef:
name: app-config
key: [Link]
- name: HOST
valueFrom:
configMapKeyRef:
name: app-config
key: [Link]
Secrets
What is a Secret?
● Stores sensitive data (passwords, tokens, keys)
● Base64 encoded (NOT encrypted by default)
● Should be encrypted at rest (enable encryption in etcd)
● More secure handling than ConfigMaps
Secret Types
# Opaque (default): Arbitrary user-defined data
# [Link]/service-account-token: Service account token
# [Link]/dockercfg: Serialized ~/.dockercfg
# [Link]/dockerconfigjson: Serialized ~/.docker/[Link]
# [Link]/basic-auth: Basic authentication
# [Link]/ssh-auth: SSH authentication
# [Link]/tls: TLS certificate and key
# [Link]/token: Bootstrap token
Creating Secrets
From Literals:
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password='S3cr3tP@ssw0rd!'
From Files:
# Create files
echo -n 'admin' > [Link]
echo -n 'S3cr3tP@ssw0rd!' > [Link]
kubectl create secret generic db-credentials \
--from-file=username=[Link] \
--from-file=password=[Link]
# Clean up
rm [Link] [Link]
TLS Secret:
kubectl create secret tls tls-secret \
--cert=path/to/[Link] \
--key=path/to/[Link]
Docker Registry Secret:
kubectl create secret docker-registry regcred \
--docker-server=[Link] \
--docker-username=myuser \
--docker-password=mypassword \
--docker-email=user@[Link]
Generic from Env File:
# [Link]
USERNAME=admin
PASSWORD=S3cr3tP@ssw0rd!
kubectl create secret generic app-secrets --from-env-file=[Link]
Secret YAML
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: default
type: Opaque
data:
# Base64 encoded values
username: YWRtaW4= # "admin"
password: UzNjcjN0UEBzc3cwcmQh
# Benefits: Performance (no watches), Security (cannot be changed)
Best Practices
# 1. Use Secrets for sensitive data, ConfigMaps for non-sensitive
# 2. Enable encryption at rest for Secrets
# Configure in kube-apiserver
# 3. Use RBAC to restrict access
apiVersion: [Link]/v1
kind: Role
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
# 4. Mount as volumes, not environment variables (more secure)
# Volumes: Updated automatically, not visible in process listing
# Env vars: Static, visible in process listing
# 5. Use external secret managers (production)
# - HashiCorp Vault
# - AWS Secrets Manager
# - Azure Key Vault
# - Google Secret Manager
# 6. Don't commit secrets to Git
# Use tools like sealed-secrets, external-secrets operator
10. Storage
Storage Concepts
Volume: Directory accessible to containers in a pod PersistentVolume (PV): Cluster-level
storage resource PersistentVolumeClaim (PVC): Request for storage by a user
StorageClass: Dynamic provisioning of PVs
Volume Types
emptyDir
● Temporary storage, deleted when pod is removed
● Shared between containers in same pod
apiVersion: v1
kind: Pod
metadata:
name: emptydir-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: cache-volume
mountPath: /cache
- name: sidecar
image: busybox
volumeMounts:
- name: cache-volume
mountPath: /cache
volumes:
- name: cache-volume
emptyDir: {}
# emptyDir:
# medium: Memory # Use RAM (faster but lost on restart)
# sizeLimit: 1Gi
hostPath
● Mounts file/directory from host node
● Survives pod restart but node-specific
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: host-volume
mountPath: /data
volumes:
- name: host-volume
hostPath:
path: /var/data
type: DirectoryOrCreate # Directory, File, FileOrCreate, etc.
configMap Volume
apiVersion: v1
kind: Pod
metadata:
name: configmap-volume-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: config
mountPath: /etc/config
volumes:
- name: config
configMap:
name: app-config
secret Volume
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: secret
mountPath: /etc/secret
readOnly: true
volumes:
- name: secret
secret:
secretName: db-credentials
downwardAPI
● Exposes pod/container metadata
apiVersion: v1
kind: Pod
metadata:
name: downwardapi-pod
labels:
app: myapp
annotations:
build: "123"
spec:
containers:
- name: app
image: busybox
command: ["sleep", "3600"]
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: [Link]
- path: "annotations"
fieldRef:
fieldPath: [Link]
- path: "pod-name"
fieldRef:
fieldPath: [Link]
- path: "namespace"
fieldRef:
fieldPath: [Link]
PersistentVolumes (PV)
PersistentVolume Spec
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-example
labels:
type: local
spec:
# Storage capacity
capacity:
storage: 10Gi
# Access modes
accessModes:
- ReadWriteOnce # RWO: Single node read-write
# - ReadOnlyMany # ROX: Multiple nodes read-only
# - ReadWriteMany # RWX: Multiple nodes read-write
# Reclaim policy
persistentVolumeReclaimPolicy: Retain # Retain, Delete, Recycle
# Storage class
storageClassName: manual
# Volume mode
volumeMode: Filesystem # Filesystem or Block
# Mount options
mountOptions:
- hard
- nfsvers=4.1
# Node affinity
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: [Link]/hostname
operator: In
values:
- node-1
# Storage backend (examples)
hostPath:
path: /mnt/data
type: DirectoryOrCreate
# Or NFS
# nfs:
# server: [Link]
# path: /exports/data
# Or AWS EBS
# awsElasticBlockStore:
# volumeID: vol-0123456789abcdef0
# fsType: ext4
# Or GCE Persistent Disk
# gcePersistentDisk:
# pdName: my-disk
# fsType: ext4
# Or Azure Disk
# azureDisk:
# diskName: myDisk
# diskURI: /subscriptions/.../myDisk
PersistentVolumeClaims (PVC)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-example
namespace: default
spec:
# Access modes (must match PV)
accessModes:
- ReadWriteOnce
# Storage class
storageClassName: manual
# Volume mode
volumeMode: Filesystem
# Resource requests
resources:
requests:
storage: 5Gi
# Selector (optional, to bind to specific PV)
selector:
matchLabels:
type: local
matchExpressions:
- key: environment
operator: In
values:
- dev
Using PVC in Pod
apiVersion: v1
kind: Pod
metadata:
name: pvc-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: persistent-storage
mountPath: /usr/share/nginx/html
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: pvc-example
StorageClass (Dynamic Provisioning)
apiVersion: [Link]/v1
kind: StorageClass
metadata:
name: fast-ssd
annotations:
[Link]/is-default-class: "true"
provisioner: [Link]/aws-ebs
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
fsType: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer # Immediate or WaitForFirstConsumer
Popular Provisioners:
# AWS EBS
provisioner: [Link]/aws-ebs
parameters:
type: gp3
# GCE Persistent Disk
provisioner: [Link]/gce-pd
parameters:
type: pd-ssd
# Azure Disk
provisioner: [Link]/azure-disk
parameters:
storageaccounttype: Premium_LRS
# NFS (external provisioner)
provisioner: nfs-provisioner
parameters:
server: [Link]
path: /exports
# Local path (Rancher)
provisioner: [Link]/local-path
parameters:
path: /opt/local-path-provisioner
PVC with Dynamic Provisioning
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd # References StorageClass
resources:
requests:
storage: 20Gi
# PV is automatically created by provisioner
StatefulSet with PVC
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
# Volume claim templates (creates PVC per replica)
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi
Storage Commands
# PersistentVolumes
kubectl get pv
kubectl describe pv <pv-name>
kubectl delete pv <pv-name>
# PersistentVolumeClaims
kubectl get pvc
kubectl describe pvc <pvc-name>
kubectl delete pvc <pvc-name>
# StorageClasses
kubectl get storageclass
kubectl get sc
kubectl describe sc <sc-name>
# Check PV status
kubectl get pv -o wide
# Status: Available, Bound, Released, Failed
# Check PVC status
kubectl get pvc -o wide
# Status: Pending, Bound, Lost
# Get PV details
kubectl get pv <pv-name> -o yaml
# Get PVC details
kubectl get pvc <pvc-name> -o yaml
# View which PV is bound to PVC
kubectl get pvc <pvc-name> -o jsonpath='{.[Link]}'
# Expand PVC (if allowVolumeExpansion: true)
kubectl patch pvc <pvc-name> -p
'{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
# Create PV
kubectl apply -f [Link]
# Create PVC
kubectl apply -f [Link]
# Check storage capacity
kubectl get pv --sort-by=.[Link]
Volume Snapshots
# VolumeSnapshotClass
apiVersion: [Link]/v1
kind: VolumeSnapshotClass
metadata:
name: csi-snapclass
driver: [Link]
deletionPolicy: Delete
---
# VolumeSnapshot
apiVersion: [Link]/v1
kind: VolumeSnapshot
metadata:
name: pvc-snapshot
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: pvc-example
---
# Restore from snapshot
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-restored
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi
dataSource:
name: pvc-snapshot
kind: VolumeSnapshot
apiGroup: [Link]
Storage Best Practices
# 1. Use StorageClasses for dynamic provisioning
# 2. Set resource requests and limits
# 3. Use ReadWriteOnce for single-pod access
# 4. Use ReadWriteMany only when necessary (limited support)
# 5. Set appropriate reclaim policies
# - Retain: For production (manual cleanup)
# - Delete: For development
# 6. Enable volume expansion in StorageClass
# 7. Use volumeBindingMode: WaitForFirstConsumer for topology-aware
scheduling
# 8. Regular backups via volume snapshots
# 9. Monitor storage usage
# 10. Use CSI drivers for modern storage features
11. Setting Up a Three-Node Cluster
Prerequisites
System Requirements (per node): - 2 CPU cores minimum - 2 GB RAM minimum (4 GB
recommended) - 20 GB disk space - Ubuntu 20.04/22.04 or CentOS 7/8 - Network
connectivity between nodes - Unique hostname, MAC address, product_uuid per node
Node Setup: - 1 Control Plane Node (Master) - 2 Worker Nodes
Step 1: Prepare All Nodes
# Run on ALL nodes (control plane + workers)
# 1. Update system
sudo apt-get update
sudo apt-get upgrade -y
# 2. Disable swap (required by Kubernetes)
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 3. Load kernel modules
cat <<EOF | sudo tee /etc/modules-load.d/[Link]
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# 4. Configure sysctl
cat <<EOF | sudo tee /etc/sysctl.d/[Link]
[Link]-nf-call-iptables = 1
[Link]-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
# 5. Verify modules
lsmod | grep br_netfilter
lsmod | grep overlay
# 6. Verify sysctl
sysctl [Link]-nf-call-iptables [Link]-nf-call-ip6tables
net.ipv4.ip_forward
Step 2: Install Container Runtime (containerd)
# Run on ALL nodes
# 1. Install containerd
sudo apt-get install -y containerd
# 2. Configure containerd
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/[Link]
# 3. Enable SystemdCgroup
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g'
/etc/containerd/[Link]
# 4. Restart containerd
sudo systemctl restart containerd
sudo systemctl enable containerd
# 5. Verify containerd
sudo systemctl status containerd
Step 3: Install kubeadm, kubelet, kubectl
# Run on ALL nodes
# 1. Install dependencies
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
# 2. Add Kubernetes apt repository
curl -fsSL [Link] | sudo gpg
--dearmor -o /etc/apt/keyrings/[Link]
echo 'deb [signed-by=/etc/apt/keyrings/[Link]]
[Link] /' | sudo tee
/etc/apt/[Link].d/[Link]
# 3. Install Kubernetes components
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
# 4. Hold versions (prevent auto-upgrade)
sudo apt-mark hold kubelet kubeadm kubectl
# 5. Verify installation
kubeadm version
kubelet --version
kubectl version --client
# 6. Enable kubelet
sudo systemctl enable kubelet
Step 4: Initialize Control Plane
# Run ONLY on CONTROL PLANE node
# 1. Initialize cluster
sudo kubeadm init \
--pod-network-cidr=[Link]/16 \
--apiserver-advertise-address=<CONTROL_PLANE_IP> \
--control-plane-endpoint=<CONTROL_PLANE_IP>
# Example:
# sudo kubeadm init \
# --pod-network-cidr=[Link]/16 \
# --apiserver-advertise-address=[Link] \
# --control-plane-endpoint=[Link]
# 2. Save the output! You'll need the join command
# kubeadm join [Link]:6443 --token <token> \
# --discovery-token-ca-cert-hash sha256:<hash>
# 3. Configure kubectl for regular user
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/[Link] $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 4. Verify cluster
kubectl get nodes
kubectl get pods -A
# 5. Check control plane components
kubectl get pods -n kube-system
Step 5: Install Pod Network (CNI)
# Run on CONTROL PLANE node
# Option 1: Flannel (simple, good for learning)
kubectl apply -f
[Link]
ml
# Option 2: Calico (production-ready, supports network policies)
kubectl create -f
[Link]
[Link]
kubectl create -f
[Link]
[Link]
# Option 3: Weave Net
kubectl apply -f
[Link]
[Link]
# Wait for pods to be ready
kubectl get pods -n kube-system --watch
# Verify network
kubectl get pods -n kube-system -o wide
Step 6: Join Worker Nodes
# Run on WORKER nodes
# 1. Use the join command from control plane init output
sudo kubeadm join [Link]:6443 \
--token <token-from-init> \
--discovery-token-ca-cert-hash sha256:<hash-from-init>
# 2. If you lost the join command, generate new token on control plane:
kubeadm token create --print-join-command
# 3. Verify join on control plane
kubectl get nodes
# Output should show all nodes:
# NAME STATUS ROLES AGE VERSION
# control-plane Ready control-plane 10m v1.28.x
# worker-1 Ready <none> 5m v1.28.x
# worker-2 Ready <none> 5m v1.28.x
Step 7: Verify Cluster
# Run on CONTROL PLANE node
# 1. Check all nodes
kubectl get nodes -o wide
# 2. Check system pods
kubectl get pods -n kube-system
# 3. Check component status
kubectl get componentstatuses
# 4. Deploy test application
kubectl create deployment nginx --image=nginx --replicas=3
# 5. Expose as service
kubectl expose deployment nginx --port=80 --type=NodePort
# 6. Check deployment
kubectl get deployments
kubectl get pods -o wide
kubectl get services
# 7. Test access
curl [Link]
# 8. Clean up test
kubectl delete deployment nginx
kubectl delete service nginx
Step 8: Label Worker Nodes
# Run on CONTROL PLANE node
# Label nodes for better organization
kubectl label node worker-1 [Link]/worker=worker
kubectl label node worker-2 [Link]/worker=worker
# Add custom labels
kubectl label node worker-1 disktype=ssd
kubectl label node worker-2 disktype=hdd
# Verify labels
kubectl get nodes --show-labels
Cluster Configuration Files
Important locations:
# Kubernetes configuration
/etc/kubernetes/
├── [Link] # Admin kubeconfig
├── [Link] # Kubelet kubeconfig
├── [Link]
├── [Link]
└── manifests/ # Static pod manifests
├── [Link]
├── [Link]
├── [Link]
└── [Link]
# Kubelet configuration
/var/lib/kubelet/[Link]
# Container runtime
/etc/containerd/[Link]
# Kubeconfig (user)
~/.kube/config
Cluster Management Commands
# View cluster info
kubectl cluster-info
kubectl cluster-info dump
# View nodes
kubectl get nodes
kubectl describe node <node-name>
# Drain node (before maintenance)
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
# Uncordon node (after maintenance)
kubectl uncordon <node-name>
# Cordon node (mark unschedulable)
kubectl cordon <node-name>
# Remove node from cluster
kubectl drain <node-name> --ignore-daemonsets --force
kubectl delete node <node-name>
# On the worker node being removed:
sudo kubeadm reset
sudo rm -rf /etc/cni/net.d
sudo rm -rf $HOME/.kube/config
# View certificates
kubeadm certs check-expiration
# Renew certificates
sudo kubeadm certs renew all
# Upgrade cluster (control plane)
sudo apt-mark unhold kubeadm
sudo apt-get update && sudo apt-get install -y kubeadm=1.29.0-00
sudo apt-mark hold kubeadm
sudo kubeadm upgrade plan
sudo kubeadm upgrade apply v1.29.0
# Upgrade kubelet and kubectl
sudo apt-mark unhold kubelet kubectl
sudo apt-get update && sudo apt-get install -y kubelet=1.29.0-00
kubectl=1.29.0-00
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet
Troubleshooting Cluster Setup
# Check kubelet logs
sudo journalctl -u kubelet -f
# Check containerd logs
sudo journalctl -u containerd -f
# Check pod logs
kubectl logs <pod-name> -n kube-system
# Describe pod
kubectl describe pod <pod-name> -n kube-system
# Check apiserver
kubectl get pods -n kube-system | grep apiserver
kubectl logs -n kube-system kube-apiserver-<hostname>
# Check etcd
kubectl get pods -n kube-system | grep etcd
kubectl exec -it -n kube-system etcd-<hostname> -- etcdctl member list
# Reset node (if initialization fails)
sudo kubeadm reset
sudo rm -rf /etc/cni/net.d
sudo rm -rf $HOME/.kube/config
sudo rm -rf /etc/kubernetes/
# Then retry initialization
# Common issues:
# 1. Swap not disabled: sudo swapoff -a
# 2. Port conflicts: Check ports 6443, 2379-2380, 10250-10252
# 3. Firewall: Ensure required ports are open
# 4. Network plugin: Wait for CNI pods to be ready
High Availability Setup (Optional)
For production, use multiple control plane nodes:
# Initialize first control plane
sudo kubeadm init \
--control-plane-endpoint="[Link]" \
--upload-certs \
--pod-network-cidr=[Link]/16
# Join additional control planes
sudo kubeadm join [Link] \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane \
--certificate-key <cert-key>
# Requirements:
# - Load balancer in front of control planes
# - External etcd cluster (recommended for large clusters)
# - Shared storage for cluster data
Summary and Best Practices
Key Takeaways
1. Pods: Smallest unit, ephemeral, share network/storage
2. ReplicaSets: Maintain desired pod count, self-healing
3. Deployments: Declarative updates, rolling updates, rollbacks
4. Services: Stable networking, load balancing, service discovery
5. Namespaces: Logical isolation, multi-tenancy, resource quotas
6. Networking: Network policies control traffic, Ingress manages external access
7. ConfigMaps/Secrets: Decouple configuration, manage sensitive data
8. Storage: Volumes for data persistence, dynamic provisioning with StorageClasses
9. Cluster: Understanding architecture is crucial for troubleshooting
Production Best Practices
# Always set resource requests and limits
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
# Use health probes
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
# Use namespaces for organization
# Use labels for selection
# Use annotations for metadata
# Security
# - Use RBAC
# - Network policies
# - Pod security standards
# - Encrypt secrets at rest
# - Regular updates
# Monitoring
# - Prometheus + Grafana
# - ELK Stack for logs
# - Jaeger for tracing
# Backup
# - etcd snapshots
# - Volume snapshots
# - GitOps for declarative configs
Next Steps
1. Practice with minikube or kind for local development
2. Learn Helm for package management
3. Explore GitOps (ArgoCD, Flux)
4. Study CKA/CKAD certifications
5. Implement CI/CD pipelines
6. Learn service mesh (Istio, Linkerd)
7. Explore operators and custom resources
8. Master troubleshooting techniques
stringData:
username: admin
password: S3cr3tP@ssw0rd!
**TLS Secret**:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: [Link]/tls
data:
[Link]: <base64-encoded-cert>
[Link]: <base64-encoded-key>
Docker Registry Secret:
apiVersion: v1
kind: Secret
metadata:
name: regcred
type: [Link]/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-docker-config>
Using Secrets in Pods
As Environment Variables:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:1.0
# Single environment variable from Secret
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
# All keys as environment variables
envFrom:
- secretRef:
name: db-credentials
As Volume Mounts:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentials
items:
- key: username
path: db-username
mode: 0400
- key: password
path: db-password
mode: 0400
Docker Registry Secret:
apiVersion: v1
kind: Pod
metadata:
name: private-image-pod
spec:
containers:
- name: app
image: [Link]/myapp:1.0
imagePullSecrets:
- name: regcred
ConfigMap & Secret Commands
# List ConfigMaps
kubectl get configmaps
kubectl get cm
# List Secrets
kubectl get secrets
# Describe ConfigMap
kubectl describe configmap app-config
# Describe Secret (values hidden)
kubectl describe secret db-credentials
# Get ConfigMap YAML
kubectl get configmap app-config -o yaml
# Get Secret YAML
kubectl get secret db-credentials -o yaml
# Decode secret value
kubectl get secret db-credentials -o jsonpath='{.[Link]}' | base64
--decode
# Edit ConfigMap
kubectl edit configmap app-config
# Edit Secret
kubectl edit secret db-credentials
# Delete ConfigMap
kubectl delete configmap app-config
# Delete Secret
kubectl delete secret db-credentials
# Create ConfigMap from directory
kubectl create configmap game-config --from-file=./config-directory/
# Update ConfigMap
kubectl create configmap app-config --from-literal=KEY=VALUE --dry-run=client
-o yaml | kubectl apply -f -
# Watch for changes
kubectl get configmap app-config --watch
Immutable ConfigMaps and Secrets
apiVersion: v1
kind: ConfigMap
metadata:
name: immutable-config
immutable: true
data:
DATABASE_URL: "postgres://db:5432/mydb"
---
apiVersion: v1
kind: Secret
metadata:
name: immutable-secret
immutable: true
type: Opaque
data:
password: UzNjcjN0