We closed the previous lesson with the map of the cluster's pieces: apiserver, etcd, scheduler, controllers, kubelet. Those are the pieces that run Kubernetes. Now comes the other vocabulary, the one you will be writing every day: pods, Deployments, Services, ConfigMaps, PVCs, namespaces. It is a large vocabulary and the temptation is to learn it like a dictionary, term by term, which is a bad idea: Kubernetes concepts are not independent, they form families with clear hierarchical relationships. This lesson gives you that complete mental map, so that when a PersistentVolumeClaim turns up in module 5 you already know where it fits and who it talks to. We are not going to work through the practical use of each object —each one has its own lesson— but to place them on the map.
Contents
- The foundation: resource, object and API
- Cluster and node
- The pod: the smallest unit
- The workload family
- The networking family
- The configuration family
- The storage family
- Organisation: namespaces, labels, selectors and annotations
- Controllers, operators and CRDs
- Master table of concepts
- The foundation: resource, object and API
Before the concrete names, three terms that get confused constantly:
| Term | Definition | Example |
|---|---|---|
| Resource | A type of entity the API knows how to handle, with its endpoint | pods, deployments, services |
| Object | A concrete instance of a resource, stored in etcd | The pod called bookings-api-7d9f in rutas-norte-dev |
| Manifest | The YAML file describing the object you want | k8s/base/bookings-api.yaml |
And the rule that governs the whole system, which we already saw in the architecture:
spec: what you want. You write it.status: what is really there. The cluster writes it.
Everything that exists in Kubernetes —an application, a virtual IP, a password, a disk, a permission— is an object with spec and status stored in the same API. That uniformity is what makes a single command, kubectl get, work for everything. The detailed anatomy of an object is covered in Objects, YAML Manifests and the Declarative Model.
- Cluster and node
- Cluster: the whole set. A control plane plus a group of nodes that present themselves as a single system. When you say "I deploy to the cluster", you are saying "I hand my intention to the API and let the system decide where".
- Node: a machine —physical or virtual— that runs workloads. It is also an API object:
kubectl get nodesproves it. It has capacity (CPU, memory, maximum pods), labels (zone, disk type) and conditions (Ready,MemoryPressure).
Rutas Norte will use a single-node practice cluster (minikube) throughout the course, and we will talk about multi-node clusters when we get to scheduling (module 6) and high availability (module 9).
- The pod: the smallest unit
This is the foundational concept, and the one that surprises people most when they come from Docker.
Kubernetes does not run containers: it runs pods. A pod is a group of one or more containers that share networking and storage and are always scheduled together on the same node.
What the containers in a pod share:
- A single IP. They see each other over
localhostand cannot use the same port. - The volumes declared in the pod.
- The lifecycle: they are born together, they die together, they move together.
In the vast majority of cases —and in almost all of Rutas Norte's— a pod contains a single container. Multi-container pods are reserved for specific patterns (sidecar, init container, adapter) covered in module 6.
Two properties of the pod to internalise from day one:
- It is ephemeral. A pod is not "repaired" and it does not move: if its node goes down, that pod is written off and a brand-new one is created, with a different name and a different IP. Never depend on a pod's name or IP.
- It is rarely created by hand. A loose pod has nobody to bring it back. Except for one-off tests, it is always created through a workload controller. In the lesson The Course Project you will create a loose one precisely to experience that fragility first-hand.
A minimal snippet, just to see the shape:
apiVersion: v1
kind: Pod
metadata:
name: bookings-api
spec:
containers:
- name: api
image: registry.rutasnorte.example/bookings-api:2.4.0
- The workload family
All these objects have the same purpose: creating and maintaining pods. They differ in how and when.
4.1. The essential hierarchy
flowchart TD
D["Deployment<br/>manages versions"] --> RS["ReplicaSet<br/>guarantees N replicas"]
RS --> P1["Pod"]
RS --> P2["Pod"]
RS --> P3["Pod"]
P1 --> C1["container"]
P2 --> C2["container"]
P3 --> C3["container"]
Read it like this, from the bottom up:
- The container runs your process.
- The pod wraps one or more containers and is the unit that gets scheduled onto a node.
- The ReplicaSet makes sure there are exactly N identical pods; if one is missing, it creates it.
- The Deployment manages ReplicaSets so it can change version gradually: it creates a new ReplicaSet with the new image and shrinks the old one.
That is why you write Deployments, hardly ever ReplicaSets or Pods. The ReplicaSet does exist, and you will see it with kubectl get rs, but it is an intermediary the Deployment manages for you.
4.2. The six workloads
| Object | What it is for | Distinguishing trait | In Rutas Norte |
|---|---|---|---|
| Deployment | Stateless applications, with interchangeable replicas | Progressive update and rollback | web-store, bookings-api, notifications-worker |
| ReplicaSet | Keeping N identical copies | Managed by the Deployment; not written by hand | Created automatically by the above |
| StatefulSet | Stateful applications with a stable identity | Fixed names (-0, -1), a disk of its own per replica, ordered start-up |
bookings-postgres |
| DaemonSet | One copy on every node | Adjusts itself when nodes are added or removed | Log and metrics agents (module 7) |
| Job | A task that runs to completion | It finishes; it can retry and parallelise | Database schema migrations |
| CronJob | A Job launched on a schedule | cron syntax | occupancy-reports, overnight |
The key question when choosing is always the same: are my replicas interchangeable? If any of them can serve any request and they store nothing on disk, it is a Deployment. If each replica has its own identity and data, it is a StatefulSet.
- The networking family
Pods are ephemeral and their IPs change. The networking family exists to add stability and reachability on top of that chaos.
- Service: a stable virtual IP and DNS name that spread traffic across a set of pods, chosen by labels. It is the piece that makes it possible for
web-storeto callbookings-apiby name without knowing any IP. Main types:ClusterIP(internal, the usual one),NodePortandLoadBalancer(external exposure), andExternalName. - Ingress: HTTP/HTTPS rules that route by domain and path to different Services, with TLS termination. It is what will let us publish
www.rutasnorte.exampleandapi.rutasnorte.exampleon a single public IP. An Ingress does nothing on its own: it needs an Ingress Controller installed in the cluster (NGINX, Traefik…). - NetworkPolicy: a firewall at pod level. By default, in Kubernetes every pod can talk to every other pod. A NetworkPolicy restricts that: for example, so that only
bookings-apimay connect tobookings-postgres. Since that database stores personal data, it is not optional for Rutas Norte. - EndpointSlice: the automatically maintained list of the healthy pod IPs sitting behind a Service. You do not write it by hand, but it is the first thing to look at when a Service "goes nowhere".
flowchart LR
U["User<br/>www.rutasnorte.example"] --> ING["Ingress"]
ING --> SVC1["Service<br/>web-store"]
ING --> SVC2["Service<br/>bookings-api"]
SVC1 --> PA["Pod web-store"]
SVC1 --> PB["Pod web-store"]
SVC2 --> PC["Pod bookings-api"]
SVC2 --> PD["Pod bookings-api"]
PC --> SVC3["Service<br/>bookings-postgres"]
PD --> SVC3
SVC3 --> PG["Pod bookings-postgres"]
- The configuration family
The golden rule: the image contains the code; never the configuration or the credentials. The very same bookings-api:2.4.0 image must be able to run in rutas-norte-dev and in rutas-norte-pro without being rebuilt.
| Object | Content | How it reaches the container | Warning |
|---|---|---|---|
| ConfigMap | Non-sensitive configuration: URLs, cache sizes, .conf files |
Environment variables or a mounted file | It encrypts nothing; do not put passwords in it |
| Secret | Sensitive data: passwords, tokens, TLS certificates | Same as the ConfigMap | It is base64-encoded, which is not encryption; it needs RBAC and encryption at rest |
| ServiceAccount | The identity of a pod towards the Kubernetes API | Token mounted automatically | Different from a person's identity |
One detail that prevents a lot of misunderstandings: base64 is encoding, not protection. Anyone with read permission on Secrets can decode them in a second. Real protection comes from RBAC (module 8) and from etcd's encryption at rest.
Related to this family are resource limits (CPU and memory requests and limits), the per-namespace ResourceQuota and the LimitRange, all covered in module 3, which determine the pod's quality of service (QoS) class.
- The storage family
A container's filesystem is ephemeral: on restart, it is lost. For bookings-postgres that is unacceptable, hence this family.
| Object | What it is | Analogy |
|---|---|---|
| Volume | A directory mounted in the pod, defined inside the pod itself | A shared directory; many types die with the pod (emptyDir) |
| PersistentVolume (PV) | A piece of real cluster storage, with a life independent of the pod | The physical disk available |
| PersistentVolumeClaim (PVC) | A user's request: "I want 20 GiB in read-write mode" | The voucher you exchange for a disk |
| StorageClass | The "type" of storage and how it is provisioned automatically | The catalogue: ssd-fast, hdd-cheap |
The normal flow, which you will see in module 5, is: you declare a PVC in your manifest → the StorageClass dynamically provisions a real PV → the PVC is bound to it → the pod mounts that PVC as a volume. As a developer, you almost always write only the PVC.
The PV/PVC split reflects a division of responsibilities: the cluster administrator manages supply (PVs and StorageClasses), the developer expresses demand (PVCs), and neither needs to know the other's details.
- Organisation: namespaces, labels, selectors and annotations
With 40 objects, the cluster becomes unmanageable without organisation. Kubernetes offers two complementary axes.
8.1. Namespace: the hard division
A namespace is a logical partition of the cluster. Rutas Norte will use three: rutas-norte-dev, rutas-norte-pre and rutas-norte-pro.
What a namespace gives you:
- Name uniqueness: there can be a
bookings-apiService in each one with no conflict. - RBAC scope: "this team can deploy to dev but only read in pro".
- Quota scope: CPU and memory limits per environment.
- DNS: a service resolves as
bookings-api.rutas-norte-dev.svc.cluster.local.
What it does not give you: network isolation (that is what NetworkPolicy is for) or node isolation. On top of that, some objects are cluster-scoped and live in no namespace: Node, PersistentVolume, StorageClass, ClusterRole, and Namespace itself.
8.2. Labels and selectors: the soft division
A label is a key-value pair attached to an object. A selector is a query over labels. This pair is the coupling mechanism of the whole of Kubernetes: a Service does not know its pods by name, it finds them by label. A ReplicaSet does the same with its replicas.
Conventions Rutas Norte will use on every object:
metadata:
labels:
app: bookings-api # the component
app.kubernetes.io/part-of: rutas-norte # the platform
environment: dev # the environmentAnd the matching selector in a Service:
Annotations are also key-value pairs, but with the opposite purpose: you cannot select on them. They are there to attach metadata consumed by tools: an Ingress Controller's configuration, the Git commit that triggered the deployment, a certificate identifier. Rule of thumb: if you are going to filter by it, label; if it is information for a tool or for a human, annotation.
- Controllers, operators and CRDs
These three close the map and are the door to the advanced part of the course.
- Controller: a process that runs the previous lesson's reconciliation loop over one type of object. The ReplicaSet controller watches ReplicaSets and creates pods. It is the universal Kubernetes pattern.
- CRD (Custom Resource Definition): an object that defines a new type of object. When you apply a CRD, your cluster's API starts to understand, for example,
kind: Certificateorkind: PostgresCluster, with its ownkubectl get certificates. - Operator: the combination of a CRD with a controller of its own that knows how to reconcile it. It is "an expert administrator's knowledge turned into software": a PostgreSQL operator can create replicas, take backups and run a failover with no human intervention.
flowchart LR
CRD["CRD<br/>defines the PostgresCluster type"] --> API["Kubernetes API"]
CR["PostgresCluster object<br/>'bookings', 3 replicas"] --> API
API --> CTRL["Operator controller"]
CTRL --> SS["StatefulSet"]
CTRL --> SVC["Services"]
CTRL --> SEC["Secrets"]
CTRL --> PVC["PVCs"]
When in module 6 you weigh up whether bookings-postgres should be managed with a StatefulSet of your own or with an operator, this will be the key distinction.
- Master table of concepts
Keep this table: it is the conceptual index of the whole course.
| Concept | What it is in one sentence | Where we will see it in Rutas Norte | Module |
|---|---|---|---|
| Cluster | Control plane + nodes as a single system | The practice cluster and the future production one | 1, 10 |
| Node | A machine that runs pods | minikube's single node; multi-node with kind | 1, 6 |
| Pod | The smallest unit: containers sharing networking and disk | The first deployment of web-store |
1, 2 |
| ReplicaSet | Guarantees N identical pods | Created by the Deployments | 2 |
| Deployment | Manages ReplicaSets to deploy and update without downtime | web-store, bookings-api, notifications-worker |
2 |
| StatefulSet | Replicas with their own identity and disk | bookings-postgres |
6 |
| DaemonSet | One pod per node | Log and metrics agent | 6, 7 |
| Job | A task that runs to completion | Bookings schema migration | 6 |
| CronJob | A Job scheduled by time | occupancy-reports, every night |
6 |
| Service | A stable IP and name + balancing towards pods | bookings-api, bookings-postgres, redis-cache |
2, 4 |
| Ingress | HTTP routing by domain and path, with TLS | www.rutasnorte.example, api.rutasnorte.example |
4 |
| NetworkPolicy | A firewall between pods | Only the API reaches the database | 4, 8 |
| ConfigMap | Externalised non-sensitive configuration | The API's parameters and the store's nginx.conf |
3 |
| Secret | Sensitive data managed separately | bookings-postgres password, SMTP credentials |
3, 8 |
| ServiceAccount | The pod's identity towards the API | A minimal account for each component | 3, 8 |
| Volume | A directory mounted in the pod | Temporary cache, files shared between containers | 5 |
| PersistentVolume | Real storage with a life of its own | The database's disk | 5 |
| PersistentVolumeClaim | A storage request | The 20 GiB bookings-postgres asks for |
5 |
| StorageClass | Catalogue and dynamic provisioning of disks | minikube's default class and cloud classes | 5 |
| Namespace | A logical partition of the cluster | rutas-norte-dev, -pre, -pro |
2 |
| Label / selector | Queryable metadata / a query over it | app, environment, app.kubernetes.io/part-of |
2 |
| Annotation | Non-queryable metadata, for tools | Ingress configuration, deployed commit | 2, 4 |
| Resource / object | A type of entity / a concrete instance | Everything you write under k8s/ |
1 |
| Controller | A process that reconciles one type of object | The ones creating your pods without you seeing them | 1, 6 |
| CRD | Defines a new type of object in the API | The one cert-manager installs | 4, 6 |
| Operator | CRD + controller with expert logic | A managed alternative for PostgreSQL | 6 |
| HPA | Adjusts the number of replicas automatically | Bank-holiday and holiday peaks | 9 |
| Probe | A container's health check | The API's liveness and readiness | 7 |
| RBAC | Who may do what on which resources | Permissions for the team and for the pods | 8 |
Common Mistakes and Tips
- Confusing pod and container. A pod can have several containers, but it is scheduled, moved and scaled as a unit. "Scaling" means creating more pods, not more containers inside the pod.
- Creating loose pods in production. Nobody brings them back. Always through a Deployment, StatefulSet, DaemonSet or Job.
- Storing a password in a ConfigMap. It is the most frequent security mistake in real clusters. The ConfigMap/Secret distinction exists precisely so that different RBAC can be applied to each.
- Assuming a Secret is encrypted.
base64is encoding. Without encryption at rest and RBAC, a Secret is plain text with one extra step. - Believing a namespace isolates the network. It does not: a pod in
rutas-norte-devcan reach one inrutas-norte-prounless a NetworkPolicy exists. - Changing labels casually. Labels are the coupling mechanism: if you modify a pod's label, its Service stops seeing it and its ReplicaSet creates a new one to replace it. This is done deliberately in some debugging techniques, but never by accident.
- Tip: when an object you do not know turns up, ask yourself three questions — which family does it belong to (workload, networking, configuration, storage)?, which other object creates it or consumes it?, is it namespace-scoped or cluster-scoped? With those three answers you can already place it. And
kubectl explain <resource>answers them without leaving the terminal.
Exercises
Exercise 1: Choosing the right object
For each Rutas Norte need, state which object (or combination of objects) you would use and why:
- Run 4 interchangeable copies of
bookings-apiand be able to move from version 2.4.0 to 2.5.0 without service downtime. - Make
bookings-postgreskeep its data even if its pod is recreated, and always have the same name. - Generate an occupancy report every night at 02:30.
- Let
web-storereachbookings-apiby a fixed name even though the replicas change IP. - Keep the database password outside the image.
- Prevent
notifications-workerfrom connecting directly to the database.
Exercise 2: The chain of responsibility
Explain in your own words, in 6 lines at most, what happens from the moment you write replicas: 3 in a Deployment until there are three containers running, naming all the objects and components involved. Then answer: if you delete one of those three pods by hand, who exactly recreates it?
Exercise 3: Label or annotation
Classify each piece of metadata as a label or an annotation and justify it in one line:
app: bookings-apienvironment: prorutasnorte.example/commit: 9f3a1c2nginx.ingress.kubernetes.io/proxy-body-size: 8mapp.kubernetes.io/part-of: rutas-norterutasnorte.example/owner: [email protected]
Solutions
Solution 1
- Deployment. It is a stateless application with interchangeable replicas; the Deployment manages ReplicaSets and enables progressive updates and rollback.
- StatefulSet + PersistentVolumeClaim (with its StorageClass behind it). The StatefulSet gives a stable identity (
bookings-postgres-0) and a PVC of its own per replica that survives the pod being recreated. - CronJob, which in turn creates a Job and that a Pod on each scheduled run.
- A Service of type ClusterIP. It provides a stable DNS name and virtual IP and spreads traffic across the healthy pods selected by label.
- A Secret, mounted as an environment variable or a file, with restrictive RBAC. Never a ConfigMap.
- A NetworkPolicy allowing ingress traffic to
bookings-postgresonly from pods carrying the labelapp: bookings-api.
Solution 2
You write the Deployment and apply it; the apiserver stores it in etcd. The Deployment controller creates a ReplicaSet with replicas: 3. The ReplicaSet controller observes that there are 0 pods and creates 3 Pod objects. The kube-scheduler assigns a node to each. Each node's kubelet asks the runtime, over CRI, to pull the image and start the container. The full chain: Deployment → ReplicaSet → Pod → container.
If you delete a pod by hand, it is recreated by the ReplicaSet controller (not the Deployment): it detects that there are 2 pods where its spec asks for 3 and creates a new one, with a different name and IP.
Solution 3
- Label. It identifies the component and is the key Services and ReplicaSets select by.
- Label. It is used for filtering and for per-environment selectors.
- Annotation. It is traceability; you are never going to select objects by commit and the value changes on every deployment.
- Annotation. It is configuration consumed by the Ingress Controller, not a selection criterion.
- Label. A recommended standard label that lets you query the whole platform at once (
-l app.kubernetes.io/part-of=rutas-norte). - Annotation. Contact information for humans; filtering by it makes no sense and its free format does not fit a label's value restrictions.
Conclusion
You now have the complete map of the vocabulary: Kubernetes objects group into families with clear roles —workloads that create pods, networking that gives them stability and access, configuration that parameterises them, storage that gives them persistence— organised by namespaces and coupled to each other through labels and selectors, with controllers reconciling everything and CRDs and operators making it possible to extend the system. The Deployment → ReplicaSet → Pod → container hierarchy is the backbone worth keeping in mind at all times, and the master table serves as your index for the rest of the course.
So far, everything has been theory. It is time to have a cluster of your own in which to try out everything we learn: in the next lesson, Setting Up a Kubernetes Cluster, we will build the practice environment we will use across the twelve modules, step by step.
Kubernetes Course
Module 1: Introduction to Kubernetes
- What Is Kubernetes?
- Kubernetes Architecture
- Key Concepts and Terminology
- Setting Up a Kubernetes Cluster
- The Kubernetes CLI: kubectl
- Objects, YAML Manifests and the Declarative Model
- The Course Project: the Rutas Norte Platform
Module 2: Core Kubernetes Components
- Pods
- ReplicaSets
- Deployments
- Updates, Rollbacks and Deployment Strategies
- Services
- Namespaces
- Labels, Selectors and Annotations
Module 3: Configuration and Secret Management
- ConfigMaps
- Secrets
- Environment Variables
- Resource Quotas and Limits
- LimitRanges and Quality of Service (QoS) Classes
- ServiceAccounts and API Access from Pods
Module 4: Networking in Kubernetes
- Cluster Networking
- Service Types
- Internal DNS and Service Discovery
- Ingress Controllers
- TLS and Certificate Management with cert-manager
- Network Policies
Module 5: Storage in Kubernetes
- Volumes
- Persistent Volumes
- Persistent Volume Claims
- Storage Classes
- Dynamic Provisioning, Expansion and Snapshots
- Backup and Restore of Persistent Data
Module 6: Advanced Kubernetes Concepts
- StatefulSets
- DaemonSets
- Jobs and CronJobs
- Init Containers, Sidecars and Multi-Container Patterns
- Scheduling: Affinity, Taints and Tolerations
- Custom Resource Definitions (CRDs)
- Operators and the Controller Pattern
Module 7: Monitoring and Logging
- Health Checks and Probes
- Metrics Server and kubectl top
- Monitoring with Prometheus
- Visualization and Alerting with Grafana and Alertmanager
- Centralized Logging with Elasticsearch, Fluentd and Kibana (EFK)
- Application Debugging and Cluster Events
Module 8: Kubernetes Security
- Role-Based Access Control (RBAC)
- Security Contexts and Container Hardening
- Pod Security Policies and Pod Security Standards
- Network Security
- Image Security
- Auditing, Scanning and Vulnerability Management
Module 9: Scaling and Performance
- Horizontal Pod Autoscaling
- Vertical Pod Autoscaling
- Cluster Autoscaling
- Event-Driven and Custom-Metric Scaling with KEDA
- High Availability: PodDisruptionBudgets and Topology
- Performance Tuning
Module 10: Kubernetes Ecosystem and Tooling
- Minikube and Local Environments with kind
- Kubeadm
- Helm
- Kustomize
- GitOps with Argo CD and Flux
- Managed Kubernetes: EKS, AKS and GKE
Module 11: Case Studies and Real-World Applications
- Deploying a Web Application
- Running Stateful Applications
- CI/CD with Kubernetes
- Deployment Strategies: Blue-Green and Canary
- Multi-Cluster Management
- Production Operations: Incidents, Runbooks and Costs
