Почему использование памяти контейнера остается высоким, а OOM часто

Go

Недавно я пересматривал и думал (писал PPT), разбираясь в статус-кво, и обнаружил, что эта проблема существует уже давно.После некоторых перипетий я, наконец, определил виновника и относительно возможное решение, поэтому я также поделитесь ходом расследования здесь.

Лента новостей:

  • Первые пол года существования Kubernetes я пользовался только Kubernetes, у меня не было разрешения на разработку, и бизнес-сервисов было очень мало, я был занят написанием нового бизнеса, и ветер был спокоен.

  • Во второй половине прошлого года Kubernetes было мало бизнес-сервисов, и его время от времени пробуждала поэтапная эксплуатация и обслуживание.Почему у вас такой высокий уровень использования служебной памяти?В это время все все еще бегут к новому бизнесу, догадываясь, что это может быть проблема с бизнес-кодом, но нет никакой корректировки кода, чтобы попытаться ее решить.

  • На втором году Kubernetes бизнес-сервисы постепенно увеличивались, и лимит контейнеров в целом увеличился.Некоторые бизнес-сервисы превратились в монстров памяти.Поэтому, если нет лимита, чрезмерное использование сервиса приведет к выселению, поэтому язык обратной связи тоже изменится. стало: "Почему использование вашей служебной памяти так велико, это всегда OOM Kill, поторопитесь и проверьтеСообщается, что несколько бизнес-лидеров отправились на расследование (из-за отзывов OOM), и кажется, что окончательное решение не было достигнуто.

Мы не можем не задуматься, почему память отдельных бизнес-сервисов Go всегда так велика, а лимит контейнера часто исчерпан, так что пассивный OOM Kill не представляет угрозы безопасности?

Феномен

Память остается высокой

Обнаружено, что использование памяти отдельными бизнес-сервисами довольно велико, что вызывает тревогу, а через Grafana установлено, что ранним утром (без трафика) использование памяти все еще выравнивается, и планов по снижению нет. , а пик еще более невероятный, как бомба памяти:

image

И сервис, который я наблюдал, был всего 100 МБ в первые годы. Сейчас, с итерацией и ростом бизнеса, он достиг стабильных 4ГБ, а лимиты контейнеров открыли ему путь, но я думаю, что это не может быть бесконечное увеличение ресурсов, это большая проблема.

Введите цикл перезапуска

Для некоторых бизнес-сервисов бизнес-объем небольшой, поэтому, естественно, лимит контейнера не регулируется, поэтому, если ресурсы памяти недоступны, а лимит превышен, он войдет в сумасшедший цикл перезапуска:

image

Перезагрузка почти 300 раз — это очень ненормально, не говоря уже о полученных оповещениях.

Проверить

Гипотеза 1: часто обращаются за повторяющимися объектами

Отдельные бизнес-сервисы, которые имеют проблемы, имеют несколько характеристик, то есть они в основном представляют собой функции обработки изображений, такие как декомпрессия изображений, пакетная генерация QR-кодов, генерация PDF-файлов и т. д. Таким образом, сомнительно, что повторяющиеся объекты часто применяются, когда объем большой. , а сам Go вовремя не освобождал память, в результате чего постоянное занятие.

sync.Pool

В принципе, чтобы решить проблему «частых обращений за повторяющимися объектами», большинство из нас будет использовать многоуровневые пулы памяти, или мы можем использовать наиболее распространенный sync.Pool, Здесь вы можете обратиться к «Ночному чтению», цитируемому Замечание по поводу синхронизации пула, сценарии для таких ситуаций:

Когда несколько горутин должны создать один и тот же объект, если горутин слишком много, количество создаваемых объектов резко возрастет, что приведет к увеличению нагрузки на сборщик мусора. Это образует порочный круг «большой параллелизм — большое использование памяти — медленный сборщик мусора — снижение способности обрабатывать параллелизм — больший параллелизм».

Проверить сцену

В описании есть несколько ключевых слов: большой параллелизм, слишком много горутин, повышенная нагрузка на сборщик мусора и медленный сборщик мусора. Другими словами, вышеупомянутые жесткие условия должны быть выполнены, прежде чем можно будет считать, что это соответствует гипотезе.

Вытащив горутину PProf, мы увидим, что количество горутин невелико:

image

Кроме того, ранним утром до 6 часов, когда нет трафика, это не соответствует ситуации большого параллелизма и слишком большого количества горутин.Если вы хотите дополнительно подтвердить, вы можете использовать Grafana для подтверждения уровня количество.

Исходя из заключения, я не думаю, что есть особо прямая связь с этим, но предполагаю, что косвенная связь между соответствующей бизнес-функцией и результатом должна существовать.

Гипотеза 2: Неизвестная утечка памяти

Памяти много, и один из ответов — угадать, есть ли утечка, и в настоящее время у нас есть только один процесс Go, работающий в нашем контейнере, поэтому сначала проверьте, нет ли проблемы с приложением Go. В это время вы можете использовать кучу PProf (вы можете использовать base -diff):

image

