Основой паттерна Декларативное развертывание является ресурс развертывания Deployment. Эта абстракция инкапсулирует процессы обновления и отката группы контейнеров и обеспечивает повторимость и автоматизацию развертывания.
Мы можем настроить изолированные окружения в виде пространств имен и размещать службы в этих окружениях с минимальным участием человека благодаря услугам планировщика. Но с ростом числа микросервисов постоянное обновление и замена их новыми версиями становятся все более трудоемкими.
Обновление службы до следующей версии включает такие действия, как запуск новой версии пода, безопасная остановка старой версии пода, ожидание и проверка успешности запуска, а иногда, в случае неудачи, откат до предыдущей версии. Эти действия выполняются либо ценой простоя, когда не работает ни одна из версий службы, либо без простоев, но за счет увеличения использования ресурсов двумя версиями службы, действующими одновременно в период обновления. Выполнение этих шагов вручную может привести к человеческим ошибкам и потребовать значительных усилий для правильного выполнения сценариев, что быстро превращает процесс выпуска новой версии в узкое место.
Императивное развертывание обновлений с помощью kubectl устарело
Фреймворк Kubernetes с самого начала поддерживал возможность обновления. Первая реализация была императивной по своей природе; клиент kubectl сообщал серверу, что делать на каждом шаге обновления.
Хотя команда kubectl roll-update все еще поддерживается, пользоваться ею не рекомендуется из-за следующих недостатков такого императивного подхода:
Вместо описания желаемого конечного состояния kubectl rolling-update выполняет команды, чтобы привести систему в нужное состояние.
Все управление заменой контейнеров и контроллеров репликации (ReplicationControllers) выполняется клиентом kubectl, который взаимодействует с API сервера, пока протекает процесс обновления, перекладывая на клиента всю ответственность, которую, по идее, должен нести сервер.
Может потребоваться более одной команды, чтобы привести систему в нужное состояние. Эти команды необходимо автоматизировать и повторять в разных окружениях.
Кто-то другой позже сможет затереть ваши изменения.
Процесс обновления требуется задокументировать и постоянно обновлять по мере развития службы.
Единственный способ узнать, что развертывание состоялось, — проверить состояние системы. Иногда состояние текущей системы может не соответствовать желаемому, и тогда его следует отразить в документации с описанием процедуры развертывания.
Вместо процедуры следует использовать новый объект ресурса Deployment, который поддерживает декларативное обновление, полностью управляемое серверной стороной Kubernetes. Поскольку декларативные обновления имеют так много преимуществ, а поддержка императивного обновления со временем исчезнет, мы сосредоточимся исключительно на декларативных обновлениях в этом паттерне.
К счастью, Kubernetes предлагает средства автоматизации этой деятельности. Используя понятие развертывания, можно описать, как должно обновляться приложение, применяя разные стратегии и настраивая разные аспекты процесса обновления. Если за один цикл выпуска (который, в зависимости от команды и проекта, может длиться от нескольких минут до нескольких месяцев) развертывание каждого экземпляра микросервиса выполняется несколько раз, тогда эта автоматизация, предлагаемая Kubernetes, поможет сэкономить немало усилий.
В главе 2 мы увидели, что для эффективной работы планировщику необходимы: достаточный объем ресурсов на хост-системе, соответствующие политики размещения и контейнеры с правильно определенными профилями ресурсов. Аналогично, чтобы механизм развертывания успешно справился со своей работой, контейнеры должны быть сформированы для выполнения в облачном окружении. В основе механизма развертывания, как нетрудно догадаться, лежит способность запускать и останавливать наборы подов. Чтобы этот механизм работал должным образом, сами контейнеры обычно должны принимать и обрабатывать события жизненного цикла (такие, как SIGTERM; см. главу 5 «Управляемый жизненный цикл»), а также предоставлять конечные точки для проверки их работоспособности, как описано в главе 4 «Проверка работоспособности».
Если контейнеры в точности выполняют эти два требования, платформа сможет чисто закрыть старые контейнеры и заменить их, запустив обновленные экземпляры. В таком случае все остальные аспекты процесса обновления можно определить декларативным способом и выполнить как одно атомарное действие с предопределенными шагами и ожидаемым результатом. Давайте рассмотрим варианты обновления контейнеров.
Декларативный способ обновления приложений в Kubernetes производится с использованием понятия развертывания. Внутренне механизм развертывания создает набор реплик ReplicaSet, который поддерживает селекторы меток. Кроме того, абстракция развертывания позволяет определить поведение процесса обновления с использованием таких стратегий, как RollingUpdate (непрерывное обновление, используется по умолчанию) и Recreate (воссоздание). В листинге 3.1 показаны важные детали настройки для стратегии непрерывного обновления.
Листинг 3.1. Непрерывное развертывание обновления
apiVersion: apps/v1
kind: Deployment
metadata:
name: random-generator
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: random-generator
template:
metadata:
labels:
app: random-generator
spec:
containers:
- image: k8spatterns/random-generator:1.0
name: random-generator
readinessProbe:
exec:
command: [ "stat", "/random-generator-ready" ]
Объявление трех реплик. Для непрерывного обновления необходимо больше одной реплики.
Число подов, которые могут временно запускаться в дополнение к репликам, созданным для обновления. В этом примере максимальное число реплик равно четырем.
Число подов, которые могут быть недоступны в ходе обновления. В данном случае только два пода могут быть недоступны в ходе обновления.
Точки входа для проверки работоспособности (readiness probes) необходимы для развертывания без простоев — не забудьте о них (см. главу 4 «Проверка работоспособности»).
Стратегия RollingUpdate гарантирует отсутствие простоев в процессе обновления. Внутри реализация развертывания выполняет аналогичные действия, создавая новые наборы реплик и заменяя старые контейнеры новыми. Одним из плюсов использования механизма развертывания является возможность контролировать скорость развертывания нового контейнера. Объект Deployment позволяет управлять диапазоном доступных и избыточных подов через параметры maxSurge и maxUnavailable. На рис. 3.1 показано, как протекает процесс непрерывного обновления.
Рис. 3.1. Непрерывное развертывание
Запустить декларативное обновление можно тремя способами:
• Заменить все определение развертывания Deployment новой версией определения с помощью команды kubectl replace.
• Наложить исправления (kubectl patch) или внести правки интерактивно (kubectl edit), чтобы установить новый образ контейнера с новой версией.
• С помощью kubectl set image установить новый образ в развертывание Deployment.
Смотрите также полный пример (http://bit.ly/2Fc6d6J) в нашем репозитории, который демонстрирует использование этих команд и то, как можно отслеживать или откатывать обновления с помощью kubectl rollout.
Помимо устранения ранее упомянутых недостатков императивного способа развертывания служб, механизм развертывания Deployment дает следующие преимущества:
• Deployment — это объект ресурса Kubernetes, состояние которого полностью управляется фреймворком Kubernetes. Весь процесс обновления выполняется на стороне сервера, без взаимодействия с клиентом.
• Декларативный характер механизма развертывания Deployment позволяет увидеть конечное состояние после развертывания, а не шаги, необходимые для его достижения.
• Deployment — это выполняемый объект, опробованный и протестированный в нескольких окружениях, прежде чем он достиг состояния, готового к использованию в промышленном окружении.
• Процесс обновления также полностью записывается и имеет средства для приостановки, возобновления и отката к предыдущим версиям.
Стратегия RollingUpdate гарантирует отсутствие простоя во время обновления. Но в ходе обновления одновременно действуют две версии контейнера, что является побочным эффектом. Это может вызвать проблемы у потребителей услуг, особенно когда в процессе обновления устанавливаются изменения в API, несовместимые со старой версией, а клиент не способен обработать эту ситуацию у себя. Для таких сценариев существует стратегия воссоздания Recreate, которая показана на рис. 3.2.
Стратегия Recreate заключается в присваивании параметру maxUnavailable числа объявленных реплик. То есть, согласно этой стратегии, сначала останавливаются все контейнеры с текущей версией, а затем запускаются сразу все новые контейнеры. В результате имеет место некоторый период
Рис. 3.2. Фиксированное развертывание с использованием стратегии Recreate
простоя, когда все контейнеры со старыми версиями уже остановлены, а новые еще не запущены или не готовы к приему и обработке запросов. Положительным моментом является тот факт, что одновременно не будут работать контейнеры двух версий, что упростит жизнь потребителям услуг.
Сине-зеленое развертывание (Blue-Green deployment) — это стратегия, используемая для развертывания программного обеспечения в промышленном окружении, позволяющая минимизировать время простоя и уменьшить риски. Абстракция развертывания в Kubernetes — основополагающая идея, которая позволяет определить, как Kubernetes переходит от использования контейнеров одной версии к другой. Для реализации этой более продвинутой стратегии сине-зеленого развертывания можно использовать объект Deployment в комплексе с другими объектами Kubernetes.
Без использования таких расширений, как Service Mesh и Knative, сине-зеленое развертывание придется выполнять вручную. С технической точки зрения суть заключается в создании второго развертывания с последней версией контейнеров (назовем это зеленым), пока не обслуживающих никаких запросов. На этом этапе продолжают работать и обслуживать запросы старые реплики подов (называемые синими).
Убедившись, что новая версия подов работоспособна и готова обрабатывать запросы, весь трафик переключается на новые реплики. В Kubernetes это можно выполнить, изменив селектор службы, чтобы он соответствовал новым контейнерам (отмечены как зеленые). Как показано на рис. 3.3, когда весь трафик начинает поступать в зеленые контейнеры, синие контейнеры можно удалить, а ресурсы освободить для будущих сине-зеленых развертываний.
Рис. 3.3. Сине-зеленое развертывание
Преимущество сине-зеленого развертывания в том, что в каждый конкретный момент времени запросы обслуживает только одна версия приложения, что снижает сложность поддержки нескольких версий на стороне потребителей услуги. Но кластер должен иметь вдвое большую емкость, потому что в период развертывания в нем одновременно выполняются как синие, так и зеленые контейнеры. Кроме того, во время переходов могут возникнуть значительные осложнения с продолжительными процессами и изменениями в состоянии базы данных.
Канареечное развертывание (Canary deployment) — это способ постепенного развертывания новой версии приложения в рабочем окружении путем замены старых экземпляров новыми с помощью небольших блоков. Этот метод снижает риски, связанные с внедрением новой версии, открывая доступ к новым версиям только некоторым пользователям. Если новая версия хорошо зарекомендовала себя при обслуживании небольшой выборки пользователей, производится замена всех старых экземпляров. На рис. 3.4 показано, как выполняется канареечное развертывание.
Рис. 3.4. Канареечное развертывание
В Kubernetes этот метод можно реализовать, создав набор реплик ReplicaSet для новой версии контейнера (предпочтительно с использованием объекта развертывания Deployment) с небольшим количеством реплик, который можно использовать в роли канареечного экземпляра. На этом этапе служба должна отсылать запросы некоторых пользователей в обновленные экземпляры подов. Убедившись, что новый набор реплик ReplicaSet действует должным образом, его можно масштабировать вверх, а старый набор — вниз, до нуля. В некотором смысле выполняется контролируемое и проверенное пользователем пошаговое развертывание.
Объект развертывания Deployment наглядно показывает, как Kubernetes превращает утомительный процесс ручного обновления приложений в декларативное действие, которое можно повторять и автоматизировать. Стратегии развертывания, поддерживаемые изначально (непрерывного обновления и воссоздания), управляют заменой старых контейнеров новыми, а стратегии выпуска (сине-зеленая и канареечная) определяют, как открывается доступ потребителям к новой версии. Последние две стратегии выпуска основаны на решении о переходе, принимаемом человеком и, как следствие, автоматизированы не полностью и требуют взаимодействия с человеком. На рис. 3.5 показано, как меняется количество экземпляров новой и старой версий во время переходов при использовании разных стратегий развертывания и выпуска.
Рис. 3.5. Стратеги развертывания и выпуска
Каждый программный продукт уникален, и развертывание сложных систем часто требует дополнительных шагов и проверок. Методы, представленные в этой главе, охватывают процесс обновления подов, но не включают обновление и откат других зависимостей, таких как карты конфигураций ConfigMap, секретов Secret и других зависимых служб.
На момент написания этой книги было предложено реализовать в Kubernetes возможность вызова дополнительных обработчиков в процессе развертывания. Обработчики Pre и Post, как предполагалось, должны позволить выполнять пользовательские команды до и после выполнения стратегии развертывания. Эти команды могли бы выполнять дополнительные действия во время развертывания и при необходимости прерывать, повторять или продолжать развертывание. Они могли бы стать первым шагом к созданию новых стратегий автоматического развертывания и выпуска. На данный момент используется подход, заключающийся в создании сценария процесса обновления на более высоком уровне, который управляет процессом обновления служб и их зависимостей с использованием объекта развертывания Deployment и других примитивов, обсуждаемых в этой книге.
Независимо от выбранной стратегии развертывания, чтобы выполнить требуемую последовательность шагов для достижения конечного состояния, Kubernetes должен знать, что поды приложения запустились и выполняются. Поэтому далее, в главе 4, мы рассмотрим паттерн Health Probe (Проверка работоспособности), используя который ваше приложение может сообщать Kubernetes о своем состоянии.
• Пример декларативного развертывания (http://bit.ly/2Fc6d6J).
• Непрерывное обновление (http://bit.ly/2r06Ich).
• Описание объекта Deployment (http://bit.ly/2q7vR7Y).
• Запуск приложения без состояния с помощью Deployment (http://bit.ly/2XZZhlL).
• Статья Мартина Фаулера (Martin Fowler) «Blue-Green Deployment» (http://bit.ly/1Gph4FZ).
• Статья Данило Сато (Danilo Sato) «Canary Release» (https://martinfowler.com/bliki/CanaryRelease.html).
• Книга «DevOps with OpenShift» (https://red.ht/2W7fdAQ).
Перевод на русский язык доступен по адресу https://habr.com/ru/post/309832/. — Примеч. пер.