Привет! Время идет, k8s и docker стали неотъемлемой частью нашей рабочей среды, но, по моему наблюдению, многие компании до сих пор считают что решить проблему deployment bottleneck возможно кучей bash скриптов или отдельным чатиком в телеграмме, в который нужно сообщать всем о новых деплоях.
Что за проблема такая, deployment bottleneck? Легче всего объяснить на примере, который вероятно покажется вам знакомым.
Пример, показывающий что такое deployment bottleneck
Представим Компанию ООО "Хорошая компания". В ней работает всего 2 человека, назвавшихся СТО и СЕО. Один из них раньше работал синьором в компании ООО "Не очень хорошая компания" и в один момент, встретившись с человеком при деньгах, будущим СЕО - они решают создать свою, хорошую компанию. СТО прекрасно знает что их компания будет успешной, а продукт максимально востребованным, поэтому изначально развивает микросервисную архитектуру их проекта. Он также хорошо знаком со всеми новыми технологиями и практиками - докеры, куберы, логи в stdout и все в этом духе. И вот спустя несколько месяцев ребята готовы показать МВП своим пользователям, которых за время разработки нашел СЕО.
Давайте слегка остановимся на этом моменте и подытожим то что имеем.
Пайплайн разработки выглядит как одна ветка master в каждом из 4 микросервисов, которые раскатаны в кластере k8s. Именно такая простота выкатывания новых фич и позволила им закончить МВП всего за пару месяцев. Тут все круто, ребята красавцы!
Проект действительно начал привлекать новых пользователей, и вот в компании работает уже 4 бекэнд и 2 фронтэнд разработчика, 1 мануальный тестировщик, проект менеджер, СТО и СЕО. Все ребята очень мотивированы и теперь их можно назвать настоящей IT компанией! У них уже 2 кластера - один для разработчиков, а другой для пользователей. Пайплайн их разработки выглядит как-то так: есть 2 одинаковые ветки - master и develop, ребята создают свою feature-ветку от develop и делают свою магию, а затем вливают все свои фичи в develop и раскатывают на dev-кластер. Именно там тестирует все мануальный тестировщик и после сообщения "ок" в телеграмме - develop вливается в мастер и самый старший по званию программист нажимает кнопку "Deploy to production" в каком-нибудь gitlab-ci. Процессы! Ребята снова красавцы!
И вот компания растет еще больше, пользователей становится много, они предлагают кучу разных новых фич и хорошие ребята из Хорошей компании с радостью их реализуют. Теперь у них в штате целых 8 бекэнд, 4 фронтэнд, 2 QA инженера, проектный менеджер и все те же, но уже менее худые, СЕО и СТО. Их кластера работают отлично, но вот незадача - их пользователи начинают все чаще жаловаться на баги, а разработчики почему-то все чаще жалуются на свою жизнь. Они проводят митинги, пытаются понять в чем же проблема, а достаточно было просто глянуть на то, как разрабатывается их проект.
8 бекэнд разработчиков работают над 8 разными задачами, их фичи из develop часто возвращают бдительные QA инженеры и git hist выглядит ну прям совсем ужасно. При вливании в мастер возникают огромные конфликты из-за критических багфиксов, которые приходится чинить самому невезучему из команды (тот что на картинке), по пути перетирая новые фичи и создавая еще больше багов.
Это значит что наши ребята из Хорошей компании столкнулись с проблемой deployment bottleneck - когда желающих раскатить свои правки одновременно слишком много. Они и раньше с ней сталкивались, но "затыкали" эту проблему созданием ветки develop и не очень масштабируемым git-flow, даже не подозревая к чему это приведет.
Но как же решить эту проблему?
Проблему мы уже описали, теперь нужно понять из-за чего она возникает. Программисты - это люди (пока что), а люди - совершают ошибки. Когда вы сваливаете в одну ветку develop сразу несколько фич с потенциальными багами. Потом пытаетесь решить конфликты в коде, который вы видите первый раз в жизни так как его написал ваш коллега, а вместе с этим другой ваш коллега исправляет свой баг, который ему вернул QA и перетирает ваши исправления конфликтов. Весь ваш гит-флоу превращается в кашу.
Подожди, но ведь можно деплоить на дев кластер отдельную ветку каждой фичи. Да, можно. Но ведь это вас не избавит от начальной проблемы, из-за которой вы создали эту ветку develop. Вы столкнетесь с проблемой, когда один и тот же сервис пытаются раскатать с разными тегами на один и тот же кластер - deployment bottleneck.
И тут на сцену выходят stage-окружения (либо чатик в телеграмме, в котором разработчики "бронируют" свободный слот, чтобы проверить их фичу на дев кластере).
Если коротко, то stage-окружения это нужные для конкретной фичи набор сервисов, который можно полноценно протестировать или показать заказчику. А теперь давайте перейдем к практике.
Сперва предлагаю установить следующий гит-флоу: мы создаем ветку от мастера, в ней разрабатываем фичу, после этого создаем merge request в мастер и вливаем если все ок. Из мастера мы можем раскатать все на dev кластер, а потом, отбив тег, раскатать в прод к нашим пользователям.
Вместе с этим гиф-флоу необходимо добиться сборки докер образа вашего сервиса для каждой ветки и возможность деплоя этих образов как отдельные, независимые друг от друга окружения. По итогу мы должны получить: на проде крутятся образы с релизными тегами, на деве крутятся образы из мастера (или просто latest), а так же у каждой созданной ветки должен быть свой докер-образ.
Самое сложное позади!
Как правильно и быстро раскатывать эти branch-based образы - комплексная проблема, которую решают по разному, но я решил собрать сахар в кучу и сделать небольшой open-source проект, который и сам буду использовать во всех своих проектах. Проект я назвал k8sbox и он позволяет, составив одну toml спецификацию, раскатывать ваши микросервисы по вашему кластеру.
Объединив термины и слегка подумав я получил самый простой и понятный интерфейс для этой спецификации. У нас есть окружение (environment), в котором находятся некие коробки (boxes), внутри которых лежат наши микросервисы (applications).
И вот как выглядит примерная toml спецификация:
id = "${TEST_ENV}" # It could be your ${CI_SLUG} for example
name = "test environment"
namespace = "test"
variables = "${PWD}/examples/environments/.env"
[[boxes]]
type = "helm"
chart = "${PWD}/examples/environments/box1/Chart.yaml"
values = "${PWD}/examples/environments/box1/values.yaml"
name = "first-box-2"
[[boxes.applications]]
name = "service-nginx-1"
chart = "${PWD}/examples/environments/box1/templates/api-nginx-service.yaml"
[[boxes.applications]]
name = "deployment-nginx-1"
chart = "${PWD}/examples/environments/box1/templates/api-nginx-deployment.yaml"
[[boxes]]
type = "helm"
chart = "${PWD}/examples/environments/box2/Chart.yaml"
values = "${PWD}/examples/environments/box2/values.yaml"
name = "second-box-2"
[[boxes.applications]]
name = "service-nginx-2"
chart = "${PWD}/examples/environments/box2/templates/api-nginx-service.yaml"
[[boxes.applications]]
name = "deployment-nginx-2"
chart = "${PWD}/examples/environments/box2/templates/api-nginx-deployment.yaml"
[[boxes]]
type = "helm"
chart = "${PWD}/examples/environments/ingress/Chart.yaml"
name = "third-box"
values = "${PWD}/examples/environments/ingress/values.yaml"
[[boxes.applications]]
name = "www-ingress-toml"
chart = "${PWD}/examples/environments/ingress/templates/ingress.yaml"
Вся документация доступна и находится в ссылках в конце этой статьи, но тут вроде бы и так все понятно, достаточно лишь взглянуть на пример (который полностью лежит в репозитории гитхаба).
Вместе с этим чартом - мы можем запустить наш инструмент k8sbox и он раскатит данное окружение на вашем k8s кластере. Как-то так:
$ k8sbox run -f environment.toml
А если вдруг наш QA нашел баг и нам требуется перезалить окружение - мы просто выполняем ту же самую команду run. Он удалит все предыдущие чарты и установит их заново, причем сделает это очень быстро. Вот так:
Вы также всегда сможете посмотреть какие окружения вы уже раскатали на кластере и узнать подробности, с помощью команд
$ k8sbox get environment // list of saved environments
$ k8sbox describe environment {EnvironmentID} // describe the environment
Ну, а если ваш QA инженер сказал "ОК", то мы сможем легко отчистить кластер от нашего окружения, выполнив команду:
$ k8sbox delete -f environment.toml
Все ваши сервисы раскатываются на ВАШЕМ k8s кластере, а это значит вы сможете настроить любые желаемые параметры для них. А так же из плюсов - готовые докер образы с entrypoint как раз на k8sbox
. То есть вы сможете легко интегрировать данный инструмент в любые ваши CI-пайплайны.
Этот инструмент позволит вам решить проблему deployment bottleneck очень простым способом - разделив разработку новых фич, дав разработчикам возможность делать свою магию параллельно и независимо друг от друга.
Полезные ссылки:
Статья про deployment bottleneck в которой советуют реже деплоить -_-
Комментарии (6)
dyadyaSerezha
30.05.2023 19:22k8sbox get environment // list of saved environments
По параметрам подразумевается как раз вторая команда - дать инфу по окружениию. Лучше что-то типа listenvs (если нет других команд с list, или list envs, если есть).
k8sbox describe...
Очень длинное слово. Лучше view, show, info.
k8sbox delete -f environment.toml
А почему бы не сохранять toml-файл в установленном окружении. Тогда удалять окружение можно (и нужно, по логике) по имени -
k8sbox delete envID
P. S. Кстати, именно такой подход (свое окружение на каждую фичу и несколько интеграционных QA-envs) был в нашем проекте.
Twelvee Автор
30.05.2023 19:22Я старался сделать интерфейс схожим с kubectl и его командами, так как планируется добавлять множество команд для просмотра статистики и разделения уже задеплоеных окружений. get и describe кажутся идеально подходящими, хотя вероятно стоит добавить к ним шорткаты.
А почему бы не сохранять toml-файл в установленном окружении. Тогда удалять окружение можно (и нужно, по логике) по имени -
Изначально был план сделать отдельный файл для окружений, по примеру с terraform, однако понял что будет излишне. Отсюда и такой не очень интуитивный механизм удаления
environment.toml всегда лежит где-то рядом обычно, скорее всего в репозитории в котором лежат другие деплойменты. Однако сами окружения (их описания) сохраняются в /tmp/k8sbox/saves в json формат. Именно так можно посмотреть что мы уже раскатили и детали окружения с помощью команды
k8sbox describe {EnvironmentID}
Удаление по ID окружения - как раз то над чем сейчас работаю, будет уже на неделе, как, думаю, и шорткаты для основных команд)
dyadyaSerezha
30.05.2023 19:22Но вы понимаете, что вот тут:
k8sbox get environment // list of saved environments
Команда противоречит описанию? Ну хотя бы тогда get envs или get environments.
Я не знаю kubectl, но обычно, если команда состоит из двух отдельных слов, то значит, второе слово это параметр, который может меняться. Ну приставьте себе вместо команды dir что-то вроде get files. Юзер сразу думает, а что еще можно "гетнуть"? А ничего.
Twelvee Автор
30.05.2023 19:22Не вижу тут противоречия, если честно, из официальной документации kubectl:
# Get commands with basic output kubectl get services # List all services in the namespace kubectl get pods --all-namespaces # List all pods in all namespaces kubectl get pods -o wide # List all pods in the current namespace, with more details kubectl get deployment my-dep # List a particular deployment kubectl get pods # List all pods in the namespace kubectl get pod my-pod -o yaml # Get a pod's YAML
Однако команда
kubectl get pod
равнозначна командеkubectl get pods
В ближайшем будущем появится возможность просматривать не только активные окружения, но так же и другие сущности, как и фильтровать их по имени окружения, активным боксам и так далее. Команда get идеально подходит для таких вещей, а сущности, которые мы хотим получить, могут иметь кучу удобных для пользователя алиасов.
На другом примере из kubectl можно посмотреть на команды ниже, которые вернут один и тот же результат.
kubectl get services kubectl get service kubectl get svc
Скорее всего в следующей версии k8sbox будет реализовано получение списка окружений с помощью следующих команд:
k8sbox get environments k8sbox get environment k8xbox get env
Я активно работаю над этим и стараюсь сделать проект максимально понятными для пользователя такого высокоуровневого инструмента как k8sbox. Однако если есть крутые идеи - с радостью буду ждать пулл реквестов)
dsoastro
Не ясно следующее - мы мержим нашу фичу в мастер, и потом начинаем тестировать ее. В это время кто-то от мастера сделает бранч для своей фичи, и получается, что он использует код с багами из мастера. Как быть?
Twelvee Автор
Не совсем, мы создаем feature-ветку от мастера. Пишем в ней код, затем именно эту ветку раскатываем как stage-environment (в моем примере с помощью k8sbox) и тестируем ее отдельно от dev/prod окружений.
Как только фича протестирована - мы мержим ее в мастер. Из мастера можно настроить автоматический деплой в dev-environment при каждом новом пуше.
Перед публикацией релиза в прод - желательно проверить как будут вести себя все ваши фичи на dev-окружении, не будут ли они конфликтовать друг с другом.
Если все ок - отбиваем тег и выкатываем в прод. Если какая-то фича с багом - делаем revert мерж-коммита конкретной фичи и отправляем на доработку, а релиз выпускаем без нее.
В мастере желательно хранить только протестированный на stage-feature-окружениях код. Однако если все же баг нашли в мастере и от него уже ответвились другие разработчики - после фикса и мержа этого фикса в мастер - пускай они просто подтянут новые изменения перед созданием своего merge-request