Giới hạn CPU và điều tiết tích cực trong Kubernetes

Ghi chú. bản dịch.: Lịch sử mở rộng tầm mắt này của Omio—một công cụ tổng hợp du lịch châu Âu—đưa người đọc từ lý thuyết cơ bản đến những vấn đề thực tế phức tạp hấp dẫn về cấu hình Kubernetes. Làm quen với những trường hợp như vậy không chỉ giúp bạn mở rộng tầm nhìn mà còn ngăn ngừa những vấn đề không hề nhỏ.

Giới hạn CPU và điều tiết tích cực trong Kubernetes

Bạn đã bao giờ gặp trường hợp một ứng dụng bị kẹt tại chỗ, ngừng phản hồi khi kiểm tra tình trạng và không thể hiểu tại sao? Một lời giải thích có thể có liên quan đến giới hạn hạn ngạch tài nguyên CPU. Đây là những gì chúng ta sẽ nói về trong bài viết này.

TL; DR:
Chúng tôi thực sự khuyên bạn nên tắt giới hạn CPU trong Kubernetes (hoặc tắt hạn ngạch CFS trong Kubelet) nếu bạn đang sử dụng phiên bản nhân Linux có lỗi hạn ngạch CFS. Trong cốt lõi nghiêm túc và nổi tiếng một lỗi dẫn tới việc điều tiết và trì hoãn quá mức
.

ở Omio toàn bộ cơ sở hạ tầng được quản lý bởi Kubernetes. Tất cả khối lượng công việc có trạng thái và không trạng thái của chúng tôi đều chạy độc quyền trên Kubernetes (chúng tôi sử dụng Google Kubernetes Engine). Trong sáu tháng qua, chúng tôi bắt đầu quan sát thấy sự chậm lại ngẫu nhiên. Các ứng dụng bị treo hoặc ngừng phản hồi khi kiểm tra tình trạng, mất kết nối với mạng, v.v. Hành vi này khiến chúng tôi bối rối trong một thời gian dài và cuối cùng chúng tôi quyết định xem xét vấn đề một cách nghiêm túc.

Tóm tắt bài báo:

  • Đôi lời về container và Kubernetes;
  • Cách thực hiện các yêu cầu và giới hạn CPU;
  • Giới hạn CPU hoạt động như thế nào trong môi trường đa lõi;
  • Cách theo dõi việc điều chỉnh CPU;
  • Giải pháp vấn đề và sắc thái.

Một vài lời về container và Kubernetes

Kubernetes về cơ bản là tiêu chuẩn hiện đại trong thế giới cơ sở hạ tầng. Nhiệm vụ chính của nó là điều phối container.

Container

Trước đây, chúng tôi phải tạo các tạo phẩm như Java JAR/WAR, Python Eggs hoặc các tệp thực thi để chạy trên máy chủ. Tuy nhiên, để chúng hoạt động, cần phải thực hiện thêm công việc: cài đặt môi trường thời gian chạy (Java/Python), đặt các tệp cần thiết vào đúng vị trí, đảm bảo khả năng tương thích với một phiên bản cụ thể của hệ điều hành, v.v. Nói cách khác, cần phải chú ý cẩn thận đến việc quản lý cấu hình (thường là nguồn gây tranh cãi giữa các nhà phát triển và quản trị viên hệ thống).

Container đã thay đổi mọi thứ. Bây giờ hiện vật là một hình ảnh vùng chứa. Nó có thể được biểu diễn dưới dạng một loại tệp thực thi mở rộng không chỉ chứa chương trình mà còn chứa môi trường thực thi chính thức (Java/Python/...), cũng như các tệp/gói cần thiết, được cài đặt sẵn và sẵn sàng để sử dụng. chạy. Các container có thể được triển khai và chạy trên các máy chủ khác nhau mà không cần bất kỳ bước bổ sung nào.

Ngoài ra, các container hoạt động trong môi trường sandbox của riêng chúng. Họ có bộ điều hợp mạng ảo riêng, hệ thống tệp riêng với quyền truy cập hạn chế, hệ thống phân cấp quy trình riêng, giới hạn riêng về CPU và bộ nhớ, v.v. Tất cả điều này được thực hiện nhờ một hệ thống con đặc biệt của nhân Linux - không gian tên.

Kubernetes

