Русский
Русский
English
Статистика
Реклама

Observability

Новые ограничения в использовании Docker Hub и как GitLab реагировал на их ввод

30.11.2020 22:22:08 | Автор: admin

Ни для кого уже не новость, что начиная с 2 ноября 2020 года Docker Hub ввел ограничения на скачивание образов: для анонимных пользователей он будет равен одной сотне за шесть часов, а для авторизованных пользователей будет зависеть от уровня подписки.

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

Что же произошло?

Публичный регистр контейнеров Docker Hub очень широко и часто используется сообществом DevOps для достижения разнообразных целей: запуска CI/CD задач, стандартизации процессов, выката контейнерных приложений как в sandbox, так и в production. Как только мы узнали о введении новых ограничения по количеству запросов, мы начали анализ новых правил, чтобы понять, как они повлияют на работу наших пользователей, и как мы можем почем решить возможные проблемы.

Согласно новым правилам после 100 запросов за 6 часов с одного клиентского IP адреса каждый новый docker pull вернет ошибку 429 - too many requests, что несомненно приведет к сломанным CI/CD конвейерам, невозможности выкатить приложения и целому букету ошибок в ваших Kubernetes кластерах. Вполне понятно, что этот лимит может быть очень быстро достигнут, особенно если все задачи выполняются с одного и того же GitLab Runner агента, или если команда из нескольких инженеров работает с одного и того же публичного адреса.

Аутсорсинг Dependency Proxy

Как отметил мой коллега Tim Rizzi "Нам следует срочно всем рассказать о Dependency Proxy", который изначально создавался для проксирования и кэширования образов Docker Hub. Этот функционал существует в GitLab уже довольнейший давно (начиная с версии 11.11), но до сегодняшнего дня был доступен только для пользователей Enterprise подписки уровня Premium. Перед продуктовой командой встал вполне резонный вопрос: "Стоит ли нам вынести Dependency Proxy в open source версию продукта, помогая таким образом широкому сообществу пользователей минимизировать проблемы из-за новых ограничений Docker Hub?"

Не вдаваясь в подробности, ответ на этот вопрос был ДА. При принятии решения, в какой уровень подписки должен попадать тот или иной функционал продуктовая команда GitLab всегда руководствуется вопросом "Кто является целевым пользователем?". Согласно этому принципу те возможности, которые чаще всего запрашивает индивидуальный участник или разработчик, попадают в Core или Open Source версию продукта. Скачивание образов с Docker Hub вполне соответсвует этому описанию. Более того аутсорсинг Dependency Proxy поможет большому количеству разработчиков повысить надежность и производительность их CI/CD конвейеров.

Как результат, начиная с версии 13.6 вышедшей 22 ноября 2020 года, проксирование и кэширование образов в GitLab стало абсолютно бесплатным для всех наших пользователей!

Что дальше?

К сожалению, идеальные решения не появляются мгновенно. Мы как никто другой знаем, что нашему продукты есть куда расти, поэтому активно работаем над улучшением функционала Dependency Proxy и уже в ближайших релизах планируем добавить новые возможности / решить имеющиеся проблемы:

  • 13.7 (22 декабря, 2020)

    • gitlab-#11582 сделает возможным использование Dependency Proxy для приватных групп в GitLab (сегодня работает только для публичных проектов)

    • gitlab-#241639 позволит использовать закэшированный образ даже если Docker Hub недоступен. На сегодня это невозможно, так как даже при наличии закэшированного образа, его манифест всегда скачивается из конечного регистра

    • gitlab-#21619 добавит новый параметр pull_policy в YAML описании CI/CD задач, позволяющий разработчикам самим указывать политику скачивания контейнера (always, if-not-present, never) вместо того, чтобы полагаться на настройки GitLab Runner

    • gitlab-runner-#26558 позволит конфигурировать GitLab Runner с набором политик для скачивания образов (always, if-not-present)

Мониторинг ограничений

Решения обозначенные выше, а также в блог посте моего коллеги Steve Azzopardi, помогут упростить работу с новыми ограничениями, но не избавят от них на все 100%. Поэтому мы также выработали набор советов и инструментов, цель которых - помочь широкому сообществу адаптироваться к новым лимитам за счет их мониторинга.

Как проверить текущее значение лимита?

Документация Dockerрекомендует использовать HTTP запрос для проверки текущего значения ограничений запросов в Docker Hub.

Начнем с определения образа, для которого мы хотим получить текущее ограничение и получения токена для авторизации:

$ IMAGE="ratelimitpreview/test"$ TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$IMAGE:pull" | jq -r .token)$ echo $TOKEN

Следующий шаг - симуляция запросаdocker pull. Вместо использования методаGETотправляемHEADзапрос (он не учитывается при подсчете ограничений). Ответ на этот запрос содержит параметрыRateLimit-Limit и RateLimit-Remaining.

$ curl --head -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/$IMAGE/manifests/latest

В примере ниже количество запросов ограничено2500, из которых2495еще доступны.21600определяет шестичасовой период (в секундах)

RateLimit-Limit: 2500;w=21600RateLimit-Remaining: 2495;w=21600

Автоматизация проверки лимитов

Michael Friedrich, один из евангелистов GitLab, поделился готовым решением на Python для проверки ограничений. Проект включает подробную документацию по установке и использованию

$ python check_docker_hub_limit.py --helpusage: check_docker_hub_limit.py [-h] [-w WARNING] [-c CRITICAL] [-v] [-t TIMEOUT]Version: 2.0.0optional arguments:  -h, --help            show this help message and exit  -w WARNING, --warning WARNING                        warning threshold for remaining  -c CRITICAL, --critical CRITICAL                        critical threshold for remaining  -v, --verbose         increase output verbosity  -t TIMEOUT, --timeout TIMEOUT                        Timeout in seconds (default 10s)

Скрипт возвращает следующие exit коды в зависимости от указанных параметров

  • 0- OK

  • 1- WARNING

  • 2- CRITICAL

$ python3 check_docker_hub_limit.pyOK - Docker Hub: Limit is 5000 remaining 4997|'limit'=5000 'remaining'=4997$ echo $?0$ python3 check_docker_hub_limit.py -w 10000 -c 3000WARNING - Docker Hub: Limit is 5000 remaining 4999|'limit'=5000 'remaining'=4999$ echo $?1$ python3 check_docker_hub_limit.py -w 10000 -c 5000CRITICAL - Docker Hub: Limit is 5000 remaining 4998|'limit'=5000 'remaining'=4998$ echo $?2

Экспортер Prometheus

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

Репозиторий проекта включает демо контейнер, включающий себе экспортер, Prometheus, Grafana, и docker-compose инструкции для его выката

$ cd example/docker-compose$ docker-compose up -d

Перейдите по адресу http://localhost:3000 для доступа к дэшборду Grafana

Надеюсь, наш опыт и рекомендации окажутся для вас полезными!

Подробнее..

EBPF современные возможности интроспекции в Linux, или Ядро больше не черный ящик

22.09.2020 14:11:12 | Автор: admin


У всех есть любимые книжки про магию. У кого-то это Толкин, у кого-то Пратчетт, у кого-то, как у меня, Макс Фрай. Сегодня я расскажу вам о моей любимой IT-магии о BPF и современной инфраструктуре вокруг него.

BPF сейчас на пике популярности. Технология развивается семимильными шагами, проникает в самые неожиданные места и становится всё доступнее и доступнее для обычного пользователя. Почти на каждой популярной конференции сегодня можно услышать доклад на эту тему, и GopherCon Russia не исключение: я представляю вам текстовую версию моего доклада.

В этой статье не будет уникальных открытий. Я просто постараюсь показать, что такое BPF, на что он способен и как может помочь лично вам. Также мы рассмотрим особенности, связанные с Go.

Я бы очень хотел, чтобы после прочтения моей статьи у вас зажглись глаза так, как зажигаются глаза у ребёнка, впервые прочитавшего книгу о Гарри Поттере, чтобы вы пришли домой или на работу и попробовали новую игрушку в деле.

Что такое eBPF?


Итак, о какой такой магии вам тут собирается рассказывать 34-летний бородатый мужик с горящими глазами?

Мы с вами живём в 2020 году. Если вы откроете Твиттер, то прочитаете твиты ворчливых господ, которые утверждают, что софт сейчас пишется такого ужасного качества, что проще всё это выкинуть и начать заново. Некоторые даже грозятся уйти из профессии, так как не могут это больше терпеть: всё постоянно ломается, неудобно, медленно.



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

BIOS, EFI, операционная система, драйвера, модули, библиотеки, сетевое взаимодействие, базы данных, кеши, оркестраторы типа K8s, контейнеры типа Docker, наконец, наш с вами софт с рантаймами и сборщиками мусора. Настоящий профессионал может отвечать на вопрос о том, что происходит после того, как вы вбиваете ya.ru в браузере, несколько дней.

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

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

Один из подходов проанализировать логи. Но проблема в том, что доступны только те, которые заложил в свою систему разработчик. Они не гибкие.

Второй популярный подход изучить метрики. Три самые популярные системы для работы с метриками написаны на Go. Метрики очень полезны, однако, позволяя увидеть симптомы, они не всегда помогают понять причины.

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

Дать возможность менять уровень логирования на лету? Приконнектиться дебаггером к работающей программе и что-то там сделать, не прерывая её работу? Понять, какие запросы поступают в систему, визуализировать источники медленных запросов, посмотреть, на что уходит память через pprof, и получить график её изменения во времени? Замерить latency одной функции и зависимость latency от аргументов? Все эти подходы я отнесу к observability. Это набор утилит, подходов, знаний, опыта, которые вместе дадут вам возможность сделать если не всё что угодно, то очень многое наживую, прямо в работающей системе. Современный швейцарский IT-ножик.



Но как это реализовать? Инструментов на рынке было и есть много: простых, сложных, опасных, медленных. Но тема сегодняшней статьи BPF.

Ядро Linux event-driven-система. Практически всё, что происходит в ядре, да и в системе в целом, можно представить в виде набора событий. Прерывание событие, получение пакета по сети событие, передача процессора другому процессу событие, запуск функции событие.
Так вот, BPF это подсистема ядра Linux, дающая возможность писать небольшие программы, которые будут запущены ядром в ответ на события. Эти программы могут как пролить свет на происходящее в вашей системе, так и управлять ею.

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

В 1994 году появилась первая версия BPF, с которой некоторые из вас наверняка сталкивались, когда писали простые правила для утилиты tcpdump, предназначенной для просмотра, или сниффанья сетевых пакетов. tcpdump можно было задать фильтры, чтобы видеть не все, а только интересующие вас пакеты. Например, только протокол tcp и только порт 80. Для каждого проходящего пакета запускалась функция, чтобы решить, нужно сохранять этот конкретный пакет или нет. Пакетов может быть очень много это значит, что наша функция должна быть очень быстрой. Наши tcpdump фильтры как раз преобразовывались в BPF-функции, пример которой виден на картинке ниже.


Простенький фильтр для tcpdump представлен в виде BPF-программы

Оригинальный BPF представлял из себя совсем простенькую виртуальную машину с несколькими регистрами. Но, тем не менее, BPF значительно ускорила фильтрацию сетевых пакетов. В свое время это был большой шаг вперед.

Оригинальный BPF представлял собой простенькую виртуальную машину с несколькими регистрами. Но тем не менее он значительно ускорил фильтрацию сетевых пакетов. В то время это был большой шаг вперёд.



