Привет! Меня зовут Игорь Латкин, я управляющий партнер и системный архитектор в KTS. Сегодня расскажу, как мы в компании выстраивали процессы деплоя приложений так, чтобы разработчики могли делать это самостоятельно.
Поделюсь, как мы формируем регламенты в KTS и на каких «трёх китах» стоит инфраструктура глазами разработчика. Статья написана по мотивам моего доклада на kuberconf.io.
Оглавление:
-
Роль разработчиков и DevOps-инженеров в управлении инфраструктурой
-
Принципы построения платформы для разработки и развертывания приложений
В большинстве IT-компаний принято, что чем меньше ограничений и правил, тем быстрее и эффективнее процесс разработки продукта. Однако на практике это не всегда так. Без четко выстроенных регламентов и процессов каждый разработчик начинает работать «по-своему», что может привести к «хаосу». Регламентация процессов разработки — структурированный подход к созданию приложения, который, наоборот, помогает команде разработки.
Рассмотрим типичное приложение, которое разрабатывается у нас в компании. Команда разработчиков отвечает за создание приложений, которые чаще всего взаимодействуют с одной или несколькими базами данных (это могут быть как реляционные БД, In-memory хранилища, брокеры сообщений, системы аналитики и т.д.). Но если посмотреть на картину шире, то мы заметим, что для успешной разработки и запуска проекта, помимо самого приложения и систем хранения, нужна дополнительная инфраструктура. Она включает в себя: CI/CD, мониторинг, сбор логов, трейсов, системы обеспечивающие непрерывное тестирование (QA), управление конфигом приложения, его секретами и в целом контроль доступа разных людей к разным подсистемам.
Роль разработчиков и DevOps-инженеров в управлении инфраструктурой
В процессе разработки приложения участвует множество ключевых людей: разработчики, менеджеры, QA, DevOps-инженеры и другие. Каждый из них имеет свое видение и требования к реализации проекта.
На практике при разработке приложений инфраструктура часто оказывается удобной только для DevOps инженеров, а не для целевых пользователей: например, не для разработчиков, QA или менеджера. Это происходит не из-за какого-то злого умысла, а из-за естественного процесса работы любого человека над чем-либо — «если мне понятно и удобно, то значит всем остальным удобно». Поэтому везде, а особенно в управлении инфраструктурой и ее поддержкой нужно постоянно собирать обратную связь с команды разработки, а ещё лучше — наблюдать как они работают, и с высоты своего опыта предлагать улучшения процессов.
В IT-компаниях часто применяют модель, при которой DevOps-инженер управляет всеми аспектами разработки приложения, включая выдачу доступов, управлением CI/CD, контролем доступа и т.д. С первого взгляда такая схема представляется весьма эффективной, поскольку позволяет централизовать ответственность и упрощает взаимодействие: все запросы и проблемы поступают к одному человеку/команде.
Но у этой модели есть недостатки. Например, в случае обнаружения ошибки в коде только DevOps-инженер имеет доступ к анализу лог-файлов, метрикам или переменным окружения. В результате разработчики не могут самостоятельно решить проблему и вынуждены ожидать, когда хоть кто-нибудь из DevOps-команды освободится от текущих задач.
В идеальном мире DevOps-инженер управляет единой системой, которую он полностью поддерживает и обслуживает. Для разработчика «идеальный мир» — это набор различных инструментов, баз данных и методов хранения — фактически, это означает «полную свободу» в работе с инфраструктурой. А что, если предоставить разработчикам полную свободу действий? Ожидаемо возникнут проблемы:
-
Отсутствие гарантий безопасности
-
Необходимость учитывать возможность мониторинга
-
Нет контроля конечной стоимости решений
Какая же модель наиболее эффективная? На основе нашего опыта оптимальное решение — предоставить максимально возможностей разработчикам по различным процессам — это может быть CI/CD, сетап необходимой инфраструктуры и т.д., сняв рутинные и повторяющиеся задачи с DevOps-отдела. При этом, DevOps-команда, освободившись от рутинных задач, могут заниматься улучшениями «платформы», ускорением процессов и конечно же решением проблем и более серьезных обращений. В то же время команда разработки получает хорошо-работающие и поддерживаемые компоненты «из коробки» не задумываясь об их управлении.
Platform Engineering: принципы построения платформы для разработки и развертывания приложений
Многие воспринимают облако «платформой». Однако очень важно понять (и, к сожалению, нередко встречаешь IT команды, где такое происходит), что предоставление разработчикам просто доступа к облаку не является полноценным решением и не решает многих проблем, связанных с высокой доступностью и поддерживаемостью.
Platform Engineering — это не только про облако и ресурсы, а комплексное решение, которое объединяет процессы взаимодействия. Это интегрированная платформа с разными сервисами: CI/CD, управлением доступа и секретами. Платформа не является только облачным сервисом, она решает гораздо более тонкие и сложные вопросы в целом по организации процессов в командах и компании в целом.
Развитие инфраструктурной платформы — само по себе непрекращающийся процесс в любой компании и я еще ни разу не слышал, чтобы кто-то сказал «мы всё настроили идеально, больше нам делать в инфраструктуре нечего, все довольны». Всегда появляются новые задачи, новые челленджи, с которыми приходится сталкиваться как командам разработки, так и DevOps.
Как появились регламенты в KTS
За длительное время мы эволюционировали от ручного управления серверами (с различными системами IaC — тогда это был в основном SaltStack), которым занимались отдельные DevOps-инженеры, к созданию полноценной DevOps-команды. Основной задачей этой команды стало непосредственное создание и улучшение процессов, теперь она отвечает за разработку и поддержание платформенных решений.
Роли DevOps решались раньше в каждой команде своими отдельными людьми и по-своему
Ключевые проблемы, которые легли в основу создания регламентов в компании:
-
Разные команды выполняют одни и те же задачи по-разному.
-
Недостаточный мониторинг и за редким исключеним отсутствие общие инфраструктурных компонентов.
-
Неэффективные и небезопасные процессы деплоя приложений как в dev, так и prod окружения.
-
Отсутствие контроля за расходами проектов/юнитов.
Сейчас мы пришли к тому, что разработчик умеет самостоятельно, и тут хочется это подчекрнуть — в 90% случаях без прибегания к помощи DevOps-команды развертывать приложение на дев и прод-средах. Кроме того, он имеет прямой доступ к системам CI/CD, настройке инфраструктуры в облаке и on-premise и управлению конфигом и секретами приложения.
Конечно же, в случае проблем или более сложного (а нередко еще и кастомного) деплоя мы привлекаем DevOps-инженеров для этого. Но тут важно отметить, что и здесь благодаря «готовым рецептам» по развертыванию копии или аналога инфраструктуры в другом контуре занимает с каждым годом все меньше и меньше ресурсов.
Выделение команды DevOps-инженеров помогло лучше организовать процессы запуска приложенийЧтобы взаимодействие работало как надо, мы придерживаемся примерно следующей схемы распределения ролей:
-
Команда DevOps:
-
Создание и поддержка общей инфраструктуры.
-
Организация управления доступом (RBAC).
-
Настройка базового CI/CD.
-
Разработка и внедрение общих практик релизов.
-
Организация хранения и управления секретами.
-
Реакция на инциденты общей инфраструктуры и проектов (в случае эскалации)
-
-
Разработчик:
-
Создание всех необходимых ресурсов в облаке и связанных системах (Sentry, S3, алерты, и т.д.) для своей работы.
-
Доработка CI/CD процессов по мере необходимости.
-
Выдача доступа внутри своей команды.
-
Реакция на инциденты в рамках проекта.
-
«ТРИ КИТА» создания инфраструктуры
1 КИТ — CI/CD как ключевой элемент инфраструктуры
Рассмотрим два основных аспекта, с которыми мы чаще всего сталкиваемся в компании, когда говорим о CI/CD:
-
Возможность простого использования, а также кастомизации CI/CD пайплайна.
-
Быстрое понимание, что идет не так в процессе деплоя. Предполагается, что во время, например, сборки Docker-образа зачастую понятно, что могло пойти не так, если это не инфраструктурные проблемы.
Говоря о первом аспекте, большинство проектов в компании деплоить можно с использованием стандартизированных шаблонов CI/CD под разные типы проектов — например, бэкенд проекты на Python с использованием Django или внутреннего асинхронного фреймворка, бэкенд проекты на Go, фронтенд проекты, мобильные проекты, проекты-библиотеки для разных языков программирования и т.д.
include: project: mnt/ci file: back/ktstools/ci.yaml variables: DOMAIN_DEV: 'projectA.example.com' DOMAIN_PROD: 'projectA.prodexample.com' KTSBILLING_DEPARTMENT: 'special' KTSBILLING_PROJECT: 'projectA' PYTHON_IMAGE: 'python:3.12-slim'
Эти шаблоны существенно упрощают жизнь разработчикам и дают удобные интерфейсы для конфигурации процесса деплоя с добавлением новых компонентов, изменения существющих и т.д. Мы стараемся наиболее «популярные» и востребованные настройки выносить как можно ближе и как показано на сниппете выше — есть возможность, например, сменить домен, на котором разложится проект в разных окружениях или используемый Docker-образ для Python-проекта.
Рассмотрим теперь другой аспект CI/CD, который связан с разруливанием проблем при выкатке, связанных с неправильной конфигурацией приложения, базовых настроек ресурсов и так далее.
Представьте ситуацию: разработчик запускает пайплайн и он падает на этапе деплоя в продакшн. Что в такой ситуации должен делать разработчик? Чаще всего он вынужден обратиться за помощью к DevOps-ам по простой причине того, что если в экране джобы деплоя он видит ситуацию ниже, то ему ничего не остается как либо гадать, либо идти за помощью к DevOps-команде, чтобы они посмотрели причину таймаута.
DevOps-инженер может быть занят решением других задач, и потребуется время, чтобы он освободился. Когда DevOps изучит логи сервиса, проверит настройки CI/CD, разберётся, что за процесс запускается и в чем может быть проблема — только потом сможет предложить какое-то решение.
Вернувшись к разработчику, он может указать, что, например, не был прописан listen порт в приложении или нужно увеличить memory limit процесса, или как в ситуации на скриншоте выше — миграции завершились с ошибкой. Разработчик вносит необходимые исправления, и процесс запускается заново.
В чем минус такой схемы решения проблемы? Очевидно, в огромной потере времени.
Для решения этой проблемы важно дать разработчику максимально подробную информацию о произошедших ошибках. В нашей практике мы используем инструмент Kubedog — он выводит ошибки из окружения и лог-файлы подов прямо в консоль CI/CD процесса. Это облегчает работу DevOps-инженерам, снижает нагрузку на них, и позволяет разработчикам самим оперативно понимать и устранять проблемы.
Под спойлером удобный скрипт из просторов Интернета, который помогает затрекать все возможные Deployment и StatefulSet в разворачиваемом Helm-чарте.
kubedog multitrack
ret=0; { helm get manifest ${RELEASE_NAME} | kubectl get -o json -f - | jq '.items | group_by(.kind) | map({"kind":.[0].kind, "items":map({"ResourceName":.metadata.name,"Namespace":.metadata.namespace,"SkipLogsForContainers":["istio-proxy"]})}) | reduce .[] as $item ({"Deployments":[],StatefulSets:[]}; if $item.kind=="Deployment" then . + {"Deployments":$item.items} else if $item.kind=="StatefulSet" then . + {"StatefulSets":$item.items} else . end end)' | kubedog multitrack ; } || ret=$? if [[ "$ret" -ne 0 ]]; then exit 2 fi
Также не забывайте управлять GitOps по кнопке. Хотя pull-based GitOps с инструментами вроде Argo CD или Flux кажется удобным, он может создать высокий порог входа для разработчиков. Так они теряют контроль над моментом выкатки приложения.
Опыт показал, что GitOps лучше работает, когда разработчики получают возможность самостоятельно запускать выкатку через manual job. Это позволяет контролировать процесс и взаимодействовать с приложением напрямую.
Например, можно пойти путем, как в коде ниже.
deploy: when: manual script: - argocd app set myapp -p image.tag=$CI_COMMIT_REF_SLUG-$CI_PIPELINE_IID - argocd app sync myapp - argocd app wait myapp
Или применить более сложные практики: в той же ручной джобе, сделать автоматический коммит нужной версии образа в репозиторий с values проекта, из которого уже ArgoCD подтянет изменения и выкатит приложение. Так мы не ломаем основную идею GItOps — желаемое состояние, включая версии приложения зафиксированы в Git.
Помимо базового CI/CD, кастомизации пайплайнов и улучшение опыта взаимодействия разработчиков с пайплайнами, отдельного внимания заслуживают динамические окружения. Их использование способно эффективно решать проблемы, связанные со сборкой и проверкой новых фичей как можно быстрее и в большом количестве, не пересекаясь с другими членами команды.
И самое главное — разработчики самостоятельно способны выкатывать такие демо-стенды своих наработок без привлечения сторонней помощи, что также ускоряет процессы. Подробнее об этом я рассказывал на Хабре в этой статье.
2 КИТ — Управление ресурсами
Рассмотрим другую ситуацию: разработчик деплоит приложение и понимает, что для выполнения задачи необходима база данных, например, PostgresSQL или MongoDB. Далее он обращается к DevOps, который настраивает необходимые инструменты: Terraform и Vault, разворачивает базу данных (например, в Yandex Cloud) или добавляет новую БД в существующий инстанс, если предполагаемые нагрузки будут небольшими. После этого разработчик получает креды от базы данных, настраивает конфиг, но затем осознает, что ему для проекта нужен еще и S3-бакет. Цикл обращения к DevOps повторяется. Такой подход приводит к потере времени и снижению эффективности работы команды.
Чтобы решить эту проблему, мы внедрили управление инфраструктурой через код, но самое главное — это то, что разработчикам был предоставлен доступ к репозиторию с Terraform-конфигурациями всей нашей инфраструктуры. Но здесь важно отметить еще наличие удобных и понятных для использования модулей, чтобы добавление БД выглядело как copy-paste 5 строчек TF кода, а не написание и тестирование развернутых скриптов. Разумеется, здесь нет ничего сверхъестественного, но тем не менее, построив процессы именно вокруг IaC и дав командам разработки к нему доступ, нам удалось заметно снизить нагрузку на DevOps-инженеров по десяткам типовых запросов в месяц.
Теперь процесс взаимодействия выглядит следующим образом: разработчики выявляют, что им необходима БД, S3-бакет и очередь в Kafka. Они заходят в репозиторий Terraform, настраивают все необходимые ресурсы, запускают CI/CD пайплайн, и данные для доступа к этим ресурсам появляются в Vault для данного проекта. Эти данные автоматически синхронизируются с Kubernetes, после чего разработчик может успешно развернуть приложение, не прибегая к помощи других специалистов и даже не имея доступа к самим логинам/паролям от связанных систем.
Однако чтобы обезопасить инфраструктуру, и «случайно» не сломать ничего, мы ограничили возможность разработчиков самостоятельно выкатывать изменения через Terraform. Было принято решение внедрить этап одобрения (approve) от DevOps команды. После получения «апрува» запускается полный пайплайн — инфраструктура развертывается в автоматическом режиме. Сам же запрос на апрув мердж-реквеста попадает в нашу систему обращений через специализированного бота.
Таким способом мы автоматизируем следующие процессы:
-
БД и пользователи в MongoDB
-
Очереди в RabbitMQ / Kafka
-
S3-бакеты
-
Инфраструктурные компоненты Kubernetes, такие как, например, Ingress-контроллеры
-
DNS
-
Sentry
-
Keycloak + InfraHQ.
-
… (список пополняется с появлением новых инструментов в компании)
Ниже рассмотрим, как, например, выглядит процесс создания новой БД в Selectel-облаке с помощью специализированного Terraform-модуля. Кстати часть из используемых нами модулей можно найти на нашем Github.
Алгоритм действий достаточно простой:
-
Генерируем случайный пароль, который будет использован для создания пользователя.
-
Создаем пользователя с указанным именем и сгенерированным паролем в облаке.
-
Сохраняем полученные логин/пароль в Vault.
-
Создаем БД в указанном инстансе СУБД в облаке, с овнером, равным пользователю, созданным ранее.
Код самого модуля доступен по ссылке или ниже под спойлером.
Создание БД
resource "random_password" "password" { length = 24 special = false } resource "selectel_dbaas_user_v1" "user" { project_id = var.project_id region = var.region datastore_id = var.datastore_id name = var.name password = random_password.password.result } resource "vault_generic_secret" "credentials" { path = var.vault_credentials_key delete_all_versions = true data_json = <<EOT { "${var.vault_credentials_master_host_attr}": "master.${var.datastore_id}.c.dbaas.selcloud.ru", "${var.vault_credentials_port_attr}": "5433", "${var.vault_credentials_port_direct_attr}": "5432", "${var.vault_credentials_username_attr}": "${var.name}", "${var.vault_credentials_password_attr}": "${random_password.password.result}", "${var.vault_credentials_db_attr}": "${var.name}" } EOT } resource "selectel_dbaas_database_v1" "db" { project_id = var.project_id region = var.region datastore_id = var.datastore_id owner_id = selectel_dbaas_user_v1.user.id name = var.name lc_ctype = var.lc_ctype lc_collate = var.lc_collate }
3 КИТ – Secrets & RBAC
Управление секретами и разграничением доступа само по себе является достаточно объемной темой и заслуживает отдельной публикации. Здесь же хочется отметить основные моменты, на которые мы обращали внимание в разрезе комфорта разработчиков во время работы над своими проектами.
Пример иерархии секретов в Vault
Первым делом мы в определенный момент времени решили (как и многие другие компании) централизовать хранение секретов и HashiCorp Vault — зачастую очевидный выбор в такой ситуации. Как раз по такому пути мы пошли в своё время.
Для начала необходимо было перенести секреты из разрозненных мест в некоторую структуру в Vault. Здесь оптимальной для нас оказалась схема, разделяющая секреты по иерархии «Окружения → Департаменты → Проекты».
Так как у нас достаточно плоская структура команд, это позволило сильно легче выдавать доступ людям. Например, можно выдать доступ ко всему dev-окружению (вне зависимости от департамента или проекта), но также оставив возможность выдавать гранулярно доступ к определенному проекту. В каждой организации могут быть свои особенности, и, может быть, где-то лучше подойдет схема «Департаменты → Окружения → Проекты» или иная. Визуально наша схема выглядит так:
—apps —- dev —---<department> —-----<project> —-------config
Вторым шагом мы уже начали автоматизировать создание сервисных аккаунтов к инфраструктурным компонентам (базам данных, очередям и т.д.). Собственно, TF-модулем выше создавался секрет для Selectel-БД как раз внутри «директории» <project>
. Расширяя дальше эту стратегию, получаем следующую иерархию:
—apps —- dev —---<department> —-----<project> —-------config —-------pg —-------rabbitmq —-------s3
Здесь как раз в ключах pg
, rabbitmq
, s3
хранятся конфигурации соответствующих компонентов, полностью достаточной для подключения и они затем объединяются в единый конфиг для приложения.
Распространение секретов
После того, как мы секреты сложили в центральное хранилище, встаёт насущный вопрос: а как эти секреты довести до приложения? Тут есть 2 основных способа, которыми мы пользуемся:
-
Во время CI/CD джобы, когда мы готовы выполнить
helm upgrade
, идем в Vault, получаем нужный секрет, генерируем из него Kubernetes Secret и деплоим вместе с приложением. -
В случае, если используется GItOps-подход, можно автоматически синхронизировать секреты в Vault с секретом в Kubernetes. Для этого можно использовать инструменты external-secrets или mirrors (наш собственный контроллер, который мы использовали для похожих нужд).
Оба способа имеют право на жизнь в зависимости от условий, в которых работает ваше приложение.
Доступы к секретам
В общем случае у разработчиков есть доступ на чтение определенных секретов и у более старших — доступ на редактирование config
секретов, в нем хранятся исключительно настройки самого приложения. Это позволяет, как и во всех остальных сценариях, снять нагрузку с DevOps-отдела по довольно рутинной операции — настройке конфига приложения. Как показала практика, гораздо лучше работает, когда за приложение, его конфигурацию и запуск целиком несет ответственность команда разработки.
RBAC
Как и с секретами, зачастую рано или поздно в любой компании приходят к централизованному управлению пользователями на основе, например LDAP, Active DIrectory, OIDC. На большом количестве сотрудников это открывает удобные возможности как массовой настройки прав доступа (например, по группам), так и более гранулярной (по пользователям).
Группы и варианты создания их иерархии — тоже может быть темой отдельного обсуждения, но в целом любой из вариантов может быть удобен, если есть достаточно гибкие средства автоматизации их настройки.
Как и со всей остальной инфраструктурой в KTS, группами мы управляем через Terraform, для этого есть специально написанные модули. Это позволило нам заметно упростить рутинные действия по настройке, а также сохранить as a code все наработки.
На начальном этапе разработки мы рекомендуем заранее разработать стратегию управления ключами и секретами в Vault и группами в LDAP. Такой подход значительно оптимизирует процессы и сэкономит время.
Соответствующие Terraform-модули записывают ключи в Vault. Затем эти данные подтягиваются единым пайплайном из шаблонных проектов и деплоятся в инфраструктуру. У вас в компании могут быть другие требования и регламенты — выбирайте, что лучше встроится у вас.
Распределение ролей и выдача доступов
В KTS мы разработали систему эскалации прав доступа среди членов команды. Часть прав доступна разработчикам, другая — лидам, и отдельный набор прав — DevOps и т.д. Эти права могут пересекаться между собой. Такая схема распределения позволяет выстроить иерархию управления доступами без создания узких мест, зависящих от одного сотрудника.
Здесь стоит отметить, что, конечно, большинство прав доступа к различным инфраструктурным компонентам компании, по-прежнему остаются в ведении DevOps отдела. Это, на мой взгляд, скорее хорошо, чем плохо, потому что слишком большие права на выдачу доступов могут повлечь неразумное распоряжение ими и, как следствие, бреши в безопасности.
Пока это не является для нас узким горлышком, но естественно, таких запросов становится все больше с ростом компании, и мы думаем об улучшениях этих процессов в будущем.
Итог и вывод
Применяя такие подходы к управлению инфраструктурой и процессами, мы заметно сгладили сложности, с которыми изо дня в день сталкиваются разработчики. С тех пор как разработчики в KTS получили возможность самостоятельно развертывать и настраивать приложения, а также управлять инфраструктурой, расширять и дополнять существующие CI/CD пайплайны, скорость развертывания проектов заметно выросла. И самое главное — снизились процессы ожидания одной команды другой
Разработчики могут самостоятельно диагностировать большинство возникающих ошибок и конфигурировать приложение. DevOps команда освобождается также и от рутинных задач, например, от выдачи простых доступов, настройки приложений, занимаясь платформенными задачами. Проекты быстро запускаются — а это хороший показатель эффективности разделения обязанностей и предоставления разработчикам «большей свободы».
Подведём итоги:
-
CI/CD должен быть максимально адаптирован для удобства разработчиков и обеспечить им комфортную работу в проекте.
-
Можно ускорить рабочие процессы, если дать разработчикам возможность самим создавать БД, очереди, бакеты и другие инфраструктурные компоненты.
-
Чтобы улучшить безопасность и упростить доступ, используем централизованное управление секретами.
-
Разработчики должны иметь возможность управлять конфигурацией своих проектов.
-
Учитываем потребности всех участников команды, чтобы каждому было удобно работать с инфраструктурой.
На этом DevOps не заканчивается. В нашем блоге есть и другие полезные статьи, которые помогут прокачать DevOps-знания:
ссылка на оригинал статьи https://habr.com/ru/articles/854750/
Добавить комментарий