Слышали ли вы когда-нибудь о такой практике, как “Monitoring Events Enrichment”? Если вкратце, то это практика наполнения дополнительной информацией сообщений от мониторинга, так что на выяснение причины проблемы уходит меньше времени и телодвижений.
Есть даже контора, которая помогает внедрять эту практику на коммерческой основе, у них на сайте есть неплохие примеры того, что и зачем можно добавить к обычному сообщению от Nagios.
Сама эта идея мне очень нравится, и вот после очередного неинформативного
сообщения от мониторинга,
которое пришло тогда, когда я был далеко от компьютера, и гласило
CheckDockerStats CRITICAL: 91% CPU Used!
, я решил добавить
таким сообщениям полезного контекста.
Конечно, очевидным кандидатом на добавление к сообщению от мониторинга в данном случае является список процессов в контейнере, отсортированных по использованию CPU. Если это не кажется очевидным, то представьте, что вам приходит сообщение от мониторинга, что в каком-то контейнере превышен порог использования CPU/памяти, а вы не у компьютера, и к тому моменту, как вы можете проверить, что же там случилось, контейнер уже вернулся к своему нормальному потреблению ресурсов.
Для обычных проверок check_load
и check_memory
из пакета Nagios-plugins
я добавил топ-5 потребителей ресурсов достаточно легко - обернул стандратные
плагины в простенький wrapper на bash, получилось примерно так:
#!/bin/bash
# check_memory wrapper
output=$(/usr/lib/nagios/plugins/check_memory "$@")
status=$?
message=$(echo "$output" | cut -d\| -f 1)
perfdata=$(echo "$output" | cut -d\| -f 2)
echo -n "$message"
if [ 0 -ne "$status" ]; then
echo
ps aux --sort -pmem | head -n 5
fi
echo " | $perfdata"
exit $status
При превышении заданных лимитов вывод этого wrapperа получается такой:
root@host:~# /usr/lib/nagios/plugins/check_memory_wrapper.sh -f -C -w 15 -c 10
CRITICAL - 4.9% (1198364 kB) free!
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
traffic+ 28086 11.3 91.7 25970900 22651436 ? Sl Mar18 5553:57 /usr/bin/traffic_server -M --httpport 8080:fd=9
haproxy 26404 7.0 0.2 85832 59364 ? Ss Mar19 3332:04 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -D -sf 25137
syslog 25077 0.5 0.1 332568 29764 ? Ssl Apr06 109:24 rsyslogd
root 15342 0.1 0.0 84124 15212 ? S Mar08 95:21 /usr/bin/perl -w /usr/sbin/ldirectord /etc/ldirectord.cf start
| TOTAL=24678520KB;;;; USED=23480156KB;20976742;22210668;; FREE=1198364KB;;;; CACHES=980600KB;;;;
и сразу из сообщения о проблеме видно, кто сожрал всю память на сервере.
Для контейнера получить такую информацию сложнее, поскольку:
- в контейнере может не быть ни NRPE-сервера, ни sshd;
- утилиты top, ps и прочие, получающие данные из общесистемных счетчиков
в
/proc
, показывают в контейнерах неверные данные; - cAdvisor не предоставляет информации о процессах внутри контейнера.
Поскольку информацию об использовании памяти/CPU я беру по HTTP из cAdvisor, я решил следовать этому же сценарию и для обогащения сообщений о проблемах, и написать свой мини-сервис для получения информации о том, какие процессы запущены в контейнере.
Поскольку сервис получался комплиментарным к cAdvisor, я назвал его cAdvisor-companion.
В общем, алгоритм решения задачи оказался не сложным - достаточно всего лишь
пройтись по файловой системе /proc
на родительском хосте, собрать данные
по всем процессам, и фильтровать их по cgroup.
Поскольку мой сервис должен был собирать в отдельном потоке данные, и при этом отвечать на HTTP-запросы к API, да еще делать все это быстро, я выбрал для реализации не привычный Python, а “стильный, модный, молодежный” Golang.
Сразу скажу, что это решение оказалось удачным, и позволило мне достаточно быстро и просто написать этот небольшой сервис, уложившись меньше чем в 500 строк кода вместе с тестами.
Итогом нескольких дней разработки в свободное от работы время стал сервис,
пригодный к разворачиванию в контейнере, которому для работы требуется только
read-only доступ к /proc
, и который по HTTP API отдает в виде JSON историю
запущенных процессов для указанного контейнера.
Подробнее о том, как этот сервис запускать, что из себя представляет API, почему я не стал использовать Docker API, и прочие полезные вещи можно почитать на Github-страничке cAdvisor-companion, ну а я хочу рассказать как я использую этот самописный сервис, и какие у него есть недостатки.
Использую я его как и планировал - получаю список процессов в контейнере в случае сообщений от мониторинга, и вывожу вместе с алертом. Теперь сообщение из начала статьи, которое и побудило меня к написанию своего сервиса, выглядит примерно так:
CheckDockerStats WARNING: 83.32% CPU used!
USER PID %CPU %MEM VSZ RSS STAT COMMAND
65534 1285 18.3 0.0 18396 1632 S /usr/sbin/nutcracker -v 11 -c /etc/twemproxy/config.yml
1000 24891 7.5 1.4 299524 99200 S python /path/to/program.py
1000 4474 7.4 1.4 303356 103416 S python /path/to/program.py
1000 4468 7.4 1.4 301416 101228 S python /path/to/program.py
1000 4475 7.4 2.5 382636 181792 S python /path/to/program.py
Проверки использования ресурсов в контейнерах, пример вывода которых я привел выше, я делаю все тем же скриптом check_cadvisor.py, только слегка доработанным для получения данных о процессах от cAdvisor-companion.
Ну а теперь о недостатках:
- мы видим не PID процесса в контейнере, а PID процесса в родительской системе
- %CPU, который мы получаем от cAdvisor-companion, это относительный процент использования CPU в контейнере. Чтобы было понятнее - мы вычисляем то, какой процент из всего процессорного времени, использованного за некий interval контейнером, использовал конкретный процесс, в то время как ps и top показывает то, какой процент доступного процессорного времени использовал процесс за тот же interval.
Несмотря на перечисленные недостатки, сервис со своей задачей справляется, так что если хотите обогатить сообщения от мониторинга полезным контекстом
- попробуйте cAdvisor-companion
в паре с check_cadvisor.py,
это очень легко и абсолютно безопасно - при запуске в виде контейнера он получает
только read-only доступ к
/proc
.