Skip to content

Core Kubernetes objects

Everything in Kubernetes is a resource — a declarative record stored in etcd. You write a YAML manifest describing your desired state, apply it, and controllers work to make reality match. This applies to every object on this page.

A Pod is the smallest deployable unit. It wraps one or more containers that share a network namespace (same IP, same localhost) and can share volumes.

apiVersion: v1
kind: Pod
metadata:
name: my-app
labels:
app: my-app
spec:
containers:
- name: web
image: my-registry/my-app:1.0
ports:
- containerPort: 8080

A Deployment declares how many replicas of a Pod template should run and manages rolling updates when you change the image or config.

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: web
image: my-registry/my-app:1.0
ports:
- containerPort: 8080

Key behaviors:

  • Rolling update — by default, Kubernetes replaces Pods one at a time so there is no downtime.
  • Rollbackkubectl rollout undo deployment/my-app reverts to the previous revision.
  • Scalingkubectl scale deployment/my-app --replicas=4 adjusts the replica count.

The Deployment creates a ReplicaSet under the hood. You interact with the Deployment; the ReplicaSet is an implementation detail.

Pods get ephemeral IPs that change on restart. A Service provides a stable DNS name and IP that routes traffic to a set of Pods selected by label.

apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080

With this Service in place, other Pods in the same namespace can reach your app at http://my-app:80. The Service load-balances across all Pods matching app: my-app.

For more on Service types (ClusterIP, NodePort, LoadBalancer), see Service types.

An Ingress exposes HTTP(S) routes from outside the cluster to Services inside it. It requires an Ingress controller (already running on Kestrel).

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- my-app.<your-tenant>.kestrel.arbutus.cloud
secretName: my-app-tls
rules:
- host: my-app.<your-tenant>.kestrel.arbutus.cloud
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80

For TLS and annotation details on Kestrel, see Ingress and TLS.

┌─────────────────────────────┐
Internet ──▶ │ Ingress │
│ (routes external traffic) │
└──────────┬──────────────────┘
┌──────────▼──────────────────┐
│ Service │
│ (stable name + load balance)│
└──────────┬──────────────────┘
┌─────────────┼─────────────────┐
│ │ │
┌────▼───┐ ┌────▼───┐ ┌────▼───┐
│ Pod (1) │ │ Pod (2) │ │ Pod (n) │
└─────────┘ └─────────┘ └─────────┘
Deployment (manages replicas)
  1. The Deployment ensures the desired number of Pods are running.
  2. The Service gives those Pods a stable network identity.
  3. The Ingress maps an external hostname to the Service.

Labels are key-value pairs on resources. Selectors filter resources by label. This is how a Service finds its Pods and how a Deployment tracks its ReplicaSet.

# On the Pod template
metadata:
labels:
app: my-app
tier: frontend
# On the Service
spec:
selector:
app: my-app