βΈοΈ Kubernetes Core Concepts
Kubernetes can feel like an ocean of jargon β pods, deployments, services, ingress, PVCs. But underneath, it's a small set of ideas that compose cleanly. This lesson builds that foundation: the architecture, the object model, and the handful of objects you'll use every single day.
π― Learning Objectives
By the end of this lesson, you will be able to:
- Describe the Kubernetes architecture β control-plane components and node components
- Explain the object model and the spec/status reconciliation loop
- Define Pods and choose the right controller (Deployment, StatefulSet, DaemonSet, Job)
- Use Services and Ingress for stable networking and external access
- Manage configuration with ConfigMaps, Secrets, PersistentVolumes, and namespaces
Estimated Time: 40β50 minutes β’ Difficulty: Intermediate
Hands-on: Write a Deployment and Service from scratch and reason about how they connect.
In This Lesson
What Is Kubernetes?
Kubernetes (Greek for "helmsman," and often abbreviated K8s) is an open-source container orchestrator. Google built its ancestor, Borg, to run their own services for years, open-sourced Kubernetes in 2014, and donated it to the Cloud Native Computing Foundation. Today it's the industry standard, available as a managed service on every major cloud.
Its defining trait is a declarative, self-reconciling design: you describe the state you want and controllers work continuously to achieve and maintain it. You don't restart crashed pods, re-balance load, or reschedule work after a node dies β Kubernetes does.
π‘ The one sentence to remember: Kubernetes is a control loop. It compares the world you asked for (spec) against the world that exists (status) and acts to close the gap β over and over, forever.
Cluster Architecture
A cluster is a control plane plus one or more worker nodes. The control plane decides; the nodes run your containers.
Control-plane components
- API Server β the front door. Every command and component talks through it; nothing bypasses it.
- etcd β a consistent, highly available key-value store holding the entire cluster state.
- Scheduler β assigns newly created pods to suitable nodes.
- Controller Manager β runs the control loops (node, replication, endpoints, and more) that reconcile state.
Node components
- kubelet β the agent on each node that makes sure the containers described in its assigned pods are actually running and healthy.
- kube-proxy β maintains the network rules that let traffic reach your pods.
- Container runtime β the software that runs containers (commonly
containerd; Docker'sdockershimwas removed in Kubernetes 1.24).
π How a Deployment becomes running pods
You kubectl apply a Deployment β the API server stores it in etcd β the controller manager creates a ReplicaSet and the pods it needs β the scheduler assigns each pod to a node β that node's kubelet tells the runtime to start the containers β kubelet reports status back to the API server. Every arrow passes through the API server.
The Object Model
Everything in Kubernetes is an object β a persistent record of intent. Every object has two key parts:
- spec β provided by you: the desired state
- status β filled in by Kubernetes: the current, observed state
Controllers exist to drive status toward spec. Objects are almost always written as YAML with four required top-level fields:
apiVersion: v1 # which API group/version
kind: Pod # what kind of object
metadata: # identity: name, labels, namespace
name: nginx-pod
labels:
app: nginx
spec: # the desired state
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
Labels deserve special attention: they're arbitrary key-value tags, and nearly everything in Kubernetes β how a Service finds its pods, how a Deployment tracks its ReplicaSet β works by selecting objects that match a label query. Get your labels right and the rest composes naturally.
Pods β The Basic Unit
A Pod is the smallest deployable unit. It wraps one or more containers that are tightly coupled β they share the same network namespace (one IP, reachable via localhost to each other), and can share storage volumes.
π‘ Analogy: A Pod is like an apartment. The containers are rooms that share the same address and utilities (network, storage). The building is the node, housing many apartments.
A Pod moves through a defined lifecycle: Pending β Running β Succeeded or Failed (with Unknown if the node can't be reached). Here's a Pod with resource limits and health probes:
apiVersion: v1
kind: Pod
metadata:
name: web-app
labels:
app: web
tier: frontend
spec:
containers:
- name: web-app
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 10
β οΈ You almost never create Pods directly
A bare Pod has no self-healing β delete its node and it's gone for good. In practice you let a controller (usually a Deployment) create and manage pods for you. Direct Pod creation is reserved for one-off debugging and learning.
Controllers
Controllers are the reconciling control loops that manage pods on your behalf. Choosing the right one is mostly about your workload's shape.
Deployment β stateless apps
The workhorse. A Deployment manages a ReplicaSet, which keeps the requested number of identical pods running. It gives you scaling, rolling updates, and rollbacks for free.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
StatefulSet β databases & stateful apps
When pods need stable identities and their own persistent storage β like the members of a database cluster β use a StatefulSet. Each pod gets a predictable name (db-0, db-1) and its own PersistentVolumeClaim that survives restarts.
DaemonSet β one pod per node
Guarantees a copy of a pod runs on every node (or a selected subset). Ideal for node-level agents: log collectors, metrics exporters, and network plugins.
Job & CronJob β run to completion
A Job runs pods until they finish successfully (batch work, migrations). A CronJob runs Jobs on a schedule (nightly backups, reports).
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *" # every day at 02:00
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:1.4
restartPolicy: OnFailure
| Controller | Best for | Examples |
|---|---|---|
| Deployment | Stateless apps | Web servers, APIs, microservices |
| StatefulSet | Stateful apps needing stable identity | PostgreSQL, MongoDB, Kafka |
| DaemonSet | Node-level agents | Log collectors, monitoring exporters |
| Job | Run-once tasks | Data processing, migrations |
| CronJob | Scheduled tasks | Backups, periodic reports |
Services & Ingress
Pods are ephemeral and their IPs change constantly. A Service gives a group of pods a stable name, a stable virtual IP, and automatic load balancing. It uses a label selector to find its pods.
Service types
- ClusterIP (default) β reachable only inside the cluster. The right choice for internal service-to-service traffic.
- NodePort β exposes the service on a static port on every node's IP.
- LoadBalancer β provisions a cloud load balancer for external access.
- ExternalName β maps the service to an external DNS name.
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP
selector:
app: backend # sends traffic to pods labeled app=backend
ports:
- port: 80 # the port the Service exposes
targetPort: 8080 # the port the pods listen on
Inside the cluster, DNS makes services discoverable at {service}.{namespace}.svc.cluster.local β so your backend can just call http://database-service.
Ingress β smart HTTP routing
A LoadBalancer per service gets expensive fast. Ingress is a single entry point that routes external HTTP(S) traffic to many services by hostname and path, and handles TLS termination. It needs an ingress controller (like ingress-nginx) running in the cluster.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
tls:
- hosts:
- example.com
secretName: example-tls
Config, Secrets & Storage
ConfigMaps
A ConfigMap holds non-sensitive configuration, keeping it out of your container image. Inject it as environment variables or mount it as files.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
database.host: "db.example.com"
database.port: "5432"
---
# consumed in a pod:
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: database.host
Secrets
A Secret is like a ConfigMap but for sensitive data. Be clear on one thing: by default Secret values are only base64-encoded, not encrypted. For real protection, enable encryption at rest for etcd, lock access down with RBAC, and for production prefer an external manager (HashiCorp Vault, AWS Secrets Manager) via the External Secrets Operator.
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YWRtaW4= # base64 of "admin"
password: c3VwZXJzZWNyZXQ= # base64 of "supersecret"
Persistent storage
Containers are ephemeral, so data written inside them vanishes on restart. Kubernetes separates storage into three cooperating objects:
- PersistentVolume (PV) β a piece of cluster storage (a cloud disk, an NFS share).
- PersistentVolumeClaim (PVC) β a request for storage by a workload.
- StorageClass β describes a "type" of storage and enables dynamic provisioning: create a PVC and a matching PV is created automatically.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-claim
spec:
storageClassName: standard
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
Namespaces & RBAC
Namespaces partition a cluster into isolated virtual sub-clusters. They give you a scope for names, a boundary for access policies, and a unit for resource limits β invaluable when many teams share one cluster.
apiVersion: v1
kind: Namespace
metadata:
name: development
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: development
spec:
hard:
pods: "20"
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
RBAC (Role-Based Access Control) governs who can do what. The model is small: a Role (or cluster-wide ClusterRole) lists allowed verbs on resources, and a RoleBinding grants that role to a subject (a user, group, or ServiceAccount). Follow the principle of least privilege β grant only what each subject truly needs.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: development
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
Hands-on Exercise
ποΈ Wire Up a Deployment and Service
Objective: Prove you understand how labels connect a Service to the pods a Deployment creates.
Instructions:
- Write a Deployment named
apithat runs 3 replicas of an image on container port8080, with the labelapp: api. - Write a
ClusterIPService namedapi-servicethat exposes port80and forwards to the pods' port8080. - Explain, in one sentence, how the Service knows which pods to route to.
π‘ Hint
The Service's selector must match the pod template's labels exactly. The Deployment's spec.selector.matchLabels, the pod template labels, and the Service selector should all agree on app: api.
β Solution
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: my-registry/api:1.0.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
type: ClusterIP
selector:
app: api
ports:
- port: 80
targetPort: 8080
How it connects: the Service's selector: app=api matches the label on the pods the Deployment creates, so kube-proxy load-balances traffic hitting api-service:80 across those pods on their port 8080.
π― Quick Quiz
Question 1: Which control-plane component stores the entire cluster state?
Question 2: You need to run a PostgreSQL cluster where each pod keeps its own stable identity and persistent disk. Which controller fits?
Question 3: What is true about Kubernetes Secrets by default?
Summary & Quiz
π Key Takeaways
- A cluster is a control plane (API server, etcd, scheduler, controller manager) plus worker nodes (kubelet, kube-proxy, runtime).
- Every object has a spec (desired) and status (actual); controllers reconcile one toward the other.
- Pods are the atomic unit, but you manage them through controllers β Deployment, StatefulSet, DaemonSet, Job.
- Services give stable networking via label selectors; Ingress routes external HTTP by host and path.
- ConfigMaps, Secrets, PVs/PVCs, and namespaces handle configuration, sensitive data, storage, and isolation.
π Further Reading
- Kubernetes Concepts (official docs)
- kubectl Cheat Sheet
- Interactive Kubernetes Basics tutorial
- Helm β the Kubernetes package manager
π What's Next?
You now know the vocabulary and the building blocks. Next we put them to work in Deploying Applications on Kubernetes β real deployment strategies (rolling, blue-green, canary), Helm and Kustomize, autoscaling, and observability.
π Solid foundation!
Pods, controllers, services, config β you've got the core. Let's ship something.