Working model#
An image is a packaged filesystem and launch contract. A container is a running process under isolation controls. A Pod is Kubernetes' smallest scheduling envelope.
Start with the application concepts Kubernetes assumes#
Kubernetes does not replace the application process, network protocol, or storage engine. It coordinates them. Before reading its object names, keep these ordinary runtime facts visible:
| Application fact | What Kubernetes needs from it |
|---|---|
| Process | An entrypoint that starts, remains in the foreground, reports failure through exit status, and stops on signal |
| Listener | An IP address and port where the process accepts traffic; declaring a port does not make a process listen |
| Health behavior | A startup, readiness, or liveness signal whose failure has a stated consequence |
| Packaged files | An immutable image reference plus runtime configuration and mounted data |
| Durable state | A database, object store, or volume contract that survives replacement when the workload requires it |
| Resource demand | CPU, memory, storage, and optional devices that the scheduler and runtime can account for |
| Network dependency | A resolvable name, route, policy, credential, deadline, and retry behavior |
| Declaration | Structured YAML or JSON that identifies an API type and supplies a desired-state object |
A process that only listens on 127.0.0.1 will not accept ordinary traffic sent to the Pod IP. A process that writes its only copy of an order to the container writable layer loses that state when the Pod is replaced. A health endpoint that reports a shared dependency outage as a liveness failure can make every replica restart together. Kubernetes executes the contract you declare; it cannot infer the intended behavior from the application.
YAML is a common serialization format for Kubernetes objects, not the control system itself. Indentation forms nested maps and lists, --- separates documents, and the API schema decides which fields are valid. The parsed object sent to the API server is the contract. A rendered manifest should therefore be reviewed and validated like any other API request.
If one of these foundations is unfamiliar, LL1: The kernel boundary explains processes, signals, files, sockets, and system calls; LL6: Containers and cgroups explains namespaces and resource controls; and CI1: AWS foundations explains addresses, ports, DNS, routes, identity, and storage. You can begin here and follow those links only when the lower layer blocks the current explanation.
Kubernetes coordinates processes across machines#
One machine can run a container directly. The harder production problem is keeping several copies alive across many machines, replacing failures, rolling out a new image, giving changing copies a stable network name, and attaching configuration or storage. Kubernetes addresses that problem through an API and controllers.
A cluster is one Kubernetes control plane plus its worker nodes. A node is a machine, physical or virtual, that runs workload processes. The control plane stores desired objects and makes cluster-wide decisions. A node agent called the kubelet receives assigned Pods and asks a container runtime to start them. CI3: Control planes, etcd, and reconciliation follows that full path.
flowchart TD
accTitle: Kubernetes cluster from API request to running containers
accDescr: A user or delivery controller sends object definitions to the Kubernetes API server. The control plane stores desired state and controllers create dependent objects. The scheduler assigns pending Pods to worker Nodes. Each Node's kubelet asks a container runtime to start the Pod's containers. The application process runs inside those containers rather than inside the control plane.
USER["User, CI system,<br/>or GitOps controller"] --> API["Kubernetes API server"]
API --> STORE["Persisted cluster objects"]
STORE --> CONTROLLERS["Controllers"]
STORE --> SCHED["Scheduler"]
CONTROLLERS --> PODS["Pending Pod objects"]
SCHED --> BIND["Pod-to-Node binding"]
subgraph NODEA["Worker Node A"]
KUBELETA["kubelet"] --> RUNTIMEA["container runtime"]
RUNTIMEA --> PODA["Pod<br/>one or more containers"]
end
subgraph NODEB["Worker Node B"]
KUBELETB["kubelet"] --> RUNTIMEB["container runtime"]
RUNTIMEB --> PODB["Pod<br/>one or more containers"]
end
BIND --> KUBELETA
BIND --> KUBELETB
kubectl apply sends desired object data to the API server. It does not SSH to a node or start a container directly. Controllers and node agents perform the later work, which is why an accepted manifest can still lead to a Pending Pod, failed image pull, unready process, or empty Service.
Kubernetes objects use a common shape:
apiVersionselects the API group and version.kindnames the object type, such asDeployment,Service, orPod.metadatacarries identity, namespace, labels, annotations, and versioning data.specstates the desired configuration supplied by a user or controller.status, when the type provides it, reports what the responsible controller currently observes. It is not another place to declare intent.
Labels are small identifying key-value pairs. Selectors use those labels to join objects, such as a Deployment to its Pods or a Service to its backends. A typo can produce valid YAML and valid objects that no longer select one another.
Most application objects are namespaced. A namespace scopes names and can participate in quota, policy, and access control, but it is still inside one cluster and shares that cluster's control plane. Nodes, PersistentVolumes, and some other resources are cluster-scoped. A namespace does not create another VPC, node fleet, kernel, or Kubernetes cluster.
The fields below spec depend on kind. A Deployment contains a Pod template, which is the recipe its controller uses to create Pod objects; the template is not itself a running Pod. A Service uses a label selector rather than embedding or owning those Pods.
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders
namespace: bookshop
spec:
replicas: 3
selector:
matchLabels:
app: orders
template:
metadata:
labels:
app: orders
spec:
containers:
- name: api
image: registry.example/orders@sha256:<digest>
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /ready
port: http
---
apiVersion: v1
kind: Service
metadata:
name: orders
namespace: bookshop
spec:
selector:
app: orders
ports:
- name: http
port: 80
targetPort: http
The Deployment asks for three interchangeable Pods. The Service selects ready network endpoints carrying app: orders, gives clients a stable Service name and virtual address, accepts traffic on port 80, and forwards it to the named http port on each selected Pod. The application still has to bind a listener on port 8080; containerPort records intent and supplies a named port, but it does not open a socket or firewall rule.
A Deployment creates Pods; a Service routes to them#
These objects cooperate without doing the same job:
| Object | Job | What it does not do |
|---|---|---|
| Deployment | Declares interchangeable replicas and rollout policy | Provide a stable client address or route external HTTP |
| ReplicaSet | Maintains one revision's desired Pod count | Choose rollout strategy across revisions |
| Pod | Describes colocated containers, volumes, identity, and runtime needs | Survive replacement as one stable application instance |
| Service | Provides stable discovery and transport to selected endpoints | Create, restart, or roll out Pods |
| EndpointSlice | Records batches of current backend addresses, ports, and readiness | Decide the desired replica count |
| Gateway or Ingress path | Routes external or cross-service protocol traffic to a Service | Replace Service backend selection or Deployment rollout |
flowchart TB
accTitle: Deployment ownership and Service traffic are separate Kubernetes graphs
accDescr: A Deployment owns a ReplicaSet, which owns three replaceable Pods. A Service does not own those Pods; its selector matches their labels, and the control plane records ready Pod addresses in EndpointSlices. Internal clients call the Service DNS name. An optional Gateway or Ingress route sends external HTTP traffic to that same Service.
subgraph OWNERSHIP["Workload ownership and replacement"]
DEPLOY["Deployment<br/>replicas: 3"] --> RS["ReplicaSet<br/>one template revision"]
RS --> PODS["Three replaceable Pods<br/>label: app=orders"]
end
subgraph TRAFFIC["Discovery and traffic"]
INTERNAL["Internal client"] --> SVC["Service<br/>selector: app=orders"]
EXTERNAL["External client"] --> ROUTE["Gateway or Ingress route"]
ROUTE --> SVC
SVC --> EPS["EndpointSlice<br/>ready Pod IPs and target port"]
end
SVC -. "label selector" .-> PODS
EPS -. "ready addresses" .-> PODS
Ownership and selection have different lifecycle effects. Deleting a Deployment normally garbage-collects the ReplicaSets and Pods it owns. Deleting a Service does not delete selected Pods; it removes that stable traffic path and its managed EndpointSlices. Changing a Service selector can redirect traffic without creating a Deployment rollout. Changing the Deployment's Pod template creates a new ReplicaSet but leaves the Service identity in place as long as the new Pods carry matching labels.
A Service with type ClusterIP is ordinarily reachable through cluster networking. NodePort adds a port on cluster nodes, and LoadBalancer asks an installed implementation to provision or connect an external load balancer. Gateway API or Ingress adds protocol-aware routes such as host and path matching in front of Services. CI4: Kubernetes networking, storage, and security follows those packet and controller paths.
Learn object families before memorizing fields#
| Family | Common objects | Question answered |
|---|---|---|
| Workload | Pod, Deployment, StatefulSet, DaemonSet, Job, CronJob | What should run, how many copies, with which identity and completion rule? |
| Discovery and traffic | Service, EndpointSlice, Gateway, HTTPRoute, Ingress | Which current endpoints receive which traffic? |
| Configuration and credentials | ConfigMap, Secret | Which runtime values are delivered to the Pod? |
| Storage | PersistentVolumeClaim, PersistentVolume, StorageClass, CSI objects | Which storage interface, lifecycle, topology, and provider satisfy the mount? |
| Scaling and disruption | HorizontalPodAutoscaler, PodDisruptionBudget | When may replica intent change, and how much voluntary disruption is allowed? |
| Placement and isolation | Node, Namespace, ResourceQuota, NetworkPolicy | Where can work run, which names and budgets are scoped, and which traffic is admitted? |
| API extension | CustomResourceDefinition and custom resources | Which domain object and reconciliation behavior does an operator add? |
An object name does not guarantee an implementation. A Service needs a functioning network data plane; a NetworkPolicy needs a network plugin that enforces it; a PersistentVolumeClaim needs matching storage or a provisioner; a custom resource needs a controller if creating it should cause domain work.
An image isn't a tiny virtual machine#
An Open Container Initiative (OCI) image contains ordered filesystem layers plus metadata such as the entrypoint, arguments, environment defaults, and platform. A runtime unpacks that image and starts a host-kernel process with namespaces, cgroups, capabilities, mounts, and a restricted root filesystem around it.
Those Linux terms describe separate controls. A namespace changes what the process can see, such as process IDs, mounts, or network devices. A cgroup accounts for processes and can apply CPU or memory policy. Capabilities split privileged kernel operations into narrower checks. None creates a separate kernel; LL6: Containers and cgroups assembles the complete host-side path.
The process still makes system calls into the node's kernel. That shared kernel is why a container starts quickly and why its security boundary differs from a hardware virtual machine. Immutability applies to the image artifact; a running container can still write to its writable layer or mounted volumes.
A registry stores image manifests and layers. A tag such as stable is a movable name; a digest identifies exact content. Production promotion should preserve the tested digest, while signatures, provenance, vulnerability scanning, and a software bill of materials (SBOM) answer separate supply-chain questions. A multi-platform image index can point to different manifests for architectures such as linux/amd64 and linux/arm64, so confirm the resolved platform when a Pod pulls on one node type but fails on another.
A Pod is a colocated process group#
Every container in a Pod lands on the same node and shares the Pod network namespace. They can use localhost, see the same Pod IP, and mount shared volumes when configured. They don't merge filesystems or process namespaces by default. A sidecar should exist only when tight lifecycle and locality are part of the design.
Regular init containers run to completion in order before application containers start. Kubernetes also supports restartable sidecars as init containers with restartPolicy: Always; they start in init order, keep running with the Pod, and no longer prevent a Job from completing after its main container finishes. Check the target cluster version before adopting that form because older clusters used only ordinary application containers as sidecars.
Pods are replaceable. Their names, UIDs, and IPs change as controllers create new instances. Put stable access behind a Service and durable state behind an external store or persistent volume; don't teach clients to remember a Pod.
Fictional case. Each bookshop
storefront-apiPod runs an API container and an OpenTelemetry Collector sidecar. The sidecar receives telemetry over localhost, but clients reach the replaceable Pods through a Service. Nothing about the Pod name or IP becomes durable application identity.
Controllers own replacement and rollout#
A Deployment manages interchangeable replicas through ReplicaSets. StatefulSets add stable ordinal identity and storage claims; DaemonSets target nodes; Jobs seek completion, while CronJobs create Jobs on a schedule. Pick the controller whose failure and completion semantics match the process.
ConfigMaps carry non-secret configuration. Secret objects improve API separation and role-based access control (RBAC), but base64 is only an encoding, and Kubernetes stores Secret data unencrypted in etcd unless the cluster enables encryption at rest. Keep credentials out of images and Git, restrict API and node access, encrypt the store, and arrange rotation before the first incident. Mounting a Secret or injecting it as an environment variable changes delivery, not its lifetime or authority.
- Deployment: stateless replicas and rolling replacement
- StatefulSet: ordered identity or one claim per replica
- DaemonSet: one eligible copy per node
- Job or CronJob: finite work now or on a schedule
A DaemonSet derives its count from eligible nodes#
A Deployment starts from a requested replica count. A DaemonSet starts from the current node set. Its controller creates one Pod on every eligible node, creates another when a matching node joins, and removes the corresponding Pod when that node leaves. “One per node” means one per node that passes selectors, affinity, taints, and tolerations, not one per cluster and not one sidecar inside every application Pod.
This fits node-local infrastructure such as CNI and CSI agents, log collectors, security sensors, and storage daemons. Each DaemonSet Pod still requests CPU and memory, so its overhead reduces the allocatable capacity left for application Pods. A node autoscaler must account for that overhead when deciding whether a new machine can fit pending work.
| Controller | Desired cardinality | Identity and completion contract |
|---|---|---|
| Deployment | Explicit interchangeable replica count | Replaceable Pods; rolling rollout through ReplicaSets |
| StatefulSet | Explicit replica count with stable ordinals | Ordered identity and usually one persistent claim per ordinal |
| DaemonSet | One Pod on each eligible node | Node-local instance follows node eligibility and lifetime |
| Job | Enough Pods to reach the declared completions | Finite work; retries until success or its failure policy stops it |
| CronJob | Jobs created from a schedule | Scheduling policy plus each child Job's finite-work contract |
| Custom controller | Whatever relationship its API defines | Must be learned from its custom resource, status, and reconciliation |
Read a fictional base as one workload contract#
Suppose the bookshop keeps the following toy repository. These paths and values exist only in this note:
deploy/storefront/base/kustomization.yaml
├─ deployment.yaml → Pod template, probes, resources, lifecycle
└─ service.yaml → selector and target port
deploy/storefront/overlays/dev/kustomization.yaml → two replicas and a dev image digest
deploy/storefront/overlays/prod/kustomization.yaml → four replicas and a prod image digest
The base includes a Deployment and Service. Both use the label app: storefront-api; the Service exposes port 80 and forwards it to the named container port http on port 8080. The Pod template declares readiness at /ready, requests CPU and memory, and gives the process time to drain on termination. An overlay supplies a replica count and immutable image digest.
Render each overlay with kubectl kustomize and inspect the result before applying it. A changed image digest alters the Pod template and starts a rollout. A changed Service selector alters traffic membership without replacing Pods. A selector typo can therefore produce healthy Pods and an empty EndpointSlice—the Kubernetes object that lists a Service's current network backends—at the same time. CI4 follows that traffic path in full.
The example is self-contained; build the field relationships rather than copying a deployment from another service.
The word base is overloaded. A Kustomize base is reusable YAML that renders Kubernetes objects. An OCI image root filesystem is the packaged directory tree from which a container starts. An immutable filesystem template can be a separate ext4 block image selected by an identifier in that YAML. Patching a template UUID into an overlay changes a reference; it does not insert the referenced repository files into the Kubernetes manifest.
A Deployment changes ReplicaSets when its Pod template changes#
Take a four-replica Deployment with maxSurge: 1 and maxUnavailable: 1. During a rolling update, the controller may run at most five old-plus-new Pods and should keep at least three available under that strategy. It creates a new ReplicaSet for the changed Pod template, increases the new ReplicaSet, and decreases the old one while readiness determines which Pods count as available. Scheduling, image download, startup, and readiness can pause the sequence independently.
Only changes to the Deployment's Pod template start this rollout. Editing a ConfigMap that a Pod reads does not by itself change the template or replace existing Pods. Common patterns put a content hash or versioned ConfigMap name in the template so a reviewed configuration change also changes the template. Keep the old ReplicaSet long enough for rollback, but remember that rolling back a Deployment cannot reverse a database migration or restore an overwritten external object.
Availability is based on Pod readiness and any minimum-ready time, not merely a Running phase.
desired replicas = 4
maxSurge = 1 → at most 5 Pods during rollout
maxUnavailable = 1 → at least 3 available Pods
Pod-template change → new ReplicaSet → ready new Pods → old scale-down
Read the controller chain before opening a shell#
Start with kubectl get deployment,replicaset,pod in the intended namespace and compare desired, current, updated, available, and ready counts. kubectl rollout status deployment/<name> reports rollout progress, while kubectl describe shows conditions and recent events. Match the Pod template labels to the Service selector before assuming traffic reaches the new Pods. Sort namespace events by time when a scheduling or image problem is suspected.
A Pending Pod points first to scheduling events, storage claims, namespace quotas, or image credentials; CI4 covers claims and CI5 covers quotas and placement. ImagePullBackOff points to image reference, registry reachability, or authentication. A Running but unready Pod points to the readiness path, listener, configuration, or dependency. CrashLoopBackOff calls for current and previous container logs, exit reason, and restart count. An OOM-killed process calls for memory limit and working-set evidence. Use kubectl debug or an ephemeral container—a temporary diagnostic container added to an existing Pod—only when the image lacks tools and policy allows it; record any change made during diagnosis.
- Controller evidence: Deployment conditions and ReplicaSet counts.
- Node evidence: scheduling event, assigned node, image pull, and mount status.
- Process evidence: exit code, previous logs, restart count, probes, and resource events.
- Traffic evidence: Service selector, EndpointSlice readiness, and target-port listener.
Summary#
Kubernetes schedules process groups, not miniature machines. The image defines what to start, the Pod defines what must be colocated, and a controller defines how instances are replaced, completed, or kept present.
- Kubernetes assumes an ordinary application contract: a foreground process, real listener, health behavior, resource demand, network dependencies, and an external durability plan for state that must survive replacement.
kubectl applywrites desired objects through the API server. Controllers, the scheduler, kubelets, and container runtimes perform the later work; an accepted object does not prove a running or reachable application.- An OCI image is layered filesystem content plus launch metadata; a container is a host-kernel process constrained by namespaces, cgroups, mounts, capabilities, and policy.
- A Kubernetes cluster consists of a control plane and worker nodes. Nodes run Pods through kubelet and a container runtime; the control plane stores and reconciles objects rather than directly running application code.
- Kubernetes objects separate identity in metadata, desired state in spec, and observed state in status. Labels and selectors connect controllers, Pods, and Services.
- A Deployment owns ReplicaSets that own replaceable Pods. A Service selects matching endpoints and provides stable discovery; it does not create or roll out those Pods.
- EndpointSlices record current Service backends. Gateway or Ingress routes can place protocol-aware external routing in front of a Service, while the Deployment remains responsible for workload replicas.
- Namespaces scope many object names and policies inside one cluster. They do not create separate control planes, nodes, VPCs, or kernels.
- Tags are movable registry names, while digests identify content. Promote the tested digest and treat signatures, provenance, scanning, and an SBOM as related but separate checks.
- Containers in one Pod share a node, network namespace, and optionally volumes. They do not share filesystems or process namespaces by default.
- Regular init containers finish before application startup. Restartable init containers provide native sidecar lifecycle on supported Kubernetes versions.
- Pods are disposable identities. Put stable network access behind a Service and durable state behind an external store or persistent volume.
- Use Deployments for interchangeable replicas, StatefulSets for ordered identity or per-replica claims, and Jobs or CronJobs for finite work. A DaemonSet derives one instance from each eligible node and consumes capacity there.
- Keep a Kustomize YAML base, an OCI image root filesystem, and an immutable filesystem-template base separate. Patching an identifier into YAML selects bytes stored elsewhere.
- A Deployment rollout begins only when its Pod template changes. A ConfigMap edit alone does not replace Pods unless a version or content hash is part of that template.
- During rollout, maximum Pods equal desired replicas plus max surge; minimum available Pods equal desired replicas minus max unavailable. Readiness, not merely Running state, determines availability.
- Diagnose from controller to process: Deployment conditions, ReplicaSet counts, scheduling and mount events, image pulls, current and previous logs, probe state, Service selectors, and ready EndpointSlices.
References#
- OCI Image Format Specification
- OCI Runtime Specification
- Kubernetes cluster architecture
- Understanding Kubernetes objects
- Kubernetes labels and selectors
- Kubernetes namespaces
- Kubernetes Pods
- Kubernetes init containers
- Kubernetes sidecar containers
- Kubernetes workload management
- Kubernetes Deployments
- Kubernetes DaemonSets
- Kubernetes Services
- Kubernetes EndpointSlices
- Kubernetes Gateway API
- Kubernetes ConfigMaps
- Kubernetes Secrets
- Declarative management with Kustomize