Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 46 — Containers and Orchestration — Kubernetes Decision

Architecture Decision-Making Bài 46/60

Mở đầu — vì sao bài này quan trọng

Có một cái bẫy tư duy mà gần như mọi kiến trúc sư hệ thống ở Việt Nam đều rơi vào ít nhất một lần trong sự nghiệp: nhìn thấy Kubernetes (viết tắt là K8s) xuất hiện trong mọi bài blog kỹ thuật, mọi bài tuyển dụng senior, mọi hội thảo công nghệ, rồi kết luận rằng "hệ thống nghiêm túc thì phải chạy Kubernetes". Đây là một quyết định kiến trúc, và như mọi quyết định kiến trúc khác trong khóa học này, nó phải được đưa ra dựa trên trade-off cụ thể chứ không phải theo trào lưu.

Bài này nằm đúng ở lằn ranh giữa hai thế giới: bạn đã học về monolith, microservices, modular monolith (Bài 16–18) và sắp bước vào serverless (Bài 47). Container và Kubernetes chính là nền tảng vận hành (runtime platform) đỡ lấy những kiến trúc đó. Việc chọn đóng gói bằng container hay không, và xa hơn là có cần một hệ thống điều phối (orchestration) như Kubernetes hay không, ảnh hưởng trực tiếp đến chi phí vận hành, tốc độ ra tính năng, và cả khả năng tuyển được người của đội bạn.

Điều tôi muốn bạn rút ra sau bài này không phải là "cách cài Kubernetes" — đó là kỹ năng thao tác, tra tài liệu là làm được. Điều quan trọng là khung ra quyết định: khi nào container là đủ, khi nào bạn thực sự cần orchestration, và khi nào một quản lý (managed) đơn giản hơn Kubernetes là lựa chọn khôn ngoan hơn. Đây mới là phần khiến bạn khác biệt ở vai trò senior và principal.

Khái niệm cốt lõi

Container là gì và giải quyết vấn đề gì

Hãy bắt đầu từ nỗi đau kinh điển: "trên máy tôi chạy được mà". Ứng dụng của bạn cần đúng phiên bản Node 18, đúng thư viện hệ thống, đúng biến môi trường. Máy dev có, máy staging thiếu, máy production lại khác nữa. Container ra đời để đóng gói toàn bộ ứng dụng + thư viện phụ thuộc (dependencies) + môi trường chạy (runtime) vào một đơn vị duy nhất, chạy giống hệt nhau ở mọi nơi có công cụ container.

Docker là công cụ phổ biến nhất để làm việc này. Bạn viết một Dockerfile mô tả cách dựng image, build ra một image (bản chụp bất biến — immutable — của ứng dụng), rồi chạy image đó thành một container (một tiến trình đang chạy). Image bất biến nghĩa là image v1.2.3 bạn test ở staging chính xác là image chạy ở production, không sai lệch một byte.

Điểm khác biệt cốt lõi so với máy ảo (VM): VM ảo hóa cả phần cứng và mang theo nguyên một hệ điều hành khách (guest OS) nặng vài GB. Container chia sẻ nhân (kernel) của hệ điều hành máy chủ, chỉ cô lập ở tầng tiến trình. Kết quả: container nhẹ hơn nhiều (thường vài chục đến vài trăm MB), khởi động trong mili-giây thay vì phút, và bạn nhồi được nhiều container hơn trên cùng một máy. Đây là lý do container trở thành đơn vị đóng gói tiêu chuẩn của hạ tầng hiện đại.

Từ "chạy được container" đến "vận hành nhiều container ở quy mô"

