Привет! Время идет, 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. Он удалит все предыдущие чарты и установит их заново, причем сделает это очень быстро. Вот так:

2 сервиса и nginx-ingress за 16 секунд.
2 сервиса и nginx-ingress за 16 секунд.

Вы также всегда сможете посмотреть какие окружения вы уже раскатали на кластере и узнать подробности, с помощью команд

$ 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 в которой советуют реже деплоить -_-

Ссылка на k8sbox репозиторий

Ссылка на документацию по k8sbox

Ссылка на докерхаб

Комментарии (6)


  1. dsoastro
    30.05.2023 19:22

    Не ясно следующее - мы мержим нашу фичу в мастер, и потом начинаем тестировать ее. В это время кто-то от мастера сделает бранч для своей фичи, и получается, что он использует код с багами из мастера. Как быть?


    1. Twelvee Автор
      30.05.2023 19:22
      +1

      Не совсем, мы создаем feature-ветку от мастера. Пишем в ней код, затем именно эту ветку раскатываем как stage-environment (в моем примере с помощью k8sbox) и тестируем ее отдельно от dev/prod окружений.

      Как только фича протестирована - мы мержим ее в мастер. Из мастера можно настроить автоматический деплой в dev-environment при каждом новом пуше.

      Перед публикацией релиза в прод - желательно проверить как будут вести себя все ваши фичи на dev-окружении, не будут ли они конфликтовать друг с другом.

      Если все ок - отбиваем тег и выкатываем в прод. Если какая-то фича с багом - делаем revert мерж-коммита конкретной фичи и отправляем на доработку, а релиз выпускаем без нее.

      В мастере желательно хранить только протестированный на stage-feature-окружениях код. Однако если все же баг нашли в мастере и от него уже ответвились другие разработчики - после фикса и мержа этого фикса в мастер - пускай они просто подтянут новые изменения перед созданием своего merge-request


  1. dyadyaSerezha
    30.05.2023 19:22

    k8sbox 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) был в нашем проекте.


    1. Twelvee Автор
      30.05.2023 19:22

      Я старался сделать интерфейс схожим с kubectl и его командами, так как планируется добавлять множество команд для просмотра статистики и разделения уже задеплоеных окружений. get и describe кажутся идеально подходящими, хотя вероятно стоит добавить к ним шорткаты.

      А почему бы не сохранять toml-файл в установленном окружении. Тогда удалять окружение можно (и нужно, по логике) по имени -

      Изначально был план сделать отдельный файл для окружений, по примеру с terraform, однако понял что будет излишне. Отсюда и такой не очень интуитивный механизм удаления

      environment.toml всегда лежит где-то рядом обычно, скорее всего в репозитории в котором лежат другие деплойменты. Однако сами окружения (их описания) сохраняются в /tmp/k8sbox/saves в json формат. Именно так можно посмотреть что мы уже раскатили и детали окружения с помощью команды k8sbox describe {EnvironmentID}

      Удаление по ID окружения - как раз то над чем сейчас работаю, будет уже на неделе, как, думаю, и шорткаты для основных команд)


      1. dyadyaSerezha
        30.05.2023 19:22

        Но вы понимаете, что вот тут:

        k8sbox get environment // list of saved environments

        Команда противоречит описанию? Ну хотя бы тогда get envs или get environments.

        Я не знаю kubectl, но обычно, если команда состоит из двух отдельных слов, то значит, второе слово это параметр, который может меняться. Ну приставьте себе вместо команды dir что-то вроде get files. Юзер сразу думает, а что еще можно "гетнуть"? А ничего.


        1. 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. Однако если есть крутые идеи - с радостью буду ждать пулл реквестов)