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

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

Незначительные сбои с несоразмерными последствиями

В самом начале своей карьеры разработчика в Amazon я трудился над парком оборудования, с помощью которого отображается содержимое сайта Amazon.com. Работая над изменением – добавлением инструментов и визуализацией качества работы программного обеспечения, я, к сожалению, допустил ошибку. Последствия возникали редко, но определенный веб-сервер выводил на экран пустые страницы с ошибками при каждом запросе. Устранить проблему можно было только путем перезагрузки сервера. Мы обнаружили эту ошибку и быстро откатили изменения, добавили множество тестов и улучшили процессы для выявления подобных ситуаций в будущем. Но пока ошибка находилась в производстве, несколько серверов большого парка в итоге вышли из строя, как было описано выше.
 
Одна из причин, по которым ошибку было особенно сложно выявить, заключалась в том, что сервер не определял свое состояние как неработоспособное. Кроме того, сервер не мог передать данные о своей работоспособности системам мониторинга, поэтому он не выводился из эксплуатации автоматически и не запускал свои обычные оповещения. Проблема усугублялась тем, что сервер ускорял работу и создавал пустые страницы с ошибками гораздо быстрее, чем работоспособные серверы такого же ранга прорисовывали обычные веб-страницы. Технология балансировки нагрузки, используемая в нашей компании в то время, отдавала предпочтение быстрым серверам, а не медленным, поэтому она направляла непропорциональный объем трафика на неработоспособные серверы, из-за чего последствия становились еще тяжелее.

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

Компромиссы при проверке работоспособности

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

Как мы видели из примера с ошибкой на сайте, когда неработоспособные серверы продолжают использоваться, это несоразмерно снижает доступность всей системы. Если в парке из десяти серверов один неисправен, доступность парка составит 90 % или меньше. Ситуация усугубляется тем, что некоторые алгоритмы балансировки нагрузки, например «наименьшие запросы», направляют больше нагрузки на самый быстрый сервер. Во многих случаях сервер, вышедший из строя, начинает с высокой скоростью отклонять запросы, создавая «черную дыру» в парке сервиса, получая больше запросов, чем работоспособные серверы. В некоторых случаях мы создаем дополнительную защиту, чтобы предотвратить образование «черных дыр». Для этого мы замедляем отклонение запросов до средней задержки успешно выполненных запросов. Но есть и другие сценарии, например средства опроса очередей. Обойти в них такую проблему гораздо сложнее. Например, если средство опроса очередей опрашивает сообщения со скоростью их поступления, неработоспособный сервер тоже станет черной дырой. При таком наборе разнообразных сред для распределения работы мы можем по-разному подходить к вопросу защиты сервера, частично вышедшего из строя, учитывая особенности системы.

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

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

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

Способы измерения работоспособности

Многие элементы сервера могут выйти из строя, поэтому мы измеряем его работоспособность во многих точках наших систем. В результате одних проверок работоспособности может достоверно выясниться, что сервер независимо вышел из строя, другие же результаты могут быть неопределенными и ложноположительными в случае взаимосвязанных сбоев. Некоторые проверки работоспособности сложно внедрить. Другие внедряются при настройке вместе с такими сервисами, как Amazon Elastic Compute Cloud (Amazon EC2) и Elastic Load Balancing. У каждого типа проверки работоспособности есть свои преимущества.

Проверки активности

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

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

Локальные проверки работоспособности

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

• Невозможность записи на диск или чтения с диска. Иногда хочется верить, что сервис без сохранения состояния не требует диска с возможностью записи. Но в сервисах Amazon используются диски для мониторинга, ведения журналов и публикации данных асинхронных измерений.
• Остановка или прерывание критически важных процессов. Некоторые сервисы отправляют запросы с помощью прокси на сервере (подобно NGINX) и применяют бизнес-логику в другом серверном процессе. С помощью проверки активности можно лишь выяснить, выполняется процесс прокси или нет. Локальная проверка работоспособности может затронуть весь маршрут от прокси до приложения, чтобы выяснить, работают ли они и правильно ли отвечают на запросы. Интересно, что в примере с сайтом, приведенном в начале статьи, применявшаяся тогда проверка работоспособности была достаточно глубокой и обеспечивала выполнение рендеринга и реагирование, но не обеспечивала правильного реагирования.
• Отсутствующие вспомогательные процессы. На хостах без демонов могут «вслепую» работать операторы и отсутствовать сведения о работоспособности размещенных сервисов. Другие вспомогательные процессы передают записи об измерении и использовании выставления счетов либо получают обновленные учетные данные. Серверы с прерванными вспомогательными процессами ставят функциональность под угрозу такими способами, которые сложно выявить.