В 2014 году Алексей Старовойтов расширил функциональность BPF. Он увеличил количество регистров и допустимый размер программы, добавил JIT-компиляцию и сделал верификатор, который проверял программы на безопасность. Но самым впечатляющим было то, что новые BPF-программы могли запускаться не только при обработке пакетов, но и в ответ на многочисленные события ядра, и передавали информацию туда-сюда между kernel и user space.

Эти изменения открыли возможности для новых вариантов использования BPF. Некоторые вещи, которые раньше реализовывались путём написания сложных и опасных модулей ядра, теперь делались относительно просто через BPF. Почему это круто? Да потому что любая ошибка при написании модуля часто приводила к панике. Не к пушистой Go-шной панике, а к панике ядра, после которой только ребут.

У обычного пользователя Linux появилась суперспособность заглядывать под капот, ранее доступная только хардкорным разработчикам ядра или недоступная никому. Эта опция сравнима с возможностью без особых усилий написать программу для iOS или Android: на старых телефонах это было или невозможно, или значительно сложнее.

Новая версия BPF от Алексея получила название eBPF (от слова extended расширенная). Но сейчас она заменила все старые версии BPF и стала настолько популярной, что для простоты все называют её просто BPF.

Где используют BPF?


Итак, что же это за события, или триггеры, к которым можно прицепить BPF-программы, и как люди начали использовать новоприобретённую мощь?

На данный момент есть две большие группы триггеров.

Первая группа используется для обработки сетевых пакетов и для управления сетевым трафиком. Это XDP, traffic control-ивенты и ещё несколько.

Эти ивенты нужны, чтобы:

  • Создавать простые, но очень эффективные файрволы. Компании вроде Cloudflare и Facebook с помощью BPF-программ отсеивают огромное количество паразитного трафика и борются с самыми масштабными DDoS-атаками. Так как обработка происходит на самой ранней стадии жизни пакета и прямо в ядре (иногда BPF-программа даже пушится сразу в сетевую карту для обработки), то таким образом можно обрабатывать колоссальные потоки трафика. Раньше такие вещи делали на специализированных сетевых железках.
  • Создавать более умные, точечные, но всё ещё очень производительные файрволы такие, которые могут проверить проходящий трафик на соответствие правилам компании, на паттерны уязвимостей и т. п. Facebook, например, занимается таким аудитом внутри компании, несколько проектов продают такого рода продукты наружу.
  • Создавать умные балансировщики. Самым ярким примером является проект Cilium, который чаще всего используется в кластере K8s в качестве mesh-сети. Cilium управляет трафиком: балансирует, перенаправляет и анализирует его. И всё это с помощью небольших BPF-программ, запускаемых ядром в ответ на то или иное событие, связанное с сетевыми пакетами или сокетами.

Это была первая группа триггеров, связанная с сетевыми вопросами и обладающая возможностью влиять на поведение. Вторая группа связана с более общим observability; программы из этой группы чаще всего не имеют возможности на что-то влиять, а могут только наблюдать. Она интересует меня гораздо больше.

В данной группе есть такие триггеры, как:

  • perf events ивенты, связанные с производительностью и с Linux-профилировщиком perf: железные процессорные счётчики, обработка прерываний, перехват minor/major-исключений памяти и т. п. Например, вы можете поставить обработчик, который будет запускаться каждый раз, когда ядру надо вытащить из свопа какую-то страницу памяти. Представьте себе, например, утилиту, которая отображает программы, которые в данный момент используют своп.
  • tracepoints статические (определённые разработчиком) места в исходниках ядра, при прикреплении к которым можно достать статическую же информацию (ту, которую заранее подготовил разработчик). Может показаться, что в данном случае статичность это плохо, ведь я говорил, что один из недостатков логов заключается в том, что они содержат только то, что изначально добавил программист. В каком-то смысле это так, но tracepoints обладают тремя важными преимуществами:
    • их довольно много раскидано по ядру в самых интересных местах;
    • когда они не включены, они не тратят ресурсы;
    • они являются частью API, стабильны и не меняются, что очень важно, так как другие триггеры, о которых пойдёт речь, не имеют стабильного API.

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

  • USDT то же самое, что tracepoints, только для user space-программ. То есть вы как программист можете добавлять такие места в свою программу. И многие крупные и известные программы и языки программирования уже обзавелись такими трейсами: MySQL, например, или языки PHP, Python. Часто они выключены по умолчанию и для их включения нужно пересобрать интерпретатор с параметром enable-dtrace или подобным. Да, в Go у нас тоже есть возможность регистрировать такие трейсы. Кто-то, может быть, узнал слово DTrace в названии параметра. Дело в том, что такого рода статические трейсы были популяризированы в одноимённой системе, которая зародилась в ОС Solaris: например, момент создания нового треда, запуска GC или чего-то, связанного с конкретным языком или системой.

Ну а дальше начинается ещё один уровень магии:

  • ftrace-триггеры дают нам возможность запускать BPF-программу в начале практически любой функции ядра. Полностью динамически. Это значит, что ядро вызовет вашу BPF-функцию до начала выполнения любой выбранной вами функции ядра. Или всех функций ядра как угодно. Вы можете прикрепиться ко всем функциям ядра и получить на выходе красивую визуализацию всех вызовов.
  • kprobes/uprobes дают почти то же самое, что ftrace, только у нас есть возможность прицепиться к любому месту при исполнении функции как ядра, так и в user space. В середине функции есть какой-то if по переменной и вам надо построить гистограмму значений этой переменной? Не проблема.
  • kretprobes/uretprobes здесь всё аналогично предыдущим триггерам, но мы можем стриггернуться при завершении выполнения функции ядра и программы в user space. Такого рода триггеры удобны для просмотра того, что функция возвращает, и для замера времени её выполнения. Например, можно узнать, какой PID вернул системный вызов fork.

Самое замечательное в этом всём, повторюсь, то, что, будучи вызванной на любой из этих триггеров, наша BPF-программа может хорошенько осмотреться: прочитать аргументы функции, засечь время, прочитать переменные, глобальные переменные, взять стек-трейс, сохранить что-то на потом, передать данные в user space для обработки, получить из user space данные для фильтрации или какие-то управляющие команды. Красота!

Не знаю, как для вас, но для меня новая инфраструктура как игрушка, которую я долго и трепетно ждал.

API, или Как это использовать


Окей, Марко, ты нас уговорил посмотреть в сторону BPF. Но как к нему подступиться?

Давайте посмотрим, из чего состоит BPF-программа и как с ней взаимодействовать.



Во-первых, у нас есть BPF-программа, которая, если пройдёт верификацию, будет загружена в ядро. Там она будет скомпилирована JIT в машинный код и запущена в режиме ядра, когда триггер, к которому она прикреплена, сработает.

У BPF-программы есть возможность взаимодействовать со второй частью с user space-программой. Для этого есть два способа. Мы можем писать в циклический буфер, а user space-часть может из него читать. Также мы можем писать и читать в key-value-хранилище, которое называется BPF map, а user space-часть, соответственно, может делать то же самое, и, соответственно, они могут перекидывать друг другу какую-то информацию.

Прямолинейный путь


Самый простой способ работы с BPF, с которого ни в коем случае не надо начинать, состоит в написании BPF-программ на подобии языка C и компиляции данного кода с помощью компилятора Clang в код виртуальной машины. Затем мы загружаем этот код с помощью системного вызова BPF напрямую и взаимодействуем с нашей BPF-программой также с помощью системного вызова BPF.



Первое доступное упрощение использование библиотеки libbpf, которая поставляется с исходниками ядра и позволяет не работать напрямую с системным вызовом BPF. По сути, она предоставляет удобные обёртки для загрузки кода, работы с так называемыми мапами для передачи данных из ядра в user space и обратно.

bcc


Понятно, что такое использование далеко от удобного для человека. Благо под брендом iovizor появился проект BCC, который значительно упрощает нам жизнь.



По сути, он готовит всё сборочное окружение и даёт нам возможность писать единые BPF-программы, где С-часть будет собрана и загружена в ядро автоматически, а user space-часть может быть сделана на простом и понятном Python.

bpftrace


Но и BCC выглядит сложным для многих вещей. Особенно люди почему-то не любят писать части на С.

Те же ребята из iovizor представили инструмент bpftrace, который позволяет писать BPF-скрипты на простеньком скриптовом языке а-ля AWK (либо вообще однострочники).



Знаменитый специалист в области производительности и observability Брендан Грегг подготовил следующую визуализацию доступных способов работы с BPF:



По вертикали у нас простота инструмента, а по горизонтали его мощь. Видно, что BCC очень мощный инструмент, но не суперпростой. bpftrace гораздо проще, но при этом уступает в мощности.

Примеры использования BPF


Но давайте рассмотрим магические способности, которые стали нам доступны, на конкретных примерах.

И BCC, и bpftrace содержат папку Tools, где собрано огромное количество готовых интересных и полезных скриптов. Они одновременно являются и местным Stack Overflow, с которого вы можете копировать куски кода для своих скриптов.

Вот, например, скрипт, который показывает latency для DNS-запросов:

 marko@marko-home ~$ sudo gethostlatency-bpfccTIME   PID  COMM         LATms HOST16:27:32 21417 DNS Res~ver #93    3.97 live.github.com16:27:33 22055 cupsd         7.28 NPI86DDEE.local16:27:33 15580 DNS Res~ver #87    0.40 github.githubassets.com16:27:33 15777 DNS Res~ver #89    0.54 github.githubassets.com16:27:33 21417 DNS Res~ver #93    0.35 live.github.com16:27:42 15580 DNS Res~ver #87    5.61 ac.duckduckgo.com16:27:42 15777 DNS Res~ver #89    3.81 www.facebook.com16:27:42 15777 DNS Res~ver #89    3.76 tech.badoo.com :-)16:27:43 21417 DNS Res~ver #93    3.89 static.xx.fbcdn.net16:27:43 15580 DNS Res~ver #87    3.76 scontent-frt3-2.xx.fbcdn.net16:27:43 15777 DNS Res~ver #89    3.50 scontent-frx5-1.xx.fbcdn.net16:27:43 21417 DNS Res~ver #93    4.98 scontent-frt3-1.xx.fbcdn.net16:27:44 15580 DNS Res~ver #87    5.53 edge-chat.facebook.com16:27:44 15777 DNS Res~ver #89    0.24 edge-chat.facebook.com16:27:44 22099 cupsd         7.28 NPI86DDEE.local16:27:45 15580 DNS Res~ver #87    3.85 safebrowsing.googleapis.com^C%

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

А это скрипт, который шпионит за тем, что другие набирают на своих терминалах:

 marko@marko-home ~$ sudo bashreadline-bpfccTIME   PID  COMMAND16:51:42 24309 uname -a16:52:03 24309 rm -rf src/badoo

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

