Представьте, что есть проект, где 200 разработчиков, 20+ независимых продуктовых команд и у каждой свой собственный DevOps. Они всё автоматизируют — все довольны и занимаются исключительно своей работой. Разработчики даже успевают красить зелёные кнопочки в красный цвет. У DevOps современные инструменты и они помогают автоматизировать релизы. У каждого product owner независимая команда, он никем не блокируется, планирует свой бэклог, быстро двигается и развивается. А “эффективный менеджер” больше не сомневается стоит ли разрешать разработчикам самим катить в продакшен. Представили? Тогда прикинем, что может случиться с ними через год. Обо всем этом и об интересном подходе к разработке в Ситилинке расскажет Константин Осипов — руководитель DevOps, QA и 140+ backend/fronted-разработчиков. Он познакомит нас с проблемами, которые стояли перед командой разработки и с тем, как они их решили. Поделиться своими мыслями, как не надо делать и объяснит как перестать быть эникейщиком. А главное, как со своими задачами в разработке справляются в Ситилинке. Чтобы погрузиться в тему, посмотрим на типичный Git-репозиторий, что в нём есть: 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 простые истории, что стало с такой командой через командой. Приходит product owner в команду и спрашивает почему микросервис по покраске делался неделю вместо 3 часов? Разработчик, посовещавшись с DevOps’ом, объясняет, что сначала он 1 час ждал DevOps’а, пока тот создавал Git-репозиторий и настраивал права. Потом разработчик за 3 часа сделал layout приложений, подключал логгер, реализовывал HTTP-сервер. Ещё за 3 часа накидал реализацию. Правда, потом DevOps ему помогал настраивать dev-окружение, потому что сам разработчик не хочет этим заниматься. Потом вспомнил, что надо тесты написать — ещё 3 часа. Добавлял метрики и логи — ещё 3 часа. Потом разработчик опять встретился с DevOps’ом, обсудили, что нужно, чтобы всё это дело в продакшен затащить. Тут вспомнили, что надо бы сигналы обрабатывать, мягкое завершение в приложении сделать, да и трейсинг хотелось бы. Сделал это ещё за 3 часа. Дальше DevOps написал pipeline для сборки и деплоя. Потом с QA потестировали, поправили какую-то проблемку, потратили часик. Напилили дашборд в Grafana, чтобы всё удобно передать поддержке. В теории потратили на эту работу 4 дня, а не 3 часа. Но на практике, учитывая затраты на ожидание и переключение контекста, потратили ещё больше — неделю. Казалось бы, независимая команда, а потратили столько денег, несмотря на то что в каждую команду поставили по DevOps’у помогающему всё автоматизировать. Но он всё равно там один, а разработчиков несколько. Поэтому они друг друга всё время ждут, и на это ожидание тратится дополнительное время. Затраты разработки и DevOps на написание boilerplate-кода просто невообразимые. При этом наверняка у этих команд и продуктов много общего, замечается много копипаста. Разделение труда, на самом деле приводит к задержкам. Хотелось бы, чтобы один человек мог сделать всё и сам дотащил задачу от старта до продакшена. Приходит безопасник к DevOps’у и сообщает, что нашли уязвимость в плагине деплоя в k8s, нужно как можно скорее везде исправить. Безопасник ожидает, что во всех 20 командах типовой pipeline и они как-то договариваются между собой. 20 DevOpses собирают большой ZOOM и понимают, что все 20 команд независимы и делают всё по-разному. Во-первых, в реальности pipeline выглядит иначе. Во-вторых, оказывается, что разработчики сделали 200 микросервисов и на самом деле pipelines не 20, а 200, и их доработка займёт 800 часов. Каждая команда по-своему пишет сервисы, несмотря на договорённости. Если они независимые, можно хоть договариваться, хоть нет — всё равно всё разъедется. Глобальные задачи — это всегда боль, особенно если там микросервисная архитектура и их очень много. Они приводят ко множеству коммуникаций, огромным затратам на реализацию и совершенно непредсказуемым результатам. Приходит как-то support и говорит, что пользователи жалуются, что не могут создать заказ. Разработчик отвечает, что у него локально всё работает, это проблема DevOps. DevOps говорит, что всё внимательно посмотрел, ошибок не видит — контейнеры крутятся, лавеха мутится. Там какая-то сложная бизнес-логика, он ее не писал, это к разработчику. Разработчик утверждает, что посмотрел локально, всё работает, по логам тоже ничего нет и отправляет к DevOps. Потому что не знает инфраструктуру и не понимает, как его код работает в продакшене. И так по кругу до бесконечности. Нет единого ответственного за работу сервиса в целом. DevOps не понимает бизнес-логики, чтобы найти ошибку, а разработчик не понимает как работает его сервис в продакшен. Каждый отвечает за свой кусок, а серая область остаётся, никто не отвечает за весь сервис. Приходит поддержка к DevOps и говорит, что в мониторинге видно, что некоторые поды с PHP зависают со 100% утилизацией CPU. Рестарт ненадолго помогает. Посмотрите через strace в чём дело. DevOps отвечают, что не знают strace, это что-то про Linux, а они только умеют писать пайплайны, helm charts, деплоить в k8s и работать с высокоуровневыми абстракциями. DevOps теряет навыки из-за постоянной работы с абстракциями и ежедневного потока однотипных задач написания pipeline. Он изо дня в день занимается одним и тем же — копипастом. Поначалу, пока это были новые технологии, ему было интересно, весело, задорно. Но потом, когда каждый день была одна и та же работа, она превратилась в ад и скуку. В силу изолированности команды отсутствовали разнообразные задачи. Как правило, в одной команде задачи плюс-минус одинаковые. DevOps не видит того, что инфраструктуру строят другие люди. Он просто помогает писать pipeline. Теперь стало понятно, кто такие эникейщики. В интернете есть разные определения, но смысл один: DevOps-инженер — это специалист, который: синхронизирует этапы разработки программного продукта;  знает в чём работа разработчиков, QA, менеджеров и автоматизирует их задачи; умеет программировать и быстро изучает новые инструменты. Так как перестать быть эникейщиком? Никак! DevOps-инженер — это и есть эникейщик. Казалось бы, на этом можно закончить статью, но в определении DevOps есть слова «автоматизирует их задачи». Об этом и поговорим. Конечно, в Ситилинк не втащили всё, о чём было рассказано выше. Там сначала в голове прокрутили все эти истории и решили, что это не тот путь. В Ситилинк взяли в команду трёх Рембо-DevOps. Рембо — потому что они не только умеют писать pipeline, но ещё понимают в Linux, в сетях и Kubernetes. Могут нормально развернуть с помощью Kubespray. Чтобы у них всё было хорошо, решили, что один Рембо будет старшим, чтобы остальных не бросало в разные стороны по технологиям, и они шли единым клином и приходили к каким-то результатам. Из этого получилось 20 продуктовых команд и DevOps-команда. Причём, чтобы не было проблемы, когда DevOps-команда стоит за линией и даже 3 Рембо не могут справиться с 20 отрядами бравых разработчиков, Рембо тоже решили сделать продуктовой командой. Так получилось выкрутиться из ситуации, не раздувая штат DevOps. DevOps остались вместе с разработчиками за чертой по одну сторону, и тоже стали продуктовой командой. У DevOps могут быть такие продукты: В Ситилинк это: Telegram bot, который позволяет автоматически получать и оказывать цифровые услуги; Kubernetes как сервис;  Tarantool как сервис; Postgres как сервис; CDN, чтобы нарезать картинки; S3, чтобы разработчикам было проще работать с одним протоколом и запускать большую часть сервисов в Kubernetes stateless; Helm Chart template (базовый шаблон); Мониторинг как сервис; Запускатели тестов как сервис; Dev server как сервис — когда невозможно что-то большое запустить на своей тачке. Самый маленький из них — Telegram bot для получения и оказания услуг. Несмотря на простоту, этот сервис принёс бы вам максимум пользы, если бы вы реализовали его у себя. Поэтому о нём и поговорим далее. Но прежде чем начать определим красную жирную черту между DevOps и разработчиками. Их нужно разграничить, чтобы они не кидались стрелками между собой. Сделать это можно безопасно, потому что продукты у них разные. DevOps: Разработчики: Предоставляют цифровые IT-услуги Разрабатывают внутренние IT-продукты Отвечают за качество и стабильность предоставляемых услуг и продуктов Непрерывно собирают обратную связь Разрабатывают бизнес-продукты Отвечают за качество и стабильность своих продуктов Разрабатывают новые цифровые услуги для себя самих Важно, чтобы это всё работало, DevOps должны непрерывно собирать обратную связь с разработчиков, какие им цифровые продукты нужны, потому что у DevOps пользователи — это разработчики, а у разработчиков уже клиенты. Разработчики, чтобы не грузить дальше DevOps, в принципе могут разрабатывать новые цифровые услуги сами для себя. Чтобы было понятно, о чём идёт речь, разберём, как выглядит получение одной из цифровых услуг с точки зрения её получателя. Приведём скрины из реального чат-бота. Бэкенд-разработчик захотел себе сделать микросервис на Go. Он пытается авторизоваться через сервис внутренней авторизации. К слову, о продуктах, сервис внутренней авторизации, SDK, который легко прикрутить к ботам и к любому веб-приложению, опять же разрабатывают DevOps,  это их продукт. Разработчик авторизуется в этой системе, получает доступ. Отправляет секретный код в чат. Дальше предлагается выбрать язык разработки. На скрине разработчик выбрал Go, также он решил писать микросервис, а не библиотеку: Здесь предлагается только один шаблон:gRPC. Дальше спрашивается номер задачи в Jira, потому что эта система полностью автоматизированная. Здесь никто не будет ничего апрувить, всё создаётся реально и хотелось бы потом знать, по какой задаче было принято решение создать новый микросервис. В данном случае фоном этот бот проверит, что задача существует и находится в нужном статусе. Следующим шагом вводится название репозитория, например, test-color, а после краткое описание: десь «тестовый сервис, который красит кнопочки в красный цвет». Теперь понятно, для чего этот микросервис и кто его сделал. Вводится фамилия, имя майнтейнера и его email: Дальше нужно подтвердить, что всё ОК. Разработчик подтверждает, и буквально через 5-10 секунд видит уже радостное сообщение, что репозиторий создан: Что получили в итоге? Создался в bitbucket репозиторий. Есть приложение. Оформлен текст Git-коммита, как принято в компании, всё можно потом найти.  В readme и в принципе везде, задано имя приложения, добавлено описание: А ещё добавлены: Обработка сигналов Graceful shutdown Настроен logger Tracing Метрики gRPC-сервер HTTP-сервер Роутинг Сконфигурированы переменные окружения/флаги Сборка Деплой по тегам в stage/prod Unit-тесты Локальное окружение Деплой в sandbox через bot Линтеры Создан дашборд в Grafana на все метрики Полезные утилиты run.sh Рассмотрим, как это работает с точки зрения того, кто эту услугу хочет предоставить. Допустим, DevOps или разработчики решили для себя самих начать предоставлять услугу, которую мы только что получили. Здесь приведён конфиг этого бота. В основной секции ничего сложного нет — как видели, так и пишется: Указываем, что шаблон для языка и типа — gRPC микросервис: Указываем папку в bitbucket — проект, куда создавать полученные сгенерированные новые репозитории, потом указываем репозиторий шаблона (в Ситилинк называется grpc-skeleton): Дальше указаны расширения файлов, в которых переменные будут заменены на значения, чтобы не заменить что-то ненужное в других файлах. Ещё можно сделать игнорирование копирования каких-то директорий:  Например, в папке _docs лежат доки по самому шаблону. Они актуальны пока это ещё шаблон, а когда он становится сервисом, это уже к нему отношения не имеет. Поэтому копировать их не надо. Дальше идёт самое интересное — это переменные, на основе которых в коде шаблона происходит замена. Вопросы, что нам задавали в боте, доступны здесь с помощью переменных. Дальше можно их заменить в любом месте проекта. Также из них можно составить какие-то новые переменные или создать новую переменную содержащую произвольное значение. Ещё добавили “небезопасные переменные”, которые по сути заменяют в тексте ключ на значение: Например, они могут быть полезны, если делать шаблон какого-нибудь Go-микросервиса, у него есть пути импортов. Если сразу там подставить эти переменные, он возникнет проблема с билдом. Это очень неудобно, потому что невозможно протестировать шаблон, а ведь нужно ещё, чтобы тесты в репозитории с шаблоном успешно отрабатывали. Поэтому на более позднем этапе можно просто какую-то текстовую строку заменить на какую-то переменную. Перемещение директорий (moveDirs) в нашем примере нужно, чтобы переименовать “cmd/grpc-skeleton” в “cmd/test-color”. Теперь посмотрим как выглядит шаблон репозитория с переменными для замены: Здесь приведен для примера один values-файл и Dockerfile, в которых используются подстановки переменной “BS_REPO_NAME” (название репозитория). Также бывают специфичные случаи, когда от пользователя нужно получить ответы на дополнительные вопросы: Для этого в шаблон можно добавить файл 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!