Проверки работоспособности зависимостей

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

• Неправильная конфигурация или устаревшие метаданные. Если процесс асинхронно ищет обновления метаданных или конфигурацию, но механизм обновления на сервере неисправен, сервер может работать не синхронно с другими серверами своего ранга – неправильно, непредсказуемо и непроверенным способом. Но если сервер не ищет обновлений некоторое время, о неисправном механизме обновлений не будет известно. Кроме того, система централизованного обновления может перестать сообщать об обновлениях всем серверам.
• Невозможность обмена данными с серверами того же ранга или находящимися в зависимости. Известно, что необычное поведение сети негативно сказывается на возможности группы серверов парка обмениваться данными с другими серверами, находящимися в зависимости с ними. При этом возможность отправки трафика на такой сервер остается прежней. Проблемы с программным обеспечением, например взаимные блокировки или ошибки в пулах подключений, также могут препятствовать обмену данными в сети.
• Другие необычные ошибки в программном обеспечении, требующие резкого изменения процесса. Взаимные блокировки, потери доступа к памяти или ошибки, связанные с ухудшением состояния, могут привести к серьезной рассинхронизации сервера. 

Обнаружение аномалий

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

• Рассинхронизация часов. Когда серверы находятся под высокой нагрузкой, может внезапно возникнуть значительная рассинхронизация часов. В соответствии с мерами защиты, например при оценке подписанных запросов к AWS, время на часах клиента может отличаться от фактического не более чем на пять минут. В противном случае запросы к сервисам AWS не будут выполнены.
• Старый код. Если сервер отключится от сети или электроэнергии на длительное время, а затем снова включится, он может выполнять опасно устаревший код, несовместимый с остальной частью парка.
• Любой непредвиденный режим сбоя. Иногда серверы выходят из строя таким образом, что возвращают ошибки, идентифицируя их как клиентские, а не собственные (HTTP 400 вместо 500). Серверы могут не выходить из строя, а замедлять работу или же реагировать быстрее других одноранговых серверов. Это говорит о том, что они возвращают ложные ответы своим операторам. Обнаружение аномалий – это общий термин, означающий непредвиденные виды сбоя.

Чтобы обнаружение аномалий сработало на практике, требуется выполнение нескольких условий:

• Серверы должны выполнять примерно одинаковые задачи. Если мы явно направляем разные типы трафика к разным типам серверов, поведение серверов может быть недостаточно схожим для выявления аномалий. Но если направлять трафик на серверы с помощью балансировщиков нагрузки, реагирование будет сопоставимым.
• Парки должны быть более-менее однородными. В парках, включающих разные типы инстансов, некоторые инстансы могут быть медленнее других, что приведет к ложному обнаружению неисправного сервера. Чтобы решить эту проблему обходным способом, мы сверяем показатели по типам инстансов.
• Ошибки или разницу в поведении необходимо регистрировать. Мы полагаемся на то, что серверы будут сами регистрировать ошибки, но что произойдет, если их системы мониторинга тоже сломаются? К счастью, клиент сервиса – отличное место для добавления инструментария. Такие балансировщики нагрузки, как Application Load Balancer, публикуют журналы доступа, в которых указано, с каким внутренним сервером контактировали при том или ином запросе, каким было время ответа, а также был ли выполнен запрос. 

Эффективное реагирование на выявление неработоспособности

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

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

Открытие при отказе

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

Например, AWS Network Load Balancer направляет трафик на все серверы, если ни один сервер не сообщает о своей работоспособности. Он также удаляется из неработоспособных зон доступности, если все серверы в зоне доступности сообщают о своей неработоспособности. (Чтобы узнать больше об использовании балансировщиков Network Load Balancer, см. документацию Elastic Load Balancing.) Наш Application Load Balancer поддерживает открытие при отказе, как и Amazon Route 53. (Чтобы узнать больше о настройке проверок работоспособности с помощью Route 53, см. документацию Route 53.)

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

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

Проверки работоспособности без автоматического выключателя

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