Chạy một container bằng docker run rất dễ. Vấn đề bắt đầu khi bạn có 20, 50, 200 container trải trên 10 máy chủ và phải trả lời những câu hỏi vận hành:

  • Container nào chạy trên máy nào để cân bằng tài nguyên? (scheduling — lập lịch)
  • Một container chết thì ai khởi động lại? (self-healing — tự phục hồi)
  • Traffic đến thì chia cho các bản sao (replicas) thế nào? (load balancing)
  • Deploy phiên bản mới mà không downtime thì làm sao? (rolling update)
  • Tải tăng đột biến thì tự nhân bản thêm container kiểu gì? (auto-scaling)
  • Các service tìm thấy nhau bằng cách nào? (service discovery — sẽ đào sâu ở Bài 52)
Trả lời tất cả những câu này một cách tự động, đó chính là công việc của một hệ thống điều phối container (container orchestration). Và Kubernetes là chuẩn de-facto (chuẩn thực tế) cho việc này.

Kubernetes — mô hình tư duy

Đừng học Kubernetes như một danh sách lệnh. Hãy hiểu triết lý cốt lõi của nó: mô hình khai báo (declarative) và vòng lặp hòa giải (reconciliation loop). Bạn không ra lệnh "hãy khởi động 3 container". Bạn khai báo trạng thái mong muốn: "tôi muốn luôn có 3 bản sao của service này chạy". Kubernetes liên tục so sánh trạng thái thực tế với trạng thái mong muốn, và tự động hành động để kéo thực tế về đúng mong muốn. Một container chết, nó tự thấy "đang có 2, mong muốn 3", và tự tạo thêm một cái.

Vài khái niệm nền tảng bạn cần nắm:

  • Pod: đơn vị nhỏ nhất Kubernetes quản lý, bọc một (hoặc vài) container.
  • Deployment: khai báo bạn muốn bao nhiêu bản sao của pod, và quản lý việc cập nhật phiên bản.
  • Service: một điểm truy cập ổn định (địa chỉ cố định) đứng trước nhóm pod hay thay đổi.
  • Node: một máy chủ (vật lý hoặc ảo) trong cụm (cluster).
  • Control plane: "bộ não" điều phối toàn bộ cụm.
Sức mạnh của Kubernetes là nó đóng gói toàn bộ các bài toán vận hành phân tán thành một nền tảng chuẩn hóa. Nhưng chính sự tổng quát đó là nguồn gốc của cái giá phải trả: độ phức tạp.

Cái giá thật của Kubernetes

Đây là phần các bài blog quảng cáo K8s hay giấu đi. Kubernetes không miễn phí về mặt nhận thức và vận hành:

  • Đường cong học tập dốc: networking (CNI), storage (PV/PVC), RBAC, ingress, secrets... mỗi thứ là một chủ đề riêng.
  • Cần người có kỹ năng: một kỹ sư DevOps/Platform giỏi Kubernetes ở Việt Nam có mức lương thuộc nhóm cao, và tuyển được không dễ.
  • Chi phí "thuế nền tảng": control plane, monitoring, logging đều tiêu tài nguyên kể cả khi ứng dụng của bạn còn nhỏ.
  • Rủi ro cấu hình sai: một YAML sai resource limits có thể làm sập cả node.
Vì vậy quyết định "có dùng Kubernetes không" thực chất là quyết định "lợi ích orchestration có vượt cái giá phức tạp không, và ở quy mô đội của tôi thì nên tự vận hành hay dùng managed?".

Phổ lựa chọn — không chỉ có "K8s hay không K8s"

Sai lầm là nghĩ đây là câu hỏi nhị phân. Thực tế là một phổ (spectrum) các lựa chọn, từ đơn giản đến phức tạp:

  • Một VM + Docker Compose: đóng gói bằng container, chạy vài container trên một máy. Đủ cho phần lớn ứng dụng nhỏ.
  • PaaS quản lý (Render, Railway, Fly.io, Heroku, Google Cloud Run, AWS App Runner): bạn đưa container, nền tảng lo scaling và vận hành. Không cần biết K8s.
  • Managed Kubernetes (GKE, EKS, AKS): nhà cung cấp lo control plane, bạn lo workload. Đây là "điểm ngọt" (sweet spot) cho đa số ai thực sự cần K8s.
  • Self-managed Kubernetes: bạn tự vận hành cả control plane. Chỉ hợp lý với đội platform chuyên trách hoặc yêu cầu on-prem đặc thù.
