Skip to main content

☸️ 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.

graph TD subgraph CP[Control Plane] A[API Server] B[etcd] C[Scheduler] D[Controller Manager] end subgraph N1[Worker Node] E[kubelet] F[kube-proxy] G[Container Runtime] H[Pods] end A --- B A --- C A --- D A --- E E --- G G --- H

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's dockershim was 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.

graph TD A[Deployment] --> B[ReplicaSet] B --> C[Pod] B --> D[Pod] B --> E[Pod]

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
ControllerBest forExamples
DeploymentStateless appsWeb servers, APIs, microservices
StatefulSetStateful apps needing stable identityPostgreSQL, MongoDB, Kafka
DaemonSetNode-level agentsLog collectors, monitoring exporters
JobRun-once tasksData processing, migrations
CronJobScheduled tasksBackups, 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.

graph LR A[Client] --> B[Service: stable IP] B --> C[Pod] B --> D[Pod] B --> E[Pod]

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:

graph TD A[StorageClass] -->|dynamically provisions| B[PersistentVolume] C[PersistentVolumeClaim] -->|binds to| B D[Pod] -->|mounts| C
  • 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:

  1. Write a Deployment named api that runs 3 replicas of an image on container port 8080, with the label app: api.
  2. Write a ClusterIP Service named api-service that exposes port 80 and forwards to the pods' port 8080.
  3. 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

πŸš€ 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.