Tinh chỉnh các tham số swap của Linux cho các node Kubernetes ổn định
Tìm hiểu sâu về các tham số kernel như swappiness và watermarks để khám phá cách bật swap một cách an toàn trong Kubernetes v1.34 mà không gây ra lỗi OOM kill.
Được dịch tự động từ bản gốc tiếng Anh.
Trong một bài đăng trên Kubernetes Blog vào tháng 8 năm 2025, Ajay Sundar Karuppasamy từ Google đã trình bày chi tiết cách tinh chỉnh swap của Linux cho các cụm Kubernetes. Bài viết đề cập đến việc chuẩn bị ổn định hóa tính năng NodeSwap trong Kubernetes v1.34, cho phép các node sử dụng dung lượng ổ đĩa làm bộ nhớ ảo. Sự thay đổi này đòi hỏi cấu hình cẩn thận các tham số kernel để cân bằng giữa mức độ sử dụng tài nguyên và sự ổn định của hệ thống.
Điều gì đã xảy ra
Trong nhiều năm, lời khuyên tiêu chuẩn dành cho những người vận hành Kubernetes là tắt hoàn toàn swap để đảm bảo hiệu suất dự đoán được. Tuy nhiên, sự ra mắt của tính năng NodeSwap đã thay đổi mô hình này bằng cách cho phép các node Linux chuyển các trang bộ nhớ ít được sử dụng sang lưu trữ thứ cấp. Khả năng này nhằm mục đích giảm thiểu các trường hợp bị giết do hết bộ nhớ (OOM kills) và cải thiện hiệu quả tổng thể của tài nguyên. Bất chấp những lợi ích này, việc bật swap không chỉ đơn giản là một công tắc. Nếu không tinh chỉnh chính xác, nó có thể dẫn đến suy giảm hiệu suất nghiêm trọng và cản trở khả năng quản lý việc trục xuất pod một cách êm ái của Kubelet.
Bài viết nhấn mạnh rằng các thiết lập swap cấu hình sai có thể khiến kernel hoạt động không thể đoán trước dưới áp lực bộ nhớ. Trong các thử nghiệm với thiết lập mặc định, các node đã gặp phải tình trạng khởi động lại bất ngờ và bị OOM kill sớm khi chịu tốc độ phân bổ bộ nhớ cao. Vấn đề cốt lõi nằm ở tương tác giữa các thuật toán thay thế trang của kernel Linux và logic trục xuất của Kubernetes. Nếu kernel không thu hồi bộ nhớ đủ nhanh, hoặc nếu nó thu hồi sai loại bộ nhớ, node có thể trở nên không ổn định trước khi Kubelet kịp hành động.
Để giải quyết những thách thức này, tác giả đã tiến hành một loạt các bài kiểm tra tải (stress tests) trên các node Google Kubernetes Engine (GKE) chạy Kubernetes v1.33.2. Các bài kiểm tra liên quan đến các ứng dụng Go tùy chỉnh được thiết kế để mô phỏng các mẫu truy cập và áp lực bộ nhớ khác nhau. Bằng cách điều chỉnh các tham số kernel cụ thể, tác giả đã chứng minh cách tạo ra một cửa sổ vận hành an toàn hơn cho việc quản lý bộ nhớ, ngăn ngừa các lỗi nghiêm trọng trong quá trình tăng đột biến nhu cầu.
Cơ chế hoạt động
Linux quản lý bộ nhớ theo từng trang, thường là 4KiB mỗi trang. Khi RAM vật lý đầy, kernel phải quyết định di chuyển những trang nào sang không gian swap. Nó phân biệt giữa bộ nhớ ẩn danh (anonymous memory), chẳng hạn như dữ liệu heap và stack, và bộ nhớ dựa trên tệp (file-backed memory), như mã thực thi và cache. Các trang ẩn danh phải được ghi vào thiết bị swap để được thu hồi, trong khi các trang dựa trên tệp sạch có thể chỉ cần loại bỏ. Kernel sử dụng một số tham số để hướng dẫn các quyết định này, chủ yếu là vm.swappiness, tham số kiểm soát ưu tiên giữa việc swap các trang ẩn danh so với việc xóa cache tệp.

Hai tham số quan trọng khác là vm.min_free_kbytes và vm.watermark_scale_factor. Thiết lập min_free_kbytes xác định một vùng đệm an toàn gồm bộ nhớ trống. Khi bộ nhớ khả dụng giảm xuống dưới ngưỡng này, kernel sẽ tích cực thu hồi các trang. watermark_scale_factor xác định khoảng cách giữa các watermark bộ nhớ thấp, min và cao. Một khoảng cách lớn hơn cung cấp cho quy trình thu hồi nền, được gọi là kswapd, thêm thời gian để di chuyển các trang sang swap một cách dần dần. Điều này ngăn hệ thống chạm vào watermark min tới hạn quá nhanh, vốn sẽ chặn các phân bổ quy trình và kích hoạt OOM kills.
Chi tiết chính
- Tính năng NodeSwap dự kiến sẽ đạt trạng thái ổn định trong Kubernetes v1.34.
- Các bài kiểm tra được thực hiện trên các node GKE với 8GiB RAM và 50GB swap trên các đĩa pd-balanced.
- Các thiết lập mặc định (
swappiness=60,min_free_kbytes=68MB) dẫn đến OOM kills dưới tải cao. - Tăng
min_free_kbyteslên 512MiB buộc việc thu hồi bộ nhớ diễn ra sớm hơn, cung cấp một vùng đệm an toàn lớn hơn. - Đặt
watermark_scale_factorthành 2000 mở rộng cửa sổ swap, cho phépkswapdhoạt động hiệu quả hơn. - Các thành phần hệ thống quan trọng như kubelet và container runtime nên tắt swap thông qua cgroups.
Tại sao điều này quan trọng
Đối với các kỹ sư phần mềm và nhóm nền tảng, việc bật swap giới thiệu một sự đánh đổi phức tạp giữa dung lượng và độ trễ. Swap chậm hơn đáng kể so với việc truy cập RAM, vì vậy nếu tập làm việc đang hoạt động (active working set) của một ứng dụng bị chuyển sang đĩa, hiệu suất sẽ bị ảnh hưởng do thời gian chờ I/O tăng lên. Tuy nhiên, swap được tinh chỉnh đúng cách có thể ngăn chặn các sự cố sụp đổ ứng dụng đột ngột do OOM kills, vốn thường gây gián đoạn nhiều hơn so với các đợt tăng độ trễ tạm thời. Hiểu rõ các cơ chế kernel này là điều cần thiết để xây dựng các hệ thống bền vững có thể xử lý việc cấp phát quá mức bộ nhớ (memory overcommitment) một cách an toàn.

Hơn nữa, việc tinh chỉnh không đúng cách có thể che giấu các vấn đề tiềm ẩn như rò rỉ bộ nhớ. Thay vì thất bại nhanh chóng với một lệnh OOM kill, một ứng dụng bị rò rỉ có thể làm suy giảm hiệu suất node từ từ bằng cách tiêu thụ không gian swap, khiến việc chẩn đoán trở nên khó khăn. Nó cũng có nguy cơ bỏ qua các cơ chế trục xuất êm ái của Kubernetes. Nếu kernel kích hoạt OOM kill trước khi Kubelet có thể trục xuất các pod dựa trên chính sách, các khối lượng công việc có mức độ ưu tiên cao hơn có thể bị chấm dứt ngoài ý muốn. Do đó, việc đồng bộ hóa hành vi của kernel với kỳ vọng của Kubernetes là rất quan trọng để duy trì sức khỏe của cụm.
Bạn có thể làm gì
- Bắt đầu với
vm.swappiness=60cho các khối lượng công việc đa dụng, nhưng hãy điều chỉnh dựa trên việc ứng dụng của bạn nhạy cảm với I/O hay phụ thuộc nhiều vào cache. - Đặt
vm.min_free_kbytesxấp xỉ 2-3% tổng bộ nhớ node (ví dụ: 500MB cho một node 8GiB) để tạo ra một vùng đệm an toàn đủ lớn. - Tăng
vm.watermark_scale_factorlên 2000 để mở rộng cửa sổ swap, cho phépkswapdhoạt động hiệu quả hơn. - Tắt swap cho các thành phần hệ thống quan trọng như kubelet và container runtime thông qua cgroups.