Скрипт для просмотра флоу вызовов высокоуровневых языков:

 marko@marko-home ~/tmp$ sudo /usr/sbin/lib/uflow -l python 20590Tracing method calls in python process 20590... Ctrl-C to quit.CPU PID  TID  TIME(us) METHOD5  20590 20590 0.173  -> helloworld.py.hello5  20590 20590 0.173   -> helloworld.py.world5  20590 20590 0.173   <- helloworld.py.world5  20590 20590 0.173  <- helloworld.py.hello5  20590 20590 1.174  -> helloworld.py.hello5  20590 20590 1.174   -> helloworld.py.world5  20590 20590 1.174   <- helloworld.py.world5  20590 20590 1.174  <- helloworld.py.hello5  20590 20590 2.175  -> helloworld.py.hello5  20590 20590 2.176   -> helloworld.py.world5  20590 20590 2.176   <- helloworld.py.world5  20590 20590 2.176  <- helloworld.py.hello6  20590 20590 3.176  -> helloworld.py.hello6  20590 20590 3.176   -> helloworld.py.world6  20590 20590 3.176   <- helloworld.py.world6  20590 20590 3.176  <- helloworld.py.hello6  20590 20590 4.177  -> helloworld.py.hello6  20590 20590 4.177   -> helloworld.py.world6  20590 20590 4.177   <- helloworld.py.world6  20590 20590 4.177  <- helloworld.py.hello^C%

Здесь на примере показан стек вызовов программы на Python.

Тот же Брендан Грегг сделал картинку, на которой собрал все существующие скрипты со стрелочками, указывающими на те подсистемы, которые каждая утилита позволяет обсёрвить. Как видно, нам уже доступно огромное количество готовых утилит практически на любой случай.


Не пытайтесь что-то здесь углядеть. Картинка используется как справочник

А что у нас с Go?


Теперь давайте поговорим о Go. У нас два основных вопроса:

  • Можно ли писать BPF-программы на Go?
  • Можно ли анализировать программы, написанные на Go?

Пойдём по порядку.

На сегодняшний день единственный компилятор, который умеет компилировать в формат, понимаемый BPF-машиной, Clang. Другой популярный компилятор, GСС, пока не имеет BPF-бэкенда. И единственный язык программирования, который может компилироваться в BPF, очень ограниченный вариант C.

Однако у BPF-программы есть и вторая часть, которая находится в user space. И её можно писать на Go.

Как я уже упоминал выше, BCC позволяет писать эту часть на Python, который является первичным языком инструмента. При этом в главном репозитории BCC также поддерживает Lua и C++, а в стороннем ещё и Go.



Выглядит такая программа точно так же, как программа на Python. В начале строка, в которой BPF-программа на C, а затем мы сообщаем, куда прицепить данную программу, и как-то с ней взаимодействуем, например достаём данные из EPF map.

Собственно, все. Рассмотреть пример подробнее можно на Github.
Наверное, основной недостаток заключается в том, что для работы используется C-библиотека libbcc или libbpf, а сборка Go-программы с такой библиотекой совсем не похожа на милую прогулку в парке.

Помимо iovisor/gobpf, я нашёл ещё три актуальных проекта, которые позволяют писать userland-часть на Go.


Версия от Dropbox не требует никаких C-библиотек, но вот kernel-часть BPF-программы вам придётся собрать самостоятельно с помощью Clang и затем загрузить в ядро Go-программой.

Версия от Cilium имеет те же особенности, что версия от Dropbox. Но она стоит упоминания хотя бы потому, что делается ребятами из проекта Cilium, а значит, обречена на успех.

Третий проект я привёл для полноты картины. Как и два предыдущих, он не имеет внешних C-зависимостей, требует ручной сборки BPF-программы на C, но, похоже, не имеет особых перспектив.

На самом деле, есть ещё один вопрос: зачем вообще писать BPF-программы на Go? Ведь если посмотреть на BCC или bpftrace, то BPF-программы в основном занимают меньше 500 строк кода. Не проще ли написать скриптик на bpftrace-языке или расчехлить немного Python? Я тут вижу два довода.

Первый: вы ну очень любите Go и предпочитаете всё делать на нём. Кроме того, потенциально Go-программы проще переносить с машины на машину: статическая линковка, простой бинарник и всё такое. Но всё далеко не так очевидно, так как мы завязаны на конкретное ядро. Здесь я остановлюсь, а то моя статья растянется еще на 50 страниц.

Второй вариант: вы пишете не простой скриптик, а масштабную систему, которая в том числе использует BPF внутри. У меня даже есть пример такой системы на Go:



Проект Scope выглядит как один бинарник, который при запуске в инфраструктуре K8s или другого облака анализирует всё, что происходит вокруг, и показывает, какие есть контейнеры, сервисы, как они взаимодействуют и т. п. И многое из этого делается с использованием BPF. Интересный проект.

Анализируем программы на Go


Если помните, у нас был ещё один вопрос: можем ли мы анализировать программы, написанные на Go, с помощью BPF? Первая мысль конечно! Какая разница, на каком языке написана программа? Ведь это просто скомпилированный код, который так же, как и все остальные программы, что-то считает на процессоре, кушает память как не в себя, взаимодействует с железом через ядро, а с ядром через системные вызовы. В принципе, это правильно, но есть особенности разного уровня сложности.

Передача аргументов


Одна из особенностей состоит в том, что Go не использует ABI, который использует большинство остальных языков. Так уж получилось, что отцы-основатели решили взять ABI системы Plan 9, хорошо им знакомой.

ABI это как API, соглашение о взаимодействии, только на уровне битов, байтов и машинного кода.

Основной элемент ABI, который нас интересует, то, как в функцию передаются её аргументы и как из функции передаётся обратно ответ. Если в стандартном ABI x86-64 для передачи аргументов и ответа используются регистры процессора, то в Plan 9 ABI для этого использует стек.

Роб Пайк и его команда не планировали делать ещё один стандарт: у них уже был почти готовый компилятор для C для системы Plan 9, простой как дважды два, который они в кратчайшие сроки переделали в компилятор для Go. Инженерный подход в действии.

Но это, на самом деле, не слишком критичная проблема. Во-первых, мы, возможно, скоро увидим в Go передачу аргументов через регистры, а во-вторых, получать аргументы со стека из BPF несложно: в bpftrace уже добавили алиас sargX, а в BCC появится такой же, скорее всего, в ближайшее время.

Upd: с момента, когда я сделал доклад, появился даже подробный официальный proposal по переходу на использование регистров в ABI.

Уникальный идентификатор треда


Вторая особенность связана с любимой фичей Go горутинами. Один из способов измерить latency функций сохранить время вызова функции, засечь время выхода из функции и вычислить разницу; и сохранять время старта с ключом, в котором будет название функции и TID (номер треда). Номер треда нужен, так как одну и ту же функцию могут одновременно вызывать разные программы или разные потоки одной программы.

Но в Go горутины гуляют между системными тредами: сейчас горутина выполняется на одном треде, а чуть позже на другом. И в случае с Go нам бы в ключ положить не TID, а GID, то есть ID горутины, но получить мы его не можем. Чисто технически этот ID существует. Грязными хаками его даже можно вытащить, так как он где-то в стеке, но делать это строго запрещено рекомендациями ключевой группы разработчиков Go. Они посчитали, что такая информация нам не нужна будет никогда. Как и Goroutine local storage, но это я отвлёкся.

Расширение стека


Третья проблема самая серьёзная. Настолько серьёзная, что, даже если мы как-то решим вторую проблему, это нам никак не поможет измерять latency Go-функций.

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

Если говорить о C, то стек там имеет фиксированный размер. Если мы вылезем за пределы этого фиксированного размера, то произойдёт знаменитый stack overflow.

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

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

Тут и кроется основная проблема: uretprobes, которые используют для прикрепления BPF-функции, к моменту завершения выполнения функции динамически изменяют стек, чтобы встроить вызов своего обработчика, так называемого trampoline. И такое неожиданное для Go изменение его стека в большинстве случаев заканчивается падением программы. Упс!

Впрочем, эта история не уникальна. Разворачиватель стека C++ в момент обработки исключений тоже падает через раз.

Решения у данной проблемы нет. Как обычно в таких случаях, стороны перекидываются абсолютно обоснованными аргументами виновности друг друга.

Но если вам очень нужно поставить uretprobe, то проблему можно обойти. Как? Не ставить uretprobe. Можно поставить uprobe на все места, где мы выходим из функции. Таких мест может быть одно, а может быть 50.

И здесь уникальность Go играет нам на руку.

В обычном случае такой трюк не сработал бы. Достаточно умный компилятор умеет делать так называемый tail call optimization, когда вместо возврата из функции и возврата по стеку вызовов мы просто прыгаем в начало следующей функции. Такого рода оптимизация критически важна для функциональных языков вроде Haskell. Без неё они и шагу бы не могли ступить без stack overflow. Но с такой оптимизацией мы просто не сможем найти все места, где мы возвращаемся из функции.

Особенность в том, что компилятор Go версии 1.14 пока не умеет делать tail call optimization. А значит, трюк с прикреплением ко всем явным выходам из функции работает, хоть и очень утомителен.

Примеры


Не подумайте, что BPF бесполезен для Go. Это далеко не так: все остальное, что не задевает вышеуказанные нюансы, мы делать можем. И будем.
Давайте рассмотрим несколько примеров.

Для препарирования возьмём простенькую программку. По сути, это веб-сервер, который слушает на 8080 порту и имеет обработчик HTTP-запросов. Обработчик достанет из URL параметр name, параметр Go и сделает какую-то проверку сайта, а затем все три переменные (имя, год и статус проверки) отправит в функцию prepareAnswer(), которая подготовит ответ в виде строки.



Проверка сайта это HTTP-запрос, проверяющий, работает ли сайт конференции, с помощью канала и горутины. А функция подготовки ответа просто превращает всё это в читабельную строку.

Триггерить нашу программу будем простым запросом через curl:



В качестве первого примера с помощью bpftrace напечатаем все вызовы функций нашей программы. Мы здесь прикрепляемся ко всем функциям, которые попадают под main. В Go все ваши функции имеют символ, который выглядит как название пакета-точка-имя функции. Пакет у нас main, а рантайм функции был бы runtime.



Когда я делаю curl, то запускаются хендлер, функция проверки сайта и подфункция-горутина, а затем и функция подготовки ответа. Класс!

Дальше я хочу не только вывести, какие функции выполняются, но и их аргументы. Возьмём функцию prepareAnswer(). У неё три аргумента. Попробуем распечатать два инта.
Берём bpftrace, только теперь не однострочник, а скриптик. Прикрепляемся к нашей функции и используем алиасы для стековых аргументов, о которых я говорил.

В выводе мы видим то, что передали мы 2020, получили статус 200, и один раз передали 2021.



Но у функции три аргумента. Первый из них строка. Что с ним?

Давайте просто выведем все стековые аргументы от 0 до 4. И что мы видим? Какая-то большая цифра, какая-то цифра поменьше и наши старые 2021 и 200. Что же это за странные цифры в начале?



Вот здесь уже полезно знать устройство Go. Если в C строка это просто массив символов, который заканчивается нулём, то в Go строка это на самом деле структура, состоящая из указателя на массив символов (кстати, не заканчивающийся нулём) и длины.



Но компилятор Go при передаче строчки в виде аргумента разворачивает эту структуру и передаёт её как два аргумента. И получается, что первая странная цифра это как раз указатель на наш массив, а вторая длина.

И правда: ожидаемая длина строки 22.

Соответственно, немного фиксим наш скриптик, чтобы достать данные два значения через стек поинтер регистр и правильный оффсет, и с помощью встроенной функции str() выводим как строчку. Всё работает:



Ну и заглянем в рантайм. Например, мне захотелось узнать, какие горутины запускает наша программа. Я знаю, что горутины запускаются функциями newproc() и newproc1(). Подконнектимся к ним. Первым аргументом функции newproc1() является указатель на структуру funcval, которая имеет только одно поле указатель на функцию:



