Kubernetes is a control plane + node runtime that converts declarative YAML into real workloads. This page uses an Accordion to explain the most important Kubernetes objects and how they work together, with real-life analogies and production-style YAML examples.
https://app.company.com.
Ingress routes to Service. Service load-balances to Pods created by a Deployment.
ConfigMap holds app config. Secret holds DB password. A PVC stores uploaded files.
A Namespace is a logical partition inside the cluster. It scopes names, helps apply policies/quotas, and prevents “dev” and “prod” resources from colliding.
One cluster hosts multiple teams. Team A deploys into team-a-dev and team-a-prod.
Team B deploys into team-b-dev and team-b-prod.
RBAC can restrict who can change what.
apiVersion: v1
kind: Namespace
metadata:
name: spring-demo
labels:
owner: platform-team
env: dev
A Pod is the basic runtime unit. It groups containers that must share: network identity (same IP/port space) and volumes. Most apps use one container per pod.
A Spring Boot app runs as one container. Optionally a sidecar container ships logs to a central system. Both live in the same Pod so they can share filesystem and localhost network.
apiVersion: v1
kind: Pod
metadata:
name: spring-ui-api
namespace: spring-demo
labels:
app: spring-ui-api
spec:
containers:
- name: app
image: myacr.azurecr.io/spring-ui-api:1.0
ports:
- containerPort: 9087
A Deployment declares “I want N identical Pods” and manages lifecycle: rollout, rollback, scaling, self-healing. If a node dies, pods are rescheduled elsewhere.
You want 2 replicas of your UI/API for availability. During an upgrade, Kubernetes replaces them gradually (rolling update) to avoid downtime.
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-ui-api
namespace: spring-demo
spec:
replicas: 2
selector:
matchLabels:
app: spring-ui-api
template:
metadata:
labels:
app: spring-ui-api
spec:
containers:
- name: app
image: myacr.azurecr.io/spring-ui-api:1.0
ports:
- containerPort: 9087
Pods are ephemeral (IPs change). A Service provides a stable virtual IP/DNS name and load balances traffic to matching Pods using label selectors.
Your Ingress routes to spring-ui-api-svc. The service then distributes traffic across Pod replicas
(replica-1, replica-2, etc.).
apiVersion: v1
kind: Service
metadata:
name: spring-ui-api-svc
namespace: spring-demo
spec:
selector:
app: spring-ui-api
ports:
- name: http
port: 80
targetPort: 9087
type: ClusterIP
Ingress defines L7 rules: hostname and path → service:port. It does not handle traffic by itself; it needs an Ingress Controller.
app.company.com/ routes to your UI/API service, while
app.company.com/api routes to a backend API service.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: spring-ui-api-ingress
namespace: spring-demo
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: app.company.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: spring-ui-api-svc
port:
number: 80
The Ingress Controller watches Ingress objects and programs a real proxy/router to implement them. It is usually deployed as a Deployment + Service (type LoadBalancer).
In AKS, you might use NGINX ingress (in-cluster) or AGIC (Azure Application Gateway fronting AKS).
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
spec:
controller: k8s.io/ingress-nginx
---
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
type: LoadBalancer
selector:
app.kubernetes.io/name: ingress-nginx
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 443
A ConfigMap stores non-sensitive configuration separate from images. Lets you change config without rebuilding containers.
Configure Spring profiles, feature flags, external URLs, or logging levels.
apiVersion: v1
kind: ConfigMap
metadata:
name: spring-ui-api-config
namespace: spring-demo
data:
SPRING_PROFILES_ACTIVE: "aks"
LOGGING_LEVEL_ROOT: "INFO"
APP_WELCOME_MESSAGE: "Hello from Kubernetes!"
A Secret stores sensitive values (passwords, API keys). It can be mounted as files or injected as environment variables.
Database password or OAuth client secret used by your Spring Boot application.
apiVersion: v1
kind: Secret
metadata:
name: spring-ui-api-secret
namespace: spring-demo
type: Opaque
data:
DB_PASSWORD: c3VwZXJzZWNyZXQ= # base64("supersecret")
Pods are ephemeral. A PersistentVolume (PV) represents storage, and a PersistentVolumeClaim (PVC) is a request for that storage. In cloud clusters, PVs are often created dynamically via a StorageClass.
A report generator uploads PDFs and must retain them even if the Pod restarts.
The app mounts a PVC at /data.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: uploads-pvc
namespace: spring-demo
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
name: uploader
namespace: spring-demo
spec:
containers:
- name: app
image: myacr.azurecr.io/uploader:1.0
volumeMounts:
- name: uploads
mountPath: /data
volumes:
- name: uploads
persistentVolumeClaim:
claimName: uploads-pvc
CronJob runs a nightly inventory sync. HPA scales UI pods during Black Friday traffic. RBAC prevents developers from modifying prod namespaces.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: spring-ui-api-hpa
namespace: spring-demo
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: spring-ui-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
namespace: spring-demo
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: job
image: myacr.azurecr.io/report-job:1.0
args: ["--mode=nightly"]