Привет! Я Владислав Раев, DevOps & DevTools Engineer в команде Wildberries & Russ. Сегодня погружу вас в увлекательную историю о том, как мы наводили порядок в Nexus OSS и экспериментировали с Terraform и Ansible. Спойлер: контекст оказался важнее технологий.

Но сначала три случайных факта об авторе: 1) я пришёл в IT из строительной сферы четыре года назад, 2) люблю покатушки на эндуро, 3) обожаю животных — у меня сразу пять питомцев! А теперь к делу.

Сначала был Nexus OSS

Nexus OSS — это хранилище артефактов, то есть любых файлов, которые существуют в репозитории и используются для деплоя или распространения приложений.

Когда я пришёл в компанию, мне почти сразу вручили Nexus OSS, развёрнутый в одном-единственном инстансе. Тимлид сказал: «Володос, надо этот Nexus разделить на два: Proxy и Hosted. Первый — для публичных репозиториев, второй — для артефактов, которые производит Wildberries». Я кивнул и взялся за дело.

Фиксируем наше «До»:

  • две заявки,

  • ручное создание ресурсов,

  • пересылка паролей, ролей, юзеров и репозиториев через слабозащищённые каналы;

  • в заявках нет аппруверов.

Единая точка входа: Keycloak

Первым делом я поднял два инстанса — это была самая простая задача.

Чтобы выдать доступ по лицам в Proxy, нужен только анонимный доступ на просмотр и чтение. А вот с Hosted возникли трудности. Keycloak, нашу единую точку входа во все сервисы, нужно было интегрировать с Nexus OSS, который не имеет нативной интеграции с Keycloak.

Что делать? Первый вариант: написать плагин на Java. Честно говоря, не хотелось этим заниматься. Второй вариант: подобрать готовое решение.

Я отправился на поиски и наткнулся на плагин Community для Keycloak. Адаптация и пересборка под требования Wildberries сотворили чудо: мы получили единую точку входа в Nexus Hosted для всей компании, настроили автоматический маппинг групп из Keycloak в роли Nexus OSS и централизованное управление доступами. Плагин на GitHub доступен по ссылке. Без-воз-мезд-но, то есть даром!

Мы создали репозитории, настроили роли и стали подключать пользователей. Всё шло отлично, но пришли безопасники и сказали: «Где разделение на Prod- и Dev-артефакты? Где разделение на проекты? Почему одна команда может видеть артефакты и скачивания другой команды? Кто вообще согласует доступ в эти репозитории?» Короче, всё не так, давайте по-новой. И мы взялись за исправления.

Стандартизация прежде всего

В результате долгих дискуссий мы создали такую схему:

Так выглядит согласованная структура:

Для разграничения Prod- и Dev-артефактов, а также проектов репозитории должны называться <subproject>. <format> — это любой формат, существующий в Nexus OSS, только для Hosted-репозитория. Остаётся окружение <env>: dev и prd для соответствующих артефактов и public — если одна из команд решит опубликовать свою библиотеку или артефакт для всей компании.

Пояснения по разделу «Роли» вы можете увидеть на картинке. Далее — две сервисные учётки для работы через CI/CD. Эти данные необходимо хранить в Vault и забирать только из CI/CD. Пользователи не должны иметь доступ на пуш, удаление и редактирование файлов в Prod-репозитории.

Возвращаемся к Keycloak. Мы подключили его и согласовали три типа заявок:

  1. Создание репозиториев, ролей, юзеров и секретов в Vault.

  2. Создание в Keycloak подгрупп для выдачи прав в репозиториях Nexus OSS. Здесь же указываем, кто будет согласовывать доступ к конкретному репозиторию — тимлид или продакт-оунер.

  3. Добавление пользователей в подгруппы.

Global Inventory — наш выбор!

Отлично, мы определили, как будем именовать репозитории и выдавать доступ и где будем хранить учётные записи. Осталось решить два вопроса:

  1. Где запускать автоматизацию?

  2. На чём написать автоматизацию?

Наш основной подход к IaC — это Global Inventory (единый репозиторий с переменными для Ansible), поэтому было принято решение использовать именно его для автоматизации. Вот пример структуры Global Inventory:

В проекте devops возможно большое количество подпроектов. Именно для них — yaml-team и yaml-developer — требуется создавать сущности в Nexus OSS и выдавать роли. Подпроекты находятся в проекте, и конфигурация для них должна находиться в общих переменных для этого проекта. В нашем случае это commonservices и файл nexus-vars.yml.