В данном случае воспользуемся возможностью прямо в скрипте дефайнить структуры. Это чуть проще, чем играться с оффсетами. Вот мы вывели все горутины, которые запускаются при вызове нашего хендлера. И если после этого получить имена символов для наших оффсетов, то как раз среди них мы увидим нашу функцию checkSite. Ура!



Данные примеры это капля в море возможностей BPF, BCC и bpftrace. При должном знании внутренностей и опыте можно достать почти любую информацию из работающей программы, не останавливая и не изменяя её.

Заключение


Это всё, о чём я хотел вам рассказать. Надеюсь, что у меня получилось вдохновить вас.

BPF одно из самых модных и перспективных направлений в Linux. И я уверен, что в ближайшие годы мы увидим ещё очень и очень много интересного не только в самой технологии, но и в инструментах и её распространении.

Пока ещё не поздно и ещё не все знают о BPF, мотайте на ус, становитесь магами, решайте проблемы и помогайте своим коллегам. Говорят, что магические фокусы работают только один раз.

Что же до Go, то мы оказались, как обычно, довольно уникальными. Вечно у нас какие-то нюансы: то компилятор другой, то ABI, нужен какой-то GOPATH, имя, которое невозможно загуглить. Но мы стали силой, с которой принято считаться, и я верю, что жизнь станет только лучше.
Подробнее..

Перевод Построение инфраструктуры распределенной трассировки Netflix

15.12.2020 18:16:33 | Автор: admin

Для будущих учащихся на курсе "Highload Architect" и всех желающих подготовили перевод интересной статьи.

Также приглашаем посмотреть открытый урок
"Паттерны горизонтального масштабирования хранилищ".


Наша группа Кевин Лью (Kevin Lew),Нараянан Аруначалам (Narayanan Arunachalam),Элизабет Карретто (Elizabeth Carretto),Дастин Хаффнер (Dustin Haffner), Андрей Ушаков,Сет Кац (Seth Katz),Грег Баррелл (Greg Burrell),Рам Вайтхилингам (Ram Vaithilingam),Майк Смит (Mike Smith)иМаулик Пандей (Maulik Pandey)

@NetflixhelpsПочему "Король тигров" не идет на моем телефоне? подписчик Netflix спрашивает через Twitter

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

Распределенная трассировка: отсутствие контекста при поиске и устранении неисправностей крупномасштабных сервисов

До появления Edgar нашим инженерам приходилось просматривать горы метаданных и журналов, полученных от различных микросервисов Netflix, чтобы понять причинуконкретногосбоя стриминга, который возник у кого-то из наших подписчиков. Восстановление сеанса стриминга было утомительным и занимающим много времени процессом, в котором требовалось проследить все взаимодействия (запросы) между приложением Netflix, нашей сетью доставки контента (CDN) и серверными микросервисами. В начале процесса инженер вручную получал информацию об учетной записи подписчика, который участвовал в сеансе. Далее необходимо было сложить вместе все детали пазла в надежде, что получившаяся картина позволит решить проблему подписчика. Нам нужно было повысить продуктивность работы специалистов за счет распределенной трассировки запросов.

Если бы у нас был идентификатор каждого сеанса стриминга, то путем распределенной трассировки можно было бы легко воспроизвести сбой сеанса и увидеть при этом топологию сервисов, теги повторных попыток и ошибок, а также значения задержек для всех вызовов сервисов. Мы также могли бы получать контекстную информацию о сеансе стриминга, объединяя соответствующие трассировки с метаданными учетной записи и журналами сервисов. Эта идея привела нас к созданию Edgar инфраструктуры распределенной трассировки, ориентированной на удобство пользователей.

Рисунок 1. Поиск и устранение сбоя сеанса в EdgarРисунок 1. Поиск и устранение сбоя сеанса в Edgar

Когда четыре года назад мы начинали создавать Edgar, существовало очень мало систем распределенной трассировки с открытым исходным кодом, которые отвечали нашим потребностям. Мы решили подождать, пока эти системы достигнут зрелого состояния, и первое время собирали трассировки от Java-сервисов стриминга с помощью собственных библиотек. К 2017 году такие открытые проекты, как Open-Tracing и Open-Zipkin, достигли достаточного уровня зрелости, чтобы их можно было использовать в многоязычных средах выполнения, применяющихся в Netflix.

Наш выбор пал на Open-Zipkin, поскольку эта система лучше интегрировалась с нашей средой выполнения Java на основе Spring Boot. Мы используемMantisдля обработки потока собранных трассировок, а для хранения трассировок мы используем Cassandra. Наша инфраструктура распределенной трассировки состоит из трех компонентов: инструментарий библиотек трассировки, обработка потоков и хранилище. Трассировки, получаемые от различных микросервисов, обрабатываются как поток и перемещаются в хранилище данных. В следующих разделах описан наш путь по созданию этих компонентов.

Инструментарий трассировки: как он повлияет на наш уровень обслуживания?

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

В основе распределенной трассировки лежит распространение контекста для локальных межпроцессных вызовов и клиентских вызовов к удаленным микросервисам для любого произвольного запроса. При передаче контекста запроса фиксируются причинно-следственные связи между микросервисами во время выполнения. Мы использовали механизм распространения контекста на основезаголовков HTTP B3из Open-Zipkin. Мы следим за тем, чтобы заголовки, используемые для распространения контекста, правильно передавались между микросервисами в разнообразных средах выполнения Java и Node, которые интегрированы в нашу систему разработки ПО (внутри компании она называется paved road). В эту систему входят как базы legacy-кода, так и новые среды, например Spring Boot. Следуя принципу нашей культуры Свобода и ответственность, мы поддерживаем библиотеки трассировки и в других средах (Python, NodeJS, Ruby on Rails и др.), которые не входят в систему paved road. Нашисвободные, но высокоскоординированныеинженерные группы могут по своему усмотрению выбирать подходящую библиотеку трассировки для своей среды выполнения и отвечают за обеспечение правильного распространения контекста и интеграцию перехватчиков сетевых вызовов.

Средства интеграции со средами выполнения создают теги инфраструктуры, такие как имя сервиса, группа автоматического масштабирования и идентификаторы контейнерных инстансов. При поиске и устранении сбоев в сеансах стриминга Edgar использует эту схему тегов инфраструктуры для запроса и объединения трассировок с данными журналов. Кроме того, благодаря использованию согласованных тегов в Edgar оказалось легко устанавливать глубокие связи с разными системами мониторинга и развертывания. Обеспечив интеграцию со средами выполнения, мы должны были задать надлежащую политику выборки данных трассировки, чтобы обеспечить удобство поиска и устранения сбоев.

Обработка потока: выполнять или нет выборку данных трассировки?

Это был самый важный вопрос, который мы рассматривали при создании нашей инфраструктуры, поскольку политика выборки данных определяет количество трассировок, которые записываются, передаются и хранятся. При использовании мягкой политики выборки данных трассировки формируется большое количество трассировок в каждом сервисном контейнере, а это может привести к снижению производительности сервисов стриминга, поскольку библиотека трассировки будет потреблять больше ресурсов ЦП, памяти и сети. Еще одной проблемой, которую несет с собой мягкая политика выборки, является необходимость в масштабируемой обработке потоков и инфраструктуре хранения, позволяющей размещать крупные объемы данных.

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

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

Mantis это основная платформа обработки операционных данных в Netflix. Мы выбрали платформу Mantis в качестве магистрали для передачи и обработки больших объемов данных трассировки, поскольку нам требовалась масштабируемая система потоковой обработки, способная справляться с эффектами backpressure. Наш агент сбора данных трассировки перемещает эти данные в кластер заданий Mantis с помощьюбиблиотеки Mantis Publish. Мы помещаем диапазоны в буфер на определенный период времени, чтобы собрать все диапазоны трассировки в первом задании. Второе задание забирает поток данных из первого задания, выполняет хвостовую выборку данных и записывает трассировки в систему хранения. Такая цепочка заданий Mantis позволяет нам масштабировать все компоненты обработки данных независимо друг от друга. Дополнительное преимущество использования Mantis заключается в возможности выполнять произвольный просмотр данных в режиме реального времени вRavenс помощьюязыка запросов Mantis (MQL). Однако наличие масштабируемой платформы потоковой обработки не особо помогает, если невозможно обеспечить экономичное хранение данных.

Хранилище без переплат

Сначала в качестве хранилища данных мы использовали Elasticsearch: нас привлекли гибкая модель данных и возможности обработки запросов, которые есть в этом продукте. Мы продолжали добавлять в систему все больше сервисов стриминга, и объем данных трассировки начал экспоненциально расти. Из-за высокой скорости записи данных приходилось постоянно масштабировать кластеры Elasticsearch, вследствие чего возрастала операционная нагрузка. Запросы на чтение данных выполнялись все дольше, поскольку кластеры Elasticsearch использовали значительные вычислительные ресурсы для индексирования вносимых в них трассировок. Из-за больших объемов получаемых данных со временем упала производительность и операций чтения, и операций записи. Выйти из этой ситуации нам удалось путем перехода наCassandraв качестве хранилища данных: этот продукт позволил нам справиться с большим объемом получаемых данных. Использование простых поисковых индексов в Cassandra дает нам возможность поддерживать приемлемые задержки чтения, выполняя при этом большие объемы операций записи.

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

  1. Использовать более дешевые томаElastic Block Store(EBS) вместо инстансов EC2 с SSD.

  2. Задействовать улучшенные методы сжатия, чтобы уменьшить размер данных трассировки.

  3. Хранить только важные и интересные трассировки, используя для этого простые фильтры на основе правил.

Мы добавляли новые узлы Cassandra, когда на существующих узлах переполнялись хранилища инстансов EC2 с SSD. Использование более дешевых томов EBS Elastic вместо хранилищ на базе инстансов с SSD было привлекательным вариантом, поскольку AWS позволяет динамически увеличивать размер томов EBS без повторного выделения узла EC2. Это позволило нам увеличивать общую емкость хранилища, не добавляя новые узлы Cassandra в существующий кластер. В 2019 году наши замечательные коллеги из группы Cloud Database Engineering (CDE) протестировали производительность EBS для нашего сценария и перенесли существующие кластеры на тома EBS Elastic.

За счет оптимизации параметров Time Window Compaction Strategy (TWCS, стратегия уплотнения временных интервалов) они сократили количество операций записи на диск и объединения для файлов Cassandra SSTable, сократив тем самым нагрузку ввода-вывода на EBS. Эта оптимизация помогла нам сократить объем сетевого трафика, связанного с репликацией данных между узлами кластера, поскольку файлы SSTable создавались реже, чем в нашей предыдущей конфигурации. Кроме того, обеспечение возможности сжатия блоков Zstd в файлах данных Cassandra позволило наполовину уменьшить размер наших файлов данных трассировки. Благодаря этим оптимизированным кластерам Cassandra мы теперь тратим на 71% меньше средств на обеспечение работы кластеров и можем хранить в 35 раз больше данных, чем при использовании предыдущей конфигурации.

