Одна из важных задач при работе с Kubernetes — рациональное распределение ресурсов. Для этого необходимо правильно назначать контейнерам лимиты (limits) и запросы (requests). Но это не всегда простая задача!
Может возникнуть ситуация, когда вы зарезервировали намного больше ресурсов, чем необходимо приложению, потому что хотите избежать проблем, связанных с недостаткам памяти в контейнерах, или возникновения троттлинга ЦП при запуске приложения. В таком случае вам придётся оплачивать неиспользуемые ресурсы. Учтите, что планировщику Kubernetes будет сложнее найти подходящий узел для таких рабочих нагрузок.
Примечание переводчика
Если при написании манифеста в шаблоне контейнера вы указываете request, то при планировании пода на узел запрашиваемые ресурсы резервируется за ним и уже не могут использоваться другими подами.
Эта статья поможет разобраться, как быстро находить неиспользуемые ресурсы с помощью PromQL.
Не будьте жадным разработчиком
Предположим, что вы зарезервировали для контейнера больше ресурсов, чем требуется. Если речь идёт лишь об одном контейнере, то это не должно сильно увеличить ваши расходы. Но если речь идёт о всех запущенных контейнерах, тогда счет за услуги облачного провайдера может существенно возрасти.
К тому же, если контейнеры в подах запрашивают слишком много ресурсов, возможны проблемы с планированием таких подов на узлы кластера в соответствии с вашими приоритетами. Вам придётся потратить дополнительные усилия на отладку и решение проблем с планированием таких подов.
Нам потребуется собирать метрики производительности, для этого подойдут два open-source решения:
kube-state-metrics: Дополнительный агент, который создаёт базовые метрики для всего кластера и предоставляет к ним доступ.
CAdvisor: Анализатор использования контейнерами ресурсов.
![](https://habrastorage.org/getpro/habr/upload_files/52a/b40/537/52ab405373379825c7e1369236ec17ea.png)
Как обнаружить неиспользуемые ресурсы?
Процессор
Если вы запросите слишком мало ресурсов ЦП, то можете столкнуться с троттлингом. С другой стороны, попытка решить эту проблему, указав в запросе слишком много ядер, в итоге приведёт к простаиванию ресурсов. Попробуем решить эту проблему.
![](https://habrastorage.org/getpro/habr/upload_files/192/824/8bc/1928248bcd0807f08d1e1a76cc60fe39.png)
Обнаружение простаивающих ядер ЦП
С помощью Prometheus метрикcontainer_cpu_usage_seconds_total
и kube_pod_container_resource_requests
вы сможете легко определить количество простаивающих ядер:
sum((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[30m]) - on (namespace,pod,container) group_left avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="cpu"})) * -1 >0)
![](https://habrastorage.org/getpro/habr/upload_files/221/6d4/836/2216d4836e57ed63b55c4a57aa1ea8e1.png)
По графику видно, что более 7 ядер простаивают в течение длительного времени.
Как определить, какой namespace потребляет наибольший объём ресурсов?
Суммируя предыдущий запрос PromQL по namespace, вы можете получить более детальную картину. Теперь вы знаете, какой отдел злоупотребляет резервированием ресурсов:
sum by (namespace)((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[30m]) - on (namespace,pod,container) group_left avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="cpu"})) * -1 >0)
![](https://habrastorage.org/getpro/habr/upload_files/e76/cb5/261/e76cb5261fb6ed406e5cc97e253c9a9d.png)
Топ-10 контейнеров с избыточным CPU Request
В руководстве по началу работы с PromQL мы уже рассказывали про функцию topk
которая возвращает первые n
результатов вашего запроса. Например:
topk(10,sum by (namespace,pod,container)((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[30m]) - on (namespace,pod,container) group_left avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="cpu"})) * -1 >0))
![](https://habrastorage.org/getpro/habr/upload_files/bd7/96b/899/bd796b899062eef471677bc643948aeb.png)
Память
Правильное резервирование памяти не менее важная задача. Если при запуске приложения ему не хватит памяти, контейнер будет перезагружен. Также учтите, что узел начнёт вытеснять поды, когда ему перестанет хватать памяти, поэтому важно запрашивать оптимальный объем ресурсов, чтобы развернуть больше подов.
![](https://habrastorage.org/getpro/habr/upload_files/450/ca8/2c4/450ca82c43935529fd34a5c7b299193c.png)
Обнаружение неиспользуемой контейнерами памяти
Воспользуемся результатами запросов container_memory_usage_bytes
и kube_pod_container_resource_requests
, чтобы определить объём зарезервированной, но не утилизированной памяти:
sum((container_memory_usage_bytes{container!="POD",container!=""} - on (namespace,pod,container) avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="memory"})) * -1 >0 ) / (1024*1024*1024)
![](https://habrastorage.org/getpro/habr/upload_files/939/1a9/fc9/9391a9fc95b53ca61d61905e3b3303ac.png)
В приведённом выше примере виден объём неиспользуемой оперативной памяти.
Определим, какие namespace самые расточительные
Агрегируем результаты предыдущего запроса по namespace так же, как мы это делали для ЦП:
sum by (namespace)((container_memory_usage_bytes{container!="POD",container!=""} - on (namespace,pod,container) avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="memory"})) * -1 >0 ) / (1024*1024*1024)
![](https://habrastorage.org/getpro/habr/upload_files/afe/265/f4f/afe265f4f24f1489ee92b32241eed100.png)
Найдём контейнеры с избыточным Memory Request
Используем уже знакомую нам функцию topk
, чтобы определить 10 самых расточительных контейнеров:
topk(10,sum by (namespace,pod,container)((container_memory_usage_bytes{container!="POD",container!=""} - on (namespace,pod,container) avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="memory"})) * -1 >0 ) / (1024*1024*1024))
![](https://habrastorage.org/getpro/habr/upload_files/55e/78f/be4/55e78fbe4608187813501a53152562f1.png)
Как оптимизировать зарезервированные ресурсы?
![](https://habrastorage.org/getpro/habr/upload_files/0c1/d05/7e7/0c1d057e7f85a1753979fd6550d8bbc3.png)
Чтобы правильно рассчитать ресурсы, необходимые вашему кластеру Kubernetes, нужно проанализировать текущее использование ресурсов вашими контейнерами. Для этого воспользуйтесь запросом PromQL, который вычисляет среднюю загрузку ЦП для всех контейнеров, относящихся к одному и тому же объекту - Deployment
, StatefulSet
или DaemonSet
.
avg by (namespace,owner_name,container)((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[5m])) * on(namespace,pod) group_left(owner_name) avg by (namespace,pod,owner_name)(kube_pod_owner{owner_kind=~"DaemonSet|StatefulSet|Deployment"}))
![](https://habrastorage.org/getpro/habr/upload_files/3a9/8cd/e2e/3a98cde2e9db1ab08ff495f99cd0ddff.png)
На графике вы можете увидеть среднюю загрузку ЦП для каждого контейнера. Как правило, стоит запрашивать от 85% до 115% среднего использования ЦП или памяти.
Как измерить результаты после оптимизации параметров
![](https://habrastorage.org/getpro/habr/upload_files/69f/411/98f/69f41198f9e67aa65c8169fe284e03ef.png)
После оптимизации резервирования ресурсов необходимо проверить, как изменения повлияли на вашу инфраструктуру. Для этого вы можете сравнить текущие значения со значениями за прошлую неделю:
sum((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[30m]) - on (namespace,pod,container) group_left avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="cpu"})) * -1 >0) - sum((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[30m] offset 1w) - on (namespace,pod,container) group_left avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="cpu"} offset 1w )) * -1 >0)
![](https://habrastorage.org/getpro/habr/upload_files/d41/33c/27d/d4133c27d07471c0ab0b40917d2d5982.png)
На приведенном выше графике вы можете увидеть, что после применения предыдущих оптимизаций в нашем кластере осталось меньше неиспользуемых ядер.
Заключение
Теперь вам известны последствия проблемы "жадного разработчика" и способы обнаружения чрезмерного резервирования ресурсов платформы. Кроме того, вы узнали, как правильно настроить запросы ваших контейнеров и как измерить влияние ваших оптимизаций.