канареечный деплой что это
Как эффективно релизить монолит, в который коммитят 150+ разработчиков из разных офисов
Я работаю инженером в Miro в команде, отвечающей за улучшение процесса релизов.
За последний год у нас появился зарубежный офис разработки, инженерная команда выросла вдвое, а полгода назад компания временно перешла на удалёнку. Параллельно с этим происходил постоянный кратный рост количества пользователей нашего продукта.
На фоне этих изменений нам важно было не терять в качестве и скорости, поэтому мы серьёзно обновили процесс серверных релизов. Расскажу про изменения, которые в итоге повысили долю успешных релизов.
Серверные релизы
Наш backend — это монолитное Java-приложение, которое может быть запущено с разными ролями для выполнения разных задач. Для работы backend мы используем AWS инстансы (CPU 4 ядра, RAM 16 ГБ). Большая часть backend-серверов – приложение, которое держит постоянное веб-сокетное подключение с клиентом, чтобы пользователи всегда видели реальное состояние досок в Miro. Для этих серверов мы используем роль Board-сервер (пользователи попадают на них при работе на досках). Для работы с бизнес-логикой и API-запросами используем роль API-сервер.
Релизы мы делаем бесшовными (graceful deploy) и стараемся проводить их во время наименьшей нагрузки на сервис. Во время планового релиза у нас в среднем 60.000 онлайн-пользователей и 50 работающих board-серверов.
Мы считаем релиз успешным, если он вышел в срок и в него попали все задачи, которые были готовы к релизу на момент его запуска. Соответственно, релиз считается неуспешным, если что-то пошло не так, потому что ошибки, которые потребовали остановки или отката релиза, увеличивают время доставки (time to market).
Любые изменения в процессе проведения релизов мы оцениваем исходя из того, насколько они приближают нас к успешному релизу.
Успешный релиз — это релиз, который вышел в срок и в который попали все задачи, готовые к релизу на момент его запуска.
Процесс подготовки релиза:
На каждый пулл-реквест прогоняется релевантный набор e2e тестов. Добавить изменения в мастер можно только при успешном прохождении всех тестов. Внутри автотестов есть маппинг соответствия тестов и кода продукта. Набор e2e-тестов для пулл-реквеста определяется нашим инструментом, который выбирает тесты, основываясь на этом маппинге и анализируя изменённые файлы в пулл-реквесте.
Каждый собранный мастер проходит полную регрессионную проверку. Релиз возможен, если все тесты прошли успешно. Упавшие тесты правят команды, ответственные за функциональность.
Для того чтобы релиз вышел автоматически, мы используем Allure Enterprise Edition, в котором отмечаем false-positive тесты как Resolved.
Процесс релиза:
Ищем сборку со 100% успешных тестов и версией, которая больше текущей версии на продакшене.
Запускаем канареечный релиз.
Мониторим метрики релиза в течение 4х часов.
Ставим статус Approved или Broken по завершении канареечного релиза. При статусе Approve основной релиз автоматически запускается следующим утром, при Broken запуска не произойдет.
Для релиза на API- и board-серверах создаём инстансы с новой версией. Количество инстансов рассчитываем, исходя из текущей нагрузки и добавляем 20%, чтобы не допустить высокой нагрузки во время или сразу после релиза.
Пользователи постепенно переходят на новые сервера, старые сервера мы выключаем и удаляем.
Релиз от создания инстансов до полного перехода на новую версию занимает полтора часа.
Канареечный релиз
Канареечный релиз нужен для того, чтобы валидировать изменения на небольшой случайной выборке пользователей. В ходе него мы поднимаем несколько серверов с новой версией и наблюдаем за ситуацией. Если на канареечном релизе всё идёт хорошо — релизимся на все сервера.
Процесс канареечного релиза
Канареечный релиз не способ тестирования на проде, а дополнительный эшелон защиты. Он позволяет уменьшить количество пользователей, которые столкнулись с ошибкой, если кейс сложный или если он повторяется только на инфраструктуре продакшена.
Для быстрой реакции на ошибки в канареечном релизе мы ввели роль дежурного серверного разработчика, которую выполняет каждый разработчик по очереди. Дежурный разработчик в течение четырех часов работы канареечного релиза реагирует на новые ошибки в Sentry и на общие предупреждения из Grafana, может остановить релиз самостоятельно при необходимости. После завершения канареечного релиза он обновляет статус релизной сущности в Bamboo: Approved или Broken.
В случае срочных релизов вне расписания команды могут запустить релиз через деплой в Bamboo самостоятельно, для этого в каждой команде есть инженеры с необходимыми правами.
Пользователи попадают на канареечный релиз случайным образом, с помощью балансировщика. Случайная выборка позволяет валидировать релизы на разных пользователях, но имеет и недостатки: без изменения в коде не позволяет балансировать типами пользователей и аккаунтами, не даёт проверять функциональность на конкретных аккаунтах или досках.
Выкатить канареечный релиз на определённую выборку пользователей мы можем, только если функционал был написан с Feature Toggle, а это уже реализация через код, а не через релизы.
Hot Fix в канареечном релизе
Раньше, находя ошибку в канареечном релизе, мы блокировали мердж в мастер и весь релиз. Это было неудобно, так как блокировало работу других команд и задерживало время выхода релиза.
Нам хотелось найти подход, при котором мы могли задерживать выход релиза минимально. Мы изучили существующие подходы (Trunk-Based Development, GitFlow и т.д.) и остановились на GitLab Flow.
Как мы работаем с Hot Fix по GitLab Flow:
Отводим релизные ветки от версии из канареечного релиза.
Мерджим фикс в мастер.
Выполняем git cherry-pick в релизной ветке.
Запускаем канареечный релиз на релизной ветке.
Запускаем следующий плановый канареечный релиз на версии мастера с фиксом или выше.
Подход помог нам вдвое снизить максимальное количество дней без релиза и количество перезапусков канареечных релизов, с четырёх до двух.
Предсказуемость и прозрачность процесса релиза
Для повышения качества релиза мы поддерживаем его прозрачность и предсказуемость. Используем для этого автоматические уведомления и дашборды с ключевыми метриками.
Раньше мы публиковали большой changelog один на все команды: в общем канале со всеми изменениями в релизе. Командам было трудно и больно ориентироваться в нём. Поэтому к общему changelog мы добавили командные changelog, в котором каждая команда видит статусы только по своим задачам и версию релиза, в которой они реализованы.
Дашбордами в Grafana мы пользуемся при работе с внеплановым релизами, чтобы быстрее их завалидировать. Во время плановых релизов нам хватает алертов из Grafana на основе метрик из Prometheus.
Всю статистику по релизам из Jira и Bamboo мы собираем и визуализируем в Looker, чтобы на основе исторических данных принимать решения о качестве процессов и улучшать их.
Данные по ошибкам, количество созданных и закрытых задач.
Сейчас мы внедряем фичу, которая позволяет командам блокировать ручные и автоматические релизы, если в мастере есть ошибка. Благодаря ей мы сможем автоматически собирать статистику количества сломанных мастеров, времени фикса и понимать, какие ошибки заблокировали выход релиза.
Изменения, которые увеличили долю успешных релизов
Канареечные релизы помогли сократить количество откатов релизов на 95%.
Отдельные changelog для каждой команды повысили общую прозрачность процесса. Теперь каждая команда вовремя и удобным способом получает уведомления о том, когда выходит их функциональность.
Мониторинг канареечного релиза дежурным серверным разработчиком уменьшил время реакции команды на найденный ошибки.
Подход GitLab Flow для hotfix позволил задерживать выход релиза минимально и исправлять ошибку, не блокируя работу других команд. Автоматические релизы стимулируют команды держать мастер всегда готовым к релизу.
Сбор и анализ всей истории релизов в Looker помогает проверять гипотезы и постоянно улучшать процесс.
Ближайшие планы
Конечная наша цель — выстроить процесс так, чтобы все релизы были успешными и пользователи никогда не сталкивались с ошибками. Для этого мы планируем следующие изменения:
Разбить монолит на микросервисы. Мы начинаем двигаться в эту сторону, но это отдельный большой проект вне темы статьи, поэтому останавливаться здесь на этом не буду.
Увеличить скорость релиза. Сейчас релиз на board-серверах занимает час, релиз на API-серверах — полчаса. Мы хотим быстрее.
Дать командам инструмент для автономного управления релизами. Сейчас есть возможность запустить канареечный релиз для hotfix, но команды не могут воспользоваться GitLab Flow полностью самостоятельно. Например, не могут самостоятельно отвести релизную ветку. У нас по умолчанию включена функция «Branch merging enabled», поэтому ветки при сборке содержит код мастера, а для релизных веток командам нужна помощь со стороны для ручного отключения этой фичи.
Сократить время от момента нахождения ошибки до вывода фикса на канареечный релиз. Сейчас у нас это может занимать до 6 часов рабочего времени в худших случаях из-за сложностей в коммуникациях или процессах.
Управлять нагрузкой на канареечных релизах, чтобы с ростом пользователей мы имели возможность увеличивать скорость прогона релиза, не меняя доли пользователей, участвующих в нём.
Добавить пользовательские метрики в валидацию релиза. Пока используем только технические метрики и метрики с багами.
Буду рада, если в комментариях поделитесь опытом повышения доли успешных релизов, особенно если вы уже реализовали описанные выше задачи.
Канареечные релизы – кто использует?
Давно хочу проект, на котором можно будет попробовать организовать канареечные релизы, но масштаб и критичность не те (масштаб маловат, а критичность систем в большинстве случаев великовата для таких экспериментов).
Кто использует – расскажите, как оно, я хоть позавидую.
P.S. Если кто-то не в курсе, то канареечный релиз – это способ безопасного тестирования нового кода в продакшене, а именно – частичный выпуск на небольшое количество пользователей с автоматическим откатом или, наоборот, увеличением количества пользователей по мере понимания, что код работает корректно.
Слово “канареечный” взято не с потолка, раньше шахтеры брали с собой канареек в клетках в угольные шахты для обнаружения повышения концентрации угарного газа. Канарейка, пока все было хорошо, постоянно пела, а при малейшей концентрации угарного газа – тут же умирала. То есть как только канарейка переставала петь – все знали, что самое время эвакуироваться. Птичек, конечно, жалко, но представляю, скольким шахтерам они так жизнь спасли.
Информация полезна? Поддержи развитие проекта!
На кофе и новые материалы для читателей блога 🙂
Тест канарейками
Я работаю в IT, где иногда для тестирования систем используется «канареечный тест». Смысл заключается в том, что новую версию системы делают доступной для небольшого числа реальных пользователей, так называемых «канареек», которые продолжают работать с системой в обычном режиме, а тем временем инженеры, ответственные за процесс разработки, внимательно отслеживают аномалии, убеждаются в том, что всё работает как задумано. Если «канареечный тест» проходит успешно, то новая версия системы становится доступной для всех пользователей. Принцип работы и название «канареечного теста» пришло в IT от шахтёров: в прежние времена, клетки с канарейками заносили в шахты для того, чтобы проверить их на наличие опасных для жизни газов. Канарейки очень чувствительны к мельчайшим примесям газа в воздухе, поэтому если птица умирала или вела себя неадекватно, то шахтеры немедленно покидали опасное место.
Вопрос в том, как масштабировать подобный локальный успех: с Ридом на позиции стартового центрового мы можем на равных бодаться против медленных центровых таких как Адамс, ЛаМаркус Олдридж или Вучевич, но игра против Атланты показала, что мобильный, атлетичный центровой типа Капелы будет казаться Уилтом Чемберленом, играя против лёгкой пятерки Миннесоты. В плей-офф 2017-18 года именно Капела стал «криптонитом» для Таунса, вчистую переиграв Карла на обоих концах площадки. Сейчас эта дуэль могла бы ответить на множество вопросов о системе «five-out», жаль из-за болезни Таунса это противостояние откладывается, как и тест системы в целом.
For reference, the Wolves are shooting 56.7% from the restricted area — second worse in the league
Houston is averaging almost the same number of RA attempts and is shooting 65.8% on those looks
С момента вступления в должность генерального менеджера Розас шаг за шагом воплощает идеи баскетбола, построенного на движении мяча и игроков, взаимозаменяемости позиций, грамотного использования пространства для развития атаки. Именно это и есть главная цель тестирования текущего состава и тренерского штаба. Эти принципы хорошо объясняет автор этих видео.
D’Angelo Russell Last 6 games: 26.3 PPG 6.7 APG
Даже в победном матче против Пеликанс нападение с Рикки забуксовало в концовке, потому что в упрощенном нападении от ведущего разыгрывающего требуется в равной степени умение обострять и пасовать, неслучайно МакЛафлин выглядит лучше испанца в роли запасного разыгрывающего.
Эту вроде бы несложную задачу с тремя переменными (разыгрывающими) и игровым временем наш тренерский штаб пока решить не может, это выражается в неудачных ротациях, несвоевременных заменах, негативно отражаясь на результатах команды. Я наблюдаю за болельщиками Миннесоты на англоязычных форумах и как часто случается с болельщиками команд-аутсайдеров, часть фанбазы ударилась в отчаяние (и я прекрасно понимаю их чувства), другая часть с «короткой памятью» стала «выделять» токсичные комментарии, требуя головы главного тренера и даже менеджера. Это после 16-ти игр. Некоторые особо «чувствительные канарейки» стали хэйтить собственных(. ) игроков. Я позволю себе напомнить несколько моментов, которые лично мне помогают «дышать» в такой сложной «игровой» ситуации.
Показал свой талант МакДэниэльс, которыйт хорошо выходит на подстраховку, защищается на периметре и имеет хороший релиз броска. Ему явно не хватает мышечной массы, но посмотрите как он бьётся в защите.
Работа тренерского штаба с Эдвардсом и карьера молодого игрока только началась. Но материал для работы интересный, многообещающий. Ну и уже стандартно, где-то один раз в неделю, он делает вот так.
К сожалению, ситуация с Таунсом не позволяет нам завершить первый этап «тестирования» системы. Поэтому предварительные выводы будут сделаны когда Карл отыграет 15-20 игр. По времени это совпадает с началом активных переговорах по трейдам, включая игроков подписанных в межсезонье. Уже сейчас начинают циркулировать слухи по обменам, но опытные менеджеры ждут своего шанса, проводят оценку своих активов, пытаются вырастить молодых игроков, реанимировать «сбитых лётчиков». Именно поэтому молчит Уджри, хотя Торонто ужасны в этом сезоне. Молчит Райли, хотя Майами теряет минуты драгоценного прайма Батлера, а Оладипо просится во Флориду. Именно поэтому ждёт и Розас. В прошлом сезоне первый этап тестирования закончился кардинальными изменениями и трейдами и сегодня никто уже не вспоминает что за нас играли Дженг, Ковингтон, КБД, Джордан Белл, Напьер, Грэм и Тиг.
Болельщики Миннесоты пережили 2020 год и хочется уже увидеть больше побед от команды, но мне хочется сказать напоследок одно: в своё время канарейки умирали для того, чтобы шахтёры могли жить. Это был жестокий, но эффективный способ, чтобы люди могли работать дальше в тяжелых условиях. Я не верю в чудеса, я верю в тяжелый труд, в работу. Поэтому я уверен, что данный этап становления команды (и организации) необходим. Я уверен, что это тестирование скоро закончится будут сделаны выводы, предприняты меры и начнётся новый этап развития. Иначе зачем умирают «канарейки»?
P.S. Вы можете подписаться на мой телеграм-канал, где я ежедневно скидываю интересные новости, связанные с Волками. За сим прощаюсь, до скорого, берегите себя!
Примечание: текст готовился в течении двух недель, поэтому точные цифры статистики могут разниться. Пусть это никого смущает, ведь сути дела это не меняет.
Kubernetes: типы Deployment Strategies и Argo Rollouts
Одна из целей, которые мы преследуем внедряя ArgoCD в Kubernetes – использование новых Deployment Strategies для наших приложений.
Ниже рассмотрим типы деплойментов в Kubernetes, как работают Deployment в Kubernetes, и быстрый пример использования Argo Rollouts, который более детально будем рассматривать в следущих постах.
Deployment Strategies и Kubernetes
Кратко рассмотрим стратегии деплоя, имеющиеся в Kubernetes.
Также, Kubernetes позволяет реализовать аналоги Canary и Blue-Green deployments, хотя с ограничениями.
Recreate
Тут всё достаточно просто: при этой стратегии, Kubernetes останавливает все запущенные в Deployment поды, и на их месте запускает новые.
Очевидно, что при таком подходе будет определённый даунтайм : пока остановятся старые поды (см. Pod Lifecycle — Termination of Pods), пока запустятся новые, пока они пройдут все Readiness проверки – приложение будет недоступно для пользователей.
Имеет смысл использовать его, если ваше приложение не может функционировать с двумя разными версиями одновременно, например из-за ограничений работы с базой данных.
Пример такого деплоймента:
Деплоим version: «1.0» :
Оба пода убиваются, и затем создаются новые:
Rolling Update
С RollingUpdate всё немного интереснее: тут Kubernetes запускает новые поды параллельно с запущенными старыми, а затем убивает старые, и оставляет только новые. Таким образом, в процессе деплоя некоторое время одновременно работают две версии приложения – и старое, и новое. Является типом по-умолчанию.
При таком подходе получаем zero downtime, так как в процессе обновления часть подов со старой версией остаётся жива.
Из недостатков – могут быть ситуации, когда такой подход неприменим. Например, если при старте подов выполняются MySQL-миграции, которые меняют схему базы данных таким образом, что предыдущая версия приложения не сможет её использовать.
Пример такого деплоймента:
10 антипаттернов деплоя в Kubernetes: распространенные практики, для которых есть другие решения
Когда есть контейнеры, работающие в продакшен, нужно, чтобы продакшен-окружение оставалось стабильным и отказоустойчивым. Если один из контейнеров падает, нужно, чтобы в любое время ему на замену был запущен другой.
Kubernetes предоставляет платформу для отказоустойчивой работы распределенных систем — от масштабирования до аварийного переключения и балансировки нагрузки. И есть много инструментов, которые интегрируются с Kubernetes, чтобы помочь вам в этом.
Перевели статью о десяти распространенных практиках развертывания Kubernetes, для которых есть другие решения. Автор не вдается в детали практик, поскольку реализация может различаться у разных пользователей.
1. Размещение файлов конфигурации внутри/рядом с образом Docker
Этот антипаттерн Kubernetes связан с антипаттерном Docker (см. антипаттерны 5 и 8 в этой статье). Контейнеры дают разработчикам возможность использовать единый образ на протяжении всего жизненного цикла программного обеспечения, от разработки/контроля качества до подготовки к продакшен и деплоя в продакшен.
Однако общепринятой практикой является предоставление каждой фазе жизненного цикла собственных образов, каждый из которых построен с различными артефактами, характерными для его среды: контроль качества, stage или продакшен. Но тогда вы больше не деплоите то, что тестировали.
Не включайте конфигурационные файлы в докер-образы. Источник
Лучшей практикой является хранение конфигурации общего назначения в ConfigMaps, в то время как конфиденциальная информация (например, ключи и секреты API) может храниться в ресурсе Secrets. Он имеет кодировку Base64, но в остальном работает так же, как ConfigMaps.
ConfigMaps можно монтировать как тома или передавать как переменные среды, но секреты следует монтировать как тома. Я упоминаю ConfigMaps и Secrets, потому что они являются собственными ресурсами Kubernetes и не требуют интеграции, но у них есть свои ограничения.
Если вы отделили конфигурацию от приложения, больше не нужно пересобирать приложение, когда нужно обновить конфигурацию. Ее можно обновлять во время работы приложения. Ваши приложения получают конфигурацию во время выполнения, а не во время сборки. Что еще более важно, вы используете один и тот же исходный код на всех этапах жизненного цикла программного обеспечения.
Загрузка конфигурации во время выполнения. Источник
2. Работа без Helm или других шаблонизаторов
Вы можете управлять деплоем в Kubernetes, напрямую обновляя YAML. При развертывании новой версии кода вам, вероятно, придется обновить один или несколько ресурсов:
Этот процесс может оказаться утомительным, если вы управляете несколькими кластерами и применяете одни и те же обновления в своих средах разработки, тестовой и производственной среде. Вы в основном изменяете одни и те же файлы с небольшими изменениями во всех ваших развертываниях. Здесь много операций копирования и вставки или поиска и замены, при этом нужно не забыть о среде, для которой предназначено ваше развертывание YAML.
В этом процессе есть много возможностей для ошибок:
Возможно, вам нужно изменить ряд вещей в YAML, и если вы будете невнимательны, YAML одного деплоймента можно легко принять за YAML другого деплоймента.
Шаблоны помогают упростить установку приложений Kubernetes и управление ими. Поскольку Kubernetes не предоставляет собственный механизм шаблонов, мы должны искать сторонние шаблонизаторы.
Helm был первым доступным менеджером пакетов, появился в 2015 году. Он был назван «Homebrew для Kubernetes» и расширен за счет включения возможностей создания шаблонов.
Helm упаковывает свои ресурсы с помощью чартов, где чарт представляет собой набор файлов, описывающих связанный набор ресурсов Kubernetes. В репозитории чартов имеется более 1400 общедоступных чартов (вы также можете использовать helm search hub [ключевое слово] [флаги]), в основном многоразовые рецепты для установки, обновления и удаления в Kubernetes.
С помощью чартов Helm вы можете изменить файл values.yaml, чтобы задать изменения, необходимые для деплойментов в Kubernetes, и вы можете иметь разные чарты Helm для каждой среды. Поэтому, если у вас есть среда контроля качества, промежуточная и производственная среды, нужно управлять только тремя чартами Helm вместо того, чтобы изменять каждый YAML в каждом деплойменте в каждой среде.
Еще одно преимущество Helm заключается в том, что с помощью откатов Helm можно легко вернуться к предыдущей версии, если что-то пойдет не так:
Если вы хотите вернуться к предыдущей версии, вы можете использовать:
Вы увидите что-то вроде:
И история чарта Helm прекрасно это отслеживает:
3. Деплой приложения в определенном порядке
Приложения не должны аварийно завершаться, потому что зависимость не готова. В традиционной разработке существует определенный порядок запуска и остановки задач при запуске приложений.
Важно не переносить это мышление в оркестровку контейнеров, то есть в Kubernetes, Docker и так далее. Эти компоненты запускаются одновременно, что делает невозможным определение порядка запуска. Даже когда приложение запущено и работает, его зависимости могут выйти из строя или быть перенесены, что приведет к дальнейшим проблемам.
В реальности Kubernetes также существует множество точек потенциальных сбоев связи, где невозможно установить зависимости, во время которых может произойти сбой модуля или служба может стать недоступной. Задержки в сети, такие как слабый сигнал или прерывание сетевого соединения, является частой причиной сбоев связи.
Для простоты давайте рассмотрим гипотетическое приложение для покупок, в котором есть две службы: база данных инвентаризации и пользовательский интерфейс витрины. Прежде чем приложение сможет запуститься, внутренняя служба должна запуститься, пройти все проверки и начать работу. Затем интерфейсная служба может запуститься, выполнить ее проверки и начать работу.
Допустим, мы принудительно установили порядок деплоя с помощью команды kubectl wait, например:
Но если это условие никогда не выполнится, следующий деплой не может быть продолжен, процесс прерывается.
Вот упрощенная схема того, как может выглядеть порядок развертывания:
Процесс не может двигаться дальше, пока предыдущий этап не завершен
Поскольку Kubernetes является самовосстанавливающимся, стандартный подход состоит в том, чтобы позволить всем службам в приложении запускаться одновременно, а контейнерам дать возможность сбоя и перезапуска, пока все они не будут запущены и не заработают.
У меня есть службы A и B, запускаемые независимо, как и должно быть построено облачное приложение без сохранения состояния. Но для удобства пользователя, возможно, я мог бы указать пользовательскому интерфейсу (службе B) отображать красивое сообщение о загрузке, пока служба A не готова. При этом фактический запуск службы B не должен зависеть от службы A.
Теперь, если в поде происходит сбой, Kubernetes перезапускает службу, пока все не заработает. Если вы застряли в CrashLoopBackOff, стоит проверить свой код, конфигурацию или конкуренцию за ресурсы
Конечно, нужно делать больше, чем просто полагаться на самовосстановление. Стоит внедрять решения, справляющиеся с отказами, которые неизбежны и будут. Мы должны предвидеть, что они произойдут, и заложить основы для реагирования таким образом, чтобы избежать простоев и/или потери данных.
В моем гипотетическом приложении для покупок пользовательский интерфейс витрины (услуга B) нуждается во второй части (услуга A), чтобы предоставить пользователю то, что нужно. Поэтому при сбоях, например, если служба A недоступна в течение какого-то времени, система все равно должна иметь возможность восстановиться после этой проблемы.
Подобные временные сбои возможны, а чтобы минимизировать их последствия, мы можем реализовать шаблон Retry.
Шаблоны повторных попыток помогают повысить стабильность приложения с помощью таких стратегий, как:
Реализация схемы отключения также является важной стратегией при создании отказоустойчивых приложений на основе микросервисов. Как автоматический выключатель в доме сам отключается, чтобы защитить вас от серьезных повреждений из-за чрезмерного тока или короткого замыкания, так схема отключения цепи предоставляет метод написания приложений с ограничением влияния неожиданных сбоев, которые могут занять много времени. Это такие сбои, как, например, частичная потеря связи или полный отказ службы.
В таких ситуациях, если повторная попытка не сработает, приложение должно уметь установить, что произошел сбой, и отреагировать соответствующим образом.
4. Развертывание модулей без установленных ограничений на память и/или CPU.
Распределение ресурсов зависит от сервиса, который вы деплоите. Может быть сложно предсказать, какие ресурсы потребуются контейнеру для оптимальной производительности без тестирования в реальных условиях. Для одного сервиса может потребоваться фиксированный профиль потребления CPU и памяти, в то время как профиль потребления другого сервиса может быть динамическим.
Когда вы деплоите модули без тщательного анализа ограничений памяти и CPU, это может привести к сценариям конкуренции за ресурсы и нестабильным рабочим средам.
Если контейнер не имеет ограничения памяти или CPU, планировщик видит его использование памяти и CPU как нулевое, поэтому на любом узле можно запланировать неограниченное количество модулей. Это может привести к чрезмерному использованию ресурсов и возможным сбоям узлов и кубелетов.
Когда ограничение памяти не указано для контейнера, есть несколько возможных сценариев (они также применимы к CPU):
Объявление ограничений памяти и CPU для контейнеров в кластере позволяет эффективно использовать ресурсы, доступные на узлах кластера. Это помогает планировщику Kubernetes определить, на каком узле должен располагаться под для наиболее эффективного использования оборудования.
При установке лимитов памяти и CPU для контейнера следует позаботиться о том, чтобы не запрашивать больше ресурсов, чем установлено лимитами. Для модулей, содержащих более одного контейнера, совокупные запросы ресурсов не должны превышать установленные лимиты — в противном случае под никогда не будет запланирован.
Запросы ресурсов не должны превышать лимиты
Установка запросов памяти и CPU ниже их лимитов позволяет добиться двух целей:
Хорошей практикой считается использовать запросы CPU на уровне одного ядра или ниже, и использовать ReplicaSets для масштабирования.
Что происходит, когда разные команды соревнуются за ресурсы при деплое контейнеров в одном кластере? Если процесс превысит предел по памяти, он будет остановлен, а если он превысит предел по CPU, для процесса будет включен троттлинг, что приведет к снижению производительности.
Вы можете управлять ограничениями ресурсов с помощью квот ресурсов и LimitRange в настройках пространства имен. Эти параметры помогают учитывать развертывание контейнеров без ограничений или с большими запросами ресурсов.
Установка жестких ограничений ресурсов может быть не лучшим выбором для ваших нужд. Другой вариант — использовать режим рекомендаций ресурсов в Vertical Pod Autoscaler.
5. Использование тега latest в продакшене
Использование тега latest считается плохой практикой, особенно в производственной среде. Модули неожиданно аварийно завершают работу по разным причинам, поэтому они могут в любой момент удалить докер-образы.
К сожалению, последний тег не очень информативен, когда дело доходит до определения того, когда сборка сломалась. Какая версия образа была запущена? Когда в последний раз он работал? Это особенно плохо в продакшене, поскольку вам нужна возможность восстановить работу с минимальным временем простоя.
Не используйте тег latest в продакшен
По умолчанию для imagePullPolicy установлено значение Always, при перезапуске образ всегда скачивается. Если вы не укажете тег, Kubernetes по умолчанию будет использовать последнюю версию.
Однако развертывание будет обновлено только в случае сбоя, когда под удаляет образ при перезапуске, или при изменении шаблона модуля развертывания (.spec.template). Смотрите это обсуждение на форуме, чтобы увидеть, как тег latest не работает должным образом в процессе разработки.
Даже если вы изменили imagePullPolicy на другое значение, кроме Always, ваш под все равно будет скачивать образ, если ему необходимо перезапуститься, будь то из-за сбоя или преднамеренной перезагрузки.
Если вы используете управление версиями и устанавливаете imagePullPolicy со значимым тегом, например v1.4.0, то можете вернуться к самой последней стабильной версии и легче устранять неполадки, если что-то в коде пошло не так. Вы можете узнать больше о передовых методах управления версиями в Semantic Versioning Specification и GCP Best Practices.
Помимо использования конкретных и значимых тегов Docker, вы также должны помнить, что контейнеры не имеют состояния и неизменны. Они также должны быть эфемерными, вы должны хранить любые данные вне контейнеров в постоянном хранилище. После того, как вы развернули контейнер, нельзя его изменять: никаких патчей, никаких обновлений, никаких изменений конфигурации. Когда вам нужно обновить конфигурацию, вы должны развернуть новый контейнер с обновленной конфигурацией.
Неизменность Docker, взято из Best Practices for Operating Containers
Эта неизменность обеспечивает более безопасное и повторяемое развертывание. Вы также можете легко выполнить откат, если нужно повторно развернуть старый образ. Сохраняя неизменными образы Docker и сам контейнер, вы можете развернуть один и тот же образ контейнера в каждой отдельной среде. Смотрите антипаттерн №1, где мы говорили об экстернализации данных конфигурации, чтобы ваши образы оставались неизменными.
Мы можем вернуться к предыдущей стабильной версии, пока будем устранять неполадки
6. Деплой новых обновлений/исправлений путем уничтожения модулей, чтобы они извлекали новые образы Docker во время процесса перезапуска
Подобно тому как полагаться на тег latest для получения обновлений, полагаться на удаление модулей для развертывания новых обновлений — плохая практика, поскольку вы не управляете версиями своего кода.
Если вы убиваете модули для получения обновленных образов Docker в продакшен, то так лучше не делать. После того, как версия была выпущена в продакшен, ее нельзя перезаписывать. Если что-то сломается, вы не будете знать, где и когда что-то пошло не так и как далеко нужно вернуться, если нужно откатить код во время устранения неполадок.
Kubernetes запустит обновление с нулевым временем простоя:
7. Смешивание производственной и непроизводственной нагрузок в одном кластере
Kubernetes поддерживает пространства имен, которые позволяют пользователям управлять разными окружениями (виртуальными кластерами) в одном физическом кластере. Их можно рассматривать как экономичный способ управления различными окружениями в одном физическом кластере. Например, вы можете запустить stage и продакшен окружения в одном кластере и сэкономить ресурсы и деньги. Однако существует большая разница между запуском Kubernetes в разработке и запуском Kubernetes в рабочем окружении.
При смешивании производственных и непроизводственных нагрузок в одном кластере необходимо учитывать множество факторов. Так, нужно будет рассмотреть ограничения ресурсов, чтобы убедиться, что производительность продакшен окружения не скомпрометирована. Обычная практика — это не ставить квоты на продакшен пространства имен и устанавливать квоты на любые непродакшен пространства имен.
Вам также следует помнить об изоляции. Разработчикам требуется гораздо больше доступов и разрешений, чем нужно в продакшен окружении, и которые вы хотели бы максимально заблокировать. Хотя пространства имен скрыты друг от друга, по умолчанию они не изолированы полностью. Это означает, что ваши приложения в пространстве имен для разработки могут вызывать приложения в тестовом, промежуточном или продакшен окружении (или наоборот), что не считается хорошей практикой. Конечно, вы можете использовать NetworkPolicies, чтобы установить правила для изоляции пространств имен.