DevOps 30/08/2026 7 phút

Vượt giới hạn CPU thì container bị bóp chậm lại nhưng sống nhăn; vượt giới hạn bộ nhớ thì bị giết ngay — cùng một cặp tham số, hai số phận trái ngược

requests và limits trông như một cặp, nhưng là hai cơ chế khác nhau, và CPU với bộ nhớ còn được xử lý khác nhau nữa. Đo cả bốn tổ hợp: limit CPU 100m thì container bị bóp 100% chu kỳ mà RESTARTS vẫn 0 (chậm mà không chết), còn limit RAM 128Mi thì OOMKilled exitCode 137 và RESTARTS tăng đều. Khác biệt cốt lõi: CPU nén được nên bị bóp, bộ nhớ không nén được nên bị giết. Kèm ba lớp QoS, vì sao không khai requests là tự nhận vé chết trước, và vì sao limits.cpu thường nên bỏ.

DevOps 30/08/2026 8 phút

Ứng dụng của tôi bắt đủ SIGTERM, SIGINT, SIGHUP để dọn dẹp trước khi chết — khi bị OOMKill, nó không nhận được một tín hiệu nào và Kubernetes cũng chẳng sinh event nào

OOMKilled là trạng thái pod hay bị chẩn đoán sai nhất. Dựng nó có chủ ý: container vượt limits.memory bị kernel giết bằng SIGKILL (exitCode 137) — không tín hiệu, không giai đoạn dọn dẹp, nên mọi cơ chế tắt sạch bạn viết đều vô dụng, thứ cần giữ phải ghi LIÊN TỤC chứ không phải lúc tắt. Kubernetes không sinh event nào cho OOM — thông tin chỉ nằm ở status pod, nên bảng theo dõi dựa vào event mù hoàn toàn. Và OOMKilled (mức container, không đổi node) khác hẳn Evicted (mức node, đổi node) cả cơ chế lẫn cách chữa.

DevOps 30/08/2026 7 phút

Pod Guaranteed mang điểm chết -997, BestEffort mang 1000 — và tôi đọc được thứ tự bị OOM giết mà không phải làm cạn RAM node nào

Lớp QoS của Kubernetes không phải quy ước mềm — nó được chống lưng bằng một con số kernel thật: oom_score_adj, cộng thẳng vào điểm mà OOM killer dùng để chọn nạn nhân. Guaranteed mang -997 (gần như bất tử), BestEffort mang 1000 (chết đầu tiên), Burstable theo công thức 1000-(1000×requests/RAM node) khớp chính xác ba lần đo. Nghĩa là requests.memory không chỉ dùng để xếp lịch — nó trực tiếp hạ điểm chết của pod. Và mẹo đo: hiện tượng khó dựng (làm cạn node 16 GB) thì đọc thẳng con số quyết định nó.

DevOps 30/08/2026 8 phút

ClusterIP của Kubernetes không có tiến trình nào lắng nghe, không card mạng nào mang nó, ping cũng không nổi — vì nó chỉ là một dòng luật iptables

Service cho bạn một IP ổn định trong khi pod đến rồi đi — nhưng mở ra thì ClusterIP không tồn tại ở đâu cả: 0 socket, 0 giao diện mạng, chỉ là luật iptables DNAT đổi địa chỉ đích sang pod thật. Điều đó giải thích vì sao không ping được, telnet cổng lạ thì im lặng, tcpdump không thấy nó. Và một sự thật hay bị bỏ sót: cân bằng tải xảy ra ở mức KẾT NỐI không phải mức yêu cầu, nên HTTP keep-alive và gRPC dồn mọi yêu cầu về cùng vài pod. Cộng thêm vì sao iptables 55.000 dòng không co giãn và IPVS ra đời.

DevOps 30/08/2026 7 phút

Thêm đúng một dấu chấm vào cuối tên miền cắt số truy vấn DNS từ 3,7 xuống 1,3 mỗi lần phân giải — vì ndots:5 đang bắt mọi tên ngoài cụm đoán trượt ba lần

Cái tên web-svc biến thành IP thế nào, và vì sao tên NGOÀI cụm tốn hơn bạn nghĩ. resolv.conf mặc định có ndots:5, nghĩa là tên ít hơn 5 dấu chấm phải thử cả ba hậu tố search trước — nên google.com trượt ba lần trước khi đúng. Đo bằng chỉ số CoreDNS: google.com tốn 3,7 truy vấn mỗi lần, google.com. (một dấu chấm cuối) chỉ 1,3, và độ trễ nhanh 4,4 lần. Cùng cái bẫy đo: dig trả NXDOMAIN trong khi ứng dụng chạy tốt, vì dig không dùng search list còn getaddrinfo thì có.

DevOps 30/08/2026 7 phút

Định tuyến Ingress theo tên miền chạy ngay, còn theo đường dẫn thì trả 404 — và nó hoàn toàn đúng đặc tả, chỉ có kỳ vọng của tôi là sai

Service cho một IP một dịch vụ; Ingress cho một điểm vào nhiều dịch vụ, phân biệt bằng tên miền hoặc đường dẫn ở tầng HTTP. Đo cái giá: thêm một chặng proxy mà độ trễ không đo được (0,28 so 0,29 ms), nhưng định tuyến theo đường dẫn trả 404 vì Ingress chuyển nguyên đường dẫn xuống backend — phải viết rewrite-target, mà đó là annotation riêng của ingress-nginx chứ không phải chuẩn Kubernetes. Đổi sang bộ điều khiển khác là viết lại toàn bộ. Và thiếu ingressClassName thì Ingress nằm im, không controller nào nhận, không lỗi.