Мы заметили, что пользователи Edgar просматривали менее чем 1% собранных трассировок. Зная это, мы полагаем, что можем понизить нагрузку операций записи и помещать больше данных в систему хранения, если будем удалять трассировки, которые не нужны пользователям. В настоящее время мы используем простой фильтр на основе правил в задании Mantis по сохранению данных. Этот фильтр сохраняет интересные трассировки для путей вызовов сервисов, которые очень редко используются в Edgar. Чтобы определить, является ли трассировка интересной единицей данных, фильтр проверяет все диапазоны трассировки, помещенные в буфер, на предмет тегов предупреждений, ошибок и повторных попыток. Хвостовая выборка позволила сократить объем данных трассировки на 20% без влияния на работу пользователей. Существует возможность использовать методы классификации на основе машинного обучения, чтобы еще больше сократить объемы данных трассировки.

Несмотря на то что мы добились значительного прогресса, сейчас мы достигли очередной поворотной точки на пути построения нашей системы хранения данных трассировки. Реализация новых возможностей для пользователей Edgar может потребовать от нас хранить в 10 раз больше данных по сравнению с текущими объемами. С учетом этого сейчас мы экспериментируем с вариантом многоуровневого хранения для нового шлюза данных. Этот шлюз данных имеет интерфейс запросов, который позволяет абстрагироваться от сложностей, связанных с чтением и записью данных в многоуровневые хранилища. Кроме того, шлюз данных направляет получаемые данные в кластер Cassandra и перемещает сжатые файлы данных из кластера Cassandra в S3.Мы планируем сохранять данные за последние несколько часов в кластерах Cassandra, а остальные трассировки, хранящиеся в течение длительного времени, будут находиться в корзинах S3.

Таблица 1. Временная шкала оптимизации хранилищаТаблица 1. Временная шкала оптимизации хранилища

Дополнительные преимущества

Помимо обеспечения работы Edgar, данные трассировки используются в следующих случаях.

Мониторинг состояния приложений

Данные трассировки являются основным сигналом, используемымTelltaleпри мониторинге состояния приложений на макроуровне в Netflix. Telltale использует причинно-следственную информацию из трассировок для определения топологии микросервисов и соотнесения трассировок с данными временных рядов изAtlas. Такой подход позволяет получить более детальную картину состояния приложений.

Проектирование устойчивости

Наша группа по хаос-инжинирингу использует трассировки для проверки правильности внесения сбоев, когда наши инженеры выполняют стресс-тесты своих микросервисов с помощью платформытестирования методом внедрения отказов(FIT).

Эвакуация из облачных регионов

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

Оценка затрат на инфраструктуру при выполнении A-/B-тестирования

Группа по data science и исследованию продуктов определяет затраты на выполнениеA-/B-тестированияна микросервисах путем анализа трассировок, в которых есть соответствующие имена и теги тестов A/B.

Что дальше?

По мере роста Netflix объем и сложность наших программных систем продолжают повышаться. При расширении Edgar мы будем уделять основное внимание следующим областям:

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

  • Расширение возможностей наших аналитиков по запросу данных трассировки, чтобы компетентные пользователи в Netflix могли создавать собственные узконаправленные панели мониторинга и системы.

  • Создание абстракций, которые связывают данные из систем сбора метрик, журналов и трассировок, для предоставления дополнительной контекстной информации с целью поиска и устранения сбоев.

Пока мы развиваем инфраструктуру распределенной трассировки, наши инженеры продолжают использовать Edgar для устранения таких проблем со стримингом, как Почему "Король тигров" не идет на моем телефоне?. Наша инфраструктура распределенной трассировки способствует тому, что подписчики Netflix могут в любое время смотреть свои любимые сериалы, в том числе и Король тигров!

Мы ищем талантливых коллег, которые хотели бы присоединиться к нам в нашей работе по созданию инфраструктуры распределенной трассировки. Если вы активно интересуетесь темой мониторинга систем, обязательносвяжитесь с нами.


Узнать подробнее о курсе "Highload Architect".

Посмотреть открытый урок "Паттерны горизонтального масштабирования хранилищ".

Подробнее..

4 книги по цифровой трансформации для тимлидов, шпаргалка по Quarkus amp Observability

19.11.2020 12:06:35 | Автор: admin


Мы собрали для вас короткий дайджест полезных материалов, найденных нами в сети за последние две недели. Оставайтесь с нами станьте частью DevNation!

Начни новое:



Качай:


  • Debezium на OpenShift
    Debezium это распределенная опенсорсная платформа для отслеживания изменений в данных. Благодаря ее надежности и скорости ваши приложения смогут реагировать быстрее и никогда не пропустят события, даже если что-то пойдет на так. Наша шпаргалка поможет с развертыванием, созданием, запуском и обновление DebeziumConnector на OpenShift.
    Загрузить шпаргалку
  • Шпаргалка Quarkus & Observability (придется зарегистрироваться в девелоперской программе и стать частью community, мухахаха)



Почитать на досуге:


  • Объясняем простым языком, что такое гибридное облачное хранилище
    Что это вообще и какие задачи оно решает в условиях постоянного роста объемы данных и эволюции приложений.
    Вкратце: гибридные облачные хранилища сейчас в тренде, и не зря. Майк Пих (Mike Piech), вице-президент и генеральный менеджер Red Hat по облачным хранилищам и дата-сервисам, а также другие эксперты рассказывают о преимуществах, сценариях использования и ограничениях этой технологии.
  • 4 книги по цифровой трансформации, которые должен прочесть каждый руководитель
    Технологии это далеко не всё, на чем фокусируются руководители, успешно осуществляющие цифровую трансформацию. Представленные книги расширят ваше понимание путей развития корпоративные заказчиков, глобальных рынков и других важных тем.
    Вкратце: эти 4 книги помогут освежить понимание перспектив цифровой трансформации.


  • 7 способов применения микрокомпьютеров Raspberry Pi на предприятии
    От тимбилдинга до сверхдешевых средств безопасности и экспериментов с Kubernetes рассказываем, как задействовать Raspberry Pi на предприятиях.
    Вкратце: крохотный Raspberry Pi способен придать большой импульс развитию корпоративной ИТ-системы.

Смотри в записи:


  • jconf.dev (30 сентября)
    Бесплатная виртуальная Java-конференция прямо у вас на экране: четыре техно-трека с нашими комьюнити-экспертами по Java и облаку, 28 углубленных сессий и два потрясающих основных доклада.
  • AnsibleFest (13-14 октября)
    Два дня интереснейших докладов, демонстраций и практических занятий. Отличная возможность узнать, как разработчики, администраторы и ЛПР в сфере ИТ отвечают на вызовы перемен с помощью гибких технологий автоматизации с открытым кодом, которые позволяют перейти от того, что есть, к тому, что нужно.
  • J4K Conference (13-14 октября)
    Новая виртуальная конференция по Kubernetes, Java и облаку: 17 сессий с сотрудниками Red Hat, включая доклад Марка Литтла (Mark Little), главного человека в Red Hat по связующему ПО.
  • График предстоящих мероприятия DevNation
    Ознакомьтесь с планом мероприятия DevNation на портале Red Hat Developer, включая все вебинары Tech Talks и мастер-курсы, чтобы заранее спланировать свое расписание и зарегистрироваться на заинтересовавшие вас мероприятия.

По-русски:


Подробнее..

Перевод О наблюдаемости микросервисов в Kubernetes

17.03.2021 16:04:48 | Автор: admin

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

Привет, Хабр. В рамках курса "Microservice Architecture" подготовили для вас перевод материала.

Также приглашаем на открытый вебинар по теме
Распределенные очереди сообщений на примере кафки.


Вам нужны наблюдаемые микросервисы, но вы еще не знаете, как их реализовать с помощью Kubernetes? Ну что ж возможно это именно та статья, которую вы искали.

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

Наблюдаемость зиждется на трех столпах:

  • Логи (журналы) событий: запись событий, произошедших в системе. События (events) дискретны и содержат метаданные о системе на тот момент, когда они происходили.

  • Трассировка (трейсинг): система обычно состоит из множества частей/компонентов, которые согласованно работают для выполнения определенных важных функций. Отслеживание потока запросов и ответов через распределенные компоненты критически важно для эффективной отладки/устранения неполадок конкретной функциональности.

  • Метрики: производительность системы, измеряемая на протяжении определенного периода времени. Они отражают качество обслуживания системы.

Теперь к насущному вопросу как все это реализовать для наших микросервисов в кластере Kubernetes?

Микросервисы Kubernetes-приложение

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

Давайте рассмотрим микросервисное приложение, которое предоставляет информацию о погоде для конкретного города.

  • Weather-front: компонент, который состоит из фронтенда с интерфейсом для ввода названия города и просмотра информации о погоде. Смотрите скриншот выше.

  • Weather-services: компонент, который в качестве входных данных принимает название города и вызывает внешний погодный API для получения сведений о погоде.

  • Weather-db: это компонент с базой данных Maria, в которой хранятся данные о погоде, которые извлекаются для отслеживаемого города в фоновом режиме.

Указанные выше микросервисы развертываются с помощью объекта развертывания (Deployment object) Kubernetes, а ниже результат выполнения команды kubectl get deploy.

Ниже приведены фрагменты кода, которые будут использоваться для развертывания микросервисов.

Weather-front:

- image: brainupgrade/weather:microservices-front  imagePullPolicy: Always  name: weather-front

Weather-services:

- image: brainupgrade/weather-services:2.0.0  imagePullPolicy: Always  name: weather-services

Weather-db:

- image: mariadb:10.3  name: mariadb  ports:  - containerPort: 3306    name: mariadb

Наблюдаемость Столп первый Логи событий

Чтобы реализовать первый столп наблюдаемости, нам нужно установить стек EFK: Elasticsearch, Fluentd и Kibana. Ниже приведены несложные шаги по установке.

Elasticsearch и Kibana:

helm repo add elastic https://helm.elastic.cohelm repo updatehelm install --name elasticsearch elastic/elasticsearch --set replicas=1 --namespace elasticsearchhelm install --name kibana elastic/kibana

Fluentd:

containers:- name: fluentd  imagePullPolicy: "Always"  image: fluent/fluentd-kubernetes-daemonset:v1.12.0-debian-elasticsearch7-1.0  env:    - name:  FLUENT_ELASTICSEARCH_HOST      value: "elasticsearch-master.elasticsearch.svc.cluster.local"    - name:  FLUENT_ELASTICSEARCH_PORT      value: "9200"

После установки вы можете запустить дашборд Kibana, который будет выглядеть как на скриншоте ниже:

При запуске Fluentd вы увидите следующее (Fluentd запускается как Daemonset - скриншот 4):

Вскоре логи начнут помещаться в Elasticsearch. Их можно будет просмотреть в Kibana:

Первый столп наблюдаемости реализован, поэтому давайте сосредоточимся на следующем распределенной трассировке.

Наблюдаемость Столп второй (распределенная) трассировка

Для распределенной трассировки (distributed tracing) у нас есть несколько альтернатив Java-приложения, такие как Zipkin, Jaeger, Elasticsesarch APM и т. д.

Поскольку у нас уже есть стек EFK, давайте воспользуемся APM, предоставляемым Elasticsearch. Во-первых, давайте запустим сервер APM как Kubernetes Deployment.

Код развертывания сервера Elastic APM:

containers:- name: apm-server  image: docker.elastic.co/apm/apm-server:7.5.0  ports:  - containerPort: 8200    name: apm-port

Как только сервер APM запущен, нам следует неинвазивно добавить агента APM в наши микросервисы. Смотрите приведенный ниже фрагмент кода, используемый для микросервиса weather-front. Аналогичный код следует использовать и для компонента weather-services.

Код агента APM для микросервиса weather-front:

