Недавно мы переключили нашу производственную среду на 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 на основе количества потоков. Процессы не разветвляются, поэтому общее использование процессора сложнее предсказать.
Вопрос здесь: как правильно предсказать использование процессора и ограничения для этих приложений?
источник
Ответы:
Несколько вещей здесь:
Вы работаете в AWS ec2, поэтому все, что вы делаете в своем экземпляре для измерения ЦП, вычисляет ЦП на уровне гипервизора, а не на уровне экземпляра. Для этого запустите любой нагрузочный тест и проверьте iostat -ct 1 и загрузку процессора в cloudwatch. Загрузка ЦП в cloudwatch всегда на 10-20% больше, чем сообщает iostat, потому что iostat будет предоставлять использование ЦП на уровне гипервизора.
В качестве докера, чтобы увидеть, как сравниваются метрики kubernetes и docker, я предлагаю запускать контейнеры с --cpuset = 1 или любым числом, чтобы все контейнеры могли использовать только один vCPU.
Также в AWS 1 CPU = 2vcpu. Это гиперссылка на 2. Возможно, вы можете принять это во внимание при расчете.
Наконец, лучший показатель, который можно использовать, чтобы увидеть загрузку процессора для конкретного приложения, - это использовать htop и сопоставлять его с показателями cloudwatch.
Я также наблюдал, как иногда демон docker прикреплял себя к одному из виртуальных процессоров, и, следовательно, когда вы уменьшаете его до 1000 м, может быть, вся установка замедляется, потому что сокращение происходит на любом из vpcus. Вы можете использовать mpstat, чтобы получить подробную информацию об этом.
Наконец, на уровне хоста вы можете подключить докер к одному ЦП и наблюдать больше.
Надеюсь, это немного сблизит вас. Обновите меня, если вы уже нашли решение.
источник