Pods, deployments, services, ingress, storage, namespaces, and Helm — the full picture of container orchestration at scale.
Compose runs on one host. Kubernetes spans a cluster of machines.
The control plane makes decisions. Worker nodes run your workloads.
A pod wraps one or more containers that share a network and storage namespace.
# pod.yaml apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: app image: myapp:1.0 ports: - containerPort: 8000 resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "256Mi" - name: log-shipper image: fluent-bit:3
Never deploy a bare Pod in production. Pods don't self-heal. Use a Deployment — it recreates pods automatically.
A Deployment manages a ReplicaSet, which keeps N identical pods running at all times.
# deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: replicas: 3 selector: matchLabels: app: myapp strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: metadata: labels: app: myapp spec: containers: - name: app image: myapp:1.0 ports: - containerPort: 8000
# deploy / update kubectl apply -f deploy.yaml # watch rollout kubectl rollout status deploy/myapp # rollback kubectl rollout undo deploy/myapp
Pods come and go. A Service gives them a stable DNS name and load-balances across healthy pods.
One external LoadBalancer, many backend services. Routes traffic by hostname and path.
Decouple configuration from your container image. Never bake credentials into an image.
# configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: app-config data: LOG_LEVEL: "info" MAX_CONNECTIONS: "100" app.conf: | server.port=8000 cache.ttl=300 # secret.yaml (values are base64-encoded) apiVersion: v1 kind: Secret metadata: name: app-secrets type: Opaque stringData: # kubectl encodes for you DATABASE_URL: "postgres://user:pass@db/mydb" API_KEY: "s3cr3t-k3y-here" # pod consuming both spec: containers: - name: app envFrom: - configMapRef: name: app-config - secretRef: name: app-secrets volumeMounts: - name: config-vol mountPath: /etc/app volumes: - name: config-vol configMap: name: app-config
Base64 is encoding, not encryption. Use Sealed Secrets, Vault, or an external secrets operator for production.
Storage in Kubernetes is decoupled from pods via a claim-and-bind model.
Virtual clusters within a cluster. Divide teams, environments, or tenants without separate clusters.
# create namespace kubectl create ns production # deploy into a namespace kubectl apply -f app.yaml -n production # list pods in namespace kubectl get pods -n production # switch default namespace kubectl config set-context --current \ --namespace=production # all namespaces at once kubectl get pods -A
kind: ResourceQuota spec: hard: requests.cpu: "4" requests.memory: 8Gi pods: "20"
default — where you land without -nkube-system — system componentskube-public — readable by allCharts are parameterised YAML templates. One chart, many environments, one command to deploy.
# Chart structure mychart/ Chart.yaml # name, version, description values.yaml # default values (override these) templates/ deployment.yaml # uses {{ .Values.* }} service.yaml ingress.yaml _helpers.tpl # named templates # values.yaml (defaults) replicaCount: 1 image: repository: myapp tag: "1.0" service: port: 8000 ingress: enabled: true host: app.example.com resources: limits: cpu: 500m memory: 256Mi # template snippet (deployment.yaml) spec: replicas: {{ .Values.replicaCount }} template: spec: containers: - image: {{ .Values.image.repository }}: {{- .Values.image.tag }}
# install from chart repo helm repo add bitnami https://charts.bitnami.com/bitnami helm install mydb bitnami/postgresql # install with custom values helm install myapp ./mychart \ --namespace prod \ -f prod-values.yaml # upgrade (rolling update) helm upgrade myapp ./mychart --set image.tag=2.0 # rollback helm rollback myapp 1 # list installed releases helm list -n prod
Helm 3 removed Tiller (the server component). No special cluster permissions needed. Just brew install helm and go.
The essential commands for day-to-day cluster work.
# ── Apply & Delete ────────────────── kubectl apply -f manifest.yaml kubectl delete -f manifest.yaml kubectl delete pod mypod --grace-period=0 # ── Inspect ───────────────────────── kubectl get pods -n prod -o wide kubectl get all -n prod kubectl describe pod <name> kubectl describe node <name> # ── Logs & Exec ───────────────────── kubectl logs -f deploy/myapp kubectl logs mypod -c sidecar # specific container kubectl exec -it mypod -- /bin/sh # ── Port Forwarding ───────────────── kubectl port-forward svc/myapp 8080:80 kubectl port-forward pod/mypod 5432:5432 # ── Scaling ───────────────────────── kubectl scale deploy/myapp --replicas=5 kubectl autoscale deploy/myapp \ --min=2 --max=10 --cpu-percent=70
# ── Rollouts ──────────────────────── kubectl set image deploy/myapp app=myapp:2.0 kubectl rollout status deploy/myapp kubectl rollout history deploy/myapp kubectl rollout undo deploy/myapp kubectl rollout undo deploy/myapp --to-revision=2 # ── Config & Secrets ──────────────── kubectl create configmap app-cfg \ --from-file=config.json kubectl create secret generic mysecret \ --from-literal=password=supersecret # ── Context & Namespace ───────────── kubectl config get-contexts kubectl config use-context my-cluster kubectl config set-context --current \ --namespace=production # ── Useful flags ──────────────────── -n <ns> namespace -o yaml full YAML output -o jsonpath=… extract a field --dry-run=client validate without applying -l app=myapp filter by label
The complete picture — from single pods to production-grade orchestration.
Package manager for Kubernetes. One chart → parameterised YAML → helm install myapp ./chart -f prod-values.yaml. Atomic upgrade + rollback built in.
You declare the desired state in YAML. The control plane reconciles reality to match it — forever. Nodes fail, pods die, the scheduler replaces them. You never SSH in.