Очевидно, что указанное им использование памяти невелико.Может быть, это ошибка в PProf? Также по команде можно определить, что RSS процесса Go не высокий, а VSZ относительно "высокий", я писал об этом статью в 2019 году«Приложение Go занимает слишком много памяти, давайте проверим? (глава ВСЗ)", на этот раз ВСЗ слишком высока и навела меня на мысль.

В заключение, это также не похоже на проблему утечек памяти в процессе Go, поэтому она также исключена.

Гипотеза 3: изменение политики Madvise

  • До Go 1.12 среда выполнения Go для Linux использовалаMADV_DONTNEEDСтратегия, может заставить RSS снижаться быстрее, то есть эффективность почти.

  • В Go 1.12 и более поздних версиях среда выполнения Go специально оптимизирована для этого с использованием более эффективногоMADV_FREEСтратегия. Но побочным эффектом этого является то, что RSS не упадет сразу, а занятое не будет освобождено, пока в системе не возникнет нехватка памяти, и RSS упадет.

Проверьте версию ядра Linux контейнера:

$ uname -a
Linux xxx-xxx-99bd5776f-k9t8z 3.10.0-693.2.2.el7.x86_64 

ноMADV_FREEДля изменения политики требуется ядро ​​Linux версии 4.5 и выше (подробности см.go/issues/23687), что явно не совпадает, поэтому тоже исключается из угадывания.

Гипотеза 4: Проблема с условиями мониторинга/дискриминации

Может ли быть так, что диаграмма Grafana неверна, а критерий Kubernetes OOM Kill неверен? Это, очевидно, маловероятно, ведь мы принимаем облако, а Alibaba Cloud Kubernetes работает уже несколько лет.

image

Но в этом сомнении я узнал, что критерием для OOM является показатель container_memory_working_set_bytes, так что у меня есть предположение для следующего шага.

Гипотеза 5: Механизм контейнерной среды

Поскольку на него не влияет ни бизнес-код, ни Go Runtime напрямую, связан ли он с самой средой?Мы можем знать, что критерием для OOM контейнера является container_memory_working_set_bytes (текущий рабочий набор).

container_memory_working_set_bytes предоставляется cadvisor и соответствует следующим показателям:

image

В заключение, память конвертируется в 4 ГБ+, что является каменным молотом. Следующий вопрос — как рассчитать Память, что явно не то же самое, что RSS.

причина

отcadvisor/issues/638Можно знать, что состав индикатора container_memory_working_set_bytes на самом деле RSS + Cache. В случае высокого кэша процесс обычно имеет большое количество файловых операций ввода-вывода, а занимаемый кэш может быть относительно большим.Предположение также связано с методами выпуска и утилизации кэша в версии Go и Linux. версия ядра.

image

Общие функции каждого бизнес-модуля, такие как:

  • Пакетная распаковка изображений.
  • Пакетная генерация QR-кода.
  • Пакетная загрузка визуализированных изображений.
  • Пакетная генерация PDF.
  • ...

Пока это сервис с участием большого количества файловых IO, он в основном постоянный клиент этой проблемы.Написание таких сервисов в основном пишут то одно, то другое, потому что это смешанная проблема, а другие бизнес-сервисы чисто операционные -based очень «нормальные», памяти высокой не будет.

решение

В этом сценарии критерий container_memory_working_set_bytes, предоставленный cadvisor, является неизменяемым, то есть критерий не может быть изменен на RSS, поэтому мы можем только рассмотреть возможность проявления инициативы.

Во-первых, хорошо поработать над многоуровневым управлением пулом памяти, что может смягчить симптомы этой проблемы. Но это сложно.С другой точки зрения, как узнать, когда этот тип сервиса вдруг появится на каком кластере, не говоря уже о том, что ожидания разработчиков неравномерны, а еще можно писать многоуровневые пулы памяти и пишите баги оф.

Также нереально позволять бизнес-сервисам перезапускаться бесконечно.Пассивный перезапуск, без контроля и тревоги, есть риск.

Поэтому, чтобы проявить инициативу, вы можете сделать "ручной" HPA со скриптом в среде развертывания.Когда индекс памяти контейнера превышает согласованный лимит, запускается новый контейнер на замену, а затем освобождается исходный контейнер, и замену можно ожидать как положено.и бизнес стабилен.

image

Суммировать

Хотя временной отрезок этой проблемы относительно большой, в целом это поэтапное исследование, что можно сказать связано с незнанием Kubernetes по сути. Но в целом эта проблема предполагает широкий круг, потому что возможностей высокой памяти много, а проверять по одному, при ограниченных правах на разработку, долго и трудозатратно.

Основная идея исследования такова:

  1. Подозрительный бизнес-код (PProf).
  2. Подозреваемый другой код (PProf).
  3. Сомневайтесь в Go Runtime.
  4. Инструмент сомнения.
  5. Сомневайтесь в окружающей среде.

Большое спасибо всем большим ребятам, с которыми я консультировался в течение этого периода времени. Такое ощущение, что есть слой вуали, и он будет быстро обнаружен после его прокалывания. Если у вас есть другие решения, пожалуйста, не стесняйтесь общаться.

мой блог

Оригинальный адрес:Почему использование памяти контейнера остается высоким, а OOM часто

мой публичный аккаунт

image