Orchestration & Kubernetes

Core Objects, and Why Each One Exists

Ten object kinds cover almost everything. Learn them as answers to problems — "pod IPs change", "config must differ per environment", "this job must not run twice" — and the API stops being a vocabulary test.

The question this answers

Infrastructure question

Which Kubernetes objects actually matter, and what problem does each one exist to solve?

Application requirement

A team shipping an API, a worker, a nightly report and a database needs to express: how many replicas, how they are reached, where configuration comes from, what runs once, what runs on a schedule, and how two teams share a cluster without colliding.

What it provides

A small, composable vocabulary in which every production concern has a named home — so a new engineer can read a directory of manifests and correctly predict what the cluster will do.

Application RequirementInfrastructure RequirementComputeNetworkStorageIdentityDeploymentScalingReliabilityObservabilitySecurityCostTrade-offs

Each object is an answer to a specific problem

The fastest way to drown in Kubernetes is to learn objects as a list of fields. The fastest way to actually understand them is to ask, for each one, *what breaks without it*. Every object below was added because somebody had a real problem that the existing objects could not express.

Read the middle column first. If the problem in that column is not a problem you have, you do not need the object — and a manifest directory containing objects for problems the team does not have is one of the clearest symptoms of cargo-culted infrastructure.

ObjectThe problem it solvesWhat you would do without it
PodA container needs an address, a lifecycle, and possibly a helper process sharing both.Run a container directly on a host and manage its network by hand.
DeploymentN identical stateless replicas must exist, and be replaced gradually on release.Start and stop replicas manually, and script the rollout order.
ServicePod addresses change on every replacement, so callers cannot hard-code them.Maintain your own service registry and teach every client to read it.
ConfigMapThe same image must behave differently in staging and production.Bake configuration into the image and build one image per environment.
SecretCredentials must reach the workload without living in the image or the repository.Ship credentials in environment files on each host, with no access control around them.
Ingress / GatewayExternal HTTP traffic must reach the right service by host and path, with TLS terminated.Run and configure a reverse proxy yourself, per cluster, per environment.
StatefulSetReplicas need stable identities and their own durable storage — a database, not an API.Run stateful workloads outside the cluster, which is frequently the better answer anyway.
JobA task must run to completion exactly once, and be retried if it fails.Run a container manually and hope nobody starts it twice.
CronJobA Job must run on a schedule without a machine owning the crontab.A crontab on one machine, which is also a single point of failure.
NamespaceTwo teams share a cluster and must not collide on names, quotas or access.One cluster per team, or a naming convention enforced by hope.
The core object kinds. The middle column is the reason each one exists.

How they compose — the smallest realistic set

Kubernetes· Kubernetes 1.29 API groups. Field names move between versions; the object responsibilities do not.

Here is what a plain HTTP service actually needs, with nothing added for style. A Deployment declares the replicas. A Service gives them one stable name. A ConfigMap supplies the environment-specific values, and a Secret supplies the credential. That is the whole set — four objects — and it is a complete, defensible production deployment.

Note what the YAML is doing and what it is not. It is *stating desired state*; it is not a sequence of commands, and reading it top to bottom is not reading an execution order. The selector is the load-bearing part and the part most often wrong: the Service finds pods by label, not by belonging to the Deployment, so a mismatched label produces a Service that resolves to nothing while every pod is perfectly healthy.