initContainers:- name: elastic-java-agent  image: docker.elastic.co/observability/apm-agent-java:1.12.0  volumeMounts:  - mountPath: /elastic/apm/agent    name: elastic-apm-agent  command: ['cp', '-v', '/usr/agent/elastic-apm-agent.jar', '/elastic/apm/agent']   containers:  - image: brainupgrade/weather:microservices-front    imagePullPolicy: Always    name: weather-front    volumeMounts:    - mountPath: /elastic/apm/agent      name: elastic-apm-agent             env:      - name: ELASTIC_APM_SERVER_URL        value: "http://apm-server.elasticsearch.svc.cluster.local:8200"      - name: ELASTIC_APM_SERVICE_NAME        value: "weather-front"      - name: ELASTIC_APM_APPLICATION_PACKAGES        value: "in.brainupgrade"      - name: ELASTIC_APM_ENVIRONMENT        value: prod      - name: ELASTIC_APM_LOG_LEVEL        value: DEBUG      - name: JAVA_TOOL_OPTIONS        value: -javaagent:/elastic/apm/agent/elastic-apm-agent.jar

После повторного развертывания компонентов микросервисов, вы можете перейти в Observability -> APM console в Kibana, чтобы наблюдать, как появляются сервисы (смотрите на скриншот 6).

После того, как вы кликните на сервис weather-front, вы сможете увидеть транзакции:

Кликните на любую из транзакций, и вы увидите детализированное представление с более подробной информацией о транзакции, которая включает задержку, пропускную способность, фрагмент трассировки (trace Sample) и т. д.

На приведенном выше скриншоте показана распределенная трассировка, на которой четко проиллюстрирована взаимосвязь микросервисов weather-front и weather-services. Кликнув по Trace Sample, вы перейдете к сведениям транзакции (transaction details).

Раскрывающийся список Actions в сведениях о транзакции предоставляет возможность просматривать логи для этой конкретной транзакции.

Что ж, на данный момент мы рассмотрели два столпа наблюдаемости из трех.

Наблюдаемость Столп третий Метрики

Для реализации третьего столпа, то есть метрик, мы можем использовать дашборд служб APM, где фиксируются задержка (Latency), пропускная способность (Throughput) и процент ошибок (Error rate).

Кроме того, мы можем использовать плагин Spring Boot Prometheus Actuator для сбора данных метрик. Для этого сначала установите Prometheus и Grafana, используя следующие простые команды:

Prometheus и Grafana:

helm repo add prometheus-community  https://prometheus-community.github.io/helm-chartshelm repo add grafana https://grafana.github.io/helm-chartshelm repo updatehelm install --name prometheus prometheus-community/prometheushelm install --name grafana grafana/grafana

После того как Prometheus и Grafana заработают, вам нужно добавить код приведенный ниже в микросервисы и совершить повторное развертывание:

template:   metadata:     labels:       app: weather-services     annotations:       prometheus.io/scrape: "true"       prometheus.io/port: "8888"       prometheus.io/path: /actuator/prometheus     containers:       - image: brainupgrade/weather-services:2.0.0         imagePullPolicy: Always         name: weather-services         volumeMounts:         - mountPath: /elastic/apm/agent           name: elastic-apm-agent                  env:           - name: management.endpoints.web.exposure.include             value: "*"           - name: spring.application.name             value: weather-services           - name: management.server.port             value: "8888"           - name: management.metrics.web.server.request.autotime.enabled             value: "true"           - name: management.metrics.tags.application             value: weather-services

После повторного развертывания микросервисов откройте Grafana, импортируйте дашборд с id 12685 и выберите микросервис, метрики которого вы хотите увидеть. На скриншоте ниже приведен weather-front:

И чтобы увидеть метрики всего кластера, импортируйте дашборд Grafana с id 6417, и вы увидите что-то вроде:


Узнать подробнее о курсе "Microservice Architecture".

Смотреть открытый вебинар по теме Распределенные очереди сообщений на примере кафки.

Подробнее..

Давайте обсудим мониторинг

07.04.2021 16:22:59 | Автор: admin

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

О мониторинге в контексте метрик

Если спросить среднестатистического технаря-инженера, с чем у него ассоциируется мониторинг, то скорее всего вам ответят метрики приложения, и подразумеваться будет их сбор и некоторая визуализация. Причем, о изнанке этого процесса, как показал мой опыт, многие даже не задумываются в понимании большинства оно просто показывается в Grafana/Kibana/Zabbix/подставьте нужное.

Ответ этот, замечу, всё же не полный, поскольку одними лишь метриками всё не ограничивается. Точнее даже так: мониторинг - это не только о том, чтобы метрики собрать и вывести на дашборд. И вот с этого момента давайте поподробнее.

Из чего же сделан мониторинг?

Со временем, я для себя вывел следующие аспекты:

  1. Сбор метрик из различных источников приложения, показатели хоста, железной части площадки; различия в pull и push моделях пока не затрагиваем, об этом чуть позже

  2. Запись и дальнейшее их (метрик) хранение в базе данных с учетом особенностей самой БД и использования собранных данных

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

  4. Отслеживание показаний метрик по заданным правилам и отправка алертов

  5. В случае продвинутого мониторинга сюда можно добавить еще один пункт - выявление аномалий и проактивное информирование о деградации наблюдаемой системы на основе ML.

О сборе метрик

Итак, вы решили создать систему мониторинга. Первым шагом предлагаю задуматься о том какие метрики собирать:

  • Показатели хост-системы и окружающего железа - утилизация ресурсов CPU, RAM, нагрузка на дисковые и сетевые устройства, статистика по процессам, информация о состоянии платформы оркестровки и так далее; всех их объединяет один признак такие метрики относятся к самому низкому доступному вам уровню инфраструктуры, с которой вы работаете.
    Например, если вы предоставляете сервис для хостинга виртуальных машин, вам понадобится мониторить сервера виртуализации; а если выкатываете свой продукт на площадку клиента, это наверняка будут виртуальные машины и/или K8s-кластер

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

  • Показатели бизнес-процессов сюда относится информация, на основе которой можно делать выводы о работоспособности бизнес-функций.
    В качестве примера возьмем операцию списания денег с лицевого счета пользователя допустим, для успешного выполнения операции необходимо, чтобы был доступен личный кабинет (веб-сервер), сервис постановки задач в очередь, брокер асинхронных сообщений, платежный шлюз и база данных. Чтобы быть уверенным, что операция может быть выполнена, вам понадобится следить за состоянием всей цепочки, хотя бы в самом простом виде работают ли все приложения в ней

  • Результаты смок-тестов по сути, расширение предыдущего уровня; это показатели периодически выполняемых фейковых бизнес-операций, по которым можно будет поймать проблему

Pull VS Push

Предмет жарких споров в тематических каналах и форумах с извечным вопросом что же лучше?

Push-модель это когда у вас есть классическая БД, в которую активно пишут агенты. Отличается большим объёмом точек конфигурирования (как правило, по количеству агентов мониторинга), но в то же время дает возможность базе заниматься своей основной задачей хранить метрики и управлять их жизненным циклом, пассивно ожидая, пока в неё что-нибудь положат.

Pull-модель насколько мне известно, относительно новый подход, набравший популярность с приходом в нашу жизнь платформ для оркестровки контейнеров. В этом случае, сам сервер мониторинга ходит по пассивным экспортерам и забирает у них метрики. Плюс единая точка конфигурирования, сам сервер, которому надо рассказать, что и откуда забирать. ИМХО, он же и главный минус в случае отвала сети вы теряете показатели за время её простоя. Отлично показывает себя в эфемерных средах вроде K8s, когда количество сущностей, которые необходимо мониторить, изменяется с течением времени. За их пределами уже не столь удобен для получения метрик от хостов вам понадобятся агенты-экспортеры.

Выбор модели остается за вами исходите из ваших потребностей и задач.

О хранении

Здесь буду немногословен на текущий момент придумано немало TSDB (Time-Series DataBase), заточенных именно под временные ряды. Вам остается только выбрать то, что по соотношению доступный функционал производительность удобство и возможности языка запросов покажется максимально приемлемым.

Мой личный фаворит VictoriaMetrics, рекомендую ознакомиться.

О визуализации

Подобно тому, как метрики делились уровни, аналогично разделяется и визуализация:

  1. Уровень площадки самый высокий уровень визуализации, с которого, после получения алерта, пользователю мониторинга стоит начинать работать. Дашборд(ы) здесь представляют собой набор логически разделенных индикаторов всё хорошо/что-то сломалось.
    Например, каждая панель показывает состояние какой-либо группы приложений Nginx`ы, Apache`и, Службы виртуализации; при наличии проблемы с любой из сущностей группы панель переходит из состояния всё хорошо в состояние что-то сломалось, привлекая внимание

  2. Уровень группы следующий уровень, к которому переходит пользователь; должен быть доступен по drilldown-ссылке с предыдущего дашборда. Если ранее мы подсветили, с какой группой возникла проблема, здесь мы должны ответить на вопрос с каким именно объектом группы?.
    Продолжая начатый выше пример, здесь отображаются все Nginx на вашей площадке, по которым выведены ключевые показатели состояния процессов, состояния соединений с БД, количество ошибок и так далее. Тут не стоит сильно вдаваться в детали, пяти-шести панелей на объект наблюдений будет достаточно

  3. Уровень объекта конечная точка движения нашего пользователя в большинстве случаев.
    На этом уровне детально визуализируются метрики конкретного приложения/процесса/другого подвергнутого принудительному мониторингу объекта. Здесь пользователь должен найти для себя ответ на такой вопрос что же именно сломалось?. Начиная с этого уровня, переходы между дашбордами должны наследовать контекст если пользователь на уровне группы кликнул по панели процесса nginx_01 хоста proxy.local, метрики именно этого приложения на этом хосте и должны отображаться

  4. Уровень фрагмента объекта формально, продолжение предыдущего уровня, но введен вот зачем: если какая-либо часть нашего объекта имеет слишком много метрик и достойна того, чтобы рассматриваться отдельно, под неё заводится персональный дашборд.
    Например, у нашего Nginx есть апстримы со своими метриками; дабы не усложнять уровень объекта и не перегружать его, мы заводим под апстрим персональный дашборд, а на предыдущем оставляем только кликабельные индикаторы с состоянием апстрима онлайн/частично онлайн/оффлайн. И вновь, переходы должны наследовать контекст, чтобы облегчить пользователю навигацию

  5. Уровень инфраструктуры самый низкий уровень визуализации и отправная точка в сборе метрик.
    Тут отображаются показатели хост-системы и окружающего железа. Хорошим ходом будет разбить на разные дашборды метрики разных частей состояние CPU, RAM, дисков и т.д., организовав возможность горизонтального перехода между ними. Переход же на этот уровень можно создать с двух предыдущих, снова с учётом ранее установленного контекста; если пользователь смотрел на метрики приложения на хосте proxy.local, этого хоста метрики и должны отображаться

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

Пользователь мониторинга двигается сверху вниз, разбирая инцидентПользователь мониторинга двигается сверху вниз, разбирая инцидент

Общие рекомендации:

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

  • Также, стоит выносить в переменные различные потенциально изменяемые вещи имя базы данных с метриками, вручную составленные словари число-значение и т.п.

  • Добавляйте описание к всем панелям. Если отображаемая на панели информация кажется хоть немного неочевидной, не поленитесь дать ей описание буквально одно-два предложения сильно облегчат работу пользователей и помогут вам же, когда возникнет необходимость вернуться к работе с дашбордом

