Один из клиентов, использующих Plumbr apm недавно столкнулся с особой проблемой в их производственной среде, где один из контейнеров Docker в производстве вышел с кодом выхода 137. Установка окружающей среды была довольно простой
- самостоятельное оборудование с Ubuntu OS;
- Несколько контейнеров Docker также работают с ОС Ubuntu, работающей на машине
- Виртуальные машины Java, работающие внутри контейнеров Docker.
Расследование этого вопроса привело нас к Докер документация по теме. Чтение этого дало понять, что причиной этого кода выхода является либо ручная команда Docker Stop, либо из -за условия памяти в сочетании с последующим сигналом убийства, отправленным Oomkiller ядра.
Greppling через системное журнал подтвердил, что Oomkiller ядра ОС, развернутого на оборудовании, действительно запускается:
[138805.608851] java invoked oom-killer: gfp_mask=0xd0, order=0, oom_score_adj=0 [138805.608887] [] oom_kill_process+0x24e/0x3b0 [138805.608916] Task in /docker/264b771811d88f4dbd3249a54261f224a69ebffed6c7f76c7aa3bc83b3aaaf71 killed as a result of limit of /docker/264b771811d88f4dbd3249a54261f224a69ebffed6c7f76c7aa3bc83b3aaaf71 [138805.608902] [ ] pagefault_out_of_memory+0x14/0x90 [138805.608918] memory: usage 3140120kB, limit 3145728kB, failcnt 616038 [138805.608940] memory+swap: usage 6291456kB, limit 6291456kB, failcnt 2837 [138805.609043] Memory cgroup out of memory: Kill process 20611 (java) score 1068 or sacrifice child
Как видно из этого отрывка для логарифмического, предел 3145728 КБ (около 3 ГБ) приближался к процессу Java, что запускает прекращение контейнера Docker. Это было своеобразно, потому что сам Docker был запущен с пределом 4 ГБ в файле скомплектования Docker.
Как вы, вероятно, знаете, JVM также ограничивает использование памяти. В то время как сам Docker был настроен на предел 4 ГБ в файле Docker-Compose, с JVM был запущен. Это может создать дополнительную путаницу, но использование памяти обратите внимание на JVM под рукой может превышать предел, указанный -XMX, как описано в одном из наших предыдущих постов, анализирующих использование памяти JVM.
Понимание всего этого оставило нас все еще в замешательстве. Докер должен быть допущен на 4G памяти, так почему же он уже на 3 ГБ? Еще несколько Googling показали, что в ОС, развернутой непосредственно на оборудовании, есть еще один предел памяти.
Передай привет CGROUPS. CGROUPS (AKA Control Groups) — это функция ядра Linux для ограничения, полиции и учета использования ресурса для набора процессов. По сравнению с другими подходами (команда «Ниццы» или/etc/security/limits.conf), cgroups предлагают большую гибкость, поскольку они могут работать в (суб) наборах процессов.
В нашей ситуации CGROUPS ограничивал использование памяти (через память. LIMIT_IN_BYTES) 3GB. Теперь мы куда -то получали!
Проверка использования памяти и событий GC с использованием Plumbr показала, что большую часть времени использования памяти памяти JVM, работающей внутри докера, составляло около 700 МБ. Единственные исключения произошли непосредственно перед событиями завершения, где распределение памяти усилилось. За шипами последовали длительную паузу GC. Так что, казалось, происходило:
- Код Java, работающий внутри JVM, пытался выделить много памяти.
- JVM, после проверки, что есть много памяти для использования ниже 3 ГБ XMX Limit, попросил больше памяти из операционной системы.
- Docker подтвердил, что есть много памяти, чтобы использовать ниже предела 4 ГБ, и не обеспечил никаких ограничений.
- Ядро OS подтвердило, что предел памяти CGRUP 3 ГБ приближается и убит контейнер Docker.
- Процесс JVM был убит вместе с контейнером Docker, прежде чем он сможет выполнить свои собственные процедуры OutofmemoryError.
Понимая это, мы настроили все ограничения памяти для вовлеченных компонентов — 2,5 ГБ до докера и 1,5 ГБ на Java. После того, как это было сделано, JVM смог провести операции OutofmemoryError и бросить его OutofmemoryError. Это включило Plumbr, наконец, сделает свое волшебство — запечатлеть снимки памяти с помощью соответствующих дампов стека и быстро разоблачить, что был один запрос базы данных, который пытался загрузить почти всю базу данных в определенной ситуации.
Забрать
Даже в таком простом развертывании, были задействованы три разных предела памяти
- JVM через параметр -xmx
- Docker через параметр докера с композитом
- ОС через память.
Таким образом, всякий раз, когда вы сталкиваетесь с процессом, убитым убийцей OOM, вам нужно обратить внимание на пределы памяти всех заинтересованных элементов управления.
Еще один вынос для разработчиков Docker. Похоже, что не имеет смысла допускать запуска такого «матришкаса», когда ограничение памяти контейнера вложенного докера установлено выше, чем предел памяти CGROUPS. Кажется простой, если оператор, проверяющий это при запуске контейнеров в сочетании с предупреждающим сообщением при стартапе, сэкономит сотни часов отладки для ваших пользователей в будущем.
Оригинал: «https://dev.to/ivomagi/debugging-virtualised-containerised-environments-is-hard-onb»