Мы применяем следующие стандарты при внедрении проверок работоспособности без встроенного автоматического выключателя:

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

При создании систем для автоматического реагирования на непройденные проверки работоспособности зависимостей нужно предусматривать правильные пороги, чтобы автоматизированная система не принимала радикальные меры неожиданным образом. Команды Amazon, работающие с такими серверами с фиксацией состояния, как Amazon DynamoDB, Amazon S3 и Amazon Relational Database Service (Amazon RDS), предъявляют важные требования по долговечности при замене серверов. Они также создали схемы ограничения скорости и обратной связи, чтобы автоматические средства прекращали работу и подключали людей в случае превышения порогов. Создавая такую автоматизацию, мы должны быть уверены, что заметим непрохождение проверки работоспособности зависимостей на сервере. В отношении некоторых показателей мы полагаемся на то, что серверы самостоятельно сообщат центральной системе мониторинга о своем состоянии. Если сервер не может сообщить о своей работоспособности из-за поломки, мы сами обращаемся к нему, чтобы проверить его работоспособность. 

Назначение высокого приоритета определению работоспособности

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

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

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

К счастью, есть несколько методов простой настройки, с помощью которых мы предотвращаем постоянное усугубление ситуации. С помощью таких утилит, как iptables, и даже с помощью некоторых балансировщиков нагрузки можно определить «максимум подключений». В этом случае ОС (или балансировщик нагрузки) ограничивает количество подключений к серверу таким образом, что сервер не переполняется одновременными запросами, поэтому его работа не замедляется.

Когда с сервисом взаимодействует прокси или балансировщик нагрузки, определяющий «максимум подключений», логично, казалось бы, создать ряд рабочих потоков на HTTP-сервере, соответствующий максимальному количеству подключений в прокси. Но при такой настройке сервис будет функционировать все хуже и хуже в случае снижения работоспособности. Для проверок работоспособности прокси тоже нужны подключения, поэтому важно, чтобы рабочий пул сервера был достаточно большим и мог справиться с дополнительными запросами на проверку работоспособности. Обычно мы создаем дополнительные рабочие потоки. Их количество может быть как незначительным, так и вдвое превышать указанное максимальное количество подключений прокси. Это не затратно с финансовой точки зрения.

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

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

Балансировка между проверками работоспособности зависимостей и масштабом последствий

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

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

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

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

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

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

Реальные сбои при проверках работоспособности

Все это хорошо в теории, но что происходит с системами на практике, когда они не проходят проверку работоспособности? Мы искали закономерности в рассказах клиентов AWS и Amazon, чтобы увидеть картину шире. Мы также рассматривали компенсирующие факторы – изучали, как перестраховывались команды, чтобы предотвратить распространение проблемы из-за слабых мест при проверках работоспособности.

Развертывания

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

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

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

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

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

Асинхронные процессоры

Еще один вид сбоя связан с асинхронной обработкой сообщений. Например, сервис получает работу путем опроса SQS Queue или Amazon Kinesis Stream. В отличие от систем, получающих запросы от балансировщиков нагрузки, в этом случае нет автоматической проверки работоспособности удаленных серверов со стороны сервиса.

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

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

Переполнение дисков

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

И в этом случае смягчающие средства управления предотвращают работу сервисов «вслепую» и быстро уменьшают последствия. В системах, защищенных прокси, например Application Load Balancer или API Gateway, показатели коэффициентов ошибок и задержки будут созданы этим прокси. В таком случае оповещения будут срабатывать, даже если сервер о них не сообщает. Для систем, основанных на очередях, такие сервисы, как Amazon Simple Queue Service (Amazon SQS), передают показатели, свидетельствующие о задержке обработки определенных сообщений.

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

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

Зомби

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

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

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

Выводы

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

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

Практикум

Попробуйте реализовать некоторые из изученных здесь принципов на практике.


Об авторе

Дэвид Янацек работает старшим главным инженером в AWS Lambda. Дэвид разрабатывает программное обеспечение в Amazon с 2006 года, раньше работал над Amazon DynamoDB и AWS IoT, а также внутренними платформами веб-сервисов и системами автоматизации операций парка. Одно из любимых занятий Дэвида – анализ журналов и тщательная проверка операционных показателей. Таким образом он ищет способы сделать работу систем беспроблемной.

Тайм-ауты, повторные попытки и отсрочка с наличием джиттера