Нещодавно ми перейшли на виробниче середовище до Kubernetes. Я хотів би застосувати обмеження на процесор на контейнерах. Я отримую суперечливі показники процесора, які не підходять разом. Ось моя настройка:
- Агенти DataDog, що працюють як
Daemonset
- Існуючі програми, що працюють без обмежень процесора
- Контейнери - це багатопотокові додатки Ruby
- Дві показники:
kubernetes.cpu.usage.{avg,max}
іdocker.cpu.usage
c4.xlarge
кластерні вузли (4 vCPU або 4000m в Кубернеті)
kubernetes.cpu.usage.max
звіти ~ 600м для відповідних контейнерів. docker.cpu.usage
звіти ~ 60%. Звідси випливає, що обмеження процесора в 1000 м було б більш ніж достатньою ємністю при нормальній роботі.
Я встановив межу в 1000м. Потім docker.container.throttles
істотно піднімається вгору kubernetes.cpu.usage.max
і docker.cpu.usage
залишається таким же. У цей час система все опускається на коліна. Це для мене немає сенсу.
Я досліджував статистику Докера. Здається, що docker stats
(і базовий API) нормалізують навантаження відповідно до ядер CPU. Тож у моєму випадку docker.cpu.usage
від 60% припадає (4000м * 0,60) до 2400м у Кубернеті. Однак це не співвідноситься з жодними номерами Кубернетів. Я зробив ще один експеримент, щоб перевірити свою гіпотезу про те, що числа Кубернета є невірними. Я встановив обмеження в 2600 м (для деякого додаткового запасу). Це не призвело до жодних дроселів. Однак Kubernetes спостерігав, що використання процесора не змінилося. Це залишає мене розгубленим.
Отже, мої запитання:
- Це відчувається як помилка в Kubernetes (чи щось у стеці?)
- Чи правильно моє розуміння?
Моє наступне питання стосується того, як правильно визначити процесор для додатків Ruby. В одному контейнері використовується Puma. Це багатопотоковий веб-сервер з налаштованою кількістю потоків. HTTP-запити обробляються одним із потоків. Друга програма - ощадливий сервер, що використовує потоковий сервер. Кожне вхідне TCP-з'єднання обробляється власним потоком. Потік виходить, коли з'єднання закривається. Ruby як GIL (Global Interpreter Lock), тож лише один потік може одночасно виконувати код Ruby. Це дає змогу виконувати декілька потоків вводу-виводу тощо.
Я вважаю, що найкращим підходом є обмеження кількості потоків, що виконуються в кожній програмі, та наближення обмежень процесора Kubernetes на основі кількості потоків. Процеси не формуються, тому загальне використання процесора передбачити важче.
Тут питання: як правильно передбачити використання процесора та ліміти цих програм?