Basics
Introduction
Kubernetes (K8s) is an open-source platform for automating the deployment, scaling, and management of containerized applications. Originally developed by Google and now maintained by the Cloud Native Computing Foundation, it has become the industry standard for container orchestration. In this crash course, I’ll cover the essential 85% you need for daily work, and prepare you to explore the remaining 15% on your own.
graph TD A[What is Kubernetes?] --> B[Container Orchestration Platform] B --> C[Automates deployment] B --> D[Handles scaling] B --> E[Manages containers] B --> F[Provides self-healing] B --> G[Enables service discovery]
Prerequisites
Before we begin our Kubernetes journey, you’ll need:
- Basic understanding of containers (Docker)
- Command line familiarity
- YAML syntax knowledge
- A computer with at least 4GB RAM, 2 CPUs
These prerequisites ensure you can follow along with the hands-on parts of this crash course without getting stuck on fundamentals.
Installation: Getting Started
Now that we understand what Kubernetes is and what we need, let’s set up a local Kubernetes environment to get hands-on experience:
1. Install Docker
First, install Docker Desktop (Mac/Windows) or Docker Engine (Linux) as Kubernetes relies on containers:
# For Ubuntu
sudo apt update
sudo apt install docker.io
sudo systemctl enable --now docker
# Verify installation
docker --version
2. Install Minikube
With Docker in place, we can now install Minikube, which runs a single-node Kubernetes cluster locally:
# macOS (with Homebrew)
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 (with chocolatey)
choco install minikube
# Start your cluster
minikube start
# Verify status
minikube status
3. Install kubectl
Finally, we need kubectl, the command-line tool for interacting with our Kubernetes cluster:
# macOS
brew install kubectl
# Linux
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
# Windows
choco install kubernetes-cli
# Verify installation
kubectl version --client
With these three components installed, you now have a complete local Kubernetes environment ready for exploration.
Kubernetes Architecture
A Kubernetes Cluster consists of two main components: the Control Plane and Worker Nodes.
The Control Plane includes several critical components:
- API Server: Functions as the central communication hub for the entire cluster
- Scheduler: Responsible for assigning pods to appropriate nodes
- Controller Manager: Maintains the desired state of the cluster
- etcd: Serves as the cluster state database, storing all configuration data
Worker Nodes contain the following components:
- Kubelet: Manages pods on each node
- Kube-proxy: Establishes and maintains network rules
- Container Runtime: Executes containers within the environment
The Kubelet on Worker Nodes directly manages Pods, which contain the application containers. All components work in coordination, with the API Server acting as the central communication point between the Control Plane and Worker Nodes to maintain the desired state of applications running in the cluster.
Components Explained
Control Plane (formerly Master): The brain of the cluster
- API Server: The front door to the cluster that processes all requests
- Scheduler: Watches for new pods and assigns them to nodes
- Controller Manager: Ensures the cluster maintains the desired state
- etcd: Key-value store that holds all cluster data
Worker Nodes: Where your applications actually run
- Kubelet: Ensures containers are running in pods
- Kube-proxy: Maintains network rules on the node
- Container Runtime: Software that runs containers (Docker, containerd)
This architecture follows a central control model where the Control Plane makes decisions and Worker Nodes carry them out, creating a scalable and resilient system.
Core Kubernetes Concepts
Now that we understand the infrastructure, let’s explore the essential building blocks you’ll use daily in Kubernetes:
1. Pods
The smallest deployable unit in Kubernetes is the Pod. Think of a Pod as a wrapper around one or more containers:
# simple-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod # Name of the pod
labels:
app: nginx # Label for identification/selection
spec:
containers:
- name: nginx # Container name inside the pod
image: nginx:latest # Docker image to use
ports:
- containerPort: 80 # Port the container exposes
Apply with:
kubectl apply -f simple-pod.yaml
Key characteristics:
- Contains one or more containers that share storage/network
- Gets its own IP address
- Always runs on a single node
- Is ephemeral (temporary)
While Pods are the foundation of Kubernetes, you’ll rarely create them directly. Instead, you’ll use higher-level abstractions like Deployments, which we’ll discuss shortly.
2. Services
Since Pods are ephemeral and their IP addresses change when restarted, we need Services to provide stable network endpoints to access them:
# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx # Connects to pods with this label
ports:
- port: 80 # Port the service exposes
targetPort: 80 # Port on the pod to forward to
type: ClusterIP # Service type (internal to cluster)
Service types:
- ClusterIP: Internal-only IP (default)
- NodePort: Exposes on each node’s IP at a static port
- LoadBalancer: Uses cloud provider’s load balancer
- ExternalName: Maps to a DNS name
Services allow your applications to communicate with each other reliably, even as the underlying Pods come and go.
3. Deployments
Rather than managing individual Pods, Deployments allow us to declare a desired state for a set of identical pods (replicas):
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # Maintains 3 identical pods
selector:
matchLabels:
app: nginx
template: # Pod template
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
resources:
limits:
cpu: '0.5' # Maximum CPU usage
memory: '512Mi' # Maximum memory usage
requests:
cpu: '0.2' # Minimum CPU requested
memory: '256Mi' # Minimum memory requested
Benefits:
- Declarative updates for pods
- Rolling updates with zero downtime
- Automatic rollback capability
- Scaling capabilities
Deployments are your primary tool for managing applications in Kubernetes, handling all the complexity of ensuring the right number of Pods are running and healthy.
4. ConfigMaps and Secrets
A key principle in modern application design is separating configuration from code. Kubernetes supports this through ConfigMaps and Secrets:
# app-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DB_HOST: 'database.example.com'
API_ENDPOINT: 'api.example.com'
---
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
# Base64 encoded: echo -n "Password123" | base64
DB_PASSWORD: UGFzc3dvcmQxMjM=
Using in a pod:
spec:
containers:
- name: myapp
image: myapp:1.0
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DB_PASSWORD
This separation lets you update configuration without rebuilding container images, making your applications more flexible and secure.
5. Persistent Volumes
Containers are ephemeral by design, which means any data inside is lost when the container restarts. For data that needs to survive pod restarts, we use Persistent Volumes:
# persistent-storage.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce # Can be mounted by a single node for read/write
resources:
requests:
storage: 1Gi # Request 1GB of storage
Using in a pod:
spec:
containers:
- name: postgres
image: postgres:13
volumeMounts:
- name: data-volume
mountPath: /var/lib/postgresql/data # Mount point in container
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: data-pvc # Reference the PVC
Persistent Volumes allow stateful applications like databases to function properly in the otherwise stateless Kubernetes environment.
Essential kubectl Commands
Now that we understand the key resources, let’s explore the commands that will handle 85% of your daily interactions with Kubernetes:
# Viewing resources
kubectl get pods # List all pods
kubectl get deployments # List deployments
kubectl get services # List services
kubectl get nodes # List cluster nodes
kubectl get all # List multiple resource types
# Detailed information
kubectl describe pod <pod-name> # Show details of a pod
kubectl describe deployment <name> # Show deployment details
# Creating & updating resources
kubectl apply -f <filename.yaml> # Create/update from file
kubectl delete -f <filename.yaml> # Delete resources in file
# Logs & Debugging
kubectl logs <pod-name> # View pod logs
kubectl logs -f <pod-name> # Stream pod logs
kubectl exec -it <pod-name> -- /bin/bash # Open shell in pod
# Scaling
kubectl scale deployment <name> --replicas=5 # Scale to 5 pods
# Port forwarding (for local testing)
kubectl port-forward <pod-name> 8080:80 # Local:Pod port
These commands form the core of your daily interaction with Kubernetes. As you become more comfortable with them, you’ll be able to efficiently manage and troubleshoot your applications.
Practical Example: Deploying a Complete Application
Let’s put everything we’ve learned together by deploying a web application with a database. This example demonstrates how the various components work together:
graph TD A[User] -->|Access| B[Ingress] B -->|Routes to| C[Web Service] C -->|Balances| D[Web Pods] D -->|Connects to| E[DB Service] E -->|Routes to| F[Database Pod] F -->|Stores in| G[Persistent Volume]
Step 1: Create a namespace
First, let’s create a dedicated namespace to organize our resources:
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: demo-app
Apply it:
kubectl apply -f namespace.yaml
Step 2: Database deployment
Next, let’s deploy our database component:
# database.yaml
# Secret for database password
apiVersion: v1
kind: Secret
metadata:
name: db-secret
namespace: demo-app
type: Opaque
data:
# echo -n "Password123" | base64
password: UGFzc3dvcmQxMjM=
---
# Storage for database
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-pvc
namespace: demo-app
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
# Database deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: db-deployment
namespace: demo-app
spec:
replicas: 1 # Only 1 for database
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:13
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
- name: POSTGRES_DB
value: demoapp
volumeMounts:
- name: db-storage
mountPath: /var/lib/postgresql/data
volumes:
- name: db-storage
persistentVolumeClaim:
claimName: db-pvc
---
# Database service
apiVersion: v1
kind: Service
metadata:
name: db-service
namespace: demo-app
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
type: ClusterIP # Internal only
Notice how we’ve combined multiple resources in a single file, separated by ---
. This is a common practice that helps organize related resources.
Step 3: Web application deployment
Now, let’s deploy the web application that will connect to our database:
# webapp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
namespace: demo-app
spec:
replicas: 3 # Run 3 copies for high availability
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:alpine # Using nginx as example
ports:
- containerPort: 80
env:
- name: DB_HOST
value: db-service # Connects to the database service
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
---
# Web service
apiVersion: v1
kind: Service
metadata:
name: web-service
namespace: demo-app
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
# Ingress for external access
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: demo-app
spec:
rules:
- host: demoapp.local # Add to your hosts file for local testing
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
The Ingress resource creates an entry point for external traffic to reach our web service.
Step 4: Apply and verify
Finally, let’s apply our configurations and verify everything is running:
# Apply configurations
kubectl apply -f database.yaml
kubectl apply -f webapp.yaml
# Check resources
kubectl get all -n demo-app
# Access locally (if Ingress not working)
kubectl port-forward svc/web-service 8080:80 -n demo-app
# Then visit http://localhost:8080 in your browser
This command sequence demonstrates the typical workflow for deploying applications to Kubernetes.
Database Seeding Example
For applications with databases, you’ll often need to initialize them with data. Here’s how to create a one-time job to seed your database:
# db-seed-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: db-seed-job
namespace: demo-app
spec:
template:
spec:
containers:
- name: db-seed
image: postgres:13
command: ['/bin/bash', '-c']
args:
- |
# Wait for database to be ready
sleep 10
# Run seed script
PGPASSWORD=$DB_PASSWORD psql -h db-service -U postgres -d demoapp -c "
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL
);
INSERT INTO users (name, email) VALUES
('John Doe', 'john@example.com'),
('Jane Smith', 'jane@example.com'),
('Bob Johnson', 'bob@example.com');
"
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
restartPolicy: Never
Apply with:
kubectl apply -f db-seed-job.yaml
This Job resource runs once and terminates after completing its task, making it perfect for initialization operations.
Deployment Strategies
As your applications evolve, you’ll need to update them. Kubernetes supports several deployment strategies to minimize disruption:
graph TD A[Deployment Strategies] --> B[Rolling Update
Default strategy] A --> C[Blue-Green
Two identical environments] A --> D[Canary
Gradual traffic shift] B --> B1[Gradually replaces pods] C --> C1[Zero downtime switch] D --> D1[Tests with subset of users]
Rolling Update (Default)
The most commonly used strategy is Rolling Update, which gradually replaces old pods with new ones:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25% # Max unavailable pods during update
maxSurge: 25% # Max extra pods during update
This approach balances availability with resource usage, ensuring your application remains available during updates.
Monitoring Your Cluster
Once your applications are running, you’ll want to keep an eye on their health and performance. Here are some basic monitoring commands:
# Node status
kubectl get nodes
kubectl describe node <node-name>
# Check pod usage
kubectl top pods # Requires metrics-server
kubectl top nodes
# Event monitoring
kubectl get events --sort-by=.metadata.creationTimestamp
These commands provide insights into the current state of your cluster and can help identify potential issues before they become critical.
The Remaining 15%: Advanced Topics
You now have a solid foundation in Kubernetes, covering approximately 85% of what you’ll need for daily operations. As you grow more comfortable, here are the advanced topics to explore next:
Autoscaling
- Horizontal Pod Autoscaler (HPA) adjusts the number of pods based on CPU/memory usage
- Vertical Pod Autoscaler (VPA) adjusts resource requests and limits
- Cluster Autoscaler adds or removes nodes based on pending pods
Specialized Workload Resources
- StatefulSets provide guarantees about the ordering and uniqueness of pods
- DaemonSets ensure a pod runs on every node in the cluster
- Jobs & CronJobs handle batch processing and scheduled tasks
Security
- Role-Based Access Control (RBAC) controls who can access what
- Network Policies restrict traffic between pods
- Pod Security Context controls pod privileges and access
- Service Accounts authenticate pods to the API server
Advanced Networking
- Service Mesh (Istio, Linkerd) provides traffic management, security, and observability
- Network Policies enforce communications rules
- Custom CNI plugins for specialized networking needs
GitOps and CI/CD
- ArgoCD synchronizes your cluster with a Git repository
- Flux provides GitOps for both applications and infrastructure
- Jenkins X automates CI/CD on Kubernetes
Ecosystem Tools
- Helm simplifies application deployment with charts
- Kustomize provides template-free configuration customization
- Prometheus & Grafana enable comprehensive monitoring
- Logging solutions (EFK/ELK stack) centralize logs
Multi-cluster Management
- Cluster Federation coordinates multiple clusters
- Fleet management tools handle clusters at scale
Production Readiness
- Disaster recovery planning ensures business continuity
- Backup solutions (Velero) protect your data
- Advanced troubleshooting techniques solve complex issues
- Performance optimization ensures efficient resource usage
These advanced topics build upon the foundation you’ve established and allow you to tackle more complex scenarios as your Kubernetes journey continues.
Summary
You’ve now learned the core 85% of Kubernetes:
- Setting up a local environment with Minikube
- Understanding Kubernetes architecture
- Working with Pods, Services, and Deployments
- Managing configurations with ConfigMaps and Secrets
- Handling persistent storage
- Essential kubectl commands
- Deploying a complete application
This foundation gives you the skills to work productively with Kubernetes on a daily basis. As you encounter more complex scenarios, you can incrementally explore the advanced topics from the final 15%.
Remember that Kubernetes is constantly evolving, so continuing to reference the official documentation at kubernetes.io will always be valuable as you deepen your knowledge. With practice and exploration, you’ll soon be confidently managing containerized applications at scale.