1apiVersion: apps/v1
2kind: Deployment
3metadata: { name: api }
4spec:
5 replicas: 3 # the declared count the controller keeps true
6 selector:
7 matchLabels: { app: api } # which pods this Deployment owns
8 template:
9 metadata:
10 labels: { app: api } # must match the selector above AND the Service below
11 spec:
12 containers:
13 - name: api
14 image: registry.example/api:v7 # a tag here; a digest is safer — see container-registry
15 envFrom:
16 - configMapRef: { name: api-config }
17 - secretRef: { name: api-db } # a Secret object is NOT encrypted at rest by default
18 resources:
19 requests: { cpu: 250m, memory: 512Mi } # what the scheduler reserves
20 limits: { memory: 512Mi } # exceed this and the container is killed
21 readinessProbe:
22 httpGet: { path: /healthz, port: 8080 }
23---
24apiVersion: v1
25kind: Service
26metadata: { name: api }
27spec:
28 selector: { app: api } # matches pods by label — a typo here yields an empty Service
29 ports:
30 - port: 80
31 targetPort: 8080
A complete four-object service. Illustration of the concepts above, not a template to copy blindly.

The objects a new cluster does not need

The failure mode of this vocabulary is enthusiasm. A team's first service arrives with a HorizontalPodAutoscaler that has never been triggered, a PodDisruptionBudget that blocks node drains, a NetworkPolicy nobody can read, a ServiceMonitor pointing at a metrics endpoint that does not exist, and a custom resource for a mesh whose sidecar doubles cold-start time. Each was added because a blog post had it.

The rule that holds up: add an object when you can name the incident it prevents. An HPA is right once you have measured a traffic pattern that needs it — see Horizontal Pod Autoscaling — and Why New Capacity Is Always Late. A PodDisruptionBudget is right once node drains have actually caused a capacity dip. Until then it is a config file that will page someone at an inconvenient moment.

Week one, twelve objects, no measurements behind any of them
# api/
#   deployment.yaml          replicas: 3
#   service.yaml
#   configmap.yaml
#   secret.yaml
#   hpa.yaml                 target 60% CPU — CPU has never exceeded 12%
#   pdb.yaml                 minAvailable: 3 of 3 — silently blocks every node drain
#   networkpolicy.yaml       copied from a blog; nobody can say what it denies
#   servicemonitor.yaml      scrapes /metrics — the app does not expose /metrics
#   virtualservice.yaml      service mesh, for four services
#   destinationrule.yaml
#   certificate.yaml
#   ingress.yaml
Week one, four objects, each traceable to a requirement
# api/
#   deployment.yaml          replicas: 3   -> survives one node loss
#   service.yaml                           -> stable name for changing pod IPs
#   configmap.yaml                         -> same image, different environments
#   secret.yaml                            -> DB credential out of the image
#
# ingress.yaml added when the service must be reachable from outside.
# hpa.yaml added when a measured traffic pattern justifies it.
# pdb.yaml added after the first node drain caused a real capacity dip.

Twelve objects are not more production-ready than four; they are eight more things to debug at 03:00. The minAvailable: 3 of 3 budget in the first version is a genuine landmine — it makes every node drain hang forever.

Key points

  • Learn each object as the answer to a problem: pod IPs change (Service), config differs per environment (ConfigMap), a task must run once (Job), two teams share a cluster (Namespace).
  • A complete production service is often four objects — Deployment, Service, ConfigMap, Secret — and adding more is a decision that needs a reason.
  • Services find pods by label selector, not by ownership; a label typo produces a healthy Deployment behind a Service that routes to nothing.
  • A Secret object is a separate kind mainly for access control and handling, not because it is encrypted — see ConfigMap vs Secret — and the Honest Limit of a Secret.
  • Add an object when you can name the incident it prevents. A PodDisruptionBudget of 3-of-3 blocks node drains and will page someone.

The loop, answered

Every field is required, which is why no lesson here can recommend something without saying what it costs and what simpler thing to consider first.

How it works
  • Every object is a record in the cluster datastore with an apiVersion, a kind, metadata including labels, a spec (desired) and a status (actual).
  • Higher-level objects create lower-level ones: a Deployment creates a replica set, which creates pods; a CronJob creates Jobs, which create pods.
  • Objects are wired to each other by label selectors rather than by references, which is why labels are load-bearing configuration rather than documentation.
  • Each object kind has a controller responsible for reconciling its spec against its status.
  • Namespaces scope names, quotas and access; cluster-scoped objects such as nodes and storage classes sit outside them.
