На нашей недавней конференции GigaConf мы много рефлексировали о том, как будет развиваться направление DevOps. Оно немыслимо без инструментов. Поэтому я расскажу о том, как мы внедряем в Сбере практики GitAIOps, какие совершили ошибки и извлекли уроки, какие сделали выводы по поводу внедрения ИИ. Сегодня на всех углах рассказывают, как ИИ поможет разработчикам, но мало кто говорит о его помощи DevOps‑инженерам. Надо это исправить.
Меня зовут Юрий Спорынин, в ИТ я более 20 лет. Начинал с разработки, своими руками создавал процессинговую систему для интернет‑эквайринга. В 2016 году я перешёл в Сбер, где мы в 2018 году внедрили платформу в «Сбербанк Онлайн». Сейчас среди моих задач — кластер DevOps‑инструментов, о которых мы отчасти сегодня поговорим.
Сегодня у нас более 4500 команд ежедневно запускают свыше 300 тыс. сборок в Jenkins. Среди задач есть развёртывания приложений в тестовые, стейджинговые, эксплуатационные и другие среды. Многие из задач работают довольно долго, простой микросервис в OpenShift или Kubernetes может развёртываться больше 12 минут. Так что при наших масштабах стоило задуматься над оптимизацией и внедрением новых практик.
Сейчас у нас одних только мастеров Jenkins около 100, но когда мы начинали внедрять DevOps, их были единицы. С ними постоянно были проблемы: к полудню либо падали, либо начинались задержки. Нам это быстро надоело, и начали думать, как повысить надёжность.
Одновременно расспросили команды, какие ещё трудности у них возникают, чтобы заодно внедрить какие‑нибудь новые практики. Хотя во многих командах уже был настроен конвейер развёртывания, были и те, кто не знал, где им такой конвейер взять. Либо не знали, как и кто его будет поддерживать. «Где брали конвейер, туда и идите». Некоторые команды, даже подобравшие для себя конвейер, не знали, как его применять, и задавали разные вопросы. Например, почему вот такая CURL‑команда, которая должна загружать дистрибутив в Nexus‑репозиторий:
$> curl -o /dev/null -d @exmple.zip \F mave2.generate-pom=true
не работает?
$> curl: Failed to open example.zip
Люди не понимали, что команда-то загружает, но для этого дистрибутив надо сначала собрать.
Иногда у ребят не хватало знаний для решения той или иной технической задачи. Например, разобрались со сборкой, но вернулись к нам с вопросом, почему инструкция опять не работает?
$> command not found: -F
Не справились с переводом в Shell. Казалось бы, простая задача, но и с ней возникло затруднение.
Попробовали возможности LLM по составлению инструкций. Вроде бы, ответы даёт толковые, но если применять это в лоб, то легче не становится.
Объясню на простом примере. Вот что сгенерировала модель на Perl:
Это старая шутка, не запускайте этот код, потому что он просто удаляет корневую файловую систему.
$> rm -rf /* : insufficient privileges
Казалось бы, две простые строчки, но их работу нужно объяснять тем, кто не знаком с Perl и Ansible. В общем, нужен был новый подход. Что-то, что поможет командам, в том числе и более-менее подкованным менеджерам, быстро и просто выкатывать новые релизы. Мы решили, что это должен быть GitOps.
Кратко напомню, что это такое. В основе GitOps — декларативное описание конфигурации, а не императивное через команды. То есть мы описываем, что хотим получить, а не что нужно сделать системе. Эта конфигурация должна лежать в неизменяемом хранилище, которое поддерживает версионирование. Например, в Git, с ним удобно работать.
Также у нас всегда должен быть некий агент, который постоянно следит за изменениями состояния наблюдаемой системы и состоянием Git. И должен постоянно выполняться замкнутый цикл, который сделанные в хранилище изменения применяет и в целевой системе. Понятная и удобная концепция, которая хорошо ложится на те инструменты, которые у нас есть.
Что у нас есть для того, чтобы команды могли ежедневно развёртывать приложения и сервисы в рамках нового подхода?
Есть DevOps Pipeline Manager — средство управления (оркестрации), работающее со всеми сегментами, которые изолированы друг от друга.
Для Git у нас есть так называемый перекладчик, который позволяет синхронизировать образы контейнеров или код между сегментами. Ну и джентльменский набор — наш любимый Jenkins и Argo CD (который мы доработали), чтобы развёртывать на стендах OpenShift и Kubernetes.
Мы провели успешные пилоты, но при внедрении столкнулись с проблемами. При их решении мы получили следующий опыт.
Во-первых, конфигурацию стоит хранить не в ветках, а в каталогах одной выбранной ветки. Дело в том, что ветки часто управляются разными командами или даже отдельными разработчиками. Все они работают в разном ритме, и могут вносить изменения независимо друг от друга. И когда всё это объединяют в одну ветку, часто возникают проблемы.
Если же хранить конфигурацию в каталоге, то можно создавать гибкую и наглядную структуру под любую топологию стенда, нет необходимости разрешать конфликты, можно заранее проверять промышленную конфигурацию.
Обычно мы используем базовую папку с чартами, например, Helm, и каталог какого-нибудь стенда, который наследует информацию из Application. Мы можем оперировать стендозависимыми, так и стендонезависимыми параметрами. Это обеспечивает Customisation.yaml, который есть в Argo CD. При этом ветки нам помогают проверять получившуюся конфигурацию.
Когда нужно развернуть в эксплуатационной среде, мы выполняем pull request, который отдельно проверяется на соответствие всем нашим практикам управления релизами, кибербезопасности, применения манифестов и т. д. На этом этапе обязательно в pull request что-нибудь добавят. И после утверждения можно добавлять его в мастер-ветку, которую потом смело отправлять в эксплуатацию.
Оказалось, что нам недостаточно просто делать манифесты, которые развёртываются в Argo CD. В маленьких командах этот подход работает хорошо, потому что нетрудно сделать 5-10 манифестов руками. Но когда сервисов десятки, а стендов — сотни, да ещё и несколько независимых инсталляций всей системы, написание манифестов превращается в трудную задачу. Конечно, у нас есть инструменты, но всё равно нужно было разобраться с накопившимися скриптами Ansible и логикой Groovy.
Но есть замечательный инструмент Helmfile, аналог Argo. В его шаблонизаторе есть много разных инструкций, которые можно наследовать и собирать из различных файлов нужную нам конфигурацию. Мы пока продолжаем улучшать этот процесс.
Не все наши изменения могут быть описаны декларативно, некоторые из них по своей природе императивны. Например, секреты. При изменении структуры базы данных возникает развилка:
можно применять Kubernetes-оператор, который при декларативном описании конфигурации загрузит в пространство имён соответствующие секреты из какого-нибудь хранилища;
либо применять оператор Atlas-оператор, который может декларативно изменить базу данных.
Но не каждый, например, DBA согласится, что его база данных будет меняться онлайн при изменении приложения, когда мы его выкатываем. Если, например, какой-нибудь индекс зависнет при пересчёте, это может привести просто к полной и длительной остановке приложения. Поэтому о выборе варианта нужно очень тщательно подумать на уровне организации или конкретной команды.
ИИ сегодня развивается очень быстро и уже даёт интересные возможности при внедрении GitOps. Например, ИИ может проверять параметры. Система уже обучена на множестве конфигураций и довольно успешно справилась.
Нам этого было мало, хотелось работать с тем контекстом, который есть у конкретной команды. Ведь мы создаём очень разные приложения, с разными наборами параметров. И на помощь пришёл механизм RAG (Retrieve Augment Generator): вы можете векторизовать базу данных и передавать в таком виде модели вместе с промптом. Это очень удобно, потому что дообучать очень дорого, нужно много ресурсов. Не все это могут себе позволить. А с помощью REG можно спросить модель в рамках имеющегося у нас контекста без дообучения. Механизм уже поддерживает наши LLM-модели GigaChat и GigaCode. Вот несколько примеров.
В настройках системы могут быть параметры, которые связаны друг с другом. И ошибка в одном из них может повлиять на систему в целом. Вот так можно быстро находить различные ошибки:
Ещё можно использовать RAG для разбора сложных параметров, которые указаны в конфигурации:
Наша практика показывает, что человеческий фактор до сих пор остаётся одной из причин инцидентов, в том числе широкомасштабных. За рубежом провели исследование, согласно которому текущий уровень моделей машинного обучения при проверке конфигурации позволяет нам добиться значения метрики F1 до 0,79. А если мы передаём модели векторизированный контекст, F1 вырастает ещё на 0,04.
Есть ещё один интересный подход — подмешивание удачных и неудачных конфигураций, так называемые few shot-запросы, — который тоже позволяют добиться лучших результатов. И здесь опять же нам помогает подход GitOps и накопленная база, которую мы можем передавать модели с промптами.
Во-первых, удалось сократить длительность развёртывания до 4 раз, с 12 до 3-4 минут, в зависимости от приложения.
Самое главное достижение — мы перестали писать код в Groovy. Ребята, которые им не владеют, больше не должны в нём разбираться, когда он ломается. Им достаточно просто закидывать YAML-файлы.
В-третьих, у нас теперь общая база конфигураций, с которой все могут работать. В большой компании со множеством стендов на сбор информации, какие версии и где развёрнуты, могут уйти годы. И когда понадобится что-то мигрировать, останется только пойти к администратору кластера, попросить его выгрузить все конфигурации и накатить, иначе кластер может развалиться.
Следующее изменение — это унификация стендов. Администраторы любят время от времени что-то делать руками в рабочей среде, потом забывают, где и что сделали, и когда накатывают новую версию, всё вдруг ломается. GitOps избавляет нас от этого.
Последний результат — наша готовность к ИИ. Мы серьёзно задумались над тем, чтобы создать плагин проверки конфигурации с помощью LLM и распространить его среди команд. Возможно, рынку он тоже окажется интересен.
Недавно появился язык CueLang. Он позволяет делать относительно простые описания, которые позволяют проверять конфигурацию. Удобнее ли он по сравнению с LLM? Чтобы сделать валидатор на CueLang, понадобится в нём разобраться, описать, как надо проверять параметры (для этого, скорее всего, понадобится разработчик). Вы эти инструкции можете дать системе, она их проверит, и вы решите свою задачу. Не все варианты сможете заложить, но какие-то.
А в случае с машинным обучением администратор сопровождения может на русском языке написать комментарии, какую конфигурацию он хочет получить. А аналитик или менеджер потом может проверить, правильно ли всё задано. Сейчас я не берусь утверждать, что LLM заменит CueLang, но в будущем, наверное, нейросетям эта задача окажется по силам.