Nguyên tắc mentor: chọn nấc thấp nhất trên thang này mà vẫn đáp ứng nhu cầu. Leo lên nấc cao hơn chỉ khi có lý do rõ ràng, viết được thành một ADR (Bài 4).

Tình huống thực tế

Tình huống 1 — Startup fintech Việt Nam "chạy theo trend" và trả giá

Một startup fintech tại TP.HCM (gọi là công ty A) với đội kỹ thuật 6 người, sản phẩm là ví điện tử đang có khoảng 15.000 người dùng. CTO — vốn ngưỡng mộ kiến trúc của các công ty lớn — quyết định dựng hệ thống trên self-managed Kubernetes ngay từ đầu để "sau này scale không phải làm lại".

Kết quả sau 4 tháng: hai trong sáu kỹ sư dành phần lớn thời gian vật lộn với cụm K8s — debug networking, vá lỗi cert hết hạn, xử lý pod bị OOM (hết bộ nhớ) lúc nửa đêm. Tốc độ ra tính năng chậm lại thấy rõ. Một sự cố mất 3 giờ chỉ vì cấu hình ingress sai. Trong khi đó tải thực tế của họ — vài chục request mỗi giây — thừa sức chạy trên hai VM với Docker Compose và một load balancer.

Bài học: Kubernetes tối ưu cho bài toán vận hành nhiều service ở quy mô lớn với đội có kỹ năng nền tảng. Với 15.000 người dùng và 6 kỹ sư, "thuế phức tạp" ăn mòn chính nguồn lực quý nhất của startup: thời gian làm sản phẩm. Đúng ra họ nên bắt đầu ở nấc 1–2 của phổ lựa chọn, và chỉ leo lên khi có bằng chứng về nhu cầu.

Tình huống 2 — Sàn thương mại điện tử Đông Nam Á và điểm ngọt managed Kubernetes

Một công ty thương mại điện tử quy mô khu vực (gọi là công ty B, bối cảnh tương tự các nền tảng như Tiki hay một sàn tầm trung ở Indonesia) có khoảng 40 microservice, đội kỹ thuật hơn 80 người chia thành nhiều nhóm tự chủ, và tải tăng gấp 8–10 lần vào các đợt sale lớn như 11/11, 12/12.

Với họ, các bài toán mà Kubernetes giải quyết là có thật và cấp bách: auto-scaling theo tải sale để không phải trả tiền server dư quanh năm; rolling update để deploy nhiều lần mỗi ngày mà không downtime; cô lập tài nguyên giữa các nhóm bằng namespace. Họ chọn managed Kubernetes (GKE trên Google Cloud) thay vì tự vận hành control plane — nhờ đó nhà cung cấp lo phần "ống nước" khó nhất, đội platform 8 người của họ tập trung vào việc xây nền tảng nội bộ phục vụ 80 kỹ sư kia.

Con số minh họa: vào đợt 11/11, cụm tự nở từ khoảng 60 node lên hơn 200 node trong vài phút khi tải tăng vọt, rồi tự co lại sau đợt cao điểm — tiết kiệm chi phí đáng kể so với việc giữ 200 node quanh năm.

Bài học: Đây chính là hình mẫu Kubernetes nên được dùng — nhiều service, nhiều đội, tải biến động mạnh, và quan trọng là có đội platform chuyên trách. Nhưng ngay cả họ cũng chọn managed thay vì self-managed. Managed Kubernetes gần như luôn là lựa chọn đúng khi bạn quyết định cần K8s.

Tình huống 3 — Công ty SaaS chọn Cloud Run và tránh được K8s hoàn toàn