What you still own
  • The manifests themselves: they are source code, they belong in review, and they are production access — see Infrastructure as Code.
  • Label discipline, because selectors are how everything finds everything else and a rename is a routing change.
  • API version migrations: deprecated object versions are removed on a schedule, and an upgrade will refuse manifests that were fine last year.
  • Object sprawl — every object is reconciled forever, so unused ones cost control-plane work and reviewer attention.
How it fails
  • Selector mismatch: the Service has no endpoints, requests fail immediately, and every pod reports perfectly healthy.
  • A ConfigMap or Secret referenced but not created: pods sit in CreateContainerConfigError and never start.
  • A CronJob with a long-running Job and no concurrency policy: overlapping runs pile up and duplicate work.
  • A restrictive PodDisruptionBudget: node drains hang indefinitely, and cluster upgrades stall with no obvious cause.
  • A deprecated API version after an upgrade: manifests that deployed last month are rejected outright.
How it scales
  • Object count, not traffic, is what pressures the control plane — CI pipelines that create and destroy namespaces per branch are a common surprise.
  • Label cardinality matters for selector evaluation and much more for the monitoring system reading those labels.
  • Namespaces scale organizationally, not technically; the limit you hit is human ownership rather than any cluster ceiling.
Security
  • RBAC is expressed per object kind and namespace, so the object model is also the permission model — read access to Secrets is the one to guard hardest.
  • Namespaces are a name and policy boundary, not a hard security boundary; workloads in different namespaces share nodes and a network unless you add policy.
  • Manifests carry image references, so supply-chain control lives here too — pin by digest rather than by tag, see The Container Registry.
  • Custom resources extend the API and their controllers usually run with broad permissions; adding one is adding a privileged component.
Cost shape
  • Objects themselves cost control-plane storage and reconciliation, which is small until object churn is large.
  • The expensive objects are the ones that provision infrastructure: a Service of load-balancer type creates a real, billed load balancer per Service.
  • Persistent volume claims allocate real disks that keep billing after the workload that used them is deleted — a classic orphaned-cost source.
What to watch
  • Endpoint counts per Service — the direct check that selectors match something.
  • Object events, which are where the cluster explains its refusals in plain language and are the most under-read signal in Kubernetes.
  • Deprecated API usage warnings, which are an upgrade blocker you want to see months early.
  • The signal that lies: kubectl get pods showing everything Running. Running says the process started, not that the Service routes to it or that it can serve.
Simpler alternatives
  • A compose file, if the whole system is a handful of containers on one host — it expresses the same four concerns in a tenth of the text.
  • A managed container service where the platform's task definition covers the Deployment, Service and configuration concerns in a single object.
  • A PaaS manifest that reduces all of this to a process list and a set of environment variables.
  • Templating or packaging tools only after the raw objects are understood; a chart that hides an object you cannot debug is a liability, not a simplification.
What adopting this costs
  • Buys a precise vocabulary for every production concern; costs a genuinely large surface area that every engineer must learn to be productive.
  • Buys composability through label selectors; costs a class of silent misconfiguration where everything is healthy and nothing is connected.
  • Buys declarative review of infrastructure changes; costs verbosity — four objects and eighty lines for what a PaaS expresses in five.

What people believe, and what is true

Claim

A Service knows which Deployment it belongs to.

Reality

It knows a label selector. It routes to any pod carrying those labels, regardless of what created the pod — which is both a feature and a common outage.

Claim

Secrets are encrypted, that is why they are a separate kind.

Reality

By default they are base64-encoded in the datastore. The separate kind buys different RBAC and different handling, not encryption.

Claim

More objects means a more production-ready deployment.

Reality

Every object is a thing that can be misconfigured. An HPA that never triggers and a PDB that blocks drains are net negatives.

Apply it