В качестве средства визуализации сейчас лидирует небезызвестная Grafana, в которой есть функционал переменных, внутренних и внешних ссылок c шаблонизацией, комментариев к панелям и еще всякое-всякое.

О алертинге

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

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

  • Полнота сообщения в теле алерта должна содержаться информация о том, что именно произошло и за какой период времени.
    Пример: Средняя нагрузка на CPU превышает 90% в течение последних пяти минут; пользователь, получивший такое сообщение, видит информацию, во-первых, о событии, во-вторых, о его длительности на момент получения, что позволяет сходу примерно оценить масштабы бедствия.
    Здесь стоит уточнить, что метрика обычно оценивается за некоторый период, из которого выводится максимальное/среднее/минимальное/иное другое значение, а не её мгновенный показатель это позволяет сгладить (или же выделить зависит от того, что именно вам нужно) всплески и временнУю задержку в доставке метрик до базы данных

  • Уточняющие метаданные алерт должен сопровождаться набором тегов/лейблов, раскрывающих контекст события; это могут быть имя хоста, приложения, идентификатор uri веб-сервера и т.п.

  • Штамп времени первого срабатывания тут всё просто, в алерте жизненно необходима метка времени, чтобы при получении нотификации можно было понять, новый ли алерт у вас сработал, или это всё еще горит старый

  • Ссылка на систему управления алертами/систему визуализации метрик, на ваш выбор она нужна для получателя, чтобы он смог сразу перейти в мониторинг, а не тратил время на судорожный поиск закладки в браузере

С самим алертом, вроде, разобрались, теперь немного о процессе нотификации:

  • Не допускайте спама со стороны системы управления алертами. Когда вашего получателя заваливает письмами/СМСками/сообщениями от бота, рано или поздно он перестанет предавать им хоть какое-то значение и пропустит критичную нотификацию. Хорошим тоном будет настроить рассылку таким образом, чтобы по уже горящим алертам повторные уведомления отправлялись спустя какое-то время

  • Выделите ключевые метаданные и группируйте алерты по ним; тогда вместо, например, семи сообщений по одному объекту, ваш пользователь получит одно, в которое будут вложены остальные. Это также позволяет снизить количество нотификаций (помним про предыдущий пункт)

  • Предусмотрите возможность для себя и/или пользователя временно отключать некоторые алерты, например, на время технических работ

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

  • Разделите алерты по группам инженерам пусть приходят технические алерты, а бизнесу бизнесовые. Условной коммерческой дирекции не интересно, что у вас упал Nginx (им это ни о чем не скажет), для них куда важнее знать, что цепочка выполнения бизнес-функции покупка товара развалилась и компания несет потенциальные убытки

Рекомендую пощупать AlertManager приложение из стека Prometheus, в котором есть все описанные выше возможности. Лично для меня он стал той самой серебряной пулей, решившей сразу вагон и маленькую тележку проблем. Веб-хуки и API для интеграций входят в комплект.

Вместо заключения

В первую очередь, выражаю благодарность тем, кто прочитал до конца; надеюсь, что статья показалась вам интересной и полезной.

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

Подробнее..

Поговорим о централизованном логировании

09.04.2021 12:18:12 | Автор: admin

Эта статья продолжение текста о мониторинге. Здесь предлагаю нам с вами поговорить о роли логов в оценке состояния наблюдаемой площадки, посмотреть, что они способны нам дать, а также затронуть вопрос можно ли отрывать логи от метрик?.

По ходу дела я буду возвращаться к некоторым тезисам, высказанным в предыдущей публикации, потому рекомендую предварительно с ней ознакомиться.

Итак, давайте поговорим о логировании.

Кстати, а как будет правильно: лоГирование или лоГГирование? Лично я склоняюсь ко второму варианту, просто потому, что loGGing, но замечаю, что большинство предпочитает первый. А вы?


Разбор полетов

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

Собирать всё или только минимальный объем?

Здесь моя позиция такова собирать надо все метрики, которые объект способен отдавать. Как заметил @BugM они лежат в БД, есть не просят, никому не мешают. А вот если у вас их нет, но они вдруг понадобились, особенно за, допустим, прошлый месяц тут ничего сделать не получится.

Перефразируя знаменитую пословицу о бэкапах: люди делятся на два типа на тех, кто собирает метрики, и тех, кто их уже собирает.

Особенно актуально это в поиске аномалий на основе ML, для которых критично важно набрать объем данных с примером корректной работы наблюдаемой площадки. Тогда, чем больше у вас информации для обучения, тем точнее потом будет работать их (аномалий) выявление и прогнозирование. Человеку трудно найти закономерности в множестве, казалось бы, разрозненной информации (иначе не было бы необходимости изобретать ML), а машина может обнаружить их в неожиданных для вас местах.

При начале работ, исходить из метрик, или из инфраструктуры?

Соблюдайте баланс. Тезис, высказанный ранее, ответу на вопрос не противоречит:

такие метрики относятся к самому низкому доступному вам уровню инфраструктуры, с которой вы работаете

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

Продумать уровни тревоги

Абсолютно верная мысль @sizziff которую я недостаточно раскрыл.

Если вы заводите уровень катастрофа и отправляете нотификацию по СМС, это должно быть оправдано на 150%, иначе, рано или поздно, ваша команда поддержки будет выглядеть так:

Инженер, заваленный алертамиИнженер, заваленный алертами

Инженерный мониторинг не отражает реальное положение дел

Здесь @Dr_Wut приводит такой пример:

Простой пример из жизни по всем метрикам почтовый сервер работает, а почта во вне не ходит накосячили с spf. По итогу, пока разбирались, получили кучу претензий. А ведь достаточно было сделать простую проверку функции отправлять письмо и проверять что оно дошло.

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

Бизнес-мониторинг и мониторинг бизнес-процессов

Давайте разделять эти понятия.

Мониторинг бизнес-процессов зона ответственности инженерного мониторинга, о который мы с вами обсуждаем (наверное, стоило это сразу обозначить). Задача здесь обеспечить контроль за работоспособностью функций.

Бизнес-мониторинг и бизнес-метрики это совершенно иная кухня, в ней шеф-повара это ваш СЕО и его окружение. Тут обеспечивается контроль за прибылью от функций вашей системы, а главный инструмент BI-системы.

В своих статьях я не планирую затрагивать второй вид в силу отсутствия компетенций и релевантного опыта.

Теперь можем двигаться дальше.


О логах как источнике дополнительной информации

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

Однако же, для многих становится откровением то, что логи бывают нескольких типов. Вот два самых распространенных:

Лог работы приложения именно о нем думают в первую очередь; полагаю, вам знакома такая конструкция:

2019-04-23 00:39:10,092  INFO  DatabaseConnector  Connection estabilished

Вот это и есть запись из журнала работы. Характерные атрибуты такой записи идентификатор уровня события и сообщение о событии. Часто также появляются имя класса/библиотеки, вызвавшей логгер, и идентификатор потока, в котором была сформирована эта запись.

Лог доступа к приложению тут уже интереснее; это информация о том, какие функции приложения запрашивались. В подавляющем большинстве случаев это будут журналы обращения к API. Вот так, например, может выглядеть запись обращения к Nginx:

66.249.65.62 - - [06/Nov/2014:19:12:14 +0600] "GET /?q=%E0%A6%A6%E0%A7%8B%E0%A7%9F%E0%A6%BE HTTP/1.1" 200 4356 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"

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

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

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

Подводя итог написанному выше, давайте ответим на вопрос что логи могут нам дать?.

О логах приложения

В этом случае всё очень просто если метрика сообщает нам о факте наличия ошибки, лог рассказывает, какая именно ошибка произошла.

Пример пусть приложение работает с базой данных, тогда у неё есть счётчик ошибок database_error_count. Когда счётчик инкрементируется, мы видим, что что-то пошло не так, однако не знаем подробностей, ведь такую информацию не принято (и не стоит) выводить в метрики. Тут нам должен помочь журнал:

2019-04-27 00:39:10,092  INFO  DatabaseConnector  Error connecting to database MSSQLDB  connection refused on port 1433

И сразу понятно у нас порт недоступен.

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

О логах доступа

Как я писал выше, с этим типом журналов всё интереснее. Во-первых, эти логи, при наличии информации о затраченных на обработку запросов ресурсах, внезапно, превращаются в метрики!

Смотрите в логах HTTP-запросов принято писать о запрошенном ресурсе, времени, затраченном на обработку запроса, и еще, как минимум, фиксировать код ответа, из чего уже можно получить информацию о производительности:

Более того, такие логи, если в них есть информация о авторе запроса, превращаются в инструмент аудита доступа. С ними у нас в руках появляется еще один элемент Observability данные безопасности.

Кто и откуда запрашивал ваши ресурсы? Какие именно ресурсы? Как часто? Были ли такие запросы раньше? И еще много вопросов, на которые можно найти ответы.

На основе этих данных уже можно искать аномалии доступа:

  • Запросы из неизвестных источников (99% реквестов к такому-то API ранее выполнялись таким-то пользователем из такой-то локации, а теперь появился кто-то еще)

  • Запросы к скрытым ресурсам и функциям (кто-то ломится в непубличный API)

  • Множество неуспешных попыток авторизации (вас брутфорсят)

И так далее. Продолжите список в комментариях.

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

Трассировки

Еще на логах можно строить трассировки. Работает это примерно так:

  1. Входная точка в вашей DMZ устанавливает идентификатор трассировки (trace ID) на запрос; должна быть предусмотрена возможность ручной установки!

  2. По мере прохождения запроса по вашей инфраструктуре, приложения должны, во-первых, сохранять идентификатор неизменным, во-вторых, этим же идентификатором помечать соответствующие записи в журналах

Если такое предусмотрено вашим приложением, trace ID стоит записывать не только в лог доступа, но и в лог работы так вам будет легче связывать ошибки с конкретными запросами.

Очень обобщенно, это можно представить следующим образом:

Преимущества трассировок сложно переоценить на их основе можно, как минимум:

  • Строить маршруты выполнения операций и выявлять недостатки инфраструктуры

  • Оценивать общую их длительность; особенно это полезно в асинхронных процессах

  • Оценивать длительность выполнения операций на конкретных этапах и выявлять узкие места

О сборе логов

Видов взаимодействий тут снова два, прямо как с метриками Pull и Push.

Pull это когда приложение ведет свой журнал (в файле, в табличке в БД, в журнале операционной системы), а агент журналирования этот журнал читает, обогащает метаданными о приложении/хосте/чем-то ещё и отправляет на дальнейший парсинг и индексацию. Минус нужно деплоить и конфигурировать агенты; плюс приложению не нужно знать о системе централизованной обработки и хранения логов, от него требуется только вовремя писать свои журналы.

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

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

О структуризации

Приложение может вести логи в разных форматах plain text, jsonl, logsft, тысячи их. Здесь ключевая задача привести их к единому виду, и тут рекомендую начинать с определения контрактов событий.

Контракт соглашение о том, какой минимальный набор полей должны иметь те или иные типы логов после парсинга.

Можно выделить сначала общий контракт:

@timestamp<time>:Штамп времени событияapplication<string>:Имя приложения, к которому относится событие; должно соотноситься с таковым в мониторингеhost<string>:Имя хоста, на котором расположено приложениеlog_type<string>:Тип события; application|access|.... (если приложение пишет не только application логи)trace_id<string>:Идентификатор трассировки (при наличии)

