← DevOps← DevOps
DevOpsDevOps18 Th7, 2026Jul 18, 202617 phút đọc14 min read

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

Kiến trúc cụm Kubernetes
  1. Control Plane
    kube-apiserver
    etcdkho trạng thái
    kube-scheduler
    kube-controller-manager
    (cloud-controller-manager)
  2. (API)
    Nodekubelet, kube-proxy, containerd
    Nodekubelet, kube-proxy, containerd
    Nodekubelet, kube-proxy, containerd

Thành phần control plane:

Thành phần trên 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

Khái niệm chính

Các object cốt lõi

ObjectMụ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
ReplicaSetDuy trì N Pod giống hệt nhau (do Deployment quản lý; hiếm khi dùng trực tiếp)
DeploymentQuản lý ReplicaSet; rolling update và rollback cho ứng dụng stateless
StatefulSetDanh tính mạng ổn định + rollout có thứ tự + storage riêng cho từng replica (database, Kafka, Zookeeper)
DaemonSetMỗi node một Pod (log shipper, node agent, CNI, CSI driver)
Job / CronJobTác vụ chạy-đến-khi-xong / tác vụ theo lịch
ServiceVirtual 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 APIKế thừa của Ingress: định tuyến phong phú hơn, theo vai trò (Gateway, HTTPRoute) cho HTTP/TCP/gRPC
ConfigMap / SecretCấ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 / PVCPersistentVolume (storage) được workload yêu cầu qua PersistentVolumeClaim; cấp phát động qua StorageClass (CSI)
NamespacePhân vùng cluster ảo để cô lập, đặt quota và giới hạn phạm vi RBAC
ServiceAccountDanh 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

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

Điều khiển scheduling

Scaling

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 }

Vòng đời cấu hình và storage

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ấpGhi chú
EKSAWSControl plane được quản lý; node qua managed node group, Fargate hoặc Karpenter; auth IAM qua IRSA/EKS Pod Identity
GKEGoogle CloudTự động hóa cao nhất; chế độ Autopilot quản lý node hoàn toàn và tính tiền theo Pod
AKSAzureCó 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), minikubek3s/k3d (nhẹ, thân thiện edge).

Nền tảng bảo mật

Best Practices

  1. 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ù”.
  2. 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.
  3. 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.
  4. 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ý.
  5. 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.
  6. Lưu manifest trong Git và apply theo kiểu khai báokubectl apply/GitOps, không bao giờ kubectl edit trực tiếp trên production; cluster phải phản ánh một nguồn chân lý đã commit.
  7. Đặ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.
  8. Siết chặt Pod security — chạy non-root, drop toàn bộ capabilities, đặt readOnlyRootFilesystemallowPrivilegeEscalation: false, và cưỡng chế bằng Pod Security Admission (baseline/restricted).
  9. Á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.
  10. 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.
  11. 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.
  12. 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.
  13. 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.
  14. Cưỡng chế policy ở admission — Kyverno/Gatekeeper để bắt buộc limits, cấm :latest và Pod privileged, bắt buộc label, để sai sót bị từ chối trước khi chạy.
  15. Cấu hình graceful shutdown đúng — xử lý SIGTERM, tinh chỉnh terminationGracePeriodSeconds và dùng hook preStop để request đang xử lý được drain trong lúc rollout.
  16. Ư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

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

Kubernetes cluster architecture
  1. Control Plane
    kube-apiserver
    etcdstate store
    kube-scheduler
    kube-controller-manager
    (cloud-controller-manager)
  2. (API)
    Nodekubelet, kube-proxy, containerd
    Nodekubelet, kube-proxy, containerd
    Nodekubelet, kube-proxy, containerd

Control plane components:

Node components:

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

Key Concepts

Core objects

ObjectPurpose
PodSmallest deployable unit: one or more containers sharing a network namespace, IP, and volumes. Can include init containers and sidecars
ReplicaSetKeeps N identical Pods running (managed by Deployments; rarely used directly)
DeploymentManages ReplicaSets; rolling updates and rollbacks for stateless apps
StatefulSetStable network identity + ordered rollout + persistent storage per replica (databases, Kafka, Zookeeper)
DaemonSetOne Pod per node (log shippers, node agents, CNI, CSI drivers)
Job / CronJobRun-to-completion tasks / scheduled tasks
ServiceStable virtual IP + DNS name load-balancing to Pods (ClusterIP, NodePort, LoadBalancer, ExternalName)
IngressHTTP(S) routing from outside into Services (host/path rules, TLS termination) — requires an ingress controller
Gateway APIThe successor to Ingress: richer, role-oriented routing (Gateway, HTTPRoute) for HTTP/TCP/gRPC
ConfigMap / SecretNon-sensitive config / sensitive data injected as env vars or mounted files
PV / PVCPersistentVolume (storage) claimed by workloads via PersistentVolumeClaim; provisioned dynamically by StorageClasses (CSI)
NamespaceVirtual cluster partition for isolation, quotas, and RBAC scoping
ServiceAccountIdentity for Pods to authenticate to the API server and (via IRSA/Workload Identity) to cloud APIs

Service types, decoded

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

Scheduling controls

Scaling

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 }

Configuration and storage lifecycle

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

OfferingProviderNotes
EKSAWSManaged control plane; nodes via managed node groups, Fargate, or Karpenter; IAM auth via IRSA/EKS Pod Identity
GKEGoogle CloudMost automated; Autopilot mode manages nodes entirely and bills per-Pod
AKSAzureFree 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

Best Practices

  1. 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.
  2. 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.
  3. Never use the latest tag — deploy immutable, versioned image tags (or digests) so rollouts, rollbacks, and audits are meaningful and reproducible.
  4. Use namespaces with ResourceQuotas, LimitRanges, and RBAC — isolate teams/environments, cap their blast radius, and set sane defaults.
  5. Run at least 2–3 replicas plus a PodDisruptionBudget — survive node drains, autoscaling, and voluntary disruptions during upgrades without dropping to zero.
  6. Store manifests in Git and apply declarativelykubectl apply/GitOps, never imperative kubectl edit in production; the cluster should reflect a committed source of truth.
  7. Keep configuration in ConfigMaps/Secrets, not baked into images — same image across dev/staging/prod, differing only by config; roll Pods when config changes.
  8. Harden Pod security — run as non-root, drop all capabilities, set readOnlyRootFilesystem and allowPrivilegeEscalation: false, and enforce with Pod Security Admission (baseline/restricted).
  9. Apply default-deny NetworkPolicies — Kubernetes networking is flat and open by default; segment namespaces and open only the flows you need.
  10. 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.
  11. 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.
  12. Plan upgrades continuously — Kubernetes releases ~three times a year and APIs get removed; test version skew and deprecated APIs (kubectl deprecation warnings, pluto) in staging before upgrading.
  13. Back up etcd and persistent volumes — use Velero or provider snapshots; test restores, not just backups, and rehearse cluster recovery.
  14. Enforce policy at admission — Kyverno/Gatekeeper to require limits, forbid :latest and privileged Pods, and mandate labels, so mistakes are rejected before they run.
  15. Set graceful shutdown correctly — handle SIGTERM, tune terminationGracePeriodSeconds, and use a preStop hook so in-flight requests drain during rollouts.
  16. 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