Một công ty SaaS B2B nhỏ (công ty C) làm phần mềm quản lý cho chuỗi F&B, đội 12 kỹ sư, có khoảng 8 service. Họ từng cân nhắc EKS, nhưng khi làm bài phân tích trade-off (Bài 6) họ nhận ra: tải của họ ổn định vào giờ hành chính, không có đội DevOps chuyên trách, và cái họ cần nhất là deploy nhanh, ít việc vận hành.

Họ chọn Google Cloud Run — một PaaS chạy container không cần biết Kubernetes. Mỗi service là một container, đẩy lên là chạy, tự scale về 0 khi không có traffic (tiết kiệm chi phí ngoài giờ làm việc), tự scale lên khi có request. Đội không phải quản lý bất kỳ node hay YAML K8s nào.

Bài học: Có cả một tầng PaaS chạy container nằm giữa "Docker trần" và "Kubernetes đầy đủ". Với nhiều đội, đây mới là điểm ngọt thực sự. Câu hỏi đúng không phải "K8s hay không" mà là "nấc thấp nhất nào đủ dùng?".

Hướng dẫn từng bước

Đây là quy trình ra quyết định tôi khuyên bạn áp dụng, kết thúc bằng một ADR.

Bước 1 — Container hóa trước, orchestration tính sau. Gần như luôn nên đóng gói ứng dụng bằng container (Docker). Lợi ích tính nhất quán môi trường là quá lớn so với chi phí. Đây là quyết định gần như "mặc định đúng".

Bước 2 — Lượng hóa nhu cầu vận hành thực tế. Trả lời trung thực: Bạn có bao nhiêu service? Bao nhiêu người trong đội? Tải có biến động mạnh không? Bạn deploy mấy lần một tuần? Có yêu cầu on-prem/tuân thủ đặc thù không? Viết ra thành con số, đừng ước lượng cảm tính.

Bước 3 — Đối chiếu với phổ lựa chọn. Dùng heuristic:

  • Ít service, đội nhỏ, tải ổn định → VM + Compose hoặc PaaS (Cloud Run, Render, Fly.io).
  • Nhiều service, đội bắt đầu tách nhóm, cần auto-scale/rolling update thường xuyên → cân nhắc managed Kubernetes.
  • Có đội platform chuyên trách và/hoặc yêu cầu on-prem cứng → mới xét self-managed.
Bước 4 — Nếu chọn Kubernetes, mặc định là managed. Chỉ tự vận hành control plane khi có lý do bất khả kháng (chủ quyền dữ liệu, on-prem, chi phí ở quy mô cực lớn) và có người đủ giỏi.

Bước 5 — Ước tính "tổng chi phí sở hữu" (TCO), không chỉ chi phí server. Cộng cả thời gian kỹ sư, lương người vận hành, rủi ro sự cố. Kubernetes tự vận hành thường đắt hơn nhiều so với vẻ ngoài.

Bước 6 — Viết ADR. Ghi lại: bối cảnh, các phương án đã xét, tiêu chí, quyết định, và điều kiện để xem lại quyết định (ví dụ: "khi vượt 15 service hoặc đội vượt 30 người, xem xét lại orchestration"). Đây là điểm khiến quyết định của bạn có thể phòng thủ được về sau.

Lỗi thường gặp & mẹo

Lỗi 1 — Dùng Kubernetes vì CV và trend, không vì bài toán. Đây là lỗi phổ biến nhất. Hãy hỏi thẳng: nếu bỏ K8s đi, bài toán vận hành nào không giải được? Nếu không kể tên được, bạn chưa cần nó.

Lỗi 2 — Tự vận hành control plane khi không cần. Managed Kubernetes (GKE/EKS/AKS) gánh phần khó nhất và rủi ro nhất. Tự làm control plane chỉ để "tiết kiệm" thường là tiết kiệm giả — bạn trả bằng thời gian và sự cố.

Lỗi 3 — Quên đặt resource requests/limits. Không giới hạn tài nguyên cho pod là nguyên nhân kinh điển gây sập node dây chuyền. Luôn khai báo requests/limits ngay từ đầu.

