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