На чём писать автоматизацию? Есть API для работы с Nexus OSS из коробки, но необходимо выбрать подход: писать свой сервис или искать готовый. Я выбрал второй вариант и остановился на Terraform, у которого есть всё для создания автоматизаций и сущностей.

Что работает для 10, то ломается для 100

Я выбрал Terraform по нескольким причинам:

  • есть готовый провайдер,

  • используется IaС-подход,

  • можно стать первопроходцем и внедрить Terraform в Global Inventory,

  • личный интерес к изучению Terraform.

Я разработал модуль, который выполняет необходимое действие, и начал тестирование. На десяти проектах он работал без нареканий, но стоило увеличить количество тикетов — начинались проблемы.

Проблема масштаба. Terraform State рос, увеличивал количество времени на выполнение пайплайна и повышал риск самоповреждения. Так можно и данные потерять!

Terraform на малом масштабе: 10 проектов = State 2 Мб = план 60 секунд.

Terraform на реальном масштабе: 500 проектов = State 100+ Мб = план 10+ минут.

Проблема воркфлоу Global Inventory. Когда мы заводим тикет, в репозитории из мастер-ветки создаётся новая ветка, куда вносятся изменения и где создаётся мердж-реквест. Дежурный инженер сравнивает заявку и мердж-реквест, если всё в норме — пропускает. После успешной прокатки MR должен вливаться в мастер-ветку, но часто задерживается — иногда на час, иногда на сутки. И так с каждым тикетом.

Если помните, вся конфигурация для подпроектов хранится в commonservices и nexus-vars.yml — получается единый Terraform State. Поэтому при создании нескольких тикетов создаётся от пяти до десяти параллельных MR с разными данными.

Вдобавок играет роль человеческий фактор. Должно быть последовательно: merge → pull → apply. Забыл pull = удалил репозитории!

Представим некий проект под названием Mobile, с подпроектами фронтенда и бэкенда. Приходят фронтендеры и говорят: «Нам нужен NPM-репозиторий». Задача уходит в работу, ветка по воркфлоу Global Inventory создаётся и после проверок успешно запускается.

Автоматизация работает на ура, но MR ещё не влился в мастер-ветку, и фактически изменений нет.

Дальше приходят бэкендеры и говорят: «Нам нужен Go-репозиторий». Новый тикет, новая ветка из мастер-ветки, успешный запуск. Однако есть нюанс: всё хранится в едином файле, и свежие изменения Terraform стирают более ранние.

Можно подтянуть первый тикет в мастер-ветку, спулить во вторую ветку и потом прокатить, но где гарантии, что инженер не забудет этого сделать и мы не потеряем репозиторий?

Меньше состояние — меньше проблем. Выбираем Ansible

Я принял решение отказаться от Terraform и переписать всё на Ansible. Ему важны только конфигурация и переменные, которые мы скармливаем. Количество репозиториев, тикетов и веток не играет роли. Человеческий фактор исключён, от конфликтов спасает идемпотентность — возможность многократного вызова с гарантией того, что состояние системы изменится лишь единожды.

В конфигурационном файле Terraform нельзя явно увидеть, что хранится в конкретном Terraform State. Конфигурации Ansible прозрачны. Каждый MR независим.

Логика простая:

  • трогаем ТОЛЬКО то, что находится в vars.yml,

  • проверяем существование,

  • создаём только отсутствующее,

  • НЕ ТРОГАЕМ другие подпроекты.

Какой воркфлоу мы получили?

Напомню наше «До»: две заявки, мануальное управление ресурсами, небезопасная передачами данных и отсутствие аппруверов.

А вот наше «После»:

  • три заявки: создание репозиториев, создание подгруппы в Keycloak для управления доступами, добавление пользователей,

  • три репозитория: <subproject> — <format> — <env>,

  • четыре роли: юзер, Dev, Prod и публичная анонимная роль,

  • хранение секретов в Vault, единая точка доступа через Keycloak.

Нам удалось исключить человеческий фактор и достичь полной автоматизации Nexus OSS, настроить безопасную параллельную работу, которая не вызывает конфликтов, и обеспечить совместимость с воркфлоу Global Inventory. Процессы стали прозрачными, появилась чёткая система согласования.

Главный урок, который я вынес, — не все инструменты универсальны. Приоритезируйте контекст, а не технологию. Terraform хорош, но не для нашего воркфлоу. Под конкретную задачу и архитектуру нужно искать свой инструмент, и для нас это Ansible.

Вам доводилось работать с Terraform и Ansible? Поделитесь выводами в комментариях, буду рад обсудить!

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