Lỗi 4 — Coi Kubernetes là "cài xong là quên". K8s cần vá lỗi, nâng cấp phiên bản (vốn hay có breaking change), giám sát liên tục. Nó là cam kết vận hành dài hạn, không phải dự án một lần.

Lỗi 5 — Nhầm container hóa với orchestration. Đóng gói bằng Docker rất đáng làm gần như mọi lúc. Orchestration bằng Kubernetes thì có điều kiện. Tách bạch hai quyết định này.

Mẹo — Bắt đầu thấp, leo có bằng chứng. Bắt đầu ở nấc thấp nhất đủ dùng và định nghĩa trước "ngưỡng kích hoạt" để nâng cấp. Chuyển từ Compose/PaaS sang managed K8s sau này không hề đắt như người ta lo, đặc biệt khi ứng dụng đã container hóa sẵn.

Mẹo — Managed K8s không có nghĩa là hết việc. Ngay cả GKE/EKS vẫn cần bạn hiểu networking, ingress, secrets, monitoring. "Managed" gánh control plane, không gánh workload của bạn.

Bài tập thực hành

  • Định vị hệ thống của bạn trên phổ lựa chọn. Lấy một hệ thống bạn đang làm (hoặc công ty A trong bài). Liệt kê: số service, số người trong đội, mức độ biến động tải, tần suất deploy. Dựa vào đó, xác định nó nên nằm ở nấc nào (VM+Compose / PaaS / managed K8s / self-managed K8s) và giải thích lý do bằng 3–4 câu.
  • Viết một ADR ngắn. Với tình huống công ty C (SaaS F&B, 12 kỹ sư, 8 service, tải theo giờ hành chính), viết một ADR cho quyết định "chọn Cloud Run thay vì managed Kubernetes". Bắt buộc có mục: bối cảnh, phương án đã xét, quyết định, và điều kiện xem xét lại.
  • Phản biện một quyết định sai. Đọc lại tình huống công ty A. Viết một đoạn phản biện (khoảng 150 từ) mà bạn — với vai trò tech lead — sẽ trình bày cho CTO để thuyết phục không dùng self-managed K8s giai đoạn đầu. Dùng lập luận về TCO và chi phí cơ hội, không dùng lập luận cảm tính.
  • Ước tính TCO. Với một hệ thống 10 service, hãy ước tính (theo cảm nhận cộng con số giả định hợp lý) chênh lệch tổng chi phí sở hữu giữa "managed Kubernetes" và "self-managed Kubernetes" trong 12 tháng, tính cả lương kỹ sư vận hành và rủi ro sự cố, không chỉ tiền thuê server.

Tóm tắt

  • Container đóng gói ứng dụng + phụ thuộc + runtime thành đơn vị bất biến, chạy nhất quán ở mọi nơi; nhẹ hơn VM vì chia sẻ kernel. Container hóa gần như luôn là quyết định đúng.
  • Orchestration (Kubernetes) giải các bài toán vận hành nhiều container ở quy mô: scheduling, self-healing, load balancing, rolling update, auto-scaling. Nhưng nó đắt về độ phức tạp và nhân lực.
  • Quyết định không phải nhị phân "K8s hay không" mà là chọn nấc thấp nhất đủ dùng trên phổ: VM+Compose → PaaS (Cloud Run/Render/Fly.io) → managed K8s (GKE/EKS/AKS) → self-managed K8s.
  • Kubernetes tỏa sáng khi có nhiều service, nhiều đội, tải biến động mạnh, và đội platform chuyên trách. Với startup nhỏ, nó thường là gánh nặng hơn là lợi thế.
  • Khi đã quyết cần K8s, mặc định chọn managed, chỉ tự vận hành khi có lý do bất khả kháng.
  • Luôn ước tính TCO (gồm thời gian kỹ sư và rủi ro sự cố), và ghi quyết định thành ADR kèm điều kiện xem xét lại. Đó là dấu ấn tư duy của một kiến trúc sư trưởng thành.