Основанная на микросервисах инфраструктура Soluto, совмещенная со всеми инструментами CI/CD, позволяет осуществлять по несколько релизов в день, предоставляя пользователям новые возможности и внося исправления.
При таком быстром темпе иногда уже после релиза в производственной среде могут проскакивать ошибки. В таких случаях нужно защитить клиентов от негативного влияния этих ошибок и в то же время обнаружить их как можно раньше. Именно здесь на арену выходит Argo Rollouts, предлагающий поддержку стратегии canary-релизов.
Существует множество руководств по настройке процесса canary-релизов в кластере K8s. Эта же статья не из разряда “Как сделать то или иное…”, она предполагает, что вы уже знаете принцип работы Argo Rollouts и содержит информацию о доступных вариантах, возможных проблемах и оптимизациях, которые помогут повысить эффективность процесса релизов. Вы можете либо прочесть ее целиком для наилучшего понимания темы, либо сразу перейти к разделам “Краткого содержания” и “Усвоенных уроков”, сэкономив время.
Реализация данной стратегии преследует следующие цели:
“При каждом очередном развертывании сохранять работоспособность предыдущей версии и давать нововведению время для проверки работоспособности в производственной среде с минимальным воздействием на клиентов”.
Это означает, что нам нужно:
Для создания canary-релизов существуют такие решения, как flagger, который, кстати, подразумевает слияние с Argo Rollouts, но мы выбрали именно Argo по следующим причинам:
Это означает, что если у вас нет поставщика сетки (Istio), то Argo Rollouts разделяет трафик между версиями, создавая новый набор реплик, использующий тот же объект сервиса. При этом данный сервис по-прежнему будет разделять трафик между старыми и новыми подами поровну. Другими словами, управляя числом подов, мы контролируем процент трафика.
Разделение трафика по количеству подовЭто не идеальный вариант, но с задачей справляется. Если же у вас есть Istio, то вы можете контролировать трафик более эффективно, о чем можно прочитать в документации здесь.
Держа во внимании наши задачи, мы начинаем представлять желаемое поведение canary-процесса:
Комбинация Rollout и AnalysisTemplate в Argo Rollouts дает достаточную гибкость при настройке стратегии canary-релизов подобно описанной выше в отношении этапов, контроля трафика и анализа. Но что же такое хорошая стратегия? На этот вопрос нет универсального ответа, все зависит от конкретного случая. Ниже описан процесс формирования подходящего варианта для нашего случая. Он может подойти и вам, по меньшей мере в качестве примера.
С самого начала мы поставили перед собой следующие вопросы:
Если он будет слишком мал, canary не получит достаточно трафика, и результат анализа будет недостоверным. Если же трафика будет слишком много, то в случае возникновения сбоя, пострадает много клиентов. Другими словами, canary-релиз должен обрабатывать от 5 до 10% всего трафика.
Чтобы метрики статистического анализа были достоверными, нужно не менее 50 точек данных для получения хороших результатов, т.е. необходимо делать паузу на время, требуемое системе мониторинга (Ptometheus, DataDog и т.д.) для сбора метрик по меньшей мере 50 раз. Это время может варьироваться в зависимости от настройки, но в нашем случае мы добились сбора метрик каждые 15 секунд, т.е. минимальное время приостановки составляло около 12,5 минут при наличии активных пользователей, генерирующих трафик.
Однозначно ответить на этот вопрос нельзя, но наши разработчики не хотели тратить время на ожидание, пока что-то пойдет не так, к тому же нам нужно было оградить клиентов от возможных сбоев.
Первый шаг (Fail Fast): если в новом релизе присутствует очевидная проблема, разработчики не хотели дожидаться полного завершения анализа, чтобы ее обнаружить. Нам нужно было что-то, что может сообщить о неработоспособности кода очень быстро, поэтому анализ для первого этапа был таким:
Этапы 2 и 3 идентичны: 10% трафика при продолжительности 30 минут.
Такой вариант выглядел оптимальным. При общем времени выполнения канарейки в 1,25 часа ждать приходилось недолго. Однако, как выяснилось, такая конфигурация оказалась недостаточно хороша. Причины можете прочесть в разделе “Усвоенные уроки”.
Чтобы увидеть, что происходит при выполнении canary-релиза, можно использовать любое из этих средств:
У нас есть два типа сервисов:
В обоих случаях нужно измерить:
В API это отношение 2хх ответов к их общему числу. В сервис-работнике это уже сложнее, так как в нем нет http-вызовов. Как же в этом случае измерить показатель успешности?
В API это общее время от начала запроса до возвращения ответа. В сервис-работнике это общее время от получения сообщения до его обработки и подтверждения.
Для максимального выравнивания метрик между API и сервис-работником мы привлекли факт использования в работнике шаблона Sidecar, который выполняет потребление из очереди и вызывает основной сервис при помощи http:
Шаблон Sidecar и основанные на http метрикиТаким образом происходит преобразование основного сервиса в http-сервис, в котором можно использовать те же метрики, что и в API.
Мы позволили разработчикам использовать технику Canary-релизов в течение нескольких месяцев и наблюдали. Она спасала ситуацию немало раз, но не всегда. Да, всему виной была короткая продолжительность выполнения и ряд других факторов, которые объясняются ниже.
Да, чем больше трафика, тем больше операций, а больше операций означает более точный анализ. В нашем случае часовой пояс клиентов не совпадал с предусмотренным релизами, поэтому при выполнении они не получали нужного объема трафика. В итоге мы на собственном опыте поняли, что проблемы проще обнаруживать, если анализ выполняется при более высокой активности клиентов.
Для преодоления этой сложности анализ canary должен выполняться во время повышенного трафика, поэтому для лучшего обнаружения неполадок продолжительность этого анализа должна быть не менее 24 часов.
Когда в производственной среде возникают проблемы, требующие срочного устранения, мы не можем ждать 24 часа до выпуска исправления. Поэтому в конвейерах CD мы добавили опцию пропуска анализа canary при релизе. Эта опция стала удачным дополнением для подобных случаев.
Например, если нам нужно, чтобы показатель успешности был выше 95%, то для его вычисления используем следующий prometheus-запрос:
sum(increase(http_request_duration_seconds_count{status_code=~"2.*"}[15m])) / sum(increase(http_request_duration_seconds_count[15m]))
Как видите, он суммирует запросы всех конечных точек, но некоторые из этих точек вызываются чаще других. Это не значит, что они более важны, но в этом запросе конечные точки, вызываемые реже, не оказывают такого же эффекта на итоговый процент.
В связи с этим возникает необходимость изменить запрос, представив его в виде взвешенной суммы каждой конечной точки. Например, рассмотрим две из них:
(
sum(
increase(
http_request_duration_seconds_count{
status_code=~"2.*",
path="/api/v1/getSomething/"
}[15m]
)
)
/
sum(
increase(
http_request_duration_seconds_count{
path="/api/v1/getSomething/"
}[15m]
)
)
) * 0.2
+
(sum(
increase(
http_request_duration_seconds_count{
status_code=~"2.*",
path="/api/v1/getAnotherThing/"
}[15m]
)
)
/
sum(
increase(
http_request_duration_seconds_count{
path="/api/v1/getAnotherThing/"
}[15m]
)
)
) * 0.8
Поскольку getAnotherThing получает меньше трафика, чем getSomething, мы увеличили ее влияние на конечный результат. Это хорошо в плане чисел, но итоговый запрос может оказаться очень сложен в обслуживании, поэтому применяйте данный метод обдуманно.
После выполнения процесса canary-релиза в кластерах kubernetes при помощи Argo Rollouts на протяжении нескольких месяцев, наблюдая и собирая обратную связь, мы выработали ряд его оптимизаций:
Рекомендуется следовать этим инструкциям, чтобы добиться от canary-релизов оптимальной отдачи. Конечно же, вам также следует улучшать и подстраивать их под собственные случаи, поскольку при реализации canary не существует единого универсального порядка.
Перевод статьи Sari Alalem: Practical Canary Releases in Kubernetes with Argo Rollouts
Комментарии