Представьте, что есть проект, где 200 разработчиков, 20+ независимых продуктовых команд и у каждой свой собственный DevOps. Они всё автоматизируют — все довольны и занимаются исключительно своей работой. Разработчики даже успевают красить зелёные кнопочки в красный цвет. У DevOps современные инструменты и они помогают автоматизировать релизы. У каждого product owner независимая команда, он никем не блокируется, планирует свой бэклог, быстро двигается и развивается. А “эффективный менеджер” больше не сомневается стоит ли разрешать разработчикам самим катить в продакшен. Представили? Тогда прикинем, что может случиться с ними через год.
Обо всем этом и об интересном подходе к разработке в Ситилинке расскажет Константин Осипов — руководитель DevOps, QA и 140+ backend/fronted-разработчиков. Он познакомит нас с проблемами, которые стояли перед командой разработки и с тем, как они их решили. Поделиться своими мыслями, как не надо делать и объяснит как перестать быть эникейщиком. А главное, как со своими задачами в разработке справляются в Ситилинке.
![](https://habrastorage.org/getpro/habr/upload_files/34a/a94/c6b/34aa94c6b4861474109511aca74ace38.jpg)
Типичный Git-репозиторий
Чтобы погрузиться в тему, посмотрим на типичный Git-репозиторий, что в нём есть:
![](https://habrastorage.org/getpro/habr/upload_files/a0c/ae8/25d/a0cae825d69b98e15e206f35c19a86f1.jpg)
CI/CD pipeline, который включает в себя: линтеры кода, тесты, валидаторы, сборку, deploy в prod/stage/sandbox-окружениях, уведомления о релизах в чат, метки в мониторинг о релизе, чтобы посмотреть корреляцию;
Linters возможно специфичные линтеры или сканеры безопасности;
Helm chart, где есть values-файлы для prod/stage/sandbox-окружений и тесты на сам helm chart;
Dockerfile;
Docker-compose.yml для локальной разработки. В Ситилинке для создания локального окружения разработки не используют Kubernetes, а используют docker-compose;
Config files - файлы конфигов envoy и прочее;
Source code. В исходном коде тоже оказывается не всё так просто это: обработка флагов и переменных окружений, логирование, трейсинг, метрики, liveness/readiness probes, обработка сигналов, мягкое завершение программы Graceful shutdown, HTTP/gRPC-сервер, unit-тесты, инструменты профайлинга, layout приложения.
Вспомним историю про разработчиков, которым больше не нужно делать грязную работу и они могут красить кнопочки даже в AB тесте. Про DevOps которых больше не путают с сисадминами и про всех остальных. Разберём 4 простые истории, что стало с такой командой через командой.
История 1: Product Owner и долгие сроки
Приходит product owner в команду и спрашивает почему микросервис по покраске делался неделю вместо 3 часов? Разработчик, посовещавшись с DevOps’ом, объясняет, что сначала он 1 час ждал DevOps’а, пока тот создавал Git-репозиторий и настраивал права. Потом разработчик за 3 часа сделал layout приложений, подключал логгер, реализовывал HTTP-сервер. Ещё за 3 часа накидал реализацию. Правда, потом DevOps ему помогал настраивать dev-окружение, потому что сам разработчик не хочет этим заниматься. Потом вспомнил, что надо тесты написать — ещё 3 часа. Добавлял метрики и логи — ещё 3 часа. Потом разработчик опять встретился с DevOps’ом, обсудили, что нужно, чтобы всё это дело в продакшен затащить. Тут вспомнили, что надо бы сигналы обрабатывать, мягкое завершение в приложении сделать, да и трейсинг хотелось бы. Сделал это ещё за 3 часа. Дальше DevOps написал pipeline для сборки и деплоя. Потом с QA потестировали, поправили какую-то проблемку, потратили часик. Напилили дашборд в Grafana, чтобы всё удобно передать поддержке. В теории потратили на эту работу 4 дня, а не 3 часа.
![](https://habrastorage.org/getpro/habr/upload_files/180/398/3cd/1803983cd276999a3d9b793d23c4ffc0.jpg)
Но на практике, учитывая затраты на ожидание и переключение контекста, потратили ещё больше — неделю.
Казалось бы, независимая команда, а потратили столько денег, несмотря на то что в каждую команду поставили по DevOps’у помогающему всё автоматизировать. Но он всё равно там один, а разработчиков несколько. Поэтому они друг друга всё время ждут, и на это ожидание тратится дополнительное время.
Выводы
Затраты разработки и DevOps на написание boilerplate-кода просто невообразимые. При этом наверняка у этих команд и продуктов много общего, замечается много копипаста. Разделение труда, на самом деле приводит к задержкам. Хотелось бы, чтобы один человек мог сделать всё и сам дотащил задачу от старта до продакшена.
История 2: ИБ и 20 DevOps
Приходит безопасник к DevOps’у и сообщает, что нашли уязвимость в плагине деплоя в k8s, нужно как можно скорее везде исправить. Безопасник ожидает, что во всех 20 командах типовой pipeline и они как-то договариваются между собой. 20 DevOpses собирают большой ZOOM и понимают, что все 20 команд независимы и делают всё по-разному. Во-первых, в реальности pipeline выглядит иначе. Во-вторых, оказывается, что разработчики сделали 200 микросервисов и на самом деле pipelines не 20, а 200, и их доработка займёт 800 часов.
![](https://habrastorage.org/getpro/habr/upload_files/20c/480/ce1/20c480ce17797095d11616cfa33ae84b.jpg)
Выводы
Каждая команда по-своему пишет сервисы, несмотря на договорённости. Если они независимые, можно хоть договариваться, хоть нет — всё равно всё разъедется.
Глобальные задачи — это всегда боль, особенно если там микросервисная архитектура и их очень много. Они приводят ко множеству коммуникаций, огромным затратам на реализацию и совершенно непредсказуемым результатам.
История 3: «Перевод стрелок»
Приходит как-то support и говорит, что пользователи жалуются, что не могут создать заказ. Разработчик отвечает, что у него локально всё работает, это проблема DevOps. DevOps говорит, что всё внимательно посмотрел, ошибок не видит — контейнеры крутятся, лавеха мутится. Там какая-то сложная бизнес-логика, он ее не писал, это к разработчику. Разработчик утверждает, что посмотрел локально, всё работает, по логам тоже ничего нет и отправляет к DevOps. Потому что не знает инфраструктуру и не понимает, как его код работает в продакшене. И так по кругу до бесконечности.
Выводы
Нет единого ответственного за работу сервиса в целом. DevOps не понимает бизнес-логики, чтобы найти ошибку, а разработчик не понимает как работает его сервис в продакшен. Каждый отвечает за свой кусок, а серая область остаётся, никто не отвечает за весь сервис.
История 4: Баг в PHP extension
Приходит поддержка к DevOps и говорит, что в мониторинге видно, что некоторые поды с PHP зависают со 100% утилизацией CPU. Рестарт ненадолго помогает. Посмотрите через strace в чём дело. DevOps отвечают, что не знают strace, это что-то про Linux, а они только умеют писать пайплайны, helm charts, деплоить в k8s и работать с высокоуровневыми абстракциями.
![](https://habrastorage.org/getpro/habr/upload_files/fae/c28/dbb/faec28dbb1cad9c2a33db74a88e44220.jpg)
Выводы
DevOps теряет навыки из-за постоянной работы с абстракциями и ежедневного потока однотипных задач написания pipeline. Он изо дня в день занимается одним и тем же — копипастом. Поначалу, пока это были новые технологии, ему было интересно, весело, задорно. Но потом, когда каждый день была одна и та же работа, она превратилась в ад и скуку. В силу изолированности команды отсутствовали разнообразные задачи. Как правило, в одной команде задачи плюс-минус одинаковые. DevOps не видит того, что инфраструктуру строят другие люди. Он просто помогает писать pipeline.
Как перестать быть эникейщиком
Теперь стало понятно, кто такие эникейщики. В интернете есть разные определения, но смысл один:
DevOps-инженер — это специалист, который:
синхронизирует этапы разработки программного продукта;
знает в чём работа разработчиков, QA, менеджеров и автоматизирует их задачи;
умеет программировать и быстро изучает новые инструменты.
Так как перестать быть эникейщиком? Никак! DevOps-инженер — это и есть эникейщик.
Казалось бы, на этом можно закончить статью, но в определении DevOps есть слова «автоматизирует их задачи». Об этом и поговорим. Конечно, в Ситилинк не втащили всё, о чём было рассказано выше. Там сначала в голове прокрутили все эти истории и решили, что это не тот путь.
Опыт Ситилинк
В Ситилинк взяли в команду трёх Рембо-DevOps. Рембо — потому что они не только умеют писать pipeline, но ещё понимают в Linux, в сетях и Kubernetes. Могут нормально развернуть с помощью Kubespray. Чтобы у них всё было хорошо, решили, что один Рембо будет старшим, чтобы остальных не бросало в разные стороны по технологиям, и они шли единым клином и приходили к каким-то результатам. Из этого получилось 20 продуктовых команд и DevOps-команда.
Причём, чтобы не было проблемы, когда DevOps-команда стоит за линией и даже 3 Рембо не могут справиться с 20 отрядами бравых разработчиков, Рембо тоже решили сделать продуктовой командой. Так получилось выкрутиться из ситуации, не раздувая штат DevOps. DevOps остались вместе с разработчиками за чертой по одну сторону, и тоже стали продуктовой командой.
![](https://habrastorage.org/getpro/habr/upload_files/bf0/a01/7ce/bf0a017ce75e8c492835847ffa7681e3.jpg)
Продукты DevOps
У DevOps могут быть такие продукты:
![](https://habrastorage.org/getpro/habr/upload_files/72f/52b/4c7/72f52b4c709e5b3fd4a48287c545d991.jpg)
В Ситилинк это:
Telegram bot, который позволяет автоматически получать и оказывать цифровые услуги;
Kubernetes как сервис;
Tarantool как сервис;
Postgres как сервис;
CDN, чтобы нарезать картинки;
S3, чтобы разработчикам было проще работать с одним протоколом и запускать большую часть сервисов в Kubernetes stateless;
Helm Chart template (базовый шаблон);
Мониторинг как сервис;
Запускатели тестов как сервис;
Dev server как сервис — когда невозможно что-то большое запустить на своей тачке.
Самый маленький из них — Telegram bot для получения и оказания услуг. Несмотря на простоту, этот сервис принёс бы вам максимум пользы, если бы вы реализовали его у себя. Поэтому о нём и поговорим далее.
Но прежде чем начать определим красную жирную черту между DevOps и разработчиками. Их нужно разграничить, чтобы они не кидались стрелками между собой. Сделать это можно безопасно, потому что продукты у них разные.
DevOps: | Разработчики: |
Предоставляют цифровые IT-услуги Разрабатывают внутренние IT-продукты Отвечают за качество и стабильность предоставляемых услуг и продуктов Непрерывно собирают обратную связь | Разрабатывают бизнес-продукты Отвечают за качество и стабильность своих продуктов Разрабатывают новые цифровые услуги для себя самих |
Важно, чтобы это всё работало, DevOps должны непрерывно собирать обратную связь с разработчиков, какие им цифровые продукты нужны, потому что у DevOps пользователи — это разработчики, а у разработчиков уже клиенты.
Разработчики, чтобы не грузить дальше DevOps, в принципе могут разрабатывать новые цифровые услуги сами для себя.
Демо
Чтобы было понятно, о чём идёт речь, разберём, как выглядит получение одной из цифровых услуг с точки зрения её получателя. Приведём скрины из реального чат-бота. Бэкенд-разработчик захотел себе сделать микросервис на Go. Он пытается авторизоваться через сервис внутренней авторизации.
![](https://habrastorage.org/getpro/habr/upload_files/288/ac7/439/288ac743905b77b3d8d352df40f4065b.jpg)
К слову, о продуктах, сервис внутренней авторизации, SDK, который легко прикрутить к ботам и к любому веб-приложению, опять же разрабатывают DevOps, это их продукт.
Разработчик авторизуется в этой системе, получает доступ. Отправляет секретный код в чат.
![](https://habrastorage.org/getpro/habr/upload_files/3cc/3b4/264/3cc3b42648dd2a1ad90f5e6443c418bb.jpg)
Дальше предлагается выбрать язык разработки. На скрине разработчик выбрал Go, также он решил писать микросервис, а не библиотеку:
![](https://habrastorage.org/getpro/habr/upload_files/a7c/bba/031/a7cbba031f02475b44e573fdb02d197d.jpg)
Здесь предлагается только один шаблон:gRPC.
![](https://habrastorage.org/getpro/habr/upload_files/ae1/119/067/ae1119067edc8e205ccddc527d579b93.jpg)
Дальше спрашивается номер задачи в Jira, потому что эта система полностью автоматизированная. Здесь никто не будет ничего апрувить, всё создаётся реально и хотелось бы потом знать, по какой задаче было принято решение создать новый микросервис. В данном случае фоном этот бот проверит, что задача существует и находится в нужном статусе.
Следующим шагом вводится название репозитория, например, test-color, а после краткое описание:
![](https://habrastorage.org/getpro/habr/upload_files/958/4d9/f67/9584d9f672e419ac6c508c6ca396ba99.jpg)
десь «тестовый сервис, который красит кнопочки в красный цвет». Теперь понятно, для чего этот микросервис и кто его сделал.
Вводится фамилия, имя майнтейнера и его email:
![](https://habrastorage.org/getpro/habr/upload_files/da3/373/4ba/da33734ba75fec8d4665d4e2bfe75f24.jpg)
Дальше нужно подтвердить, что всё ОК. Разработчик подтверждает, и буквально через 5-10 секунд видит уже радостное сообщение, что репозиторий создан:
![](https://habrastorage.org/getpro/habr/upload_files/b3c/2f6/fe3/b3c2f6fe304790bb31a8bf85ae725513.jpg)
Что получили в итоге? Создался в bitbucket репозиторий. Есть приложение. Оформлен текст Git-коммита, как принято в компании, всё можно потом найти.
![](https://habrastorage.org/getpro/habr/upload_files/d10/782/0a4/d107820a43432162ef203bfd2906d320.jpg)
В readme и в принципе везде, задано имя приложения, добавлено описание:
![](https://habrastorage.org/getpro/habr/upload_files/0da/0bf/d83/0da0bfd8354166f7968e47b46592c614.jpg)
А ещё добавлены:
Обработка сигналов
Graceful shutdown
Настроен logger
Tracing
Метрики
gRPC-сервер
HTTP-сервер
Роутинг
Сконфигурированы переменные окружения/флаги
Сборка
Деплой по тегам в stage/prod
Unit-тесты
Локальное окружение
Деплой в sandbox через bot
Линтеры
Создан дашборд в Grafana на все метрики
Полезные утилиты run.sh
Как это работает?
Рассмотрим, как это работает с точки зрения того, кто эту услугу хочет предоставить. Допустим, DevOps или разработчики решили для себя самих начать предоставлять услугу, которую мы только что получили.
Здесь приведён конфиг этого бота. В основной секции ничего сложного нет — как видели, так и пишется:
![](https://habrastorage.org/getpro/habr/upload_files/88f/ce9/4f0/88fce94f0f800aa99d5ba9e9908bee66.jpg)
Указываем, что шаблон для языка и типа — gRPC микросервис:
![](https://habrastorage.org/getpro/habr/upload_files/c16/ea5/429/c16ea54298f3be9676b181516f8d6e9c.jpg)
Указываем папку в bitbucket — проект, куда создавать полученные сгенерированные новые репозитории, потом указываем репозиторий шаблона (в Ситилинк называется grpc-skeleton):
![](https://habrastorage.org/getpro/habr/upload_files/7dc/6e2/781/7dc6e27816bb2daf0e9b56e263db02c1.jpg)
Дальше указаны расширения файлов, в которых переменные будут заменены на значения, чтобы не заменить что-то ненужное в других файлах. Ещё можно сделать игнорирование копирования каких-то директорий:
![](https://habrastorage.org/getpro/habr/upload_files/50e/e79/4ab/50ee794ab25a4b72a3bee2bc3b27b25b.jpg)
Например, в папке _docs лежат доки по самому шаблону. Они актуальны пока это ещё шаблон, а когда он становится сервисом, это уже к нему отношения не имеет. Поэтому копировать их не надо.
Дальше идёт самое интересное — это переменные, на основе которых в коде шаблона происходит замена. Вопросы, что нам задавали в боте, доступны здесь с помощью переменных. Дальше можно их заменить в любом месте проекта. Также из них можно составить какие-то новые переменные или создать новую переменную содержащую произвольное значение. Ещё добавили “небезопасные переменные”, которые по сути заменяют в тексте ключ на значение:
![](https://habrastorage.org/getpro/habr/upload_files/28b/33f/3d5/28b33f3d5c110ff7d8363eb5cfff83b2.jpg)
Например, они могут быть полезны, если делать шаблон какого-нибудь Go-микросервиса, у него есть пути импортов. Если сразу там подставить эти переменные, он возникнет проблема с билдом. Это очень неудобно, потому что невозможно протестировать шаблон, а ведь нужно ещё, чтобы тесты в репозитории с шаблоном успешно отрабатывали. Поэтому на более позднем этапе можно просто какую-то текстовую строку заменить на какую-то переменную.
![](https://habrastorage.org/getpro/habr/upload_files/4af/11e/ee3/4af11eee353884659105dea0b740280f.jpg)
Перемещение директорий (moveDirs) в нашем примере нужно, чтобы переименовать “cmd/grpc-skeleton” в “cmd/test-color”.
Теперь посмотрим как выглядит шаблон репозитория с переменными для замены:
![](https://habrastorage.org/getpro/habr/upload_files/b43/868/e56/b43868e566340daf6fc69e5efa7ea7f8.jpg)
Здесь приведен для примера один values-файл и Dockerfile, в которых используются подстановки переменной “BS_REPO_NAME” (название репозитория).
Также бывают специфичные случаи, когда от пользователя нужно получить ответы на дополнительные вопросы:
![](https://habrastorage.org/getpro/habr/upload_files/456/ab7/87c/456ab787c173613514130c938b042451.jpg)
Для этого в шаблон можно добавить файл tg-bootstrap.yml, и в нём задать пользовательские вопросы, актуальные только для этого шаблона. Здесь спрашивается имя, фамилия майнтейнера и email. Ответы на эти вопросы будут доступны в переменных.
Также можно использовать инструкцию для чата, которая будет отправлена после оказания услуги. Например, написать, что репозиторий содержит демо-файлы, которые потом нужно не забыть удалить.
Как оказалось, нет ничего сложного, чтобы реализовать подобный бот, но чтобы это реально заработало одной лишь реализации бота недостаточно.
Что нужно, чтобы это взлетело?
Нужны тотальный контроль и ограничения. Почему?
Разработчики должны сами поддерживать свои шаблоны услуг. DevOps понятия не имеют, какой идеальный шаблон микросервиса на Go, а какой на PHP. Они могут в принципе не знать языка программирования для которого создан шаблон.
DevOps должны предоставлять шаблонные CI/CD pipeline и Helm charts. Это нужно, чтобы по максимуму упростить работу разработчиков по поддержке услуг, чтобы они брали типовые шаблоны и их использовали, а также снижалось количество копипасты.
Жёсткие спецификации, стандарты и регламенты разработки. Чтобы все писали код плюс-минус одинаково, использовали одинаковые подходы во всех продуктовых командах. Хоть они и независимые, эти требования должны все соблюдать одинаково.
Никакого зоопарка: технологий, языков программирования, библиотек, плюс единые код-стайлы. В противном случае это не принесёт большой пользы.
Для чего всё это нужно? Есть одна маленькая проблема:
Массовые правки
Помните задачку с ИБ? Она решается в Ситилинк за 4 часа. Для этого мы написали простенький инструмент для массовых правок «repo-linter», подающий массовые PR`s с правками. В нём нет ничего особенного, он позволяет потратить один раз 4 часа на исправление и отмасштабировать эту работу автоматически подав 400 PR`s на все репозитории.
В большинстве команд уже есть майнтейнеры, они спокойно посмотрят, чтобы не было конфликтов, замержат, проверят QA, разольют в прод. В единичных случаях будут какие-то конфликты, и то те люди, у которых эти конфликты есть, понимают, что это произошло, потому что у них какой-то свой путь т.к. они отошли от правил описанных выше.
Итог
Что мы получили из этой схемы:
15 минут — это общие накладные расходы на создание одного микросервиса.
В Ситилинк реально разработчик не обращается ни к кому, в том числе к DevOps. Он заходит в бот и пишет что ему нужно. Дальше он в принципе может ставить тег в Git. Это всё раскатится на продакшен, и его «Hello, World» будет работать. Причём это всё покрыто тестами, обложено мониторингом, использованы лучшие практики, подобран хороший layout — всё, что могли выжать из компании изнутри, выжали, дали максимум для того, чтобы разработчик писал хорошо. И на это всё нужно потратить 15 минут.
4 часа, чтобы внести минимальные правки в 300+ сервисов.
> 10К часов экономии ресурса разработки (скорее всего, гораздо больше, сложно посчитать) по повторным правкам.
Разработчики лучше понимают, как работает их сервис в продакшен, потому что они сами деплоят, сами за это отвечают, DevOps абсолютно никак в это не вмешиваются.
DevOps наконец стали автоматизировать процессы.
Нет нужды в DBA, т.к. новые БД создаются тоже через бота.
Неочевидный бонус — в Ситилинк стали не нужны люди, к которым нужно приходить на поклон, чтобы они создали тебе базу данных. Просто приходишь в бот, ему не надо кланяться. Некоторые скажут, что DBA нужны для того, чтобы нагрузки держать. Но и эту проблему удалось решить регламентами, которые вносят ряд ограничений снижающих вероятность возникновения таких ситуаций.
В итоге в Ситилинк:
счастливые разработчики — они могут творить!
счастливые DevOps — у них разнообразные задачи!
счастливые product owner — у них быстрый Time to Market!