company_banner

Мониторинг микросервисных приложений: взгляд SRE



    Современная инфраструктура — множество небольших приложений, запущенных в контексте одного менеджера приложений, который упра'вляет их количеством, обновлением и запросами на ресурсы. Она является таковой не потому, что админы решили, будто так удобно ею управлять. Такая инфраструктура отражает текущее мышление в области разработки программного обеспечения. Для того чтобы понять, почему мы сейчас говорим о микросервисной архитектуре как об идеологии, необходимо вернуться на 30 лет назад.

    В конце 80-х/начале 90-х объектно-ориентированное программирование стало ответом на разрастающийся объем программного обеспечения — ведь именно тогда персональные компьютеры начали использовать повсеместно. Если раньше программное обеспечение представляло собой небольшие «утилиты», то где-то в это время разработка масштабного ПО превратилась в крупный бизнес. Команды разработки в тысячи человек, создающие огромную функциональность, перестали быть чем-то сверхъестественным. Бизнесу было необходимо понять, каким образом можно разделить работу команд программистов так, чтобы все не перемешалось — и объектно-ориентированное программирование стало ответом на этот вопрос. Цикл релизов приложений при этом по-прежнему оставался медленным.

    Вы планировали релиз вашего продукта (пусть это будет Microsoft Office 95) за несколько лет. После того как разработка завершалась, вы детально его тестировали (ведь вы не можете легко исправить ошибки после того, как приложение будет поставлено у конечного пользователя). Затем вы отправляли «бинарники» на завод, где изготавливался тираж компакт-дисков (или дискет), все это упаковывалось в картонные коробки и доставлялось по всему миру в магазины, где пользователь их покупал и потом ставил приложение себе на компьютер… — в этом главная разница с тем, что происходит сейчас.

    Начиная с 2010-го года, микросервисная архитектура стала ответом на потребность больших приложений и компаний в максимально быстром обновлении. Нам больше не нужно устанавливать приложение на компьютеры пользователей. Мы, фактически, «устанавливаем» приложение у себя в инфраструктуре и можем его быстро обновлять. Таким образом, мы можем (и бизнес хочет) проводить обновления максимально быстро с тем, чтобы ставить эксперименты и проверять гипотезы. Бизнесу нужно создавать новую функциональность, чтобы привлекать новых пользователей и удерживать текущих. Бизнесу нужно проводить эксперименты и смотреть, в результате чего пользователь может заплатить больше денег. Наконец, бизнесу важно не отставать от конкурентов. Бизнес хочет обновлять кодовые базы хоть десятки раз в день. В теории, это можно сделать и на одном большом приложении; но если разбить его на множество маленьких кусочков, управляемость обновлений увеличится. То есть переход к микросервисной архитектуре практически никогда не основывался на желании бизнеса увеличить стабильность приложения и инфраструктуры: микросервисы — это часть agile, и бизнес как раз руководствовался возможностью увеличить «гибкость» приложения.

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

    Но это не так: недавно я наткнулся на отличную цитату, которая говорит одновременно и о плюсах, и об ужасах Kubernetes для эксплуатации.
    «Kubernetes is so awesome that one of our JVM containers has been periodically running out of memory for more than a year, and we just recently realized about it».

    Давайте вдумаемся в эту фразу. В течении года приложение время от времени падало по out of memory — и команда эксплуатации не обращала на это внимания. Означает ли это, что приложение большую часть времени продолжало стабильно работать и выполнять свою задачу? На первый взгляд, функция действительно хороша: вместо того, чтобы админ получил сообщение о падении сервиса и пошел его поднимать, Kubernetes сам обнаружил, что приложение упало, и сам его же перезапустил. В течении года это происходило регулярно, и сообщений службе администрирования не передавалось. Я видел проект, где, однако, подобная ситуация происходила строго в одном случае — при генерации ежемесячного отчета. Функциональность отчета была разработана и выкачена в продакшн в помощь бизнесу, но уже через короткое время пользователи стали получать HTTP 502 на свои запросы — приложение падало, запрос обрывался, а Kubernetes перезагружал приложение. И хотя само приложение работало при этом нормально, процесс формирования отчета не выполнялся никогда. Сотрудники компании, которым он был нужен, предпочли делать отчет по старинке и не репортили об ошибке (он же нужен всего раз в месяц — можно делать по-старому, зачем беспокоить людей), а служба эксплуатации не отдавала приоритет задаче, которая возникает от силы раз в месяц. Однако в итоге это приводит к тому, что все ресурсы, которые были затрачены для создания этой функциональности (бизнес-анализ, планирование, разработка), оказались затрачены зря. Об этом узнали также год спустя.

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

    Ребуты сервисов не мониторятся, или им не придается должное значение


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

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

    Ошибки сервиса (такие, как Fatal Error или Exceptions) не мониторятся


    Пример:
    Приложение может и не падать, но выдавать пользователю (или другому приложению через API) трейс эксепшена. Таким образом, даже если мы замониторили перезагрузки приложений — мы можем не обнаружить случаи, когда выполнение запроса завершилось некорректно.

    Что делать?
    Логи приложений следует агрегировать в какой-либо системе и анализировать. Ошибки нужно системно анализировать «глазами», а на критические ошибки — выставить алерты и эскалировать расследование немедленно.

    Endpoint c health-check-ом сервиса отсутствует или не делает осмысленной работы


    Пример:
    Создание endpoint-ов, выдающих метрики сервиса (желательно в формате OpenMetrics) для того, чтобы их можно было читать (например, Prometheus-ом), стало, слава богу, практически стандартом. Однако в условиях, когда бизнес давит в сторону развития функциональности, тратить дополнительное время на продумывание метрик разработчикам зачастую не хочется. В результате, довольно часто встречаются health-check-и сервиса, вся суть которых сводится к выводу сообщения «оk». Если приложение способно вывести что-то на экран — считается, что это уже ок. Однако это не так. Такая проверка, если приложение не может достучаться до сервера БД, все равно сообщит «ок» и будет выдавать неверную информацию, затрудняя время на обнаружение проблемы.

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

    Время ответа API и взаимодействия с другими сервисами не мониторится


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

    Что делать:
    Используйте трейсинг. Jaeger стал практически стандартом. Есть офигенная рабочая группа, трудящаяся надо форматом OpenTracing по аналогии с OpenMetrics. Вот в этом докладе и вот здесь можно найти API для вашего языка программирования, которое сможет выдать метрики opentracing-а по времени приложений и взаимодействия с сервисами для того, чтобы вы выгружали это в Prometheus.

    Сервис — это по-прежнему приложение, а приложение — это по-прежнему память и процессор (и иногда диск)


    Пример и что делать: тут, я думаю все понятно. Довольно часто упускается мониторинг производительности отдельных сервисов: сколько они потребляют процессора, сколько оперативной памяти, если измеримо — как они потребляют «диск». В целом — все стандартные метрики для мониторинга «сервера». Часто мы видим, как мониторят всю ноду целиком, не мониторя отдельные сервисы — и напрасно.

    Появление новых сервисов следует мониторить


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

    Что делать:
    Поставьте оповещение на появление новых сервисов в инфраструктуре.

    Мониторинг времени доставки и других CI/CD метрик


    Пример:
    Еще одна проблема, которая появилась относительно недавно.
    Производительность — это не только производительность приложения как таковая, но и скорость его выкладки. Сложные CI/CD процессы + усложнение процесса сборки приложения + сборка контейнера доставки = и вот простой процесс выкладки уже не такой уж простой ;-) Мы об этом написали статью.
    В какой-то момент вы можете обнаружить, что выкладка сервиса стала занимать вместо одной минуты — двадцать…

    Что делать:
    Мониторьте время доставки приложения, от сборки до появления его в продакшне. Если время увеличилось — изучите, что произошло.

    APM и профайлинг приложения


    Пример и что делать:
    В тот момент, когда вы узнали, что с сервисом случилась проблема — он начал долго отвечать или стал недоступен, или произошли ещё какие-либо проблемы, — вам меньше всего захочется копаться во внутренностях сервиса или пытаться его перезапустить, чтобы локализовать ошибку. В нашей практике очень часто получается отследить проблему, когда у вас есть детальный мониторинг на уровне Application Performance Monitoring: чаще всего проблема не возникает одномоментно, а «копится» во времени — и с данными APM-а вы сможете понять, когда же она произошла. Также научитесь пользоваться профайлерами, доступными на уровне системы. С развитием eBPF появилось огромное количество возможностей для этого.

    Мониторинг событий WAF, мониторинг Shodan и мониторинг безопасности образов и пакетов


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

    • замониторьте результаты сборки вашего приложения при выполнении таких команд, как npm audit или аналогичных — вы сможете получите алерты, что в текущей версии библиотек, которые вы используете, и обновить их до защищенной версии.
    • подключите API сервиса Shodan, который занимается сканированием открытых баз и портов в интернете. Проверяйте свои IP через API, для того чтобы быть уверенным, что у вас нет открытых портов, а базы не утекли в открытый доступ.
    • если вы используете WAF — поставьте алерты на события WAF, чтобы понимать, когда к вам целенаправленно придет злоумышленник, и посмотреть на векторы атаки, которые он использует.

    Бонус-трек: SRE, время ответа приложения — больше не время ответа сервера!


    Мы привыкли к тому, что замеряем производительность системы по времени ответа сервера, однако современное веб-приложение на 80% своей логикой перенесено в фронтенд. Если вы еще не замеряете время ответа приложения как время выдачи страницы с фронтенд-метриками загрузки страницы — начните прямо сейчас. Пользователю уже совершенно неважно, за 200 или 400 миллисекунд отдал сервер ответ, если фронт на Angular-е или React-е дальше отрисовывает это в течении 10 секунд. В целом, я вообще верю, что оптимизация производительности во многом уйдет во фронт или появится как новая дисциплина.

    А ещё я чуть более регулярно, чем блог здесь, веду свой телеграм-канал, подписывайтесь :-)
    ITSumma
    Собираем безумных людей и вместе спасаем интернет

    Комментарии 6

      +1

      Микросервисная архитектура не была ответом на задачу быстрейшего CI/CD. Она решает ровно одну проблему — нехватку team lead'ов на всех. team lead тщательно думает про интерфейсы, а дальше джуниоры в пределах интерфейсов творят любую фигню. Если это важная фигня, то её фиксят. Если это не важная фигня, то она так и страдает в углу, не задевая при этом приложения.


      Если bottleneck'а в teamlead'ах нет, или нет сильных teamlead'ов, вытаскивание микросервисов, это отличный метод перевести лапшу в коде в лапшу в инфраструктуре, которую будут отлаживать… кто?


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

        +1
        А я ж не говорю что это идеальное решение. Ну и ООП не идеальное, все не идеальное. Просто жить с этим все равно придется. Можно не жить в микросервисах, можно держать инфраструктуру на FreeBSD, вопрос просто в том что:
        — для тех, кому надо будет искать работу есть вопрос о том, какие навыки стоит иметь
        — для тех, кому надо нанимать, надо понимать какой идеологии будут придерживаться люди и какие навыки, опять же, будут иметь.
        Но вообще, это тема другой статьи :)
        То что я во вступлении сказал, это скорее про то, что надо понять что это все не временно и надо учиться жить с тем, что породила эта идеология (толерантность к техническому долгу, которая может привести к проблемам в эксплуатации про которые раньше не думали).
          0

          Микросервисы — не решение проблемы, о которой вы говорите. Т.е. это всё равно, что утверждать, что тротуары на улицах — это решение проблемы загрязнённого воздуха. Совершенно ортогональные явления, не связные друг с другом.


          Да, бывает так, что админу предлагают это всё "починить", вывалив на него лапшу из коннективити и плавающих api. В принципе, я помню электрика, который отказывался чинить что-либо в доме, где проводка была сделана с помощью knob-and-spool, причём частично. Вот примерно такое же ощущение.

        0
        Хорошая статья, спасибо!

        «такой healthcheck должен осмотреть здоровье/доступность критичных для функционирования сервиса систем (доступы до очередей, БД, доступность сервисов И так далее)»

        Слишком глубокий healthcheck может привести к расширению радиуса проблемы, если их дергает балансировщик. В случае проблемы с БД или внешним сервисом — все healthcheck-и могут отвалиться и балансировщик может вывести из строя чуть ли не весь флот. Поэтому смотря для чего используется healthcheck — ему стоит или не стоит проверять здоровье внешних зависимостей. Для мониторинга — стоит дергать deep healthcheck (не говоря конечно о том, что внешние зависимости должны иметь и свой мониторинг тоже). Для работы балансировщика — стоит дергать только local healthcheck, который проверяет что сервис локально здоров (память, диск, конфиги, credentials) и способен обрабатывать запросы.

        Хорошая статья от AWS с более детальным анализом healthcheck-ов (как и вся коллекция).
          0
          «Логи приложений следует агрегировать в какой-либо системе и анализировать» — агрегация логов вещь всегда необходимая, но не совсем для анализа на операционные проблемы и ошибки сервиса в реальном времени. Для этого лучше использовать метрики, fault rate для данного примера вполне подошло бы. Если у кого-то появлялось желание анализировать логи, то у нас обычно спрашивали «Какой метрики тут не хватает?». Хотя, CloudWatch Logs позволяет создавать метрики из логов, тоже вариант. Но идея в том, что алерты реагируют именно на метрики, а не на логи.
            0

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

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

          Самое читаемое