Kubernetes Argo Rollouts
Last updated
Last updated
https://habr.com/ru/companies/slurm/articles/704502/
Что такое Argo Rollouts? Это контроллер Kubernetes и набор CRD для дополнительных возможностей развёртывания — сине-зелёное, канареечное, прогрессивное, анализ канареечного развёртывания и экспериментирование.
В этой статье поговорим о продвинутых возможностях развёртывания с кастомными ресурсами Kubernetes.
Как мы увидим, Argo Rollouts предоставляет ресурс rollout в Kubernetes API, на который можно заменить встроенный ресурс deployment. Использование расширенных возможностей в виде ресурса Kubernetes API даёт несколько преимуществ:
Знакомые методы работы — управляйте развёртываниями с помощью манифестов Kubernetes и kubectl CLI.
Простота понимания — используйте знакомые возможности развёртывания.
Аутентификация/авторизация — используйте имеющиеся в Kubernetes механизмы для аутентификации и авторизации.
Привычная программируемость — используйте знакомый API для Argo Rollouts.
Совместимость с любым решением для непрерывной поставки (CD) — Argo Rollouts развёртывается с помощью манифеста Kubernetes, поэтому может использоваться с любым решением CD.
Другие популярные решения с расширенными возможностями развёртывания не дают этих преимуществ:
Spinnaker: опенсорс-решение для непрерывной поставки.
SASS для непрерывной интеграции: GitLab, GitHub Actions,CodeFresh и т. д.
Все примеры кода и конфигураций, которые мы будем использовать в этой статье, можно скачать здесь.
Примеры из этой статьи выполнялись в кластере Google Kubernetes Engine (GKE) версии 1.17.14-gke.1600, но должны работать в любом другом кластере Kubernetes. Лучше если это будет Kubernetes версии 1.15.x и выше.
Итак, вам понадобится:
Кластер Kubernetes версии 1.15.x и выше.
kubectl CLI, совместимый с кластером.
Установка контроллера Argo Rollouts в кластер.
Установка плагина Argo Rollouts kubectl.
Рабочая нагрузка в этом примере представляет собой Express Hello World и клиент Prometheus для Node.js. У рабочей нагрузки есть две конечные точки: / возвращает Hello World!, а /metrics возвращает метрики в формате Prometheus. Помимо стандартных метрик Node.js конечная точка /metrics предоставляет две метрики, которые мы будем использовать в примерах:
app_requests_total: общее число запросов, исключая конечную точку /metrics, обработанных рабочей нагрузкой.
app_not_found_total: общее число запросов, которые не соответствовали двум конечным точкам; возвращается ошибка 404.
Рабочая нагрузка встроена в образ контейнера и доступна в репозитории Docker Hub. В примере мы будем использовать три тега:
0.2.0 и 0.3.0: образы, которые работают ожидаемо.
0.3.1: повреждённый образ, у которого запросы к конечной точке / входят в метрику app_not_found_total.
В этой статье мы будем развёртывать разные вариации рабочей нагрузки для иллюстрации разных концепций. Развёртывать рабочие нагрузки мы будем с помощью манифестов Kubernetes в папках проекта. Их имена будут начинаться на k8s.
Здесь мы её использовать не будем, но в проекте есть папка k8s с финальной работающей рабочей нагрузкой. Она используется в конфигурации Travis CI для иллюстрации простейшего процесса непрерывной поставки (в этой статье мы не будем его рассматривать).
В целях иллюстрации нам понадобится HTTP-трафик для рабочих нагрузок. В папке load проекта есть нужные манифесты Kubernetes для создания равномерного распределения запросов к конечной точке / (каждое задание создаёт один запрос в секунду) по всем pod’ам рабочей нагрузки.
Чтобы использовать расширенные возможности анализа в Argo Rollouts, нам понадобится рабочая нагрузка Prometheus в кластере, которая скрейпит конечные точки сервисов, предоставляющие метрики в формате Prometheus. Для визуализации метрик, которые мы будем анализировать, мы также запустим в кластере Grafana.
Для удобства в папке monitoring есть нужные манифесты Kubernetes для создания подходящих рабочих нагрузок Prometheus и Grafana. Больше об этих рабочих нагрузках см. в статьях с примерами Prometheus и с примерами Grafana.
Прежде чем приступить к использованию возможностей Argo Rollouts, давайте вспомним, как мы создаём канареечное развёртывание с помощью deployment. В первой вариации рабочей нагрузки, в изначально стабильном состоянии, у нас есть следующие ресурсы:
Сервис app-1: предоставляет внутреннюю балансировку нагрузки для pod’ов в deployment’ах app-1 и app-1-canary.
Deployment app-1: содержит пять pod’ов с рабочим образом, 0.3.0.
Deployment app-1-canary: содержит один pod с рабочим образом, 0.3.0.
Проверим все эти ресурсы следующей командой:
Итак, всё на месте. Теперь мы подаём нагрузку и видим, что средний процент запросов с ошибкой 404 равен 0.
Напоминаю, что мы отслеживаем эти две метрики:
app_requests_total: общее число запросов, исключая конечную точку /metrics, обработанных рабочей нагрузкой.
app_not_found_total: общее число запросов, которые не соответствовали двум конечным точкам; возвращается ошибка 404.
Вот метрика, которая визуализирована на схеме:
Примечание: оператор or усложняет выражение, но зато мы видим значение 0, если средняя частота запросов равняется 0 (деление на ноль нам не мешает)
Здесь мы добавим в *deployment app-1-canary*поломанный образ 0.3.1. На практике сначала всегда нужно обновлять канареечный deployment, чтобы заметить проблемы, пока они затрагивают только часть рабочей нагрузки.
Пускаем трафик и видим, что примерно 1/6 (чуть больше 16%) запросов завершаются ошибкой 404, и все эти запросы от сломанного канареечного pod’а.
Увидев эту проблему с канареечным deployment’ом, мы решили откатить app-1-canary.
Давайте посмотрим на наши ресурсы после отката.
Обратите внимание:
Тут почти всё то же самое, что было до того, как мы добавили в app-1-canary поломанный образ, только теперь у нас есть дополнительный набор реплик с 0 реплик — это он управлял проблемным pod’ом.
Смотрим историю deployment’а:
Обратите внимание:
Версия 1 (уже не отображается) обозначала изначальное состояние с рабочим образом.
Версия 2 соответствует deployment’у с поломанным образом.
Версия 3 отражает текущее состояния после отката. Версии неизменяемы, так что откат создал новую версию.
В выходных данных мало информации. Например, непонятно, как сопоставить версии с наборами реплик.
Здесь мы реплицируем канареечную функцию с помощью Argo Rollouts. В этой вариации рабочей нагрузки мы начинаем с изначально стабильного состояния со следующими ресурсами:
Сервис app-1: предоставляет внутреннюю балансировку нагрузки для pod’ов в rollout’е app-1.
Rollout app-1: как и deployment до этого, он предоставляет пять pod’ов с рабочим образом, 0.3.0.
Проверим ресурсы следующей командой:
Проверяем rollout:
Обратите внимание:
rollout — это не deployment, так что в выходных данных команды kubectl get all мы его не видим.
Rollout, как и deployment, управляет наборами реплик (которые, в свою очередь, управляют pod’ами).
Выходные данные у rollout’а такие же, как у deployment’а, потому что у них один интерфейс API.
Мы можем узнать больше о rollout’е следующей командой:
Примечание. Дальше мы будем использовать только подробный вывод для rollout’а, потому что он гораздо интереснее. Пока мы говорили о сходствах rollout’а и deployment’а. Теперь поговорим о различиях.
В примере с рабочим deployment’ом у нас было 0% запросов с ошибкой.
Теперь добавим в *rollout app-1*поломанный образ 0.3.1. Здесь, в отличие от deployment’а, rollout обновил одну реплику и остановился.
Давайте посмотрим поближе:
Обратите внимание:
В отличие от deployment’а, rollout остановился, пока никто из pod’ов ещё не сообщил о проблеме. Deployment останавливается, только если кто-то из pod’ов не готов.
Как видим, rollout тоже создал один канареечный pod.
Давайте посмотрим на главное различие между настройкой deployment’а и rollout’а — блок strategy. Вот блок strategy для rollout’а app-1–01-rollout.yaml:
Обратите внимание:
Эти восемь шагов (steps) соответствуют восьми шагам, указанным в подробных выходных данных rollout’а, причём там мы видим, что последним был выполнен шаг 1
Weight — это процент от количества реплик, которые мы хотели обновить. На 20% процесс остановился, обновив одну реплику (5 * 0,2 = 1).
Для паузы не указана длительность, а значит мы должны вручную разрешить или запретить продолжение операции.
Этот пример немного надуманный, потому что шаги после первой паузы нам не нужны (приводятся здесь для иллюстрации).
Если бы мы пустили трафик, то увидели бы, что примерно 1/5 (чуть больше 20%) запросов завершаются ошибкой 404, и все эти запросы поступают от сломанного канареечного pod’а.
Увидев эту проблему, мы решили отменить rollout app-1.
В подробных выходных данных видно, что произошло:
Обратите внимание:
Здесь мы видим, что rollout находится в состоянии Degraded, потому что у последней версии (revision) нет реплик.
Чтобы вернуть rollout в состояние Healthy, мы обновляем rollout app-1, взяв изначальный образ 0.3.0.
Вот что у нас получится:
Обратите внимание:
В отличие от deployment’а, мы можем легко связать версию с набором реплик, например здесь у revision 3 тот же Replicaset, что и у revision 1 (из предыдущих выходных данных).
Здесь мы посмотрим, как автоматизировать действия из предыдущих примеров. В этой вариации рабочей нагрузки мы начинаем с изначально стабильного состояния со следующими ресурсами:
Сервис app-1: предоставляет внутреннюю балансировку нагрузки для pod’ов в rollout’е app-1 (тот же, что и раньше).
Rollout app-1: rollout, который автоматизирует анализ канареечного pod’а, то есть на основе метрики выбирает — продолжить или отменить rollout.
Шаблон анализа app-1: метрика и логика, которую использует rollout. В этом шаблоне мы видим те же шаги, которые делали вручную, когда смотрели на панель Grafana, чтобы узнать, всё ли в порядке с pod’ом.
Давайте посмотрим на блок strategy для этого rollout’а.
Обратите внимание:
На первом шаге мы выполняем один канаречный pod в течение пяти минут. Этого достаточно, чтобы Prometheus успел насобирать метрики.
Здесь мы проиллюстрируем использование параметризованного шаблона и передадим в AnalysisTemplate service-name: app-1.
Как видите, ничего особо не поменялось:
Теперь добавим в rollout app-1 ещё один рабочий образ 0.3.0, чтобы показать автоматическое продолжение rollout’а. Через 5 минут проверяем, как дела:
Обратите внимание:
Как видим, rollout автоматически переведён в версию 2
Появился новый ресурс: успешный AnalysisRun.
Давайте изучим его самую важную часть:
Обратите внимание:
Мы видим не только состояние Succesful, но и фактическое значение (здесь это 0), возвращённое запросом Prometheus.
Теперь добавим в rollout app-1 поломанный образ 0.3.1., чтобы показать автоматическую отмену rollout’а. Через 5 минут проверяем, как дела:
Часть AnalysisRun:
Обратите внимание:
Значение превышает 0,1, то есть проверка не пройдена.
Здесь rollout автоматически отменяется — как мы бы это сделали вручную.
Чтобы вернуть rollout в состояние Healthy, мы обновляем rollout app-1, взяв рабочий образ 0.3.0.