А затем определять контракты для других типов.

Например, для журналов приложений:

message<string>:Значимая часть событияgeneric_message<string>:Обезличенная значимая часть событияlevel<string>:Текстовая репрезентация уровня событияlevel_value<int>:Численная репрезентация уровня событияlogger_name<string>:Имя класса, сгенерировавшего событие (при наличии)thread_name<string>:Идентификатор потока, сгенерировавшего событие (при наличии)stack_trace<string>:Трассировка стека; в более общем виде - вторая и последующие строки сообщения (при наличии)

И для журналов доступа:

status_code<int>:Код ответа по запросуelapsed_time<int>:Время, затраченное на обработку запроса в миллисекундахrequested_resource<string>:Запрошенный ресурсmethod<string>:Метод запроса

Здесь очень важно соблюдать установленные контракты при передаче логов на дальнейшие индексацию и хранение.

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

Следование контракту приносит пользу сразу с нескольких сторон:

  • Упрощается индексация многие NoSQL базы не любят, когда в рамках одного сегмента данных происходит попытка сохранить поле сначала одного типа, а затем другого. В лучшем случае, в такой ситуации вы получите неадекватно разрастающийся индекс, в худшем ошибку при сохранении

  • Процесс поиска становится единообразным когда у вас есть контракт в рамках одного типа журналов, вы можете подготовить один-два дашборда для работы с ними, просто соблюдая контракт и на этапе визуализации. С единым дашбордом можно, например, одновременно анализировать логи сразу нескольких приложений в одном интерфейсе

Только не пользуйтесь, пожалуйста, уровнем EMERGENCY, если не уверены, что абсолютно каждый, кому предстоит работать с структурированными журналами, точно знает, что он обозначает. Я встречал очень мало таких людей, поэтому вместо него использую FATAL - достаточно говорящее обозначение для всех.

Обезличенные сообщения

Внимательные читатели могли заметить, что в контракте журналов приложений есть поле generic_message. Рассказываю.

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

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

Пусть исходное сообщение у нас такое:

Error on AMQP connection <0.12956.79> (127.0.0.1:52879 -> 127.0.0.1:5672, state: starting):

Из него часть информации можно заменить на маркеры, тогда получается дженерик:

Error on AMQP connection <{connection_id}> ({remote_host} -> {destination_host}, state: {connection_state}):

Извлекаемые значения при этом записываются в соответствующие поля события с становятся доступны для поиска.

Но что это дает? А вот что:

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

  • Извлеченные ключи помогают в поиске; когда у вас идентификатор сессии везде называется session_id вы получаете возможность просто и быстро выполнять сквозной поиск

  • Поиск по ключам сильно проще поиска по регулярному выражению, как с точки зрения базы данных (обращаемся к индексу, а не выполняем анализ текста), так и с точки зрения пользователя (пачку регулярок еще надо заранее подготовить и хранить всегда под рукой)

Здесь важно не перегнуть палку и покрывать дженериками действительно важные записи. Злоупотребление дополнительными полями вредит здоровью ваших индексов.

О хранении

Как и в предыдущей статье, тут буду немногословен. Я предпочитаю Elasticsearch, и еще, говорят, Loki набирает популярность. На хабре можно найти несколько статей с их сравнением, вот одна из них - http://personeltest.ru/aways/habr.com/ru/company/badoo/blog/507718/.

Ознакомьтесь, изучайте, выбирайте.

О связности с метриками

В предыдущей статье я условно разделял визуализацию так:

  1. Уровень площадки

  2. Уровень группы

  3. Уровень объекта

  4. Уровень фрагмента объекта

  5. Уровень инфраструктуры

Выводить информацию о логах рекомендую на все уровни, кроме последнего (а может и на него, если вы собираете журналы операционной системы).

Принцип тот же самый:

  1. На первом уровне к логике панели-индикатора добавляется блок, связанный с подсчетом количества событий уровня ERROR и выше

  2. На втором уровне расширяется список панелей появляется индикатор наличия ошибок в журналах, плюс обеспечивается возможность перехода к дашборду с логами с сохранением контекста и фильтра по уровню событий (если подсвечиваем именно количество ошибок, их и нужно показывать)

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

Тогда диаграмма из предыдущей статьи приобретает такой вид:

Пользователь мониторинга двигается сверху вниз, разбирая инцидентПользователь мониторинга двигается сверху вниз, разбирая инцидент

О алертинге

Основные тезисы были высказаны в предыдущей статье, тут ограничусь парой предложений:

  • Выделите критичные события и настройте по ним алертинг; это станет хорошим дополнением к уже имеющимся нотификациям и повысит наблюдаемость за площадкой

  • Обогащайте алерты по логам теми же метаданными, что и алерты по метрикам; для пользователя они должны быть прозрачны, ему не надо задумываться, откуда взялась та или иная нотификация

Заключение

Итак, допустимо ли отрывать логи от метрик?

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

Огромное спасибо всем, кто ознакомился с этой и предыдущей статьей, кто писал комментарии, ставил под сомнение высказанное и делился практиками действительно приятно видеть отклик со стороны сообщества.

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

Подробнее..

Профилирование в продакшене для поиска узких мест на сервере

08.02.2021 16:12:50 | Автор: admin

Я работаю техлидом в команде System, которая отвечает за производительность и стабильность сервиса. С марта по ноябрь 2020 года Miro вырос в семь раз до 600+ тысяч уникальных пользователей за сутки. Сейчас наш монолит работает на 350 серверах, около 150 инстансов мы используем для хранения данных пользователей.

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

Часть первая: постановка задачи и вводные

В моем понимании любое приложение можно представить в виде модели: она состоит из задач и обработчиков. Задачи выстраиваются в очереди и исполняются последовательно, как на рисунке ниже:

Не все согласны с такой постановкой проблемы: кто-то скажет, что на RESTful серверах нет очередей только хэндлеры, методы обработки запросов.

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

В Miro для поддержки коллаборации на доске мы используем WebSocket соединение, а сам сервер состоит из множества очередей задач. Есть очередь на прием данных, на процессинг, на запись обратно в сокеты или персистент. Соответственно, есть очереди и есть обработчики.

Опыт показывает, что очереди всегда есть на этом фундаменте строится весь кейс.

Что мы искали и что нашли

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

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

Вторая ошибка: искали среднее время выполнения задачи. Если измерять размер очереди смысла нет, можно рассчитать среднее время на выполнение задачи. Но в результате мы получаем цифру, которая не несет в себе полезной информации. Ниже объясню, почему.

Анализ ошибок показал, что искать нужно не среднее арифметическое, а медианное значение времени, за которое выполняется задача. Медиана помогает очертить круг задач, которые заслуживают внимания, и отбросить аномальные отклонения.

В нашем случае больше подошел перцентиль. Он помог разбить все процессы на два типа: аномалии (1%) и задачи, которые укладываются в общую картину (99%).

Перцентиль помог осознать третью ошибку: не нужно пытаться решить все проблемы с очередями. Нужно сосредоточиться только на тех, которые напрямую влияют на пользовательские действия (users action).

Поясню: отсечение 2% аномалий помогло нам сосредоточиться на оптимизации процессов, с которыми сталкивается абсолютное большинство пользователей, а не единицы тех, кто зашел с медленного интернета. В результате мы улучшили UX повысили продуктовую метрику, а не техническую. Это очень важный момент.

Часть вторая: решение проблемы

В предыдущем разделе мы определили метод поиска узких мест. Теперь найдём самую медленную часть задачи.

Опыт показывает, что время, за которое задача выполняется, можно разбить на две части: когда мы что-то считаем и когда процессор ждет завершения операции input/output (IO).

Ошибки происходят и в первой части процесса, но в этом кейсе речь не о них. Интерес представляет вторая часть период ожидания ответа от базы данных. К нему, например, относится отправка данных по сети и ожидание выполнения SQL-запросов от базы данных.

На этом этапе у нас должна быть возможность вклиниться в слой абстракции (data access layer, DAL) и написать участок кода, который может быть вызван перед началом операции и по ее завершении. Другими словами, функция должна быть наблюдаемой (observable).

Рассмотрим пример: в Miro мы используем jOOQ для работы с SQL. В библиотеке есть листенеры: они позволяют написать код на каждый SQL-запрос, который будет выполняться перед запросом и после него. В Redis используется сторонняя библиотека, которая не позволяет добавлять листнеры. В этом случае можно написать свой DAL для доступа. Другими словами, вместо прямого использование библиотеки к коде, можно прикрыть её своим интерфейсом. А у же в его реализации обеспечить вызовы необходимых нам обработчиков.

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

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

Проиллюстрируем этот процесс на примере нашего дашборда. Мы профилируем конкретную задачу и получаем как общее время ее выполнения, так и время, которое было потрачено на запросы в SQL и в Redis. Мы также можем поставить различные time-counters: например, в разрезе команд, отправляемых в Redis .

Информация по выполнению каждого запроса дублируется в Prometheus и Jaeger. Зачем нам две системы? Графики наглядные, а логи детальные. Системы дополняют друг друга.

Разберем на примере: есть команда на открытие доски Miro, ее длительность напрямую зависит от размера доски. На графике мы технически не можем показать, что маленькие доски открываются быстро, а большие медленно. Зато Prometheus в реальном времени показывает аномалии, на которые можно быстро среагировать.

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

Stack trace для частных случаев

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

Добавление data access layers дало возможность снимать stack trace. Это позволяет трейсить запросы только от определенной базы данных, для конкретного end point или команды Redis.

Мы можем снимать stack trace с каждой десятой или сотой операции и написать логи, тем самым снизив нагрузку. Выборочно обрабатывать отчеты нетрудно сложность в том, как визуализировать полученную информацию в понятном виде, чтобы ей было удобно пользоваться.

В Miro мы отправляем stack traces в Grafana, предварительно удаляя из dump все third-party библиотеки и формируем значение метрики как результат конкатенации части дампа. Выглядит это так: после перечисленных манипуляций лог projects.pt.server.RepositoryImpl.findUser (RepositoryImpl.java:171) превращается в RepositoryImpl.findUser:171.

WatchDog для постоянного мониторинга

Снятие stack trace дорогое исследование, держать его активным постоянно не получится. Кроме того, в отчетах генерируется слишком много информации, обработать которую сложно.

Более универсальный метод отслеживания аномалий WatchDog. Это наша библиотека, которая отслеживает медленно выполняющиеся или зависшие задачи. Инструмент регистрирует задачу перед выполнением, а после завершения снимает с регистрации.

Иногда задачи зависают вместо 100 миллисекунд выполняются 5 секунд. Для таких случаев у WatchDog есть свой thread, который периодически проверяет статус задачи и снимает stack trace.

На практике это выглядит так: если через 5 секунд задача еще висит, мы видим это в логе stack trace. Кроме того, система посылает alert, если задача висит слишком долго например, из-за deadlock на сервере.

Вместо заключения

В марте 2020 года, когда началась самоизоляция, число пользователей Miro росло на 20% каждый день. Все описанные фичи мы написали за несколько дней, они не были очень трудоемкими.

По сути, мы создали велосипед на рынке есть готовые продукты, которые решают нашу проблему. Но все они стоят дорого. Главная цель этого текста показать простой инструмент, который можно быстро собрать на коленке. Он не уступает большим и дорогим решениям и поможет небольшим проектам пережить быстрое масштабирование.

Подробнее..
Категории: Java , Monitoring , Observability

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru