Container Orchestration (Kubernetes)Container Orchestration (Kubernetes)
Thuộc bộ tài liệu kiến thức theo DevOps Roadmap.
Tổng quan
Chạy một container thì dễ; chạy hàng trăm container trên cả một cụm máy thì không. Container orchestration tự động hóa khối lượng công việc vận hành xuất hiện khi hệ thống lớn lên: lập lịch (schedule) container lên các node khỏe mạnh, restart khi chúng crash, scale số replica theo tải, rollout phiên bản mới không downtime, cung cấp service discovery và load balancing, gắn storage và cấu hình. Kubernetes (K8s) là orchestrator tiêu chuẩn de facto — hệ thống mã nguồn mở được thiết kế ban đầu tại Google (kế thừa hệ thống nội bộ Borg) và hiện do CNCF quản lý.
Kubernetes xoay quanh một ý tưởng mạnh mẽ: trạng thái mong muốn khai báo (declarative desired state) kết hợp reconciliation. Bạn khai báo điều mình muốn (“3 replica của image này, mở port 80”) trong manifest YAML; các controller liên tục so sánh trạng thái thực tế với trạng thái mong muốn và hành động để đưa chúng về khớp nhau. Chính mô hình này tạo ra khả năng self-healing, autoscaling, và sau này là GitOps. Bạn mô tả điểm đến, không phải từng bước rẽ.
Với kỹ sư DevOps, Kubernetes là tầng platform mà hầu hết pipeline giao phần mềm hiện đại nhắm tới. Hiểu kiến trúc và các object cốt lõi của nó là kiến thức tiên quyết cho GitOps, service mesh, observability và thiết kế cloud-native. Đây cũng là một hệ thống lớn với đường học dốc — phần thưởng là một nền tảng thống nhất, điều khiển qua API, hoạt động y hệt nhau trên mọi cloud hay on-prem.
Một mô hình tư duy
Kubernetes được hiểu tốt nhất như một cỗ máy control loop. Mọi thứ đều là object lưu trong etcd; mỗi object có một spec (điều bạn muốn) và một status (điều đang có). Các controller theo dõi object và làm cho status khớp spec. kubectl apply không “làm” gì theo kiểu mệnh lệnh — nó ghi lại ý định của bạn, và controller biến nó thành hiện thực. Thấm được điều này thì phần lớn hành vi của Kubernetes (self-healing, rollout, autoscaling, GitOps) hết còn là phép màu và trở nên hiển nhiên.
Kiến thức nền tảng
Vì sao cần orchestration
Không có orchestrator, bạn phải tự làm thủ công: quyết định đặt container ở đâu, khôi phục khi lỗi, rolling deploy, service discovery, phân phối secret, quản lý cấu hình và scale ngang — thường bằng những script dễ vỡ. Orchestrator cung cấp tất cả dưới dạng các primitive của platform, điều khiển qua API, nên mối lo vận hành trở thành cấu hình khai báo thay vì automation tự chế.
Kiến trúc Kubernetes
- Control Planekube-apiserveretcdkho trạng tháikube-schedulerkube-controller-manager(cloud-controller-manager)
- (API)Nodekubelet, kube-proxy, containerdNodekubelet, kube-proxy, containerdNodekubelet, kube-proxy, containerd
Thành phần control plane:
- kube-apiserver — “cửa chính” của cluster; mọi thao tác đọc/ghi (kubectl, controller, kubelet) đều đi qua REST API này. Xử lý authentication, authorization (RBAC) và admission control (validating/mutating webhook). Đây là thành phần duy nhất nói chuyện với etcd.
- etcd — kho key-value phân tán, nhất quán (đồng thuận Raft), lưu toàn bộ trạng thái cluster. Phải backup thường xuyên; mất etcd là mất cluster. Chạy với số member lẻ (3 hoặc 5) để có quorum.
- kube-scheduler — gán Pod cho node dựa trên resource requests, node affinity/anti-affinity, Pod affinity/anti-affinity, taints/tolerations và topology spread constraints. Nó chỉ quyết định ở đâu; kubelet mới là bên chạy.
- kube-controller-manager — chạy các vòng lặp reconciliation có sẵn (controller cho Deployment, ReplicaSet, Node, Job, EndpointSlice…) đưa trạng thái thực tế về trạng thái mong muốn.
- cloud-controller-manager — tích hợp với cloud provider cho Service loại LoadBalancer, vòng đời node và cấp phát volume.
Thành phần trên node:
- kubelet — agent trên mỗi node; theo dõi API để biết Pod nào được gán cho node mình, yêu cầu container runtime chạy chúng, mount volume, báo cáo trạng thái và thực thi liveness/readiness/startup probe.
- kube-proxy — lập trình các rule iptables/IPVS (hoặc eBPF với Cilium) để virtual IP của Service load-balance tới các Pod backend. Một số CNI (Cilium) có thể thay thế hoàn toàn kube-proxy.
- Container runtime — containerd hoặc CRI-O, giao tiếp qua CRI (Container Runtime Interface). Docker/dockershim đã bị gỡ ở v1.24.
- CNI plugin — Calico, Cilium, Flannel… hiện thực networking cho Pod để mỗi Pod có một IP định tuyến được và các Pod có thể liên lạc phẳng giữa các node.
Mô hình reconciliation
Mọi controller đều chạy cùng một vòng lặp: quan sát → so sánh (mong muốn vs thực tế) → hành động. Xóa một Pod thuộc Deployment thì trong vài giây sẽ có Pod thay thế — không phải vì có gì đó “phát hiện việc xóa”, mà vì ReplicaSet controller thấy thực tế (2) ≠ mong muốn (3) và tạo thêm một Pod. Thiết kế level-triggered (chứ không phải edge-triggered) này là lý do Kubernetes phục hồi được sau khi bỏ lỡ sự kiện, sau restart và lỗi cục bộ: nó luôn suy ra lại hành động đúng từ trạng thái hiện tại.
Object, controller và API
- API được nhóm và có phiên bản:
apps/v1(Deployment),batch/v1(Job),networking.k8s.io/v1(Ingress), corev1(Pod, Service). API trưởng thành theoalpha → beta → stablevà các version deprecated cuối cùng bị gỡ — một mối lo upgrade lặp lại. - Label và selector là chất keo: Service tìm Pod của nó qua label selector, Deployment sở hữu các Pod có label khớp selector của nó. Label là cách “loose coupling” hoạt động.
- CRD (Custom Resource Definition) cho phép mở rộng API bằng các loại object của riêng bạn; kết hợp với một controller tùy biến (operator pattern), chúng cho phép tự động hóa các app stateful (database, chứng chỉ) theo đúng cách khai báo.
Khái niệm chính
Các object cốt lõi
| Object | Mục đích |
|---|---|
| Pod | Đơn vị deploy nhỏ nhất: một hoặc nhiều container dùng chung network namespace, IP và volume. Có thể có init container và sidecar |
| ReplicaSet | Duy trì N Pod giống hệt nhau (do Deployment quản lý; hiếm khi dùng trực tiếp) |
| Deployment | Quản lý ReplicaSet; rolling update và rollback cho ứng dụng stateless |
| StatefulSet | Danh tính mạng ổn định + rollout có thứ tự + storage riêng cho từng replica (database, Kafka, Zookeeper) |
| DaemonSet | Mỗi node một Pod (log shipper, node agent, CNI, CSI driver) |
| Job / CronJob | Tác vụ chạy-đến-khi-xong / tác vụ theo lịch |
| Service | Virtual IP + tên DNS ổn định, load-balance tới các Pod (ClusterIP, NodePort, LoadBalancer, ExternalName) |
| Ingress | Định tuyến HTTP(S) từ bên ngoài vào Service (rule theo host/path, kết thúc TLS) — cần ingress controller |
| Gateway API | Kế thừa của Ingress: định tuyến phong phú hơn, theo vai trò (Gateway, HTTPRoute) cho HTTP/TCP/gRPC |
| ConfigMap / Secret | Cấu hình không nhạy cảm / dữ liệu nhạy cảm, inject qua env var hoặc mount thành file |
| PV / PVC | PersistentVolume (storage) được workload yêu cầu qua PersistentVolumeClaim; cấp phát động qua StorageClass (CSI) |
| Namespace | Phân vùng cluster ảo để cô lập, đặt quota và giới hạn phạm vi RBAC |
| ServiceAccount | Danh tính để Pod xác thực với API server và (qua IRSA/Workload Identity) với API của cloud |
Giải mã các loại Service
- ClusterIP (mặc định) — virtual IP nội bộ ổn định, chỉ truy cập được bên trong cluster.
- NodePort — mở Service trên một port tĩnh của mọi node; truy cập từ ngoài kiểu thô sơ.
- LoadBalancer — cấp phát một load balancer của cloud trỏ tới Service (qua cloud-controller-manager).
- ExternalName — một CNAME DNS trỏ tới hostname bên ngoài, không proxy.
- Headless (
clusterIP: None) — không có virtual IP; DNS trả về trực tiếp các Pod IP, điều StatefulSet dùng để địa chỉ hóa ổn định từng replica.
Ví dụ manifest
Một Deployment có probe, resource limits và security context, kèm Service và Ingress:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels: { app: web }
spec:
replicas: 3
selector:
matchLabels: { app: web }
strategy:
type: RollingUpdate
rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
template:
metadata:
labels: { app: web }
spec:
securityContext:
runAsNonRoot: true
seccompProfile: { type: RuntimeDefault }
containers:
- name: web
image: ghcr.io/example/web:1.4.2 # tag bất biến, không bao giờ :latest
ports: [{ containerPort: 8080 }]
envFrom:
- configMapRef: { name: web-config }
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 256Mi }
readinessProbe:
httpGet: { path: /healthz, port: 8080 }
livenessProbe:
httpGet: { path: /livez, port: 8080 }
initialDelaySeconds: 10
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector: { app: web }
ports: [{ port: 80, targetPort: 8080 }]
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
ingressClassName: nginx
rules:
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service: { name: web, port: { number: 80 } }
Các lệnh dùng hằng ngày:
kubectl apply -f app.yaml
kubectl get pods -o wide
kubectl describe pod web-6d4c... # xem events: vì sao Pending/CrashLooping?
kubectl logs -f deploy/web
kubectl logs web-6d4c... --previous # log của container crash lần trước
kubectl exec -it web-6d4c... -- sh # mở shell vào container
kubectl rollout status deploy/web
kubectl rollout undo deploy/web # rollback về ReplicaSet trước
kubectl get events --sort-by=.lastTimestamp
kubectl top pods # CPU/memory theo thời gian thực (cần metrics-server)
Probe: readiness vs liveness vs startup
- readinessProbe — “Pod này có phục vụ traffic được ngay bây giờ không?” Probe fail sẽ gỡ Pod khỏi endpoint của Service mà không giết nó. Đây là thứ chặn traffic trong lúc rollout và khởi động nóng.
- livenessProbe — “tiến trình này có bị treo và không tự hồi phục không?” Probe fail sẽ restart container. Hãy trỏ nó vào thứ rẻ và độc lập — đừng bao giờ trỏ vào database hay dependency phía dưới, nếu không một sự cố dependency sẽ gây bão restart toàn cluster.
- startupProbe — bảo vệ các app khởi động chậm: liveness/readiness chưa bắt đầu cho tới khi startup xanh, nên khởi động chậm không bị nhầm là treo.
Điều khiển scheduling
- Resource requests/limits — requests đặt chỗ tài nguyên và quyết định scheduling; limits chặn mức dùng. Vượt memory limit thì container bị OOM-kill; vượt CPU limit thì bị throttle.
- Taints và tolerations — taint trên node đẩy Pod ra trừ khi Pod tolerate nó (ví dụ dành riêng node GPU).
- Affinity/anti-affinity — hút hoặc đẩy Pod so với node hoặc Pod khác (ví dụ trải replica qua nhiều zone).
- Topology spread constraints — phân bố Pod đều qua các failure domain (zone, node).
- PriorityClass — Pod ưu tiên cao có thể preempt Pod ưu tiên thấp khi thiếu chỗ.
Scaling
- HPA (HorizontalPodAutoscaler) — thêm/bớt replica dựa trên CPU/memory (qua metrics-server) hoặc custom/external metric (qua Prometheus Adapter hoặc KEDA):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: web }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: web }
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 70 }
- VPA (VerticalPodAutoscaler) — điều chỉnh requests CPU/memory của Pod thay vì số replica; hữu ích để right-size. Đừng chạy VPA và HPA trên cùng một metric CPU/memory — chúng sẽ “đánh nhau”.
- KEDA — autoscaling theo sự kiện từ độ sâu queue, độ trễ Kafka, cron…, kể cả scale-to-zero.
- Cluster Autoscaler / Karpenter — scale chính các node khi Pod không thể được lập lịch (Karpenter cấp phát node đúng kích cỡ just-in-time trên AWS).
Vòng đời cấu hình và storage
- ConfigMap/Secret inject qua env var được chốt lúc Pod khởi động — đổi chúng không restart Pod; cấu hình mount thành volume có thể cập nhật tại chỗ nhưng app phải tự đọc lại. Công cụ như Reloader hoặc một annotation hash cấu hình sẽ buộc rollout khi thay đổi.
- Secret mặc định chỉ được base64-encode khi lưu; hãy bật encryption at rest (provider KMS) và siết RBAC, hoặc dùng secret manager bên ngoài (xem GitOps).
- PVC → PV → StorageClass — PVC yêu cầu storage; StorageClass cấp phát động một PV khớp qua CSI driver.
accessModes(RWO/ROX/RWX) vàreclaimPolicy(Retain/Delete) rất quan trọng cho an toàn dữ liệu.
Helm và Kustomize
Helm là trình quản lý gói của Kubernetes: template hóa một bộ manifest (chart) với values theo từng môi trường, và theo dõi mỗi lần cài đặt như một release có phiên bản, rollback được.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-redis bitnami/redis -f values-prod.yaml
helm upgrade my-redis bitnami/redis --set architecture=replication
helm rollback my-redis 1
helm template my-redis bitnami/redis -f values-prod.yaml # render local để kiểm tra/validate trong CI
Kustomize (tích hợp sẵn trong kubectl, kubectl apply -k) đi theo hướng ngược lại: overlay dạng patch, không có ngôn ngữ template. Một base/ giữ manifest chung; các overlays/dev|staging|prod/ patch image, số replica và env. Nguyên tắc: Helm để đóng gói/phân phối app tái sử dụng với nhiều tùy chọn; Kustomize để quản lý manifest của chính bạn qua nhiều môi trường mà không rối template. Chúng kết hợp được — Helm có thể render vào Kustomize.
Kubernetes managed và các lựa chọn khác
| Dịch vụ | Nhà cung cấp | Ghi chú |
|---|---|---|
| EKS | AWS | Control plane được quản lý; node qua managed node group, Fargate hoặc Karpenter; auth IAM qua IRSA/EKS Pod Identity |
| GKE | Google Cloud | Tự động hóa cao nhất; chế độ Autopilot quản lý node hoàn toàn và tính tiền theo Pod |
| AKS | Azure | Có tier control plane miễn phí; tích hợp sâu Entra ID và Workload Identity |
Dịch vụ managed vận hành và vá control plane cùng etcd thay bạn — bạn vẫn chịu trách nhiệm về workload, lịch upgrade cho node pool và cấu hình node. Các lựa chọn nhẹ hơn Kubernetes đầy đủ: Docker Swarm (đơn giản, tích hợp trong Docker, đang giảm phổ biến) và HashiCorp Nomad (scheduler đơn giản, linh hoạt, chạy được cả workload không phải container như binary thô và VM). Cho môi trường local và CI: kind (K8s trong Docker), minikube và k3s/k3d (nhẹ, thân thiện edge).
Nền tảng bảo mật
- RBAC — cấp quyền tối thiểu qua Role/ClusterRole gắn với user, group hoặc ServiceAccount. Tránh binding
cluster-admin. - Pod Security Admission — cưỡng chế chuẩn
baseline/restrictedtheo từng namespace để chặn Pod privileged, host mount và root. - NetworkPolicy — mạng cluster mặc định phẳng và default-allow; áp default-deny và chỉ mở những luồng cần thiết.
- Policy engine — Kyverno hoặc OPA Gatekeeper cưỡng chế quy tắc tổ chức ở admission (cấm
:latest, bắt buộc resource limits, chặn hostPath…). - Nguồn gốc image — xác minh chữ ký (cosign) và quét image trước khi chúng chạy.
Best Practices
- Luôn đặt resource requests và limits — requests quyết định scheduling và autoscaling; thiếu limits thì một Pod có thể “bóp nghẹt” cả node, còn thiếu requests thì scheduler bị “mù”.
- Khai báo readiness, liveness và startup probe — readiness chặn traffic trong lúc rollout; liveness restart tiến trình bị treo; startup lo cho khởi động chậm. Đừng bao giờ trỏ liveness vào dependency — gây bão restart dây chuyền.
- Không bao giờ dùng tag
latest— deploy tag image bất biến, có phiên bản (hoặc digest) để rollout, rollback và audit có ý nghĩa và tái lập được. - Dùng namespace với ResourceQuota, LimitRange và RBAC — cô lập team/môi trường, giới hạn phạm vi ảnh hưởng và đặt giá trị mặc định hợp lý.
- Chạy tối thiểu 2–3 replica kèm PodDisruptionBudget — sống sót qua node drain, autoscaling và gián đoạn chủ động khi upgrade mà không tụt về 0.
- Lưu manifest trong Git và apply theo kiểu khai báo —
kubectl apply/GitOps, không bao giờkubectl edittrực tiếp trên production; cluster phải phản ánh một nguồn chân lý đã commit. - Đặt cấu hình trong ConfigMap/Secret, không nhúng vào image — cùng một image chạy dev/staging/prod, chỉ khác config; roll Pod khi config đổi.
- Siết chặt Pod security — chạy non-root, drop toàn bộ capabilities, đặt
readOnlyRootFilesystemvàallowPrivilegeEscalation: false, và cưỡng chế bằng Pod Security Admission (baseline/restricted). - Áp dụng NetworkPolicy default-deny — mạng Kubernetes mặc định phẳng và mở hoàn toàn; phân đoạn namespace và chỉ mở đúng luồng cần.
- Right-size dựa trên dữ liệu — dùng metrics/khuyến nghị của VPA thay vì đoán requests; over-provisioning là nguyên nhân đội chi phí số một, under-provisioning gây eviction và throttle.
- Trải replica qua nhiều failure domain — dùng topology spread constraints hoặc anti-affinity để mất một node/zone không kéo sập cả service.
- Lên kế hoạch upgrade liên tục — Kubernetes phát hành ~3 lần mỗi năm và API bị gỡ bỏ dần; kiểm thử version skew và API deprecated (cảnh báo của
kubectl,pluto) ở staging trước khi upgrade. - Backup etcd và persistent volume — dùng Velero hoặc snapshot của cloud provider; kiểm thử cả việc restore, không chỉ backup, và diễn tập khôi phục cluster.
- Cưỡng chế policy ở admission — Kyverno/Gatekeeper để bắt buộc limits, cấm
:latestvà Pod privileged, bắt buộc label, để sai sót bị từ chối trước khi chạy. - Cấu hình graceful shutdown đúng — xử lý
SIGTERM, tinh chỉnhterminationGracePeriodSecondsvà dùng hookpreStopđể request đang xử lý được drain trong lúc rollout. - Ưu tiên Deployment cho stateless và StatefulSet cho stateful — đừng ép database vào Deployment; dùng StatefulSet (hoặc một operator) để có danh tính và storage ổn định.
Tài liệu tham khảo
- Tài liệu Kubernetes
- Kubernetes components
- Kubernetes concepts — Workloads
- Cấu hình liveness, readiness và startup probe
- Pod Security Standards
- Tài liệu Helm · Kustomize
- Gateway API
- KEDA · Karpenter · Cluster Autoscaler
- Kyverno · OPA Gatekeeper · Velero
- Amazon EKS · Google GKE · Azure AKS
- CNCF landscape
- Sách: Kubernetes Up & Running (Hightower, Burns, Beda — O’Reilly)
- roadmap.sh — DevOps: Container Orchestration
Part of the DevOps Roadmap knowledge base.
Overview
Running one container is easy; running hundreds across a fleet of machines is not. Container orchestration automates the operational work that appears at scale: scheduling containers onto healthy nodes, restarting them when they crash, scaling replicas with load, rolling out new versions without downtime, wiring up service discovery and load balancing, and attaching storage and configuration. Kubernetes (K8s) is the de facto standard orchestrator — an open-source system originally designed at Google (descended from its internal Borg system) and now governed by the CNCF.
Kubernetes is built around a powerful idea: declarative desired state with reconciliation. You declare what you want (“3 replicas of this image, exposed on port 80”) in YAML manifests; controllers continuously compare actual state against desired state and take action to converge them. This model is what makes self-healing, autoscaling, and later GitOps possible. You describe the destination, not the turn-by-turn directions.
For DevOps engineers, Kubernetes is the platform layer most modern delivery pipelines target. Understanding its architecture and core objects is prerequisite knowledge for GitOps, service meshes, observability, and cloud-native design. It is also a large system with a steep learning curve — the payoff is a uniform, API-driven substrate that works the same on any cloud or on-prem.
A mental model
Kubernetes is best understood as a control loop machine. Everything is an object stored in etcd; every object has a spec (what you want) and a status (what is). Controllers watch objects and work to make status match spec. kubectl apply does not “do” anything imperatively — it records your intent, and controllers make it real. Internalize this and most of Kubernetes’ behavior (self-healing, rollouts, autoscaling, GitOps) stops being magic and becomes obvious.
Fundamentals
Why orchestration
Without an orchestrator you must hand-roll: placement decisions, failure recovery, rolling deploys, service discovery, secret distribution, config management, and horizontal scaling — usually with brittle scripts. Orchestrators provide all of it as platform primitives, driven by APIs, so the operational concerns become declarative configuration rather than bespoke automation.
Kubernetes architecture
- Control Planekube-apiserveretcdstate storekube-schedulerkube-controller-manager(cloud-controller-manager)
- (API)Nodekubelet, kube-proxy, containerdNodekubelet, kube-proxy, containerdNodekubelet, kube-proxy, containerd
Control plane components:
- kube-apiserver — the front door; every read/write (kubectl, controllers, kubelets) goes through this REST API. Handles authentication, authorization (RBAC), and admission control (validating/mutating webhooks). It is the only component that talks to etcd.
- etcd — distributed, consistent key-value store (Raft consensus) holding the entire cluster state. Back it up; losing etcd means losing the cluster. Run it with an odd number of members (3 or 5) for quorum.
- kube-scheduler — assigns Pods to nodes based on resource requests, node affinity/anti-affinity, Pod affinity/anti-affinity, taints/tolerations, and topology spread constraints. It only decides where; the kubelet does the running.
- kube-controller-manager — runs the built-in reconciliation loops (Deployment, ReplicaSet, Node, Job, EndpointSlice controllers…) that drive actual state toward desired state.
- cloud-controller-manager — integrates with the cloud provider for LoadBalancer services, node lifecycle, and volume provisioning.
Node components:
- kubelet — agent on each node; watches the API for Pods assigned to its node, tells the container runtime to run them, mounts volumes, reports status, and executes liveness/readiness/startup probes.
- kube-proxy — programs iptables/IPVS (or eBPF, with Cilium) rules so Service virtual IPs load-balance to backend Pods. Some CNIs (Cilium) can replace kube-proxy entirely.
- Container runtime — containerd or CRI-O, spoken to via the CRI (Container Runtime Interface). Docker/dockershim was removed in v1.24.
- CNI plugin — Calico, Cilium, Flannel, etc. implement Pod networking so every Pod gets a routable IP and Pods can reach each other flat across nodes.
The reconciliation model
Every controller runs the same loop: observe → diff (desired vs actual) → act. Delete a Pod owned by a Deployment and a replacement appears within seconds — not because something “noticed the deletion” specifically, but because the ReplicaSet controller saw actual (2) ≠ desired (3) and created one. This level-triggered (not edge-triggered) design is why Kubernetes recovers from missed events, restarts, and partial failures: it always re-derives the correct action from current state.
Objects, controllers, and the API
- The API is grouped and versioned:
apps/v1(Deployment),batch/v1(Job),networking.k8s.io/v1(Ingress), corev1(Pod, Service). APIs graduatealpha → beta → stableand deprecated versions are eventually removed — a recurring upgrade concern. - Labels and selectors are the glue: a Service finds its Pods by label selector, a Deployment owns Pods whose labels match its selector. Labels are how loose coupling works.
- CRDs (Custom Resource Definitions) let you extend the API with your own object kinds; paired with a custom controller (the operator pattern) they let you automate stateful apps (databases, certificates) the same declarative way.
Key Concepts
Core objects
| Object | Purpose |
|---|---|
| Pod | Smallest deployable unit: one or more containers sharing a network namespace, IP, and volumes. Can include init containers and sidecars |
| ReplicaSet | Keeps N identical Pods running (managed by Deployments; rarely used directly) |
| Deployment | Manages ReplicaSets; rolling updates and rollbacks for stateless apps |
| StatefulSet | Stable network identity + ordered rollout + persistent storage per replica (databases, Kafka, Zookeeper) |
| DaemonSet | One Pod per node (log shippers, node agents, CNI, CSI drivers) |
| Job / CronJob | Run-to-completion tasks / scheduled tasks |
| Service | Stable virtual IP + DNS name load-balancing to Pods (ClusterIP, NodePort, LoadBalancer, ExternalName) |
| Ingress | HTTP(S) routing from outside into Services (host/path rules, TLS termination) — requires an ingress controller |
| Gateway API | The successor to Ingress: richer, role-oriented routing (Gateway, HTTPRoute) for HTTP/TCP/gRPC |
| ConfigMap / Secret | Non-sensitive config / sensitive data injected as env vars or mounted files |
| PV / PVC | PersistentVolume (storage) claimed by workloads via PersistentVolumeClaim; provisioned dynamically by StorageClasses (CSI) |
| Namespace | Virtual cluster partition for isolation, quotas, and RBAC scoping |
| ServiceAccount | Identity for Pods to authenticate to the API server and (via IRSA/Workload Identity) to cloud APIs |
Service types, decoded
- ClusterIP (default) — a stable internal virtual IP reachable only inside the cluster.
- NodePort — exposes the Service on a static port on every node’s IP; crude external access.
- LoadBalancer — provisions a cloud load balancer pointing at the Service (via the cloud-controller-manager).
- ExternalName — a DNS CNAME alias to an external hostname, no proxying.
- Headless (
clusterIP: None) — no virtual IP; DNS returns Pod IPs directly, which StatefulSets use for stable per-replica addressing.
Example manifests
A Deployment with probes, resource limits, and security context, plus a Service and Ingress:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels: { app: web }
spec:
replicas: 3
selector:
matchLabels: { app: web }
strategy:
type: RollingUpdate
rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
template:
metadata:
labels: { app: web }
spec:
securityContext:
runAsNonRoot: true
seccompProfile: { type: RuntimeDefault }
containers:
- name: web
image: ghcr.io/example/web:1.4.2 # immutable tag, never :latest
ports: [{ containerPort: 8080 }]
envFrom:
- configMapRef: { name: web-config }
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 256Mi }
readinessProbe:
httpGet: { path: /healthz, port: 8080 }
livenessProbe:
httpGet: { path: /livez, port: 8080 }
initialDelaySeconds: 10
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector: { app: web }
ports: [{ port: 80, targetPort: 8080 }]
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
ingressClassName: nginx
rules:
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service: { name: web, port: { number: 80 } }
Everyday commands:
kubectl apply -f app.yaml
kubectl get pods -o wide
kubectl describe pod web-6d4c... # events: why is it Pending/CrashLooping?
kubectl logs -f deploy/web
kubectl logs web-6d4c... --previous # logs from the last crashed container
kubectl exec -it web-6d4c... -- sh # shell into a container
kubectl rollout status deploy/web
kubectl rollout undo deploy/web # rollback to the previous ReplicaSet
kubectl get events --sort-by=.lastTimestamp
kubectl top pods # live CPU/memory (needs metrics-server)
Probes: readiness vs liveness vs startup
- readinessProbe — “can this Pod serve traffic right now?” Failing it removes the Pod from Service endpoints without killing it. This is what gates traffic during rollouts and warmups.
- livenessProbe — “is this process wedged and unrecoverable?” Failing it restarts the container. Point it at something cheap and self-contained — never at a database or downstream dependency, or a dependency outage triggers a cluster-wide restart storm.
- startupProbe — protects slow-starting apps: liveness/readiness don’t begin until startup succeeds, so a slow boot isn’t mistaken for a hang.
Scheduling controls
- Resource requests/limits — requests reserve capacity and drive scheduling; limits cap usage. Exceeding a memory limit gets the container OOM-killed; exceeding a CPU limit throttles it.
- Taints and tolerations — a node taint repels Pods unless they tolerate it (e.g., dedicating GPU nodes).
- Affinity/anti-affinity — attract or repel Pods relative to nodes or other Pods (e.g., spread replicas across zones).
- Topology spread constraints — evenly distribute Pods across failure domains (zones, nodes).
- PriorityClass — higher-priority Pods can preempt lower-priority ones when capacity is tight.
Scaling
- HPA (HorizontalPodAutoscaler) — adds/removes Pod replicas based on CPU/memory (via metrics-server) or custom/external metrics (via Prometheus Adapter or KEDA):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: web }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: web }
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 70 }
- VPA (VerticalPodAutoscaler) — adjusts a Pod’s CPU/memory requests instead of the replica count; useful for right-sizing. Don’t run VPA and HPA on the same CPU/memory metric — they fight.
- KEDA — event-driven autoscaling from queue depth, Kafka lag, cron, etc., including scale-to-zero.
- Cluster Autoscaler / Karpenter — scale the nodes themselves when Pods can’t be scheduled (Karpenter provisions right-sized nodes just-in-time on AWS).
Configuration and storage lifecycle
- ConfigMap/Secret injected as env vars are captured at Pod start — changing them does not restart Pods; mounted-as-volume config can update in place but your app must re-read it. Tools like Reloader or a config hash annotation force a rollout on change.
- Secrets are only base64-encoded at rest by default; enable encryption at rest (KMS provider) and restrict RBAC, or use external secret managers (see GitOps).
- PVC → PV → StorageClass — a PVC requests storage; a StorageClass dynamically provisions a matching PV through a CSI driver.
accessModes(RWO/ROX/RWX) andreclaimPolicy(Retain/Delete) matter for data safety.
Helm and Kustomize
Helm is the Kubernetes package manager: it templates a set of manifests (a chart) with per-environment values, and tracks installs as versioned releases you can roll back.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-redis bitnami/redis -f values-prod.yaml
helm upgrade my-redis bitnami/redis --set architecture=replication
helm rollback my-redis 1
helm template my-redis bitnami/redis -f values-prod.yaml # render locally to inspect/CI-validate
Kustomize (built into kubectl, kubectl apply -k) takes the opposite approach: patch-based overlays with no templating language. A base/ holds common manifests; overlays/dev|staging|prod/ patch images, replicas, and env. Rule of thumb: Helm for packaging/distributing reusable apps with many knobs; Kustomize for managing your own manifests across environments without template soup. They compose — Helm can render into Kustomize.
Managed Kubernetes and alternatives
| Offering | Provider | Notes |
|---|---|---|
| EKS | AWS | Managed control plane; nodes via managed node groups, Fargate, or Karpenter; IAM auth via IRSA/EKS Pod Identity |
| GKE | Google Cloud | Most automated; Autopilot mode manages nodes entirely and bills per-Pod |
| AKS | Azure | Free control plane tier; deep Entra ID integration and Workload Identity |
Managed services run and patch the control plane and etcd for you — you still own workloads, upgrade scheduling for node pools, and node configuration. Lightweight alternatives to full Kubernetes: Docker Swarm (simple, built into Docker, declining mindshare) and HashiCorp Nomad (a simple, flexible scheduler that also handles non-container workloads like raw binaries and VMs). For local development and CI: kind (K8s in Docker), minikube, and k3s/k3d (lightweight, edge-friendly).
Security essentials
- RBAC — grant least privilege via Roles/ClusterRoles bound to users, groups, or ServiceAccounts. Avoid
cluster-adminbindings. - Pod Security Admission — enforce
baseline/restrictedstandards per namespace to block privileged Pods, host mounts, and root. - NetworkPolicy — the cluster network is flat and default-allow; apply default-deny policies and open only required flows.
- Policy engines — Kyverno or OPA Gatekeeper enforce org rules at admission (no
:latest, require resource limits, block hostPath, etc.). - Image provenance — verify signatures (cosign) and scan images before they run.
Best Practices
- Always set resource requests and limits — requests drive scheduling and autoscaling; missing limits let one Pod starve a node, and missing requests make the scheduler blind.
- Define readiness, liveness, and startup probes — readiness gates traffic during rollouts; liveness restarts wedged processes; startup covers slow boots. Never point liveness at a dependency — it causes restart storms.
- Never use the
latesttag — deploy immutable, versioned image tags (or digests) so rollouts, rollbacks, and audits are meaningful and reproducible. - Use namespaces with ResourceQuotas, LimitRanges, and RBAC — isolate teams/environments, cap their blast radius, and set sane defaults.
- Run at least 2–3 replicas plus a PodDisruptionBudget — survive node drains, autoscaling, and voluntary disruptions during upgrades without dropping to zero.
- Store manifests in Git and apply declaratively —
kubectl apply/GitOps, never imperativekubectl editin production; the cluster should reflect a committed source of truth. - Keep configuration in ConfigMaps/Secrets, not baked into images — same image across dev/staging/prod, differing only by config; roll Pods when config changes.
- Harden Pod security — run as non-root, drop all capabilities, set
readOnlyRootFilesystemandallowPrivilegeEscalation: false, and enforce with Pod Security Admission (baseline/restricted). - Apply default-deny NetworkPolicies — Kubernetes networking is flat and open by default; segment namespaces and open only the flows you need.
- Right-size with data — use metrics/VPA recommendations rather than guessing requests; over-provisioning is the top cost driver, under-provisioning causes evictions and throttling.
- Spread replicas across failure domains — use topology spread constraints or anti-affinity so a single node or zone loss doesn’t take the whole service down.
- Plan upgrades continuously — Kubernetes releases ~three times a year and APIs get removed; test version skew and deprecated APIs (
kubectldeprecation warnings,pluto) in staging before upgrading. - Back up etcd and persistent volumes — use Velero or provider snapshots; test restores, not just backups, and rehearse cluster recovery.
- Enforce policy at admission — Kyverno/Gatekeeper to require limits, forbid
:latestand privileged Pods, and mandate labels, so mistakes are rejected before they run. - Set graceful shutdown correctly — handle
SIGTERM, tuneterminationGracePeriodSeconds, and use apreStophook so in-flight requests drain during rollouts. - Prefer Deployments for stateless and StatefulSets for stateful — don’t force databases into Deployments; use StatefulSets (or an operator) for stable identity and storage.
References
- Kubernetes documentation
- Kubernetes components
- Kubernetes concepts — Workloads
- Configure liveness, readiness and startup probes
- Pod Security Standards
- Helm documentation · Kustomize
- Gateway API
- KEDA · Karpenter · Cluster Autoscaler
- Kyverno · OPA Gatekeeper · Velero
- Amazon EKS · Google GKE · Azure AKS
- CNCF landscape
- Book: Kubernetes Up & Running (Hightower, Burns, Beda — O’Reilly)
- roadmap.sh — DevOps: Container Orchestration