Như đã nêu trước đó, Kubernetes là một bộ điều phối container. Nó hoạt động như thế này: bạn cung cấp cho nó một nhóm máy rồi nói: "Này, Kubernetes, hãy khởi chạy mười phiên bản vùng chứa của tôi với 2 bộ xử lý và 3 GB bộ nhớ mỗi phiên bản và duy trì hoạt động của chúng!" Kubernetes sẽ lo phần còn lại. Nó sẽ tìm dung lượng trống, khởi chạy các container và khởi động lại chúng nếu cần, tung ra bản cập nhật khi thay đổi phiên bản, v.v. Về cơ bản, Kubernetes cho phép bạn loại bỏ thành phần phần cứng và tạo ra nhiều hệ thống phù hợp để triển khai và chạy ứng dụng.

Giới hạn CPU và điều tiết tích cực trong Kubernetes
Kubernetes từ quan điểm của giáo dân

Yêu cầu và giới hạn trong Kubernetes là gì

Được rồi, chúng ta đã đề cập đến các container và Kubernetes. Chúng tôi cũng biết rằng nhiều vùng chứa có thể nằm trên cùng một máy.

Có thể rút ra một sự tương tự với một căn hộ chung. Một mặt bằng rộng rãi (máy móc/thiết bị) được nhiều người thuê (container) thuê. Kubernetes hoạt động như một nhà môi giới bất động sản. Câu hỏi đặt ra là làm sao để người thuê nhà không xung đột với nhau? Điều gì sẽ xảy ra nếu một trong số họ quyết định mượn phòng tắm trong nửa ngày?

Đây là nơi các yêu cầu và giới hạn phát huy tác dụng. CPU Yêu cầu chỉ cần thiết cho mục đích lập kế hoạch. Đây giống như một “danh sách mong muốn” của vùng chứa và nó được sử dụng để chọn nút phù hợp nhất. Đồng thời CPU Hạn chế có thể được so sánh với một hợp đồng cho thuê - ngay khi chúng ta chọn được một đơn vị cho container, không thể vượt quá giới hạn đã được thiết lập. Và đây chính là lúc vấn đề nảy sinh...

Cách triển khai các yêu cầu và giới hạn trong Kubernetes

Kubernetes sử dụng cơ chế điều tiết (bỏ qua chu kỳ xung nhịp) được tích hợp trong kernel để thực hiện các giới hạn CPU. Nếu một ứng dụng vượt quá giới hạn, tính năng điều chỉnh sẽ được bật (tức là ứng dụng đó nhận được ít chu kỳ CPU hơn). Các yêu cầu và giới hạn bộ nhớ được sắp xếp khác nhau nên dễ phát hiện hơn. Để thực hiện việc này, chỉ cần kiểm tra trạng thái khởi động lại lần cuối của nhóm: xem đó có phải là “OOMKilled” hay không. Việc điều chỉnh CPU không đơn giản như vậy vì K8 chỉ cung cấp số liệu theo mức sử dụng chứ không phải theo nhóm.

Yêu cầu CPU

Giới hạn CPU và điều tiết tích cực trong Kubernetes
Cách thực hiện yêu cầu CPU

Để đơn giản, hãy xem quy trình sử dụng máy có CPU 4 nhân làm ví dụ.

K8s sử dụng cơ chế nhóm điều khiển (cgroups) để kiểm soát việc phân bổ tài nguyên (bộ nhớ và bộ xử lý). Một mô hình phân cấp có sẵn cho nó: đứa trẻ kế thừa các giới hạn của nhóm cha. Chi tiết phân phối được lưu trữ trong hệ thống tệp ảo (/sys/fs/cgroup). Trong trường hợp bộ xử lý, đây là /sys/fs/cgroup/cpu,cpuacct/*.

K8s sử dụng tập tin cpu.share để phân bổ tài nguyên bộ xử lý. Trong trường hợp của chúng tôi, nhóm gốc nhận được 4096 lượt chia sẻ tài nguyên CPU - 100% sức mạnh bộ xử lý khả dụng (1 lõi = 1024; đây là giá trị cố định). Nhóm gốc phân phối tài nguyên theo tỷ lệ tùy thuộc vào phần chia sẻ của con cháu đã đăng ký trong cpu.share, và đến lượt họ, họ cũng làm như vậy với con cháu của mình, v.v. Trên một nút Kubernetes điển hình, nhóm gốc có ba nút con: system.slice, user.slice и kubepods. Hai nhóm con đầu tiên được sử dụng để phân phối tài nguyên giữa các tải hệ thống quan trọng và các chương trình người dùng bên ngoài K8. Cái cuối cùng - kubepods — được tạo bởi Kubernetes để phân phối tài nguyên giữa các nhóm.

Sơ đồ trên cho thấy nhóm con thứ nhất và thứ hai nhận được mỗi 1024 chia sẻ, với nhóm con kuberpod được phân bổ 4096 cổ phần Làm sao điều này có thể xảy ra: xét cho cùng, nhóm gốc chỉ có quyền truy cập vào 4096 cổ phần và tổng số cổ phần của con cháu bà vượt quá con số này một cách đáng kể (6144)? Vấn đề là giá trị này có ý nghĩa logic, vì vậy bộ lập lịch Linux (CFS) sử dụng nó để phân bổ tài nguyên CPU một cách tương ứng. Trong trường hợp của chúng tôi, hai nhóm đầu tiên nhận được 680 cổ phiếu thực (16,6% trong số 4096) và kubepod nhận phần còn lại 2736 cổ phần Trong trường hợp ngừng hoạt động, hai nhóm đầu tiên sẽ không sử dụng tài nguyên được phân bổ.

May mắn thay, bộ lập lịch có cơ chế tránh lãng phí tài nguyên CPU không được sử dụng. Nó chuyển công suất “nhàn rỗi” sang một nhóm chung, từ đó nó được phân phối cho các nhóm cần thêm sức mạnh bộ xử lý (việc truyền tải diễn ra theo đợt để tránh tổn thất làm tròn). Một phương pháp tương tự được áp dụng cho tất cả con cháu của con cháu.

Cơ chế này đảm bảo sự phân bổ công bằng sức mạnh của bộ xử lý và đảm bảo rằng không có quá trình nào “đánh cắp” tài nguyên từ người khác.

Giới hạn CPU

Mặc dù thực tế là cấu hình giới hạn và yêu cầu trong K8 trông giống nhau nhưng việc triển khai chúng hoàn toàn khác nhau: điều này gây hiểu lầm nhất và phần ít được ghi chép nhất.

K8 tham gia Cơ chế hạn ngạch CFS để thực hiện các giới hạn. Cài đặt của chúng được chỉ định trong tệp cfs_period_us и cfs_quota_us trong thư mục cgroup (tệp cũng nằm ở đó cpu.share).

Không giống như cpu.share, hạn ngạch được dựa trên khoảng thời gian, chứ không phải dựa trên sức mạnh bộ xử lý có sẵn. cfs_period_us chỉ định khoảng thời gian của khoảng thời gian (kỷ nguyên) - nó luôn là 100000 μs (100 ms). Có một tùy chọn để thay đổi giá trị này trong K8s, nhưng hiện tại nó chỉ có sẵn ở dạng alpha. Bộ lập lịch sử dụng kỷ nguyên để khởi động lại hạn ngạch đã sử dụng. Tệp thứ hai cfs_quota_us, chỉ định thời gian khả dụng (hạn ngạch) trong mỗi kỷ nguyên. Lưu ý rằng nó cũng được chỉ định bằng micro giây. Hạn ngạch có thể vượt quá độ dài kỷ nguyên; nói cách khác, nó có thể lớn hơn 100 ms.

Hãy xem xét hai tình huống trên máy 16 lõi (loại máy tính phổ biến nhất chúng tôi có tại Omio):

Giới hạn CPU và điều tiết tích cực trong Kubernetes
Kịch bản 1: 2 luồng và giới hạn 200 ms. Không điều tiết

Giới hạn CPU và điều tiết tích cực trong Kubernetes
Kịch bản 2: 10 luồng và giới hạn 200 ms. Quá trình điều chỉnh bắt đầu sau 20 mili giây, quyền truy cập vào tài nguyên bộ xử lý sẽ được tiếp tục sau 80 mili giây nữa

Giả sử bạn đặt giới hạn CPU thành 2 hạt nhân; Kubernetes sẽ dịch giá trị này thành 200 ms. Điều này có nghĩa là vùng chứa có thể sử dụng tối đa 200 mili giây thời gian CPU mà không cần điều tiết.

Và đây là nơi niềm vui bắt đầu. Như đã đề cập ở trên, hạn ngạch khả dụng là 200 ms. Nếu bạn đang làm việc song song mười các luồng trên máy 12 lõi (xem hình minh họa cho kịch bản 2), trong khi tất cả các nhóm khác không hoạt động, hạn ngạch sẽ hết chỉ sau 20 mili giây (vì 10 * 20 mili giây = 200 mili giây) và tất cả các luồng của nhóm này sẽ bị treo » (ga) trong 80 ms tiếp theo. Đã được đề cập lỗi lên lịch, do đó xảy ra hiện tượng điều tiết quá mức và container thậm chí không thể hoàn thành hạn ngạch hiện có.

Làm thế nào để đánh giá điều tiết trong nhóm?

Chỉ cần đăng nhập vào nhóm và thực hiện cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods - tổng số khoảng thời gian lập kế hoạch;
  • nr_throttled - số khoảng thời gian được điều chỉnh trong thành phần nr_periods;
  • throttled_time - thời gian điều chỉnh tích lũy tính bằng nano giây.

Giới hạn CPU và điều tiết tích cực trong Kubernetes

Chuyện gì đang thực sự xảy ra vậy?

Kết quả là chúng tôi nhận được mức điều chỉnh cao trong tất cả các ứng dụng. Đôi khi anh ấy ở trong một lần rưỡi mạnh hơn tính toán!

Điều này dẫn đến nhiều lỗi khác nhau - lỗi kiểm tra tính sẵn sàng, bộ chứa bị treo, ngắt kết nối mạng, hết thời gian chờ trong các cuộc gọi dịch vụ. Điều này cuối cùng dẫn đến độ trễ tăng lên và tỷ lệ lỗi cao hơn.

Quyết định và hậu quả

Mọi thứ đều đơn giản ở đây. Chúng tôi đã bỏ giới hạn CPU và bắt đầu cập nhật nhân hệ điều hành theo cụm lên phiên bản mới nhất, trong đó lỗi đã được sửa. Số lượng lỗi (HTTP 5xx) trong dịch vụ của chúng tôi ngay lập tức giảm đáng kể:

Lỗi HTTP 5xx

Giới hạn CPU và điều tiết tích cực trong Kubernetes
Lỗi HTTP 5xx đối với một dịch vụ quan trọng

Thời gian đáp ứng p95

Giới hạn CPU và điều tiết tích cực trong Kubernetes
Độ trễ yêu cầu dịch vụ quan trọng, phân vị thứ 95

Chi phí vận hành

Giới hạn CPU và điều tiết tích cực trong Kubernetes
Số giờ sử dụng phiên bản

Bắt là gì?

Như đã nêu ở đầu bài viết:

Có thể rút ra một sự tương tự với một căn hộ chung... Kubernetes hoạt động như một nhà môi giới bất động sản. Nhưng làm thế nào để người thuê nhà không xung đột với nhau? Điều gì sẽ xảy ra nếu một trong số họ quyết định mượn phòng tắm trong nửa ngày?

Đây là điều đáng chú ý. Một thùng chứa bất cẩn có thể ngốn hết tài nguyên CPU có sẵn trên máy. Nếu bạn có ngăn xếp ứng dụng thông minh (ví dụ: JVM, Go, Node VM được cấu hình đúng cách), thì đây không phải là vấn đề: bạn có thể làm việc trong điều kiện như vậy trong thời gian dài. Nhưng nếu các ứng dụng được tối ưu hóa kém hoặc không được tối ưu hóa chút nào (FROM java:latest), tình hình có thể vượt khỏi tầm kiểm soát. Tại Omio, chúng tôi có Dockerfiles cơ sở tự động với các cài đặt mặc định đầy đủ cho ngăn xếp ngôn ngữ chính, vì vậy vấn đề này không tồn tại.

Chúng tôi khuyên bạn nên theo dõi các số liệu SỬ DỤNG (mức sử dụng, độ bão hòa và lỗi), độ trễ API và tỷ lệ lỗi. Đảm bảo rằng kết quả đáp ứng mong đợi.

tài liệu tham khảo

Đây là câu chuyện của chúng tôi. Các tài liệu sau đây đã giúp ích rất nhiều cho việc hiểu điều gì đang xảy ra:

Báo cáo lỗi Kubernetes:

Bạn đã gặp phải vấn đề tương tự trong thực tế của mình hoặc có kinh nghiệm liên quan đến điều tiết trong môi trường sản xuất container chưa? Chia sẻ câu chuyện của bạn trong phần bình luận!

Tái bút từ người dịch

Đọc thêm trên blog của chúng tôi:

Nguồn: www.habr.com

Thêm một lời nhận xét