Русский
Русский
English
Статистика
Реклама

Continuous integration

Перевод Как криптомайнеры убивают бесплатные CI

28.04.2021 20:06:12 | Автор: admin

СI-платформы, такие как LayerCI, GitLab, TravisCI, и Shippable ухудшают, а то и вовсе закрывают свои бесплатные сервера из-за атак с целью скрытого майнинга.

1 сентября 2020 года GitLab объявил что ограничивает бесплатное использование CI в ответ на эксплуатацию. Два месяца спустя TravisCI анонсировал схожие ограничения из-за серьезных злоупотреблений. В это же время возросла рыночная капитализация добываемых криптовалют.

Рыночная капитализация криптовалюты подскочила со 190 млрд до 2 трлн за один год.Рыночная капитализация криптовалюты подскочила со 190 млрд до 2 трлн за один год.

Эти события связаны между собой: поскольку рыночная капитализация криптовалюты выросла с 190 млн. долларов в январе 2020 до 2 триллионов в апреле 2021, злоумышленникам стало выгодно атаковать бесплатные сервера PaaS провайдеров.

Контекст

В LayerCI мы помогаем разработчикам создавать полнофункциональные веб-сайты, создавая среды предварительного просмотра и автоматически запуская для них E2E тесты. Это называется CI (Непрерывная интеграция).

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

717 GitHub коммитов за один месяц

testronan заядлый пользователь Flask. Каждый час он делает коммит в своем единственном GitHub репозитории: testronan/MyFirstRepository-Flask.

Продуктивный программист, безусловно, должен убедиться в том, что внесенные им изменения хорошо протестируются. Поэтому его репозиторий содержит конфигурации аж для пяти разных CI-платформ: TravisCI, CircleCI, GitHub Actions, Wercker, и LayerCI.

Похоже он довольно опытен в написании скриптов. Его СI таски запускают listhen.sh: shell скрипт, который объединяет сложный NodeJS скрипт с, на первый взгляд, случайными числами:

(sleep 10; echo 4; sleep 2; echo "tex.webd";sleep 2; echo 7; sleep 1; echo 1; sleep 1; echo "exit"; sleep 2) | stdbuf -oL npm run commands  

MyFirstRepository-Flask не имеет ничего общего с Flask или веб-серверами. В нем размещены скрипты для майнинга криптовалюты, которые отправляют WebDollars (прим. криптовалюта) на анонимный адрес. Числа соответствуют настройкам реализации WebDollar на NodeJS.

Репозиторий не атакует GitHub напрямую, вместо этого он злоупотребляет функцией cron для создания нового коммита каждый час и майнинга WebDollar на четырех оставшихся платформах.

При пиковой апрельской цене WebDollar в 0.0005 долларов репозиторий зарабатывал 77 долларов в месяц значительная для многих стран сумма, особенно учитывая, что для заработка необходим лишь ноутбук и интернет-соединение.

Два кошелька, на который приходит добытая криптовалюта:

Майнинг криптовалюты в браузере

Пользователь vippro99 описывает свои намерения менее изящно. Из десятков репозиториев большинство связано либо с криптовалютой либо с автоматизацией браузера.

Репозиторий nodejs-monney содержит различные скрипты для запуска экземпляров Chrome c популярным гугловским puppeteer project.

Идея проста: майнинг криптовалюты непосредственно в CI довольно легко обнаружить (например, с помощью анализа исполняемого содержимого), но автоматизация браузера это обычная рабочая нагрузка в CI.

puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox', '--window-size=500,500', '--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36' ] }).then(async browser => {  console.log('-- Running chrome!!'); const page = await browser.newPage(); await page.goto('https://vippro99.github.io/-meocoder-nodejs-tool/index.html');  page.on('console', (msg) => console.log(msg.text())); await page.waitForTimeout(((Math.floor(Math.random() * 6) + 52) * 60) * 1000); await browser.close(); })

Данный GitHub Pages веб-сайт содержит простой браузерный майнер криптовалюты Monero, напоминающий Coinhive.

На момент написания аккаунт атаковал JFrog's Shippable CI, которая (возможно, в связи с этим) анонсировала об окончании поддержки бесплатных серверов в начале этого года.

Судя по комментариям vippro99, он проживает во Вьетнаме. При текущей цене Monero, каждый экземпляр майнера на Shippable приносит 2.5 доллара в месяц, поэтому поддержание 60 экземпляров равносильно полному рабочему дню в этой стране.

Решение проблемы

Ethereum, вторая по популярности криптовалюта, недавно объявила о планах прекращения поддержки майнинга на основе вычислений, полностью переключившись на модель Proof-of-Stake (PoS).

Помимо негативного воздействия традиционного proof-of-work майнинга на окружающую среду, существуют и другие неприятные аспекты: мировая нехватка графических процессоров и атаки на CI-платформы.

Провайдеры будут делать все возможное, чтобы обеспечить соблюдение условий обслуживания, но пока атаки выгодны и трудноотслеживаемы, они будут приобретать все более изощренный характер. Единственный долгосрочный способ и дальше пользоваться бесплатными серверами в Heroku, Netlify и GitHub это отказ от proof-of-work.


Дата-центр ITSOFT размещение и аренда серверов и стоек в двух дата-центрах в Москве. За последние годы UPTIME 100%. Размещение GPU-ферм и ASIC-майнеров, аренда GPU-серверов, лицензии связи, SSL-сертификаты, администрирование серверов и поддержка сайтов.

Правильно и недорого майнить криптовалюту можно в майнинг-отелеhttps://itsoft.ru/data-center/mining/

Подробнее..

Перевод Введение в непрерывную поставку (CD) при помощи GitLab

31.03.2021 06:22:42 | Автор: admin

Введение в непрерывную поставку (CD) при помощи GitLab


Введение в непрерывную поставку (CD) при помощи GitLab


Данный туториал позволит вам быстро прочувствовать как происходит командная работа с использованием GitLab. В целом, начать практиковать DevOps/CD с GitLab проще чем с использованием других продуктов потому что GitLab это решение "всё в одном".


В процессе этого туториала мы


  • настроим базовое управления проектом на GitLab.com;
  • создадим конвейер непрерывной поставки
  • проведём несколько циклов работы с GitLab Flow
  • изучим метрики CI/CD в GitLab

Желательны но необязательны базовые знания


  • Git;
  • Node.js;
  • React;
  • Docker;

Время от времени я буду просить вас что-то сделать. Такие моменты помечены значком "!". Пожалуйста выполняйте действия по мере чтения текста чтобы получить от данного туториала наибольшую пользу.


В оригинале места где требовалось что-то сделать были помечены эмодзи "молоток и гаечный ключ". Так как редактор habr'а вырезает эмодзи, я заменил эти эмодзи на "!".

Трудно переводить на русский вещи, которые даже русскоязычные программисты между собой называют по-английски. Я постарался литературно переводить термины для которых есть общепринятые переводы и не переводить остальные. Например, мы будем использовать словечки вроде "чекбокс" и "коммитить", а названия разных штук в GitLab останутся на английском, как есть.

Введение и знакомство с проектом


В качестве "подопытного кролика" мы будем использовать чуть модифицированный шаблонный проект, созданный утилитой create-react-app.


Почему React? Во-первых, это самая распространённая UI-библиотека на JavaScript, и многие читатели знакомы с ней. Во-вторых create-react-app даёт нам осмысленные стадии компиляции и тестирования которые уже реализованы за нас.

! Теперь давайте клонируем репозиторий с кодом с которым мы будем работать.


git clone https://github.com/ntaranov/gitlab-cd-react

! Перейдите в каталог локального репозитория


cd gitlab-cd-react

Должна отобразиться стандартная стартовая страница create-react-app.


Вы можете вместо этого этапа просто создать новое приложение при помощи create-react-app, но версия в репозитории содержит некоторые правки, и версии пакетов в коде в репозитории я тестировал.

! Установите npm пакеты локально, выполнив


npm install

Используйте npm ci если при выполнении npm install произойдёт ошибка

! Попробуйте "собрать" проект


npm run build

Обратите внимание, что в папке ./build появились соответствующие файлы, включая минифицированный JavaScript и CSS.


! Затем запустите тесты


npm run test -- --coverage --watchAll=false --forceExit

! Осталось осуществить "развёртывание", но чтобы не засорять туториал инструкциями по установке веб-сервера, давайте просто запустим отладочный веб-сервер чтобы увидеть как выглядит наше веб приложение выглядит в браузере.


npm start

Если подумать, то осталось заменить последнюю инструкцию на копирование статических файлов из папки ./build на веб-сервер и развёртывание можно считать завершённым. В принципе, это вполне хватит для самостоятельной работы, однако для работы в команде в режиме непрерывной поставки может также захотеться иметь как минимум следующее:


  • централизованный репозиторий с
    • управлением полномочиями членов команды;
    • code review;
  • багтрекер чтобы
    • планировать работу и
    • отслеживать прогресс;
  • среду сборки с
    • автоматическим запуском конвейера непрерывной поставки в нужные моменты;
    • визуализацией статуса сборки;
    • хранением артефактов;
    • автоматическим оповещением о ключевых событиях;
    • автоматическим развёртыванием в разные среды;
  • сбор метрик непрерывной доставки.

Всё это, а также многое другое, поддерживается GitLab. Платная версия предлагает больше возможностей, но для наших целей нам хватит и бесплатной версии доступной на GitLab.com.


GitLab выделяется на общем фоне отсутствием ограничений на использование собственных job runners, однако некоторые довольно базовые "управленческие" и "корпоративные" фичи GitLab вроде обязательного утверждения merge request'ов входят в платные версии. Проектам с открытым кодом все фичи GitLab доступны бесплатно.

Базовая настройка управления проектом на GitLab.com


В этой части мы создадим сам проект в GitLab и задачи над которыми будем работать в дальнейшем, а также настроим Kanban-доску для визуализации состояния проекта.


Создание проекта в GitLab


Мы будем использовать GitLab.com в качестве инсталляции GitLab чтобы избежать хлопот по установке и настройке локальной версии.


! Если у вас ещё нет учётной записи на GitLab.com, зайдите на https://gitlab.com и заведите её.


GitLab позволяет нам создать проект просто выполнив push в удалённый репозиторий.


! Воспользуемся для этого следующей командой:


git push https://gitlab.com/<user name>/gitlab-cd-react

<user name> тут ваше имя пользователя на GitLab.com.


Эта команда создаст приватный проект с именем gitlab-cd-react внутри вашей учётной записи на GitLab.com.


! Пожалуйста перейдите по адресу https://gitlab.com/<user name>/gitlab-cd-react.


Далее мы настроим канбан-доску, а заодно создадим несколько задач чтобы было вокруг чего строить дальнейшую работу и о чём собирать статистику.


Создание задач и настройка доски


Давайте начнём с создания задачи.


! Кликните Issues в левом меню, затем нажмите одну из кнопок New Issue в основной области. Откроется форма создания задачи. Укажите в качестве заголовка "Создать задания для туториала".
В описании укажите следующий текст


Создайте задачи с заголовками, перечисленными в списке ниже.- [ ] Создать метки- [ ] Настроить доску- [ ] Создать конвейер непрерывной поставки- [ ] Провести несколько итераций GitLab Flow- [ ] Изучить метрики

Да, именно эти вещи мы и будем делать в дальнейшем. Оставьте остальные значения по умолчанию. Нажмите кнопку Submit issue.


Обратите внимание, что список с пробелами в квадратных скобках вначале элементов был распознан как чеклист, а отдельные его элементы как задачи. GitLab, как и многие другие платформы, использует Markdown в качестве языка разметки.


! Назначьте задачу Создать задания для туториала на себя. Для этого нажмите ссылку Edit в панели с надписью 0 Assignees справа на странице редактирования задачи.


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


! Давайте теперь на самом деле создадим все задачи, перечисленные в описании нашей первой задачи. Укажите только заголовки, остальные поля оставьте по умолчанию. Вы можете "ставить галочки" в описании задачи Создать задания для туториала как по мере создания задач, так и все сразу когда закончите.


Существуют разные способы создавать задачи, при создании задач по списку найдите и используйте хотя бы 3 разных способа. В итоге у вас должно получиться 6 задач.


! Закройте задачу Создать задания для туториала. Для этого можете в Issues -> List или Issues -> Board кликнуть на заголовок задачи и внутри формы редактирования задачи нажать на кнопку Close issue.


Наш процесс работы


Под процессом работы(workflow) обычно подразумевается порядок этапов, которые задача проходит для того чтобы считаться выполненной. В Continuuos Delivery, Kanban и DevOps задача движется через некую последовательности состояний либо вперёд, либо может быть возвращена на один из предыдущих этапов.


Это подразумевает линеаризованные value streams. Про value streams можно почитать тут. Превращение порой затейливой диаграммы переходов в последовательность состояний является иногда очень сложной управленческой задачей, решение которой выходит за границы этой статьи.

Мы будем использовать простую последовательность состояний


  • Open
  • Dev
  • Dev: done
  • QA
  • Closed

Вначале задача оказывается в состоянии Open. Затем она затягивается(pull) в работу разработчикам в стадию Dev. После того, как работа завершена, происходит передача задачи в стадию Dev: done.


Зачем нам нужна эта дополнительная стадия? Дело в том что в Lean, на котором основаны все методологии типа CD, Kanban и DevOps, следующий этап(work center) затягивает задачи когда он готов в отличие от методологий где задачи передаются в следующий этап предыдущим. Это позволяет отслеживать задачи которые находятся в стадии ожидания, а накопление задач в стадии ожидания позволяет нам понять, где в нашем "конвейере" "бутылочное горлышко".

Итак, затем задача затягивается в этап QA где работают специалисты по качеству и автотесты, а "выпускники" QA считаются настолько хорошими что развёртываются непосредственно на продуктив.


"Ну так же не бывает, это же чих-пых и в продакшн, должны же быть другие среды типа Staging в процессе!" скажете вы. Я совершенно согласен, но тут мы используем максимально простой в реализации и для понимания конвейер чтобы меньше отвлекаться от GitLab и не закопаться где-нибудь в дебрях Kubernetes.

Настройка меток


Для организации задач GitLab использует метки(labels). Они как теги и позволяют искать, фильтровать и отображать задачи в разных местах в зависимости от наличия той или иной метки. Давайте заведём сами метки


! Откройте задачу Создать метки и назначьте его на себя. Выберите Issues -> Labels в левом меню, затем нажмите New label в правой части основной области. Заведите таким образом следующие 3 метки, выберите разные цвета на свой вкус.


  • Dev
  • Dev: done
  • QA

Названия меток тут полностью произвольные и никакого особого смысла с точки зрения GitLab в них нет.


! Когда закончите, назначьте задачу Создать метки статус Closed.


Теперь всё готово для настройки Kanban-доски.


Настройка доски


В GitLab есть Kanban-доски. Они позволяют отображать разные задачи в соответствии с их метками. Несмотря на название они могут быть использованы не только для реализации Kanban, но и Scrum а также других методологий. У нас же уже создана задача для этого.


! Назначьте эту задачу на себя.


! Выберите Issues -> Boards в левом меню. Вы увидите доску по умолчанию. В ней уже будут колонки-списки Open и Closed. Нажмите Add list в правой верхней части основной области. Создайте по одному списку для каждой из созданных ранее меток


  • Dev
  • Dev: done
  • QA

Если вы создали их в другом порядке, вы можете изменить порядок, перетаскивая списки "за заголовки".


! На странице Issues -> Boards перетащите задачу Настроить доску мышкой из столбца Open в столбец Dev. Выделите задачу кликнув на часть карточки, не занятую текстом.


Таким образом мы указываем что начали работать над этой задаче. Обратите внимание что у задачи появилась новая метка Dev.


! Так как всю работу мы уже сделали, переведите задачу в столбец Dev: done.


Мы приближаемся к стадии QA. Постарайтесь внутренне ощутить себя тестировщиком, подумайте какие разработчики неблагонадёжные люди, и, опционально, разберите при помощи отвёртки небольшой бытовой прибор.


! Затем переведите задачу в стадию QA. Тут можете несколько раз поперетаскивать задачу из столбца в столбец туда-сюда. Кстати, такие приключения порой и происходят с задачами на данном этапе. В процессе у задачи будет появляться метка нового столбца и исчезать метка предыдущего столбца. В конце концов оставьте задачу в столбце Closed и обратите внимание что в результате этого действия она была закрыта.


Есть ещё один полезный интерфейс To-Do List(в меню рядом с меню логина есть подменю). Это список действий, которые по тем или иным причинам ожидается от вас. Мы не будем останавливаться на нём подробно, но я рекомендую заглядывать в него в процессе туториала время от времени чтобы составить представление о том как эта штука работает.


Конвейер непрерывной поставки


В этой секции мы реализуем непрерывную поставку(Continuous Delivery) средствами GitLab.


Построение конвейера непрерывной поставки в GitLab


Для того чтобы на самом деле выполнять код задач конвейеров, GitLab.com использует общие агенты (shared runners) которые реализуют docker executors. Если вы зарегистрируете свои собственные агенты, вам будет доступна куда большая гибкость в выборе сред сборки.


Давайте теперь сконструируем наш конвейер.


! Назначьте issue Создать конвейер непрерывной поставки и переведите его в статус Dev.


! Создадим конвейер непрерывной поставки(CI/CD pipeline). Для этого требуется создать файл .gitlab-ci.yaml.
На момент написания этой статьи это можно было сделать так.


  1. Перейдите в проект gitlab-cd-react
  2. Либо на странице Project overview / Details либо на странице Repository вы увидите файлы в вашем репозитории.
  3. Выберите опцию New file как показано на скриншоте. Вас перенаправят на страницу создания файла.
    New file
  4. В окне File name введите имя файла .gitlab-ci.yml, поле Template правее заполнится автоматически, появится ещё одно поле Apply a template. Ознакомьтесь с доступными опциями, попробуйте повыбирать разные варианты. В конце концов выберите Bash это просто шаблон с командами bash, мы будем использовать его в качестве отправной точки.

Давайте разберём полученный файл. Официальная документ по формату файла .gitlab-ci.yml находится тут.


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

image: busybox:latestbefore_script:  - echo "Before script section"  - echo "For example you might run an update here or install a build dependency"  - echo "Or perhaps you might print out some debugging details"after_script:  - echo "After script section"  - echo "For example you might do some cleanup here"build1:  stage: build  script:    - echo "Do your build here"test1:  stage: test  script:    - echo "Do a test here"    - echo "For example run a test suite"test2:  stage: test  script:    - echo "Do another parallel test here"    - echo "For example run a lint test"deploy1:  stage: deploy  script:    - echo "Do your deploy here"

Чтобы получить нужный нам конвейер чуть модифицируем этот файл.


image: busybox:latest

image задаёт какой образ docker будет использован для выполнения jobs сборки. По умолчанию GitLab использует Docker Hub, но это можно настроить. Нам понадобится образ с предустановленным Node.js.


! Укажите


image: node:14-alpine

before_script и after_script выполняются перед и после каждой задачи соответственно. В процессе выполнения задач, мы будем использовать модули Node.js.


! Организуем кэширование модулей для того чтобы не скачивать модули каждый раз из интернета.


cache:  key: ${CI_COMMIT_REF_SLUG}  paths:    - .npm/before_script:  - npm ci --cache .npm --prefer-offline

  • Мы вначале указываем где задачи будут искать кэш, а именно внутри текущей папки в подпапке .npm.
    В качестве ключа кэша используем ${CI_COMMIT_REF_SLUG}, это означает что мы держим отдельный кэш для каждой ветви в Git.
  • В before_script мы устанавливаем пакеты в соответствии с package-lock.json, указывая в качестве пути для кэша подпапку .npm.

Таким образом, в дальше в коде .gitlab-ci.yml мы можем исходить что пакеты уже установлены.


after_script нам в этом туториале не понадобится.


Перейдём к секции build1, изначально там должно быть что-то вроде этого:


build1:  stage: build  script:    - echo "Do your build here"

Обратим внимание на кусок stage: build. stage определяет этап конвейера, этапы выполняются один за другим. Один этап может содержать одну или более задач, которые могут выполняться параллельно.


! Изменим команды чтобы действительно осуществлять сборку:


build1:  stage: build  script:  - npm run build  artifacts:    expire_in: 1 week    paths:    - 'build/'

  • npm ci устанавливает необходимые модули в соответствии с файлом package-lock.json без проверки существуют ли они уже в папке node_modules.
  • npm run build запускает саму сборку минифицированного React-приложения.
  • artifacts указывает какие файлы должны быть сохранены для использования в будущем. Webpack с настройками create-react-app по умолчанию копирует файлы в папку build. expires_in мы используем для того чтобы не тратить место на уже не нужные сборки.

Обратим теперь внимание на кусок кода для этапа stage: test


test1:  stage: test  script:    - echo "Do a test here"    - echo "For example run a test suite"test2:  stage: test  script:    - echo "Do another parallel test here"    - echo "For example run a lint test"

! Нам не нужна задача test2, а задачу test1 изменим следующим образом:


test1:  stage: test  script:  - "CI=true npm test"   dependencies:  - build1

  • npm test тут запускает модульные тесты, определённые в нашем проекте.
  • dependencies указывает что данная задача test1 зависит от результатов задачи build1 чтобы артефакты этой задачи были доступны в текущей.

Перейдём к части связанной напрямую с развёртыванием.


deploy1:  stage: deploy  script:    - echo "Do your deploy here"

Эта часть обычно завязана на специфику среды в которую осуществляется развёртывание и потому технически сложна. GitLab поддерживает развёртывание в Kubernetes с минимальными усилиями по настройке. Альтернативой является реализация логики развёртывания в другую среду своими силами. Оставим технические детали этого процесса для будущих статей. Фокус данной статьи на объяснении основ работы с GitLab, поэтому мы прибегнем к хитрости, а точнее используем то обстоятельство что наше приложение на React технически является статическим вебсайтом и развернём его на GitLab Pages, которые доступны любому у кого есть учётная запись на GitLab.com.


! Заменим job deploy1 на этот код.


pages:  stage: deploy  script:  - mv public _public  - mv build public  only:  - master  artifacts:    paths:    - public  dependencies:  - build1

Мы переименовали deploy1 в pages потому что GitLab именно по названию задачи понимает что требуется развернуть файлы доступные этой задаче в GitLab Pages.
Далее делаем 2 вещи.


  • mv public _public сохраняет папку public которая есть в приложении, сгенерированном из шаблона create-react-app. Мы делаем это потому что GitLab будет возвращать в ответ на запросы к Pages именно содержимое папки public.
  • mv build public тут мы как раз помещаем результат сборки туда где веб-сервер GitLab Pages будет искать его.

Завершим на этом работу над нашим конвейером.


! Закоммитьте отредактированный файл .gitlab-ci.yml, укажите "Add CI/CD pipeline" в качестве Commit message, оставьте в качестве ветви master.


! Переведите задачу Создать конвейер непрерывной поставки в стадию Dev: done.


Проведём несколько циклов работы с GitLab Flow


Git очень гибкая система контроля версий кода и использовать его можно очень по-разному. Если каждый член команды использует Git по-своему, возникает путаница когда трудно понять логику действий других людей из истории изменений, и никто не понимает что ожидать от других. Об измерении производительности в такой ситуации и говорить не приходится. Для того чтобы такая путаница не возникала участники команды обычно договариваются о единых правила работы с Git, такая договорённость и называется Git workflow.


Чтобы изучить реализацию GitLab Flow в GitLab мы сделаем эти вещи:


  • обсудим что такое процесс работы с Git(Git workflow) вообще;
  • посмотрим, как в GitLab устроены права доступа и защищённые ветви;
  • разберёмся как работают merge request`ы;
  • проведём несколько итераций GitLab Flow.

Процесс работы с Git


Если коротко, то GitLab Flow состоит в том что


  • при каждой необходимости что-то изменить в коде создаётся ветвь от главной (в Git по умолчанию master);
  • затем открывается merge request (в других системах называется pull request) где происходит обсуждение вносимых изменений;
  • для каждой из сред (тестовой, продуктивной и т.д.) при этом существует отдельная ветвь;
  • одна из "ветвей сред", откуда происходит безусловное развёртывание может совпадать с главной;
  • развёртывание происходит путём слияния в ветвь соответствующей среды;
  • cлияния происходят из feature branch в главную ветвь или из "менее продуктивной" ветви среды в "более продуктивную".

На самом деле для реализации CD нам годится любой процесс работы с Git который поддерживает trunk based development, и мы могли бы реализовать любой такой процесс при помощи GitLab. Однако по той причине, что GitLab рекомендует использовать GitLab Flow и для того чтобы не усложнять наш сценарий, мы его и используем.

Уровни доступа в GitLab


Существует разные уровни доступа, в порядке понижения полномочий.


  • Instance administrator доступен только для отдельных инсталляций, может всё.
  • Owner владелец группы проектов, может всё кроме чисто технических штук типа включения-отключения фич и интеграции с другими сервисами.
  • Maintainer может всё кроме некоторых действий в отношении всего проекта типа изменения его названия или степени видимости, а также деструктивных действий типа удаления задач.
  • Developer может то же что и Maintainer кроме некоторых администраторских и деструктивных функций внутри проекта типа настройки защищенности ветвей и редактирования комментариев.
  • Reporter может редактировать задачи, но не может вносить изменения в репозиторий.
  • Guest доступ только на чтение issue кроме конфиденциальных, может создавать новые задачи.
    Данные о полномочиях здесь приведены для полноты, активно использовать в рамках туториала мы их не будем.

Merge requests


Merge requests основной способ внесения изменений в код при использовании GitLab. Изменения вносятся в код, затем автором изменений создаётся merge request, который затем обсуждается, в котором происходит code review. Merge request в результате принимается, отправляется на доработку или отклоняется.


! Зайдите в задачу Провести несколько итераций GitLab Flow, назначьте его на себя и переведите в колонку Dev.


! Переведите задачу Создать конвейер непрерывной поставки в стадию QA.


Да, вот так мы будем тестировать наш конвейер, но вы должны тестировать на реальных проектах более ответственно. Не делайте как я делаю, делайте как я говорю.

Наше приложение бесполезно. В этом нет ничего необычного, ведь в интернете много бесполезных приложений. Чтобы выйти на новый уровень, давайте сделаем наше приложение токсичным. Существуют 2 фичи, которые являются проверенным способом достичь этого.


! Откройте файл src/App.js на редактирование (Repository -> Files, Edit) и добавьте эту строчку прямо вверху файла.


import {useEffect} from 'react';

В качестве Commit message укажите, например, "Add React imports". В качестве Target branch оставьте master и нажмите Commit changes.


Упс, мы только что закоммитили изменения сразу в master. Некоторые workflow позволяют такое, но мы будем работать в рамках GitLab Flow. Избежать подобных казусов в дальнейшем нам поможет фича GitLab под названием защищённые ветви(protected branches). Её смысл не в разграничении доступа, а в том, чтобы помочь членам команды работать в рамках договорённостей и избежать случайного изменения и удаления данных в репозитории.


! Нажмите в левом меню Settings -> Repository. В открывшейся странице найдите раздел Protected Branches.


Вы увидите что по умолчанию защищена только ветка master, но вы можете добавить и другие. И по умолчанию


  • force push запрещён всем
  • push разрешён Maintainers
  • merge разрешён Maintainers

! Измените Allowed to push на No one.


! Отлично, давайте вернёмся к редактированию файла src/App.js. После строчки


function App() {

перед строчкой


  return (

добавьте


  useEffect(() => {    alert('Consent to cookies and everything!');  }, []); 

Соответствующий участок кода должен теперь выглядеть примерно так


// ...other codefunction App() {  useEffect(() => {    alert('Consent to cookies and everything!');  }, []);   return (// ...other code

Укажите "Add the annoying popup" в качестве commit message.


Вы увидите что вам предлагается добавить коммит в некую ветвь и в уже автоматически сгенерировано некоторое имя ветви. Мы в будущем заменим это имя на более осмысленное и соответствующее нашему процессу работы с Git, но пока давайте проведём небольшой эксперимент.


! Замените предлагаемое имя на master и нажмите Commit changes.


Вы увидите сообщение об ошибке You are not allowed to push into this branch. Хорошо! Защита ветвей работает. Давайте теперь инициируем процедуру внесения изменений в код так как это предполагается делать.


! Замените теперь имя ветви с master на feature-cookies-consent. Оставьте чекбокс Start a new merge request with these changes установленным. Будет создана ветвь с указанным названием и изменения будут закоммичены в эту ветвь. Вы окажетесь на странице создания merge request'а. Оставьте


  • заголовок merge request'а по умолчанию.
  • Assignee и Reviewer Unassigned.
  • всё остальное также по умолчанию.

Нажмите Submit merge request


Кстати


  • Assignee ответственный за работу над merge request'ом и его слияние в целевую ветвь,
  • Reviewer же изучает предложенные изменения и может согласовать их если они кажутся правильными.

После создания merge request'а, ы окажетесь его на странице. Давайте изучим эту страницу. В основной области мы видим следующее.


  • Какую ветвь мы сливаем с какой (feature-cookies-consent в master).
  • Статус pipeline для ветви feature-cookies-consent.
  • Возможность согласовать merge request от имени текущего пользователя.
  • Кнопка Merge осуществляет принятие merge request'а и непосредственно слияние ветвей, которое приводит к внесению изменений в master.
  • Область где можно посмотреть коммиты, которые будут слиты, там же можно изменить сообщение merge commit'а.
  • Возможности принять участие в обсуждении:
    • выразить своё отношение и оставить смайлик;
    • область добавления комментария.
  • Кнопку Close merge request, которая позволяет закрыть merge request без принятия изменений в код master.

Для бесплатных подписок есть только возможность "совещательного" согласования merge request'а, в платных версиях есть возможность сделать согласование необходимым.

Если вы переключитесь на вкладку Changes, вы можете посмотреть, какие изменения в код планируется внести. Здесь же вы можете создать комментарий, который будет ссылаться на строку кода.


Кстати, merge requests также поддерживают метки, которые вы можете использовать чтобы было проще находить нужные.


К этому моменту работа конвейера уже должна завершиться и тесты завершены неуспешно. В нашем конкретном случае это потому что мы использовали window.alert который является чисто браузерным объектом и наши выполняемые в среде Node.js юнит-тесты не имеют к нему доступа.


В случае непрерывной поставки требуется очень хороший набор автоматических тестов потому что именно на них переложена почти вся, а в случае непрерывного развёртывания(Continuous Deployment) вся, ответственность за контроль качества. Поддержание такого набора тестов в актуальном состоянии главная технический вызов в реализации таких методологий.


! Исправим src/App.js, добавив проверку на то что код работает в браузере. Давайте поместим код, использующий window.alert внутрь блока кода, проверяющего что мы в браузере. Убедитесь что вы редактируете файл в ветке feature-cookies-consent.
Должно получиться примерно так


// ...other codefunction App() {  useEffect(() => {    if (typeof process === 'undefined' || process.release === undefined) {      alert('Consent to cookies and everything!');    }  }, []);   return (// ...remaining code

Добавьте "Ensure running in a browser" в качестве commit message. В качестве Target Branch должна быть установлена feature-cookies-consent.


! Давайте вернёмся в merge request Add the annoying popup и нажмём кнопку Merge и примем изменения в код. Оставьте чекбокс Delete source branch установленным.


При использовании GitLab Flow feature branches традиционно удаляют. Это позволяет избежать замусоривания системы уже неактуальными ветвями и создавать ветвь с тем же именем в случае отправки задачи на доработку.


! Откройте Repository -> Graph и обратите внимание на то что feature-cookies-consent теперь слита с master.
Затем откройте файл src/App.js в ветке master и заметьте что код уже содержит правки.


! Зайдите в задачу Провести несколько итераций GitLab Flow и в основной области нажмите стрелочку вниз рядом с кнопкой Create merge request. Укажите имя ветки, которую вы хотите создать чтобы базировать на ней новый merge request. feature-notifications-consent вполне подойдёт. Оставьте в качестве source branch master.


При создании merge request'а таким образом задача будет закрыта автоматически как только мы сольём код в основную ветвь.


Обратите внимание что автоматически сгенерированное название нашего merge request'а начинается с Draft:. Это означает что merge request помечен как черновик. Это полезно для авторов чтобы явно обозначить что работа пока не завершена и избежать слияния изменений as is в результате недопонимания. Этого же можно добиться и при помощи кнопки Mark as draft в верхней правой части основной области.


Добавим функцию оповещений в наш вебсайт чтобы он выглядел современно.


! Давайте для разнообразия внесём изменения в код чуть иначе. На странице merge request'а нажмите кнопку Open in Web IDE. Откроется чуть более удобный для работы с кодом интерфейс. Уже из него откройте файл src/App.js. Добавьте код


      Notification.requestPermission().then(function(result) {        alert(`You ${result} notifications`);      });

после ранее добавленной строчки


      alert('Consent to cookies and everything!');

! Нажмите кнопку Commit в левой нижней части. Вы увидите интерфейс коммита, укажите в качестве commit message "Add the notifications users want". Оставьте всё остальное по умолчанию и нажмите кнопку Commit.


! Перейдите в merge request, например, используя подменю в верхней правой части экрана рядом с меню логина.Нажмите кнопку Mark as ready в верхней правой части формы merge request'а. Нажмите кнопку Merge или Merge when pipeline succeeds в зависимости от того завершился ли уже конвейер.


! Откройте наш тестовый вебсайт и убедитесь что новые "фичи" работают, и наша страница ощущается как большинство современных сайтов в интернете.


! Если с сайтом всё хорошо, перетащите задачу Создать конвейер непрерывной поставки в стадию Closed на доске.


Итак, мы провели несколько полных циклов работы и теперь можем изучить метрики, которые GitLab собрал в процессе работы.


Метрики CI/CD в GitLab


В процессе командной работы полезно собирать статистику чтобы понимать повышается ли продуктивность или падает. Способы оценки производительности могут быть совсем разными в зависимости от характера работы и типа проекта или продукта.


Например, веб-студия может иметь "типовые" задачи типа отрисовки макета в рамках стандартного пакета услуг, предлагаемого клиенту. Agile стартап же может находиться в стадии когда концепция продукта меняется вместе с растущим пониманием потребностей клиента и рынка, и задачи могут быть трудно предсказуемы и часто уникальны. Вместе с тем, можно выделить чисто технические показатели производительности вроде одного из основных для DevOps времени вывода новой фичи на рынок (time to market, TTM) или просто длительности той или иной стадии. Отслеживать динамику таких показателей может быть очень полезно: за достаточно длительный период времени это позволяет понять как изменяется производительность.

Хорошей новостью является то что GitLab имеет функционал сбора этой статистики. Давайте с этим функционалом познакомимся.


! Кликните на Analytics. По умолчанию откроется раздел Value stream. В Lean вообще и в DevOps в частности считается что задачи несут в конечном итоге некоторую пользу для конечного заказчика. И движение таких полезных задач, от стадии формулирования через все этапы работы до того момента когда результаты становятся доступны заказчику называется value stream.


Основные средние величины по этапам:


  • Issue время которое уходит на то чтобы задачу "взять в работу" т.е. присвоить метку или добавить в Milestone.
  • Plan время от последнего действия в предыдущем этапе до появления первого коммита в ветке, хоты бы один из коммитов которой связан с той же задачей. То есть "время, которое уходит чтобы начать коммитить".
  • Code время существования ветки, связанной с той или иной задачей, которое уходит до появления merge request'a.
  • Test время от начала до конца всех конвейеров данного проекта.
  • Review время от создания merge request'а до его слияния или закрытия.
  • Staging время от принятия merge request до развёртывания в продуктивную среду.

Если вы сложите длительность Issue, Plan, Code, Review и Staging, вы и получите примерно то самое заветное время для вывода на рынок (TTM).


Наверху страницы в также увидите некие агрегатные показатели по проекту за выбранный период времени.


Analytics -> Repository показывает разные графики связанные с языками коммитов, показателями code coverage (если настроено), распределением коммитов во времени (месяц, дни недели, часы).


Analytics -> CI/CD показывает сводные данные по выполенным pipelines, в том числе график показывающий динамику изменения "успешности" выполнения pipelines.


Поздравляю!


Вы осуществили имплементацию непрерывной поставки(CD) при помощи GitLab начиная с задач с канбан-доской и завершая метриками.


Ссылки


  1. GitLab product handbook
  2. The DevOps Handbook
  3. Pro Git Book
Подробнее..

Да кто такой этот ваш Mobile DevOps?

20.12.2020 18:08:28 | Автор: admin

Сегодня почти у каждого проекта мобильного приложения есть базовая инфраструктура: ваш код хранится на git хостинге и весь новый код регулярно проверяется на CI, чтобы не сломать старый. Если ваша команда в несколько человек производит не очень много кода, то скорее всего вы настроили весь пайплайн один раз и радуетесь его работе. Но что если ваша мобильная команда это пара десятков или даже сотен разработчиков? Много разработчиков производят много кода, что создает нагрузку на инфраструктуру, увеличивает вероятность сломать важные флоу приложения и просто навсего унести время сборки проекта в небеса. Единожды кем-то настроенный пайплайн обречен на изменения, если он не справляется с нагрузкой.

Вы уже наверное поняли о чем пойдет речь под катом? В виде интересной истории я расскажу вам про типичные проблемы мобильных команд (да-да, вы можете узнать в них свои), а также что такое Mobile DevOps, как он поможет решить проблемы и, наконец, как получить этот самый Mobile DevOps к вам в команду.

Mobile DevOps существует, даже если вы о нем не знаете

Говорить на тему DevOps особенно в направлении Mobile можно сколько угодно. Это тема богата на детали, обсуждения, вопросы, альтернативы и многое другое. Можно прочитать море определений понятия DevOps, попытаться интегрировать сюда мобильную разработку, но так до конца и не понять что же это такое. Поэтому я приготовил для вас небольшой рассказ, который поможет понять что это за зверь - Mobile DevOps. А после самого рассказа, в ходе которого вы сами найдете ответы на вопросы, я дам немного пояснений, который прольют еще больше света на эту интересную тему. Устраивайтесь поудобнее и наслаждайтесь рассказом.

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

Представьте как выглядит обычный рабочий день Android разработчика Миши в компании Galera Development: Миша пришел в офис (или просто встал с кровати, ееее удаленка), приготовил себе чашечку кофе и сел за работу. Сегодня у него отличное настроение и работа обещает быть бодрой. Для разминки Миша берет задачу на простой фикс бага, быстро с ней справляется и создает пул реквест в develop. Остается дождаться успешной сборки на CI и пул реквест можно заливать. Миша на 200% уверен, что сборка пройдет успешно, ведь баг был прост, и он локально все проверил.

Но как оно иногда бывает - не все идет по плану. Сборка на пул реквест Миши почему-то не собралась. Наш герой подумал, что это какая-то разовая акция и перезапустил свою сборку. Проходит пара минут и Миша получает опять упавшую сборку. Что поделать, придется идти разбираться. Миша идет на CI в логи своей сборки и видит ошибку, которая совсем не похожа на те, что пишет Android Studio. Что-то на эльфийском - подумал он, но пул реквест заливать надо и надо как-то получить успешную сборку на CI. Миша слышал раньше, что настраивал CI его коллега Паша, и он наверняка должен знать как совладать с этим зверем.

Миша пишет Паше и рассказывает о проблеме. Проходит некоторое время и Паша отвечает только не снова и просит еще раз перезапустить сборку и сообщить ему о результатах. Паша бы и рад пойти посмотреть в чем действительно дело, почему вдруг стабильно работающая система начала давать сбои, но он работает над фичей срок релиза у которой вчера, поэтому у него совсем на это нет времени.

У Миши уже настроение не настолько прекрасно как было утром, но он идет и перезапускает сборку. Через пару минут сборка действительно проходит, бодрый настрой и мотивация возвращаются к Мише, он заливает свой пул реквест и сообщает Паше, что сборка прошла и все в его жизни прекрасно. Обошлось - подумал Паша.

Проходит час, и на почту Паше пишут уже 5 разработчиков, которые тоже знают, что Паша маг и знает что происходит на CI. У них такая же история как и у Миши час назад - падают сборки. Что делать? Паша сейчас занят важной фичей, которая принесет компании несколько сотен миллионов в первую же неделю релиза. Еще один человек знает как устроен этот ваш зверь CI, но теперь он руководит небольшой командой IOS разработчиков в другой компании. Да и если Паше бросать свою работу над фичей и погружаться в проблему на CI, то на обнаружение проблемы и ее решение уйдет много времени, ведь Паша сам уже не помнит когда в последний раз заглядывал на CI и смутно помнит как там все устроено.

Паша решает сообщить о возникшей ситуации своему руководителю Антону. Так как надо много чего обсудить, они договариваются на созвон.

Антон: Привет! Ну как там с билдами?

Паша: Привет! Все плохо, я посмотрел логи, но сразу ничего не могу сказать, надо садиться и разбираться, но у меня на это совсем нет времени. Я занят сам знаешь какой фичей и мне не вариант откладывать работу по ней.

Антон: А еще не вариант тормозить всю разработку проблемой на CI! Предлагаю следующее: ты сейчас разбираешься с проблемой на CI, а я подключаю к тебе на разработку фичи еще 2 разработчиков, годится?

Паша: Другого варианта не вижу, так и поступим.

Антон: Отлично, тогда подключу к тебе на помощь Костю и Артема. Свяжусь с ребятами и проинформирую их, что у них теперь новая задача. Как закончишь работы на CI, напиши им.

Паша не очень любит такие резкие переходы по задачам, да еще в и так стрессовой ситуации приближающегося релиза. Он закоммитил то что у него уже готово по этой задаче и надеется, что Костя с Артемом правильно поймут спроектированное решение и качественно его реализуют.

И вот наш герой пошел смотреть логи упавших билдов, но их уже стало на пару десятков больше с того момента как ему написали 5 его коллег. Дело похоже действительно плохо. Когда полгода не приходилось плотно сидеть за инструментом - начинаешь забывать как он устроен и что у него может пойти не так. Паша с трудом, парой часов просмотра логов и гуглением из них строк понимает, что ресурсы, используемые CI для сборок, уже недостаточны, и поэтому возникают проблемы.

Когда он полгода назад поднимал эти ресурсы для команды мобильной разработки все летало, и команда на руках его носила за проделанную работу. Паша пытается понять причину упора в потолок по ресурсам, что такого произошло, что именно сегодня начали всплывать такие проблемы. Все оказалось просто: за последние 2 месяца в команду мобильной разработки пришло пара десятков новых разработчиков. Разумеется, команда стала писать больше кода, который надо собирать на CI, что и дало нагрузку, пик которой пришелся на сегодня. Вот это денек! Паша прикинул сколько ресурсов дополнительно потребуется и создал заявку на ресурсы администраторам серверов.

А еще Паша заметил, что время сборки проекта стало в 4 раза больше! Он когда-то для проекта занимался оптимизацией времени, но сделал лишь часть от того что знал и планировал, очередные горячие фичи были приоритетнее. Паша тогда сделал лишь те оптимизации, которые существенно ускоряют сборку, а те, что ускоряют не сильно, или будут ускорять, только если проект будет намного больше - не сделал. Об этом он сейчас вспомнил и отметил себе, чтобы рассказать Антону. Так и подошел конец рабочего дня. Паша звонит Антону и рассказывает об успехах.

А вы тоже уже на этом моменте подумали как будет затратно компании если Паша уйдет?

Антон: Как успехи?

Паша: Я разобрался с причиной: мы уперлись в потолок по ресурсам. Я уже завел заявку админам, скоро нам должны выделить еще немного ресурсов, что стабилизирует сборки.

Антон: Круто! Спасибо тебе, выручил. Теперь можешь спокойно возвращаться к работе над своей фичей.

Паша: На самом деле есть еще кое-что. Я посмотрел как изменилось время сборки в последнее время - оно сильно выросло. Теперь вместо прежних 10 минут это 45 минут. Я думаю с этим надо что-то делать.

Антон: Ну и кода у нас стало больше. Скорее всего это закономерно.

Паша: Не соглашусь с тобой. У нас есть еще большой задел для оптимизации. Я могу провести ряд работ, но это займет некоторое время, чтобы провести оптимизации и протестировать их работу.

Антон: Даже если ты сейчас вернешься к работе над нашей фичей, то после нее тебя ждет другая, у нас очень много работы. Боюсь, оптимизацию придется отложить. Есть возражения?

Паша: Да. Сейчас расскажу почему. У нас работает 50 Android разработчиков, которые собирают свои пул реквесты на CI да и локально тоже, но сейчас не об этом. Сейчас сборка занимает примерно 45 минут. Я не говорю, что смогу ее ускорить до 10 прежних минут. Но проходить минут за 30 она точно будет, а в некоторых случаях и быстрее. То есть я предлагаю сэкономить на каждой сборке по 15 минут. В среднем разработчик собирает на CI по 2 пул реквеста в день. Это 0.5 часа в день экономии времени разработчика на ожидание сборки на CI и 0.5 часа занимаемых ресурсов. Ладно, отложим в сторону ресурсы и сосредоточимся на времени разработчиков: у нас 50 разработчиков, которые могут в день экономить по полчаса своего времени, то есть за месяц вся команда Android разработки сэкономит 50 разработчиков * 20 рабочих дней * 0.5 часа экономии = 500 часов разработки. Планирую я на это дело потратить примерно месяц, то есть 160 часов. Как видишь, заняться оптимизацией сейчас это просто отличная возможность сэкономить 500 часов Android разработки каждый месяц. Неплохо, не правда ли? Плюс, я могу покопаться со сборкой IOS проекта, думаю, там тоже есть большой задел для оптимизаций.

Антон: Ты заставил меня задуматься над этим. Давай я все обдумаю и созвонимся завтра. Решим что делать.

Паша: Договорились. До завтра.

Антон: Пока.

После рабочего дня у Паши двойственные чувства. С одной стороны немного досадно, что пришлось отдать работу над фичей Косте и Артему и завтра скорее всего он к ней тоже не вернется. Но с другой стороны сегодня ему удалось позаниматься и решить проблемы всей мобильной команды. Он вылечил головную боль десятков классных ребят и очень рад этому. Приятно видеть каждый день людей, которым помогаешь. Плюс такие задачи могут немного разнообразить твои текущие задачи и дают понимание, что ты делаешь нечто особенное, что под силу далеко не всем, а в твоем случае только тебе в команде.

Наступает следующий день. Паша садится с кружкой кофе (но уже покрепче в 2 раза чем вчера) и начинает смотреть почту. В целом ничего интересного: поставили на ревью, накинули замечаний в его пул реквест с небольшим баг фиксом, уведомление об обновлении версии CI завтра, корпоративные новости, день рождение коллеги Погоди, что? Обновление версии CI завтра?! Паша понял, что без внимания этот момент оставить нельзя. Совсем неизвестно как это повлияет на его текущую конфигурацию, которая теперь более менее стабильно собирает проект, а еще не известно как повлияет на интеграцию CI с другими сервисами инфраструктуры. Похоже придется провести ряд проверок, которые займут неизвестно сколько времени, а обновление уже завтра. Вот и еще одна тема для обсуждения с Антоном.

Антон: Доброе утро! Я тут все обдумал, и, кажется, ты прав, нам стоит провести ряд оптимизаций.

Паша: Привет! Это отличная новость. Когда приступать?

Антон: Можешь приступать прямо сейчас. И еще один момент, в связи с тем, что мы в последнее время довольно часто подходим к релизу со сломанными основными сценариями, которые находят, слава Богу, тестировщики, а не пользователи, то мы решили завозить в проект UI тесты, чтобы мы были уверены, что новые фичи не ломают наши основные сценарии. Прогнали UI тесты - сразу убедились, что основные сценарии целы, круто, правда? И поэтому надо научить наш CI прогонять эти UI тесты. Я пока без малейшего понятия как это реализовать, поэтому в первую очередь говорю об этом тебе. Было бы здорово, если бы ты занялся этой инфраструктурной задачей.

Паша: Неожиданно. Задача звучит очень непонятной и сложной, но мне в целом было бы интересно ей заняться. Надеюсь ты понимаешь, что это задача не на неделю и даже не на месяц, если я ее беру, то скорее всего полностью выпадаю из продуктовых Android задач.

Антон: Да, конечно. Более того, я подумал над всеми этими задачами, заглянул в наш бэклог и нашел еще несколько задач на оптимизацию наших процессов, которые могут тоже неплохо сэкономить нам ресурсы разработчиков. Я прикинул, затраты на выполнение этих инфраструктурных задач окупятся даже в ближайший квартал! Плюс, я планирую обсудить задачи такого типа еще и с IOS командой, у них наверняка тоже что-то есть. Поэтому я сделал запрос на найм к нам в команду еще одного инженера, который бы помог организовать работу нашей инфраструктуры. В общем решили искать Mobile DevOps инженера, если можно так выразиться конечно. Будем надеяться что получится найти такого, хоть и на рынке Mobile DevOps инженеров единицы. Ну а пока ты ответственный за работу нашей инфраструктуры, можешь довести до конца свои текущие Android задачи и заняться инфраструктурой. Если все-таки не получится нанять Mobile DevOps a, то тебе нужно будет взять кого-то к себе в команду, кто хотел бы заниматься инфраструктурой и ввести в курс дела. Что скажешь насчет всего этого?

Паша: Звучит здорово. Я в деле. При таком раскладе у меня есть срочная инфраструктурная задача: от админов пришло письмо, в котором сообщается об обновлении нашего CI, надо срочно проверить что в новой версии все будет работать по прежнему и всякие интеграции тоже. Планирую ей сейчас заняться.

Антон: Спасибо, что заметил это письмо и понял чем оно может для нас обернуться. Тогда не буду тебя отвлекать. О деталях твоих новых рабочих процессов поговорим позже. Поздравляю, теперь ты Mobile DevOps, ахахаха!

Паша: Договорились. Спасибо! Хорошего дня!

Антон: Спасибо, и тебе!

И Паша отправился непростой и интересной дорогой Mobile DevOps инженера!

Что я только что прочитал?

Надеюсь, я справился со своей задачей, прочитав этот небольшой рассказ вы нашли в нем ответы на вопросы:

  1. Что такое Mobile DevOps?

  2. Какие проблемы Mobile DevOps решает?

  3. Как Mobile DevOps решает проблемы?

  4. Как получить Mobile DevOps инженера в команду?

  5. Как стать Mobile DevOps инженером?

Но я все-таки отвечу на каждый вопрос более развернуто. То что вы прочитали должно дать плотный фундамент для понимания темы Mobile DevOps и этих вопросов конкретно.

Что такое Mobile DevOps?

Здесь я не скажу ничего особо отличающегося от понятия классического DevOps. Это все так же Development и Operations, но в области мобильной разработки. Инженер, занимающийся Mobile DevOps, занимается не только разработкой и поддержкой инфраструктуры (как мы увидели в рассказе мага Пашу, который умел колдовать на CI, оптимизировать сборку с помощью магического Gradle и многое другое), но и совершенствованием процессов мобильных команд (когда Паша начал продумывать как проверять UI тесты для нового кода коллег, чтобы не сломать существующий код).

Про первую часть Development вам всегда будет напоминать огромный список задач в бэклоге, про нее вы никогда не забудете. А вот про вторую часть действительно стоит помнить. Главная цель нашего Mobile DevOps - обеспечивать команде эффективную и комфортную работу. Мы должны прислушиваться к команде, слушать когда они говорят о своих болях и пытаться их решить инструментами, которые мы можем даже пока и не знать.

Mobile DevOps инженер должен хорошо знать как работает команда, понимать процессы и видеть уязвимые места, которые стоит усилить. Чтобы всегда быть в курсе процессов, возможно, не помешает участие в общих ретроспективах, чтобы не упустить какое-то важное изменение в процессах команды или услышать обратную связь по текущим процессам.

Какие проблемы Mobile DevOps решает?

Как я уже ответил в предыдущем вопросе: Mobile DevOps должен решать проблемы в инфраструктуре команды и процессах. Но так как мы ограничили область DevOps мобильной разработкой, то предлагаю конкретизировать проблемы, которые может решать Mobile DevOps.

  1. CI. Создание CI сервера, его конфигурирование, общение с админами по поводу обеспечения сервера ресурсами. Настройка сборок для всех проектов команды.

  2. Оптимизация сборок. Конфигурирование системы сборки (например, Gradle для Android) проекта для обеспечения наилучшего времени сборки как локально, так и на CI. Обеспечение эффективного использования кэша сборки.

  3. Автоматические проверки кода. Это могут быть простые тулзы-анализаторы код стайла типа Sonar или Detekt, а может что-то посложнее и самописное в виде Gradle тасок, например, таска для проверки соответствия правилам зависимостей проекта.

  4. Прогон UI тестов на CI. Обеспечение работы набора устройств или эмуляторов и обеспечение возможности прогона на них UI тестов.

  5. Impact analysis. Анализ измененного кода для получения списка затронутых UI тестов, которые надо прогнать, чтобы убедиться, что в новом коде разработчик не сломал старый.

  6. Интеграция сервисов инфраструктуры. Обеспечение стабильной интеграции сервисов инфраструктуры типа CI, git-хостинга кода и других сервисов.

  7. Release Train. Автоматизация процесса релиза вплоть до автоматического деплоя бандла в Play Console и раскатки его в продакшн.

Это я привел лишь основные направления, по которым требуются усовершенствования для любой крупной команды. Но у каждой команды есть свои особенности в процессах, из-за которых может появиться еще больше направлений для работы Mobile DevOps инженера.

Как Mobile DevOps решает проблемы?

Тут все по классике: чтобы решить проблему ее нужно понять. Как я уже говорил ранее - понимание процессов команды невероятно важно в работе Mobile DevOps инженера. Это поможет выявить слабые места в процессах и усилить их.

После того как удалось определить проблему нужно заняться проектированием решения. Хочу заметить, что в первую очередь нужно именно спроектировать решение, составить план, сценарий, который будет решать проблему, а не заниматься сначала выбором инструмента решения. Ведь хорошо спроектированное решение позволит вам ничего не упустить при реализации и выбрать лучший инструмент, с помощью которого вы решите проблему с минимальными затратами.

Вот примеры инструментов, с помощью которых можно решить инфраструктурные и процессные проблемы:

  1. Gradle таски, которые будут выполняться в нужный момент (для Android)

  2. Gradle плагины (для Android)

  3. Скрипты, которые могут выполняться как вручную, так и автоматически, например, на CI

  4. Шаги сборки на CI, которые могут выполнять абсолютно разные действия

  5. Создание клиент-серверных приложений на различном стеке технологий

  6. Контейнеризация для изоляции ваших сервисов, автоматизации их развертки и конфигурирования

  7. Виртуализация для работы с эмуляторами

  8. Создание плагинов для любого из сервисов вашей инфраструктуры, если есть открытое API

И это только примеры. Разумеется, инструменты могут быть невероятно различными.

Как получить Mobile DevOps инженера в команду?

Как вы уже поняли по рассказу, вероятно, у вас уже есть человек, который знает как работает ваша инфраструктура, он умеет решать какие-то критические проблемы. Если он имеет желание заниматься инфраструктурой и процессами вашей команды, то вам повезло и у вас есть Mobile DevOps инженер, который готов решить ваши боли.

Но хочу отметить, что ваш новоиспеченный Mobile Devops только вчера занимался парсингом jsonа и передвигал кнопки, поэтому не стоит требовать решения проблем в совсем ближайшее время. Специфика работы и инструменты решения проблем Mobile DevOps сильно отличается от Android или IOS разработки, и развитие вашего инженера будет постепенным. Через какое-то время (это может быть полгода, год, все очень ситуативно) ваш инженер будет действительно способен решать ваши проблемы быстро и качественно. И вы будете счастливы и горды, что в вашей команде есть такой специалист.

Но также есть альтернатива, которая довольно привлекательна. Мы живем в такое время, когда все технологии и процессы развиваются стремительно и есть компании, которые уже активно практикуют Mobile DevOps. Их инженеры уже решили много проблем и имеют большой опыт в практике DevOps в направлении мобильной разработки. Это все те же люди, которые постоянно развиваются и ищут новые возможности, поэтому можно попытать удачу и поискать такого инженера в сервисах для поиска работы. Разумеется найти инженера с таким уникальным опытом намного сложнее, чем Android разработчика или IOS разработчика, но они тоже люди и бывают открыты предложениям.

Сколько нужно Mobile DevOps инженеров?

На самом деле все зависит от объема работы и от результата, которого вы хотите достигнуть с помощью Mobile DevOps. Разумеется, чем больше инженеров вы выделите, тем быстрее будут развиваться ваша инфраструктура и процессы. Но даже если вы не располагаете большими ресурсами, то стоит выделить хотя бы 2 Mobile DevOps инженеров. Если у вас один инженер, то когда он уйдет в отпуск, на больничный или в другую компанию - ваша инфраструктура при возникновении проблем будет парализована, и вся разработка может просто заблокироваться на неопределенный срок. Поэтому не стоит складывать все яйца в одну корзину и стоит подумать о рисках.

А почему бы просто не привлечь настоящего DevOps?

Как я уже говорил ранее, DevOps это не только про инфраструктуру, но и про процессы. Человек, который варился много времени в процессах Android или IOS команд, хорошо понимает специфику работы и отлично понимает боли команд, потому что сам их скорее всего испытывал. Да, он пока не так хорош в инфраструктуре, как обычный DevOps, но все это можно изучить и наверстать. Сам опыт процессов невероятно важен и если вы получите к себе в команду Mobile DevOps (обучите своего или, если повезет, найдете на рынке), то станете действительно богаты в плане ресурсов инженеров.

Что делать, если вы хотите стать Mobile DevOps инженером?

Инициатива зачастую наказуема. Поэтому если вы видите проблемы в вашей инфраструктуре или в процессах команды, то обязательно расскажите об этом вашему лиду или руководителю, как это сделал Паша. Предложите ваше решение и я более чем уверен, что руководство вам не откажет. Ведь им интересно, чтобы команда работала эффективнее и выполняла больше работы. Все как всегда в ваших руках!

Заключение

Спасибо вам, дорогие читатели за внимание! Надеюсь, прочитав статью, вы получили все полезные знания, которые я постарался вложить в нее. А также приобрели мотивацию задуматься о ваших процессах и о концепции Mobile DevOps для ваших команд. Если мне все это удалось, то я очень рад, что смог вам помочь найти путь джедая Mobile DevOps!

Естественно, я приглашаю вас обсудить возникшие в ходе прочтения статьи вопросы в комментарии :)

Подробнее..

Перевод CICD обещания и реальность

24.02.2021 14:21:40 | Автор: admin


Мы говорим CI/CD и подразумеваем непрерывную интеграцию. Никто не имеет в виду (и не практикует) непрерывный деплоймент. Вообще никогда. О нем все забыли. Пора это изменить.


Поучительная история


Один большой и страшный пиратский корабль наводил ужас на всю округу, хоть и подтекал немножко. После шторма или битвы пиратам нередко приходилось откачивать воду с нижних палуб.


Однажды в особо яростном бою пушечные ядра пробили борт под ватерлинией. Ни инструментов, ни плотников, чтобы заделать пробоины, у них не было, так что они просто откачивали воду без остановки. Сначала все получалось, но потом пришлось купить в два раза больше насосов и расширить экипаж.


Скоро они обнаружили, что насосами занимается больше людей, чем кораблем и грабежами. Это было как-то не по-пиратски и очень раздражало. Они стали первоклассными экспертами по откачиванию воды из трюма, зато навыки мореплавания и абордажей применять было некогда. Их лучшие люди заметно заскучали. И года не прошло, как корабль затонул.


Мораль истории такова: устраняйте не симптомы, а причину.


Что подводит нас к разговору о CI/CD?


CI/CD (непрерывная интеграция и доставка, или деплоймент) это часть нашей жизни. Мы ходим на конференции по CI/CD, пишем статьи о CI/CD, ведем блоги о CI/CD, указываем CI/CD на странице LinkedIn. Никто даже не спорит, надо оно нам или нет. Мы все одинаково обожаем CI/CD. Так ведь?


Вот только мы говорим CI/CD и подразумеваем непрерывную интеграцию. Никто не имеет в виду (и не практикует) непрерывный деплоймент. О нем все забыли.


Нет, это замечательно, что мы так поднаторели в непрерывной интеграции за последние лет десять. Но этого же мало. Это даже не половина. Можно подумать, цель непрерывной интеграции наслаждаться своим красивым синтаксисом и логикой. Хотя это приятный бонус, кто же спорит. По факту же CI это просто подготовка к следующему этапу непрерывной доставке. Потому что вообще-то в этом вся суть.


Как пираты на этом злосчастном корабле, мы должны лечить причины, а не заниматься симптомами. А что делает дыры в нашем продакшен-корабле? Да все что угодно. Все, что происходит между моментом написания кода и его релизом, пока он проходит по этой затянутой, подтекающей, раздутой, комковатой и нестабильной системе переваривания программного обеспечения.


Что это вообще такое CI/CD?


Отличный вопрос. Отвечу с удовольствием. CI/CD это процесс и методология, цель которой убедиться, что весь код, вливаемый в главную ветку, можно в любое время протестировать и задеплоить. Автоматически. После каждого дифа.


Цель конвейера CI/CD ускорить внедрение изменений в программах настолько, чтобы мы не успевали выпасть из контекста и забыть, что это были за изменения.


Если любой в вашей команде может отправить изменения в главную ветвь, точно зная, что через 15 минут они будут протестированы и развернуты в продакшене без человеческого вмешательства, наши поздравления! Вы используете CI/CD.


Жаль, что редкая команда может похвастаться этим. Хотя это основы гигиены разработки.


Гибельный водоворот разработки


Представьте, что между разработкой и доставкой ваш код лежит в чашке Петри при комнатной температуре и обрастает вредными бактериями. Чем дольше лежит, тем крупнее дифы и медленнее ревью. Приходится ковыряться в этих монстрах и всматриваться в контекст при переходе с написания кода на ревью и обратно.


Цикл обратной связи растягивается в сторону бесконечности, люди на каждом этапе все дольше ждут друг друга (ревью, комменты, деплои, реализация изменений), и каждый раз приходится заново во все вникать и запоминать, на чем ты остановился и что вообще пытался сделать.


И даже это еще не все. Обычно мы деплоим код вручную через сто лет после его написания. Причем делает это не автор кода. В худшем случае мы выкатываем изменения от нескольких разработчиков за раз. В итоге люди не видят результаты своей работы сразу и не могут нести всю ответственность за жизненный цикл кода.


Если изменения деплоятся пакетом, невозможно отследить эффект каждого фрагмента кода по отдельности. Мы не знаем, когда выйдет код, кто отвечает за изменения в продакшене, поэтому у нас нет ни прав, ни обязанностей в отношении собственного кода. В результате инженеры большую часть времени ждут, суетятся или разбираются с техническим долгом. Никакой пользы бизнесу это не приносит.


Раз все имеющиеся разработчики заняты, нужно нанять много новых и потратить еще больше усилий на их координацию. Скоро вам понадобятся отдельные должности SRE, Ops, QA, билд-инженер, чтобы героически бороться с каждым симптомом по отдельности. Для этой оравы нужно будет больше менеджеров и так далее. Вуаля! Получаем огромную, дорогую и неповоротливую компанию.


Все становится еще медленнее, еще труднее, еще запутаннее. Нужно больше ресурсов, чтобы управлять этой навороченной системой и бороться с побочными эффектами, которые только усугубляют ситуацию. Нас затягивает в гибельный водоворот разработки. Есть и другой путь! Мы можем изначально делать все возможное, чтобы сократить интервал между написанием строки кода и ее полноценным деплойментом в продакшене. Это должно стать нашей навязчивой идеей. Давайте автоматизируем этот интервал, будем отслеживать его, потратим драгоценные ресурсы на его сокращение.


Пока у нас не будет рабочего цикла обратной связи, мы будем снова и снова бороться с симптомами. Чем дольше задержка, тем заметнее симптомы и тем больше времени мы тратим на откачку воды из трюма.


Каким должен быть интервал? В идеале 15 минут. Максимум час. Предсказуемость важна не меньше продолжительности, поэтому человек тут будет только мешать. Если сейчас вам требуется 15 дней, а не минут, не отчаивайтесь. Любые усилия по сокращению интервала обязательно окупятся. Любые!


Вижу, вы сомневаетесь. Моя команда, мол, такое не осилит, здесь вам не Facebook и не Google.


Вы идете сложным путем


Вы, наверное, удивитесь, но непрерывный деплоймент это самый простой способ писать, поставлять и выполнять код в продакшене. Звучит нелогично: делать много маленьких изменений быстро бесконечно проще, чем несколько больших изменений медленно.


Смотрите сами. Какой баг легче найти, понять, воспроизвести и исправить: баг в коде, который вы сами написали буквально только что, или баг в коде, который кто-то другой написал в прошлом году?


Как это вообще можно сравнивать? Пока код еще свеж в памяти, мы четко видим проблему и ее решение.


В процессе написания кода позаботьтесь о своем будущем я, которому через полчаса придется разбираться, получилось задуманное или нет. Итак, вы пишете код, отправляете его в главную ветку, ждете несколько минут и смотрите в продакшен. Все идет как надо? Или тут что-то не то?


В таком ритме разработчики обычно находят больше 80% всех багов сразу пока ничего еще не забылось и знаешь, на что смотреть. Если что-то пошло не так, можно тут же слепить еще один диф.


Команды, которые не практикуют такой рабочий процесс и не привыкли сразу оценивать изменения в продакшене, не находят эти баги. За них это делают клиенты.


Сложно, конечно, просто взять и начать по десять раз в день выпускать обновления для приложения, которое раньше обновлялось ежемесячно. Но вы удивитесь, как просто запустить приложение, в котором изначально предусмотрена непрерывная доставка, и сохранять этот темп, не выбиваясь из 15 минут. Скоро это войдет у вас в привычку и не будет отнимать много сил.


Если наладить CD для существующих приложений слишком сложно, попробуйте создать новые репозитории, сервисы и приложения, в которые уже заложен принцип CD. Любой CD лучше, чем его отсутствие. Если хотя бы часть кода будет деплоиться автоматически, команда освоит много полезных новых навыков. Например, сохранять обратную совместимость между версиями.


Задача номер 1 для технических руководителей в 2021 году


Почему мало кто пытается наладить непрерывную доставку? Для меня это загадка. Ведь у быстрого исполнения и короткого цикла обратной связи очень много преимуществ, о которых все прекрасно знают. Но, кажется, я начинаю понимать.


Создается впечатление, что это не техническая задача, а вопрос персонала, проблема управленцев. Руководители привыкли решать такие проблемы своими инструментами нанимая больше сотрудников.


А высшее руководство и вовсе сложно убедить в пользе такого подхода. Мы же просим их отложить план выпуска в краткосрочном периоде и согласиться на снижение надежности на неопределенное время, чтобы реализовать нечто противоречащее логике, которая подсказывает: чем медленнее, тем безопаснее.


В целом разработчики понимают ценность непрерывной доставки. Многие из них мечтают работать в таком режиме. Если у вас уже налажен CI/CD, это повысит вашу привлекательность как работодателя. Но главное ваша команда сможет эффективнее использовать существующий процесс разработки.


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


Вам бы понравилось, если бы ваши специалисты всегда вспоминали вас как человека, который изменил их мышление и подход к разработке, и были благодарны вам за то, что помогли им выйти на новый уровень? Все зависит от вас.


Не надо быть гением, чтобы раскрыть весь потенциал CI/CD. Честное слово. Надо просто приложить чуть больше усилий к тому, чтобы наладить этот процесс. Хорошие специалисты вырастают в хороших командах.


При использовании непрерывной доставки разработчики несут всю ответственность за свой код в продакшене. Но это не единственное преимущество. У вас появляются полезные привычки писать маленькие дифы, быстро ревьюить код, воплощать свои изначальные намерения, использовать переключатели функций, разделять деплои и релизы и т. д.


Можно целыми днями цепляться к разработчикам и требовать, чтобы они подчищали код и следовали лучшим методам по отдельности, а можно приложить все усилия, чтобы сократить цикл обратной связи, и тогда все встанет на свои места само по себе.


Сложно ли это? Еще как. Надеюсь, мне удалось вас убедить, что оно того стоит. Это изменит вашу жизнь. Это шаг к социотехническим системам будущего. Чем больше команд его сделают, тем лучше. А какой у вас план по CI/CD на 2021 год?


Узнать больше о CI/CD и получить практику создания пайплайнов можно на курсе Слёрма CI/CD на примере Gitlab CI.
Подробнее..

Зачем нужны непрерывная доставка и непрерывное развертывание?

01.06.2021 10:11:39 | Автор: admin

Недавно у нас на работе стихийно возник спор о том, стоит ли вводить непрерывную доставку. Не имея в виду сразу переделывать все наши процессы под непрерывную доставку, я, однако, отстаивал целесообразность такого подхода в общем. К сожалению, после начала спора я за приемлемые 510 минут так и не нашел в интернете подходящего текста, доходчиво объясняющего, зачем нужна непрерывная доставка, чтобы хорошенько подкрепить свою точку зрения. Материалов о том, как наладить непрерывную доставку, очень много, а вот статей (на русском языке) о том, зачем же это нужно, недостает.

Давайте исходить из того, что цель жизни нормального человека это написать побольше интересного кода и закинуть его на прод. С такой точки зрения, думаю, важность непрерывной доставки очевидна. Увы, оказалось, что есть и совершенно другие люди (вы можете узнать их по таким странными выражениям, как качество продукта, ресурсы, скорость исправления ошибок, трудозатраты), которым нормальные ценности чужды. Чтобы легче было достучаться до них и чтобы под рукой всегда была краткая памятка по отстаиванию единственно правильной точки зрения, я и написал этот текст.

Ладно, в некоторых довольно редких, надо сказать, ситуациях организовать непрерывную доставку действительно почти невозможно. Думаю, вы и сами поймете это, когда окажетесь в подобной ситуации. На всякий случай ближе к концу этого материала описаны случаи, в которых выгоды непрерывной доставки явно не окупят расходов на ее организацию.

Определения

В данном материале будем пользоваться следующими тремя определениями М. Фаулера.

Непрерывная интеграция (Continuous Integration) когда продукт регулярно (несколько раз в день) собирается из исходного кода и для него запускается существенная часть автоматических тестов, например все модульные тесты. Если автоматические тесты работают долго, то их можно запускать реже, например раз в сутки. Стандартный подход для организации непрерывной интеграции это запустить TeamCity или Jenkins, которые будут загружать исходный код из системы контроля версий, собирать его и запускать тесты. Другие известные решения: Travis CI, GitLab, Space, GitHub, BitBucket.

Непрерывная доставка (Continuous Delivery) продукт всегда находится в собранном состоянии и готов к передаче в промышленную эксплуатацию, даже с учетом последних изменений, внесённых разработчиками в код. Скорее всего, непрерывная доставка будет реализована на основе тех же технологий, что и непрерывная интеграция. В этой статье на Хабре приводятся еще несколько подходящих открытых инструментов. В другой работе подробно рассматривается разница между реализацией конвейера сборки в облаке и на земле.

Непрерывное развертывание (Continuous Deployment) когда обновления продукта регулярно (например, после каждого изменения, внесенного разработчиками) вводятся в промышленную эксплуатацию, и при этом все процессы предметной области продолжают работать без сбоя. В некотором роде непрерывное развертывание это хорошо автоматизированная и часто выполняющаяся непрерывная доставка.

Чтобы лучше осознать эти понятия, можно посмотреть на картинку, где приведена более-менее универсальная схема современного процесса разработки и доставки программного продукта. В каких-то компаниях используются дополнительные компоненты, а некоторые из указанных не используются.

Мне кажется, что в профессиональной среде чаще пользуются обобщающим все вышеперечисленное термином CI/CD с необходимыми уточнениями, чтобы не задумываться о том, какой из трех терминов включает регулярное автоматическое развертывание продукта в средах для разработки и тестирования.

Две деревни

Чтобы разобраться в произношении названий, предлагаю заинтересованным почитать Ответы Mail.ru, а я далее буду пользоваться латиницей. О чем это я? Конечно, о двух захолустных деревнях Villarriba и Villabajo: обе деревни живут типичным сельским трудом производят программное обеспечение. Жители первой деревни вводят обновления своего продукта в промышленную эксплуатацию каждый день, ну, может быть, кроме пятниц (то есть практикуют непрерывную доставку или развертывание), а жители второй каждые две недели. На их примере предлагаю рассмотреть несколько параметров стандартного процесса изготовления и сопровождения программных продуктов. Это поможет лучше понять, в чем заключаются рассматриваемые подходы.

Скорость устранения ошибок

Жители Villabajo выкатывают изменения раз в две недели. За две недели они делают много новой функциональности, поэтому, когда они ошибаются в одной из новинок, им приходится либо 1) откатывать сразу все изменения и, если надо, выковыривать из версии дефектное изменение, собирать версию заново и ставить ее (впопыхах, естественно), либо 2) ковыряться сразу во всех изменениях, которые они выкатили, пытаться понять, какое из этих изменений привело к ошибкам, и отлаживать это в бою.

Жители Villarriba готовы выкатывать изменения хоть несколько раз в день. Чем чаще они выкатывают обновления, тем меньше изменений у них накапливается. Чем меньше изменений одновременно вводится в промышленную эксплуатацию, тем легче откатить ошибочные изменения или, при возможности, исправить их прямо в работающем продукте, потому что изменения изолируются друг от друга и, значит, меньше друг на друга влияют.

Казалось бы, жители Villarriba ошибаются не реже жителей Villabajo, поэтому количество дефектов в промышленно эксплуатируемом продукте одинаковое! Однако жители Villarriba в среднем исправляют каждый дефект быстрее, чем жители Villabajo, потому что им легче разобраться в причинах ошибки, легче откатить ошибочные изменения и легче накатить исправленную версию.

Но и здесь не все так просто: жители Villabajo не хотят ставить обновления часто не потому, что они такие злые, а потому, что у них плохо автоматизирован процесс публикации своего продукта. В результате они, в отличие от жителей Villarriba, страдают еще и от ошибок в продукте, порожденных неправильной установкой версии. На эту тему есть интересное исследование (стр. 53).

Затраты на установку новой версии

Жители Villabajo выкатывают изменения раз в две недели. В отличие от серьезных b2b-контор из столицы 6-й экономики мира, у жителей Villabajo действительно четко запланирован объем каждого спринта, а еще они научились проверять, все ли нужное вошло в версию (и не попал ли туда мусор), в полуавтоматическом режиме.

Тем не менее каждый житель Villabajo регулярно тратит по несколько часов, чтобы:

1) проверить версию на полноту включенных изменений (да, в полуавтоматическом режиме), включить в версию то, что забыли, и выкинуть то, что попало по ошибке, договориться с остальными жителями деревни и с заказчиком о том, что пришло время ставить обновление;

2) выполнить то, что нужно сделать до установки версии, например, запустить скрипты, отладить и починить их, потому что они не были полностью готовы к запуску в бою;

3) выполнить то, что нужно сделать во время установки версии, например, запустить скрипты, отладить и починить их, потому что они опять не были полностью готовы к запуску в бою;

4) выполнить то, что нужно сделать после установки версии, например, запустить скрипты, отладить и починить их, потому что они в третий раз не были полностью готовы к запуску в бою.

Жители Villarriba готовы выкатывать изменения раз в день. Им приходится почти все время держать продукт готовым к введению в промышленную эксплуатацию с учетом самых последних обновлений. У них нет скриптов для выполнения перед установкой новой версии, во время и после нее. Они ведут разработку таким образом, чтобы можно было вводить новую функциональность постепенно, по одному небольшому контролируемому блоку за версию. Жителям Villabajo нет смысла так делать: у них же версии раз в две недели.

Что в итоге? Жители Villabajo каждые две недели тратят первую половину рабочего дня на то, чтобы установить версию, а вторую половину на просмотр YouTube (в лучшем случае чтение Хабра), потому что после стресса они не могут продуктивно работать (серьезно, см., например, вот эту публикацию, стр. 53). А жители Villarriba работают в обычном режиме, только иногда отвлекаются на то, чтобы откатить последние изменения с боевого сервера.

Рассматриваемый эффект уменьшение затрат на установку версии вполне очевиден и подтверждается реальными случаями. Например, непрерывная доставка помогла одной из команд Hewlett-Packard снизить общие затраты на разработку на 40%.

В одном исследовании (стр. 53) показывается, как непрерывная доставка избавила инженеров компании от необходимости тратить несколько дней (разумеется, в каждой компании это значение свое) на подготовку и установку версии. В этот же эффект, очевидно, включается и отмеченное там же уменьшение затрат на исправление ошибок именно в самом процессе подготовки и установки версии.

В другом исследовании (стр. 67) некоторые из опрошенных компаний в качестве существенного преимущества непрерывных подходов отметили уменьшение количества ручного труда, необходимого для управления всем циклом разработки и доставки.

Включение и отключение новой функциональности

Жители Villabajo выкатывают изменения раз в две недели. Если их заказчик хочет какую-то функциональность, то он вместе с жителями Villabajo согласует дату, когда он увидит эту функциональность, и жители Villabajo готовят ее точно к сроку. Более того, в некотором смысле заказчик не может отказаться от запланированной функциональности: сначала этой функциональности нет на боевом сервере, потом есть день установки версии, а потом эта функциональность отлита в граните.

Чтобы откатить эту функциональность (либо из-за того, что жители Villabajo ошиблись, либо из-за того, что сам заказчик или другие взаимодействующие с продуктом Villabajo системы не готовы), надо установить предыдущую версию, удалить из новой версии оказавшуюся ненужной функциональность и установить эту исправленную новую версию. Особенно жители Villabajo унывают в таких случаях, когда новая версия успела создать данные, которые не могут быть обработаны старой версией: приходится чистить или переформатировать эти данные.

Жители Villarriba готовы выкатывать изменения раз в день. Если их заказчик хочет какую-то функциональность, то, после того как она готова к публикации, он может в любой момент включить ее. Еще жители Villarriba сделали так, чтобы заказчик мог вернуться к предыдущей версии, пока полностью не убедится в том, что его устраивает новая функциональность. Такой подход выглядит наиболее естественным в случае, когда основная ветка исходного кода должна быть готова к публикации в любой момент.

В отличие от жителей Villabajo, жители Villarriba тратятся на поддержание кода в адекватном состоянии и, возможно, на переключатели функциональности, но 1) им не приходится тратиться на откатывание оказавшихся ненужными изменений и на докатывание исправленных версий; 2) их заказчикам не нужно согласовывать миг, в который новая функциональность становится неотъемлемой частью системы.

Заказчики жителей Villarriba довольны. Это и неудивительно: положительное влияние такого подхода на отношения между заказчиком и исполнителем подтверждают научные исследования (стр. 67).

Продолжительность цикла разработка публикация корректировка

Жители Villabajo выкатывают изменения раз в две недели. В большинстве случаев они не имеют возможности проверить, как поведет себя новая функциональность на боевом сервере. В отличие от серьезных b2b-контор из столицы 6-й экономики мира у них есть много сред развертывания, и одна из них по своим параметрам почти совпадает с боевой.

Но все же различия между боевой и почти боевой средой не позволяют жителям Villabajo точно предсказывать последствия публикации новой версии. Можно выкатывать новую функциональность по чуть-чуть, но кто же будет ждать, пока этих двухнедельных чуть-чуть наберется на заказанную функциональность? Тут и заказчик забудет, зачем она ему нужна, и разработчики будут каждые две недели после установки версии заново вспоминать, чем же они таким занимались и что там в готовящейся функциональности надо корректировать.

Жители Villarriba могут позволить себе выдавать новую функциональность небольшими порциями, попадающими в боевую среду каждый день. После публикации версии (а они даже и не особенно следят за этим, для них публикация версии это просто обычный рабочий день) жители Villarriba смотрят, как их небольшие изменения отразились на системе и ее пользователях, и в зависимости от этого корректируют готовящуюся функциональность, по необходимости информируя своего заказчика об изменениях.

Как видно, жители Villarriba наслаждаются коротким циклом разработка публикация корректировка, благодаря чему у них не бывает, как у жителей Villabajo, проблем с отгрузкой буквально не той функциональности, что было нужно.

Пример: положительное влияние быстрой обратной связи отмечено в исследовании (стр. 65); там же сказано, что сами разработчики при этом лучше понимают, что они делают, и больше вовлечены в рассмотрение процессов предметной области. В другом исследовании (стр. 21) указывают на прямую корреляцию между тем, 1) насколько полно компании учитывают обратную связь от клиентов для исправления своих продуктов, 2) насколько просто заинтересованные сотрудники могут увидеть и осознать жизненный цикл продукта, 3) насколько сотрудники удовлетворены работой в компании и 4) насколько сильно сотрудники ассоциируют себя с компанией.

Функциональное и регрессионное тестирование

А это немного особенный пункт, в котором рассматривается основная боль отдела обеспечения качества: функциональное и регрессионное тестирование вручную. Сначала посмотрим, зачем это нужно.

  • Очевидно, во время функционального тестирования проверяется, соответствует ли выполненная работа описанной и согласованной с клиентом постановке. В случае чего вносятся исправления.

  • В это время также может готовиться и приемочный сценарий для клиента.

  • Выявляются ошибки в тех местах, которые не покрыты автоматическими тестами.

  • Во время регрессионного тестирования проверяется функциональность, которая находится сбоку и напрямую не затрагивалась при разработке. Здесь же можно упомянуть и проверку того, как изменения, сделанные в рамках отдельных задач, дружат между собой: не появляется ли при соединении этих изменений неожиданных и нежелательных эффектов.

  • Возможно, требуется и проведение нагрузочного тестирования.

В компаниях, выпускающих новую версию раз в 23 недели, часто практикуется продолжительное ручное тестирование, к началу которого должны быть готовы все задачи, планирующиеся в предстоящую версию. После тестирования и разворачивания новой версии в боевой среде начинается новый цикл проработки технического задания, реализации и тестирования. Таким образом, если разработчики будут вливать готовые изменения в основную кодовую базу слишком часто, у отдела обеспечения качества не будет достаточного временного промежутка, когда набор внесенных изменений достаточно заморожен для того, чтобы можно было провести регрессионное тестирование.

Эту проблему решают переключатели функциональности. С их помощью можно держать внесенные изменения в спячке в тестовой среде (можно и в боевой, просто сейчас речь идет именно о тестовой среде) и включать их разом в тот момент, когда отдел собирается приступить к регрессионному тестированию. Во время тестирования разработчики вносят изменения, касающиеся других задач, например, для следующей версии. Эти изменения по умолчанию также пребывают в спячке и ждут, когда начнется тестирование следующей версии. Понятно, что переключатели функциональности помогают также и в приемочном тестировании.

Если вернуться к сюжету о двух деревнях, то скажем так: жители Villabajo заранее объединяют в одной тестовой среде все те и только те изменения, которые нужно протестировать за точно заданный временной промежуток, а жители Villarriba могут гибко менять объем тестируемых изменений и время проведения тестирования. Такое удобство досталось жителям Villarriba не бесплатно (они должны были по крайней мере внедрить переключатели функциональности), но ясно, что у нас тут вряд ли стоит искать преимущества, дающиеся без затрат.

Обобщение

Теперь давайте немного структурируем рассмотренную информацию. В следующем далее списке первыми идут те преимущества, которые дает непрерывная доставка, затем те, которые появляются и при внедрении непрерывной доставки, и при внедрении непрерывного развертывания (каждый раз новые), затем те, которые дает только непрерывное развертывание. То же и с затратами. В конце этого раздела основные идеи подытожены в виде технологического дерева компании, производящей программное обеспечение.

Преимущества

  • Установка обновлений почти полностью автоматизирована, поэтому перестают появляться ошибки из-за неправильной установки обновления.

  • Установка обновлений почти полностью автоматизирована, поэтому вы ничего не тратите на установку новой версии.

  • Изменения быстрее интегрируются и проверяются на фоне в общем стабильной системы. Заинтересованные сотрудники быстрее получают обратную связь и лучше понимают свою роль и задачи в контексте продукта в целом (как системы). Поэтому становится меньше проблем на стыке зон ответственности; исчезают симптомы вида об этом должен позаботиться кто-то другой. Качество рождается на местах, а не обеспечивается отдельным процессом (подробнее об этом здесь, на стр. 22).

  • Установка обновлений почти полностью автоматизирована, поэтому сотрудники не выгорают во время установки новой версии.

  • Установка обновлений почти полностью автоматизирована, а изменения устанавливаются небольшими порциями, поэтому вы быстрее устраняете ошибки и восстанавливаете штатную работу продукта.

  • Установка обновлений почти полностью автоматизирована, а изменения устанавливаются небольшими порциями, поэтому ошибки, которые вы допускаете, реже становятся катастрофическими.

  • Установка обновлений почти полностью автоматизирована (как и весь цикл разработки), поэтому от идеи до появления ее реализации в промышленной эксплуатации проходит очень мало времени. Благодаря этому, заказчик и исполнитель могут легко проверять свои гипотезы о новой функциональности на практике, быстрее реализовывать полезную и удалять невостребованную функциональность.

  • Обновления устанавливаются автоматически и непрерывно, поэтому вы меньше тратите на согласование установки новой версии и момента включения новой функциональности. При желании заказчик может делать это самостоятельно.

  • Обновления устанавливаются автоматически, непрерывно и небольшими порциями, поэтому вы с заказчиком начинаете лучше понимать друг друга; исчезают проблемы вида сделали не то, что хотели.

  • Обновления устанавливаются автоматически и непрерывно. Заинтересованные сотрудники быстрее получают обратную связь и лучше понимают свою роль и задачи в контексте находящегося в промышленной эксплуатации продукта. Поэтому сотрудники больше вовлечены в предприятие (и в процесс создания продукта, и в его предметную область). Они сильнее ассоциируют себя как с продуктом, так и вообще с компанией.

Затраты

  • Чтобы наладить полуавтоматическую публикацию, вы должны купить и настроить соответствующее программное обеспечение. Если у вас уже есть непрерывная интеграция, то вы наверняка сможете переиспользовать ее инфраструктуру.

  • Чтобы наладить полуавтоматическую публикацию, вы должны провести организационные изменения. Например, если единственная команда владеет доступом к нужному серверу, нужно сделать так, чтобы к серверу имели доступ все заинтересованные сотрудники. Подробности в этой статье (стр. 53).

  • Чтобы держать основную ветку исходного кода в собранном состоянии, вы не должны включать в нее потенциально разрушающие изменения. Например, если для работы новой функциональности требуется хранить данные в новом формате, то ваш продукт должен продолжительное время иметь возможность работать с данными как в старом, так и в новом формате.

  • Если вы или заказчик вручную проводите регрессионное тестирование совокупности изменений в целом, то вам нужно будет настроить переключатели функциональности. Благодаря этому, вы сможете держать в боевой среде множество изменений в скрытом виде и включать их только после того, как проверите все в тестовой среде.

  • Чтобы вводить рассмотренные изменения, вам надо будет сломать консервативную корпоративную культуру у себя и у заказчика. Это, очевидно, означает, что между вами и заказчиком должно установиться доверие достаточно высокого уровня.

  • Чтобы иметь возможность публиковать обновления продукта несколько раз в день, вы должны поменять процессы таким образом, чтобы 1) установка новой версии не требовала отдельного согласования с заказчиком или чтобы 2) заказчик мог сам управлять включением и выключением новой функциональности.

Препятствия

Есть случаи, когда внедрить непрерывные доставку или развертывание почти невозможно.

  • Некоторые предметные области плохо поддерживают непрерывное развертывание (см. стр. 69 этого исследования), например, телекоммуникационное программное обеспечение и встраиваемое медицинское оборудование.

  • Еще проблемы могут возникнуть, когда речь идет о продукте большого объема, который не покрыт автоматическими тестами или который тяжело интегрируется с остальными системами.

Как у нас?

В CUSTIS есть разные команды, так что и ситуация во всех них немного отличается. Есть команды, где налажено непрерывное развертывание, есть команды, где обновления ставятся вручную раз в несколько недель. Как было отмечено в самом начале, в моей команде как раз пока нет сильной спешки с переходом на непрерывную доставку.

Заключение

Один из проповедников непрерывной доставки отмечал в 2014 году, что многие руководители не убеждены в преимуществах такого подхода. Надеюсь, что сегодня уменьшилось хотя бы относительное число таких руководителей и что данный материал поможет аргументированно и конструктивно убедить оставшихся. Кроме этого, не забывайте и о более общих методах работы с людьми.

Если будете вести свою зловещую пропаганду, можете ссылаться на отчет State of DevOps (стр. 13). В отчете утверждается, что в 2016 году успешные компании публиковали обновления намного чаще, чем остальные (экстремальные значения показали Amazon и Netflix несколько тысяч раз в день), и у них изменения быстрее проваливались по конвейеру от включения в исходный код до введения в промышленную эксплуатацию. Но только в таком случае лучше сразу сознаться, что прямой причинно-следственной связи здесь установить нельзя. Вероятнее всего, что непрерывное развертывание это только одно из свойств успешной компании.

Источники

  1. 1cloud.ru. Справочная: что такое Continuous Delivery (2019)

  2. Martin Fowler. ContinuousDelivery (2013)

  3. Jez Humble. The Case for Continuous Delivery (2014)

  4. B. Alanna, N. Forsgren, J. Humble et al. State of DevOps Report (2016)

  5. Chen, Lianping. Continuous Delivery: Huge Benefits, but Challenges Too (2015)

  6. Leppnen, M.; Mkinen, S.; Pagels, M.; Eloranta, V. P.; Itkonen, J.; Mntyl, M. V.; Mnnist, T. The Highways and Country Roads to Continuous Deployment (2015)

  7. A. Hkli, D. Taibi, K. Syst. Towards Cloud Native Continuous Delivery: An Industrial Experience Report (2018)

  8. Г. А. Минашин. Переключатели функциональности (feature toggles): виды, преимущества и работа с ними в .NET (2019)

  9. Манифест просвещенного программиста

  10. Как продать технические задачи бизнесу (2021)

  11. Где находятся Вилларибо и Виллабаджо?

Подробнее..

Перевод Типовые ситуации при непрерывной интеграции

10.08.2020 22:11:31 | Автор: admin

Вы изучили команды Git но хотите представлять, как непрерывная интеграция (Continuous Integration, CI) происходит в реальности? Или может вы хотите оптимизировать свои ежедневные действия? Этот курс даст вам практические навыки непрерывной интеграции с использованием репозитория на GitHub. Данный курс не задуман как некий визард, который можно просто прокликать, напротив, вы будете совершать те же действия, что люди на самом деле делают на работе, тем же способом, которым они это делают. Я буду объяснять теорию по мере прохождения вами имеющих к ней отношение шагов.


Что мы будем делать?


По мере продвижения, мы будем постепенно создавать список типовых шагов CI, что есть отличный способ запомнить этот список. Другими словами, мы будем создавать список действий, которые разработчики совершают, осуществляя непрерывную интеграцию, осуществляя непрерывную интеграцию. Также мы будем использовать простой набор тестов чтобы приблизить наш процесс CI к реальному.


Этот GIF схематично показывает коммиты в вашем репозитории по мере продвижения по курсу. Как видите, тут нет ничего сложного и только самое необходимое.


Шаги непрерывной интеграции


Вы пройдёте такие стандартные для CI сценарии:


  • Работа над фичей;
  • Применение автотестов для обеспечения качества;
  • Реализация приоритетной задачи;
  • Разрешение конфликта при слиянии ветвей (merge conflict);
  • Возникновение ошибки в продуктивной среде.

Что вы узнаете?


Вы сможете ответить на такие вопросы:


  • Что такое непрерывная интеграция(CI)?
  • Какие типы автотестов используются при CI, и в ответ на какие действия они запускаются?
  • Что такое pull request и когда они нужны?
  • Что такое разработка через тестирование (Test Driven Development, TDD) и как она соотносится с CI?
  • Осуществлять слияние(merge) или применять изменения поверх(rebase)?
  • Откатывать или чинить в следующей версии?

Я вначале везде переводил штуки типа "pull request", но в результате решил вернуть фразы на английском в некоторых местах чтобы снизить градус безумия в тексте. Я порой буду использовать "программистский суржик" вроде чудного глагола "закоммитить" там, где люди на самом деле на работе его используют.

Что такое непрерывная интеграция?


Непрерывная интеграция, или CI, это техническая практика, которая заключается в том, что каждый член команды интегрирует свой код в общий репозиторий хотя бы раз в день, причём результирующий код при этом должен хотя бы собираться без ошибок.


Существуют разночтения по поводу этого термина


Предметом спора является частота интеграции. Некоторые утверждают, что объединять код лишь раз в день недостаточно на самом деле интегрироваться непрерывно. В пример приводится команда, где все берут свежий код утром и интегрируются один раз вечером. Хотя это и разумное возражение, всё же в целом считается, что вариант определения "раз в день" достаточно практичен, конкретен, и подходит для команд разных размеров.


Другое возражение состоит в том, что C++ давно не единственный язык, используемый при разработке, и простое требование сборки без ошибок, как способ валидации, слабовато. Некий набор тестов (например, модульных(unit), выполняемых локально) также должен завершиться успешно. В данный момент, сообщество тяготеет к тому, чтобы такое требование было обязательным, и, в будущем "сборка + модульные тесты", видимо, станет общепринятым, если это уже не произошло.


Непрерывная интеграция отличается от непрерывной поставки (Continuous Delivery, CD) тем, что не требует релиз-кандидата после каждого цикла интеграции.


Список шагов, который мы будем использовать на протяжении курса


  1. Pull in the latest code. Create a branch from master. Start working.
  2. Create commits on your new branch. Build and test locally. Pass? Go to the next step. Fail? Fix errors or tests and try again.
  3. Push to your remote repository or remote branch.
  4. Create a pull request. Discuss the changes, add more commits as discussion continues. Make tests pass on the feature branch.
  5. Merge/rebase commits from master. Make tests pass on the merge result.
  6. Deploy from the feature branch to production.
  7. If everything is good in production for some period of time, merge changes to master.

Процесс непрерывной интеграции (вариант)


Подготовка


Убедитесь в наличии нужного ПО


Чтобы пройти этот курс вам понадобится Node.js и Git-клиент.


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

Убедитесь, что у вас установлен Git-клиент, поддерживающий командную строку


Если у вас пока не установлен Git-клиент, поддерживающий командную строку, можете найти инструкции по установке здесь.


Подготовьте репозиторий


Вам потребуется создать личную копию(fork) репозитория-шаблона с кодом для курса на GitHub. Давайте договоримся называть эту личную копию репозиторием курса.


Сделали? Если вы не меняли настройки по умолчанию, ваш репозиторий курса скорее всего называется continuous-integration-team-scenarios-students, он находится в вашем аккаунте на GitHub, и URL выглядит так


https://github.com/<ваше имя ползователя на GitHub>/continuous-integration-team-scenarios-students

Я буду называть этот адрес просто <URL репозитория>.


Угловые скобки как <тут> будут означать, что вы должны заменить такое выражение на соответствующее значение.

Убедитесь что GitHub actions включены для данного репозитория курса. Если они не будут включены, пожалуйста, включите их, нажав на большую кнопку в середине страницы, на которую вы можете попасть, кликнув Actions в интерфейсе GitHub.


У вас не получится пройти курс, следую моим инструкциям, если GitHub Actions не будут включены.

Включите Github Actions для репозитория курса


Вы всегда можете использовать способность GitHub отображать Markdown чтобы увидеть текущее состояние списка, который мы сочиняем, тут


https://github.com/<your GitHub user name>/continuous-integration-team-scenarios-students/blob/master/ci.md

Про ответы


Хотя лучший способ завершить этот курс это делать всё своими руками, у вас могут и возникнуть сложности.


Если вы чувствуете, что не понимаете что делать и не можете продолжать, можете подсмотреть в ветку solution, которая есть в вашем стартовом репозитории.
Пожалуйста, не осуществляйте слияние solution в master во время курса. Вы можете использовать эту ветку чтобы разобраться что делать, или чтобы сравнивать свой код с авторским, используя все возможности, которые даёт нам Git. Если вы совсем потерялись, можете полностью заменить свою ветку master на ветку solution и затем сбросить свою рабочую директорию до того шага курса, который вам нужен.


Используйте это только если вам это действительно нужно


Закоммитьте(commit) свой код


git add .git commit -m "Backing up my work"

Эти команды


  • переименовывают master в master-backup;
  • переименовывают solution в master;
  • переключают(checkout) на новую ветку master и переписывают содержимое рабочей директории;
  • создают ветку "solution" из "master" (которая раньше была "solution") на случай если вам в будущем понадобится ветка "solution".

git branch -m master master-backupgit branch -m solution mastergit checkout master -fgit branch solution

После этих действий вы можете использовать git log master чтобы выяснить, какой коммит вам нужен.
Вы можете сбросить свою рабочую директорию на этот коммит так:


git reset --hard <the SHA you need>

Если вы довольны результатом, в какой-то момент вам понадобится опубликовать вашу версий репозитория в удалённый репозиторий(remote). Не забудьте явно указать удалённую ветку когда будете это делать.


git push --force origin master

Пожалуйста обратите внимание, что мы используем git push --force. Вряд ли вы часто захотите так поступать, но у нас тут крайне специфичный сценарий с одним пользователем репозитория, который вдобавок, понимает, что он делает.


Starting working


Непрерывная интеграция: Начинаем работать


Начнем составлять наш список шагов CI. Обычно вы начинаете этот шаг с извлечения последней версии кода из удаленного репозитория, но у нас еще нет локального репозитория, поэтому вместо этого мы клонируем его из удалённого.


Задание: обновите локальный репозиторий, создайте ветку из master, начните работать


  1. Клонируйте репозиторий курса из <URL репозитория>.
  2. Запустите npm install в каталоге репозитория курса; он нужен нам для установки Jest, который мы используем для запуска тестов.
  3. Создайте ветку и назовите ее feature. Переключитесь на эту ветку.
  4. Добавьте тестовый код в ci.test.js между комментариями с просьбой сделать это.


    it('1. pull latest code', () => {  expect(/.*pull.*/ig.test(fileContents)).toBe(true);});it('2. add commits', () => {  expect(/.*commit.*/ig.test(fileContents)).toBe(true);});it('3. push to the remote branch with the same name', () => {  expect(/.*push.*/ig.test(fileContents)).toBe(true);});it('4. create a pull request and continue working', () => {  expect(/.*pull\s+request.*/ig.test(fileContents)).toBe(true);});
    

  5. Добавьте текст с первыми 4 шагами в файл ci.md.
    1. Pull in the latest code. Create a branch from `master`. Start working.    2. Create commits on your new branch. Build and test locally.  Pass? Go to the next step. Fail? Fix errors or tests and try again.  3. Push to your remote repository or remote branch.  4. Create a pull request. Discuss the changes, add more commits  as discussion continues. Make tests pass on the feature branch.  
    

    Команды


# Клонируйте репозиторий курсаgit clone <repository URL>cd <repository name># Выполните npm install в каталоге репозитория курса; он установит Jest, который мы используем для запуска тестов.npm install# Создайте ветку и назовите ее feature. Переключитесь на эту в ветку.git checkout -b feature# Отредактируйте ci.test.js как описано выше.# Отредактируйте ci.md как описано выше

Создавайте коммиты в новой ветке, осуществляйте сборку и тестирование локально


Мы собираемся настроить тесты чтобы они запускались перед коммитом, а затем закоммитить код.


Типовые сценарии, когда тесты запускаются автоматически


  • Локально:
    • Постоянно или в ответ на соответствующие изменения кода;
    • При сохранении (для интерпретируемых или JIT-компилируемых языков);
    • При сборке (когда требуется компиляция);
    • При коммите;
    • При публикации в общий репозиторий.

  • На сервере сборки(build server) или в среде сборки:
    • Когда код публикуется в персональную ветку / репозиторий.
    • Тестируется код в этой ветке.
    • Тестируется потенциальный результат слияния (обычно с master).
    • В качестве этапа непрерывной интеграции/конвейера непрерывной поставки

Как правило, чем быстрее выполняется набор тестов, тем чаще вы можете позволить себе его запускать. Типичное распределение по этапам может выглядеть так.


  • Быстрые модульные тесты при сборке, в конвейере CI
  • Медленные модульные тесты, быстрые компонентные и интеграционные тесты при коммите, в конвейере CI
  • Медленные компонентные и интеграционные тесты в конвейере CI
  • Тестирование безопасности, нагрузочное и другие длительные или дорогостоящие тесты в конвейерах CI/CD, но только в определенных режимах/этапах/конвейерах сборки, например, при подготовке релиз-кандидата или при запуске вручную.

Задание


Я предлагаю сначала запустить тесты вручную, используя команду npm test. После этого давайте добавим git hook для запуска наших тестов при коммите. Есть одна загвоздка: Git hooks не считаются частью репозитория и поэтому не могут быть клонированы из GitHub вместе с остальными материалами курса. Чтобы установить hook, вам нужно запустить install_hook.sh или скопировать файл repo/hooks/pre-commit в локальный каталог .git/hooks/.
При коммите вы увидите, что выполняются тесты, и они проверяют, присутствуют ли в списке определенные ключевые слова.


  1. Запустите тесты вручную, выполнив команду npm test в папке репозитория вашего курса. Убедитесь, что тесты были выполнены.
  2. Установите hook на коммит (pre-commit hook), запустив install_hook.sh.
  3. Закоммитьте изменения в локальный репозиторий.
  4. Убедитесь, что тесты выполняются перед коммитом.

Ваш репозиторий должен выглядеть так после выполнения этих действий.
Непрерывная интеграция: первый коммит


Команды


# Установите pre-commit hook выполнив install_hook.sh.  # Закоммитьте изменения в локальный репозиторий. Используйте "Add first CI steps" в качестве сообщения при коммите.git add ci.md ci.test.jsgit commit -m "Add first CI steps"# Убедитесь, что тесты запускаются перед коммитом.  

Опубликуйте код в удаленный репозиторий или удаленную ветку


Завершив работать локально, разработчики обычно делают свой код общедоступным, чтобы его можно было в конечном итоге интегрировать с общим. С помощью GitHub это обычно достигается публикацией работы либо в персональной копии репозитория(personal fork, форк), либо в личной ветке.


  • При использовании форков разработчик клонирует удаленный общий репозиторий, создавая его личную удаленную копию, также известную как форк. После этого он клонирует этот личной репозиторий, чтобы работать с ним локально. Когда работа завершена и коммиты созданы, он помещает их в свой форк, где они доступны другим и могут быть интегрированы в общий репозиторий. Этот подход обычно используется в проектах с открытым исходным кодом на GitHub. Он также используется в моем расширенном курсе [Team Work and CI with Git] (http://devops.redpill.solutions/).
  • Другой подход состоит в том, чтобы использовать только один удалённый репозиторий и считать только ветку master совместно используемого репозитория "защищенной". В этом сценарии отдельные разработчики публикуют свой код в ветки удаленного репозитория, чтобы другие могли этот код посмотреть, если все в порядке, объединить с master общего репозитория.

В этом конкретном курсе мы будем использовать рабочий процесс, использующий ветки.


Давайте опубликуем наш код.


Задание


  • Опубликуйте изменения в удаленную ветку с тем же именем, что и ваша рабочая ветка

Команды


git push --set-upstream origin feature

Создайте pull request


Создайте pull request с названием Steps review. Установите feature как "head branch" и master как "base branch".


Убедитесь, что вы установили master в своем форке репозитория в качестве "base branch", я не буду отвечать на запросы на изменения в репозиторий с материалами курса.

На сленге GitHub "base branch" это ветка, на которой вы основываете свою работу, а "head branch" это ветка, содержащая предлагаемые изменения.


Обсудите изменения, добавьте новые коммиты по мере продолжения обсуждения


Pull request(PR)


Pull request(PR) это способ обсудить и задокументировать код, а также провести проверку(code review) кода. Pull request названы в честь общего способа интеграции отдельных изменений в общий код. Обычно человек клонирует удаленный официальный репозиторий проекта и работает над кодом локально. После этого он помещает код в свой личный удаленный репозиторий и просит ответственных за официальный репозиторий забрать(pull) его код в свои локальные репозитории, где они просматривают и, возможно, интегрируют(merge) его. Это понятие известно и под другими названиями, например, merge request.


На самом деле вам не обязательно использовать функцию pull request GitHub или аналогичных платформ. Команды разработчиков могут использовать другие способы связи, включая личное общение, голосовые звонки или электронную почту, но есть всё же ряд причин использовать такие pull requests в стиле обсуждения на форуме. Вот некоторые из них:


  • организованные обсуждения, связанные с конкретными изменениями в коде;
  • как место для просмотра отзывов о незавершенной работе как от автотестов, так и от коллег;
  • формализация проверок кода;
  • чтобы позже можно было выяснить причины и соображения, стоящие за тем или иным фрагментом кода.

Обычно вы создаете pull request когда вам нужно что-то обсудить или получить обратную связь. Например, если вы работаете над функцией, которая может быть реализована несколькими способами, вы можете создать запрос на внесение изменений еще до написания первой строки кода, чтобы поделиться своими идеями и обсудить свои планы с соавторами. Если работа более проста, pull request открывается, когда что-то уже сделано, зафиксировано и может быть обсуждено. В некоторых сценариях вы можете открыть PR только из соображений контроля за качеством: чтобы запустить автоматические тесты или инициировать проверку кода. Что бы вы ни решили, не забывайте @упоминать людей, одобрение которых требуется в вашем pull request'е.


Обычно при создании PR вы делаете следующее.


  • Указываете, что вы предлагаете изменить и где.
  • Пишете описание, объясняющее цель изменений. Вы можете захотеть:
    • добавить что-нибудь важное, что не является очевидным из кода, или что-нибудь полезное для понимания контекста, например соответствующие #баги и номера коммитов;
    • @упомянуть всех, с кем хотите начать работать вместе, или вы можете @упомянуть их в комментариях позже;
    • попросить коллег помочь с чем-то или проверить что-либо конкретное.

После того как вы откроете PR выполняются тесты, настроенные для запуска в таких случаях. В нашем случае это будет тот же набор тестов, который мы запускали локально, но в реальном проекте могут быть дополнительные тесты и проверки.


Пожалуйста, подождите пока завершатся тесты. Вы можете увидеть статус тестов в нижней части обсуждения PR в интерфейсе GitHub. Продолжайте когда тесты будут завершены.


Добавьте замечание о произвольности списка шагов CI


Список, использованный в этом курсе, является произвольным и субъективным, мы должны добавить примечание об этом.


Задание: создание pull request для данного примечения


  1. Переключитесь на ветку master.
  2. Создайте ветку с именем bugfix.
  3. Добавьте текст примечания в конец файла ci.md.
    > **GitHub flow** is sometimes used as a nickname to refer to a flavor of trunk-based development  when code is deployed straight from feature branches. This list is just an interpretation  that I use in my [DevOps courses](http://personeltest.ru/away/redpill.solutions).  The official tutorial is [here](http://personeltest.ru/aways/guides.github.com/introduction/flow/).
    
  4. Закоммитьте изменения.
  5. Опубликуйте ветку bugfix в удалённый репозиторий.
  6. Создайте pull request с именем Adding a remark с головной веткой bugfix и базовой веткойmaster.

Убедитесь, что вы установили master в своем форке репозитория в качестве "base branch", я не буду отвечать на запросы на изменения в репозиторий с материалами курса.

Вот как должен выглядеть ваш репозиторий.
Непрерывная интеграция: исправление


Команды


# Переключитесь на ветку master. Создайте ветку bugfix.git checkout master# Создайте ветку bugfix-remark.git checkout -b bugfix# Добавьте текст примечания внизу ci.md.# Закоммитьте измененияgit add ci.mdgit commit -m "Add a remark about the list being opinionated"# Опубликуйте ветку bugfix в удалённый репозиторий.git push --set-upstream origin bugfix# Создайте pull request при помощи интерфейса GitHub как описано выше

Утвердите pull request "Adding a remark"


Задание


  1. Создайте pull request.
  2. Нажмите "Merge pull request".
  3. Нажмите "Confirm merge".
  4. Нажмите "Delete branch", она нам больше не нужна.

Это диаграмма коммитов после слияния.
Непрерывная интеграция: объединение исправления с мастером


Продолжайте работать и добавлять тесты


Совместная работа над pull request часто приводит к необходимости дополнительной работы. Обычно это результат проверки кода или обсуждения, но в нашем курсе мы собираемся смоделировать это, добавив новые элементы в наш список шагов CI.


При непрерывной интеграции обычно применяется некоторое тестовое покрытие. Требования к покрытию тестами различаются и обычно находятся в документе с названием вроде "руководство для авторов"(contribution guidelines). Мы поступим просто и добавим по тесту для каждой строки в нашем контрольном списке.


При выполнении заданий сначала попробуйте закоммитить тесты. Если вы правильно установили pre-commit hook ранее, только что добавленный тест будет запущен, не будет пройден, и ничего не будет закоммичено. Обратите внимание: так мы узнаем, что наши тесты действительно что-то проверяют. Любопытно, что если бы мы начали с кода до тестов, прохождение тестов могло означать либо то, что код работает так, как ожидалось, либо что тесты на самом деле ничего не проверяют. Кроме того, если бы мы не написали тесты в первую очередь, мы могли бы вовсе их забыть, поскольку ничто не напоминало бы нам об этом.


Разработка через тестирование (TDD)


TDD рекомендует писать тесты перед кодом. Обычный процесс работы с использованием TDD выглядит так.


  1. Добавьте тест.
  2. Запустите все тесты и убедитесь, что новый тест не проходит успешно.
  3. Напишите код.
  4. Запустите тесты, убедитесь, что все тесты проходят успешно.
  5. Проведите рефакторинг кода.
  6. Повторите.

Поскольку результаты работы тестов, которые не были пройдены успешно, обычно отображается красным, а выполненные успешно зеленым, цикл также известен как "красный-зеленый-рефакторинг"(red-green-refactor).


Задание


Сначала попробуйте закоммитить тесты и дать им завершиться неуспешно, затем добавьте и закоммитьте сам текст списка шагов CI. Вы увидите, что тесты проходят ("зеленые").
Затем опубликуйте новый код в удалённый репозиторий и посмотрите, как выполняются тесты в интерфейсе GitHub в нижней части обсуждения запроса на внесение изменений, и обновляется статус PR.


  1. Переключитесь на ветку feature.
  2. Добавьте эти тесты в ci.test.js после последнего вызова it (...);.


    it('5. Merge/rebase commits from master. Make tests pass on the merge result.', () => {  expect(/.*merge.*commits.*tests\s+pass.*/ig.test(fileContents)).toBe(true);});it('6. Deploy from the feature branch to production.', () => {  expect(/.*Deploy.*to\s+production.*/ig.test(fileContents)).toBe(true);});it('7. If everything is good in production for some period of time, merge changes to master.', () => {  expect(/.*merge.*to\s+master.*/ig.test(fileContents)).toBe(true);});
    

  3. Попробуйте закоммитить тесты. Если pre-commit hook установлен, попытка коммита завершится ошибкой.
  4. После добавьте этот текст в ci.md.
    5. Merge/rebase commits from master. Make tests pass on the merge result.  6. Deploy from the feature branch with a sneaky bug to production.7. If everything is good in production for some period of time, merge changes to master. 
    
  5. Внесите и закоммитьте изменения локально.
  6. Опубликуйте изменения в ветку feature.

Теперь у вас должно получиться что-то вроде этого
Непрерывная интеграция: продолжаем работу


Команды


# Переключительна ветку featuregit checkout feature# Добавить тесты в ci.test.js как описано выше# Добавьте в индекс ci.test.js чтобы позже закоммититьgit add ci.test.js# Попытайтесь закоммитить тесты. Если pre-commit hook установлены, коммит не произойдёт.git commit# Теперь добавьте текст в ci.md как описано выше# Внесите изменения и закоммитьте ихgit add ci.mdgit commit -m "Add the remaining CI steps"# Опубликуйте изменения в ветку featuregit push

Конфликт слияния


Перейдите к запросу на внесение изменений Steps review.


Несмотря на то, что мы не сделали ничего плохого, и тесты для нашего кода прошли успешно, мы все еще не можем осуществить слияние ветки feature и master. Это потому, что другая ветка bugfix была объединена с master пока мы работали над этим PR.
Это создает ситуацию, когда удаленная ветка master имеет более новую версию, чем та, на которой мы основывали ветку feature. Из-за этого мы не можем просто перемотать HEAD master до конца ветки feature. В этой ситуации нам нужно либо осуществить слияние(merge), либо применить коммиты feature поверх(rebase) master. GitHub на самом деле может выполнять автоматическое слияние, если нет конфликтов. Увы, в нашей ситуации обе ветки имеют конкурирующие изменения в файле ci.md. Эта ситуация известна как конфликт при слиянии(merge conflict), и нам нужно разрешить ее вручную.


Merge или rebase


Merge


  • Создает дополнительный коммит слияния(merge commit) и сохраняет историю работы.
    • Сохраняет исходные коммиты веток с исходными отметками времени и авторами.
    • Сохраняет SHA коммитов и ссылки на них в обсуждениях запросов на изменения.
  • Требует однократного разрешения конфликтов.
  • Делает историю нелинейной.
    • Историю может быть трудно читать из-за большого количества веток (напоминает кабель IDE).
    • Усложняет автоматическую отладку, например, делает git bisect менее полезным он только найдет коммит слияния.

Rebase


  • Воспроизводит коммиты из текущей ветки поверх базовой один за другим.
    • Формируются новые коммиты с новыми SHA, в результате чего коммиты в GitHub соотносятся с исходными pull requests, но не с соответствующими комментариями.
    • Коммиты могут быть рекомбинированы и изменены в процессе или даже объединены в один.
  • Может потребоваться разрешить несколько конфликтов.
  • Позволяет поддерживать линейную историю.
    • Историю может быть проще читать, если только она не является слишком длинной без разумных на то причин.
    • Автоматическая отладка и устранение неполадок несколько проще: делает возможным git bisect, может сделать автоматические откаты более чёткими и предсказуемыми.
  • Требуется публикации ветви с перенесёнными коммитами с флагом --force при использовании с запросами на внесение изменений.

Обычно команды соглашаются всегда использовать одну и ту же стратегию, когда им нужно объединять изменения. Это может быть "чистое" слияние или "чистый" применение коммитов поверх или что-то промежуточное, например, выполнение применение коммитов поверх интерактивном режиме(git rebase -i) локально для веток, не опубликованных в общем репозитории, но слияние(merge) для "общедоступных" веток.


Здесь мы будем использовать слияние.


Задание


  1. Убедитесь, что код в локальной ветке master обновлён из удалённого репозитория.
  2. Переключитесь на ветку feature.
  3. Инициируйте слияние с веткой master. Будет сообщено о конфликте слияния, связанном с конкурирующими изменениями в ci.md.
  4. Разрешите конфликт так, чтобы в тексте остался и наш список шагов CI, и замечание о нем.
  5. Опубликуйте коммит слияния в удаленную ветку feature.
  6. Проверьте статус pull request'а в пользовательском интерфейсе GitHub, дождитесь пока слияние не будет разрешено.

Команды


# Убедитесь, что код в локальное ветке `master` обновлён из удалённого репозитория.git checkout mastergit pull# Переключитесь на ветку featuregit checkout feature# Инициируйте слияние с веткой master git merge master# A merge conflict related to concurrent changes to ci.md will be reported# => Auto-merging ci.md#    CONFLICT (content): Merge conflict in ci.md#    Automatic merge failed; fix conflicts and then commit the result.# Разрешите конфликт так, чтобы и наш список шагов CI, и замечание о нем остались в тексте.# отредактируйте ci.md чтоб он не содержал маркеров конфликта слиянияgit add ci.mdgit merge --continue# при коммите можете оставить сообщение по умолчанию# Опубликуйте коммит слияния в удаленную ветку feature.git push# Проверьте статус запроса на изменения в пользовательском интерфейсе GitHub, дождитесь пока слияние не будет разрешено.

Отличная работа!


Вы закончили работу со списком, и теперь вам нужно утвердить pull request в master.


Задание: Утвердите pull request "Steps review"


  1. Откройте pull request.
  2. Нажмите "Merge pull request".
  3. Нажмите "Confirm merge".
  4. Нажмите "Delete branch", так как она нам больше не нужна.

Это ваш репозиторий в данный момент
Непрерывная интеграция: интеграция фичи


Ошибка на продуктиве


Говорят, что тестирование можно использовать, чтобы показать наличие ошибок, но никогда чтобы показать их отсутствие. Несмотря на то, что у нас были тесты, и они не показали нам никаких ошибок, коварная ошибка прокралась в продакшн.


В подобном сценарии нам нужно позаботиться о:


  • том, что развёрнуто на продуктиве;
  • коде в ветке master с ошибкой, с которого разработчики могут начинать новую работу.

Откатывать или исправлять в следующей версии?


"Откатывание"(rolling back) это развертывание заведомо исправной более ранней версии в продуктивную среду и отмена(revert) коммитов, содержащих ошибку. "Исправление в следующей версии"(fixing forward) это добавление исправления в master и развертывание новой версии как можно скорее. Поскольку API и схемы баз данных меняются по мере развертывания кода в производственной среде, при непрерывной поставке и наличии хорошего покрытия тестами, откатывание, как правило, намного сложнее и рискованнее, чем исправление в следующей версии.


Поскольку откатывание не несет в нашем случае никакого риска, мы пойдём этим путём, ведь это позволяет нам


  • исправить ошибку на продуктиве как можно скорее;
  • сделать код в master сразу пригодным для начала новой работы.

Задание


  1. Переключитесь на ветку master локально.
  2. Обновите локальный репозиторий из удалённого репозитория.
  3. Отмените коммит слияния PR Steps review в master.
  4. Опубликовать изменения в удалённый репозиторий.

Это история репозитория с отмененным коммитом слияния
Непрерывная интеграция: отмена слияния


Команды


# Переключитесь на ветку master.git checkout master# Обновите локальный репозиторий из удалённого репозитория.git pull# Отмените коммит слияния PR Steps review в master.# Мы отменяем коммит слияния, поэтому нам нужно выбрать ветку истории, которую мы захотим оставитьgit show HEAD# предположим, что коммит, который был последним в ветке master до слияния, был отображён предыдущей командой первымgit revert HEAD -m 1# можете не менять сообщения коммитов# Опубликуйте изменения в удалённый репозиторийgit push

Самопроверка


Убедитесь, что ci.md больше не содержит текста "sneaky bug" после отмены коммита слияния.


Исправить список шагов CI и вернуть его в master


Мы полностью отменили коммит слияния ветки feature. Хорошая новость в том, что теперь у нас нет ошибки в master. Плохая новость в том, что исчез и наш драгоценный список шагов непрерывной интеграции. Итак, в идеале, нам нужно применить исправление к коммитам из feature и вернуть их в master вместе с исправлением.


Мы можем подойти к задаче по-разному:


  • отменить(revert) коммит, которая отменяет слияние feature с master;
  • перенести коммиты из бывшей feature.

Разные команды разработчиков в данном случае используют разные подходы, мы же перенесём полезные коммиты в отдельную ветку и создадим отдельный pull request для этой новой ветки.


Задание


  1. Создайте ветку под названием feature-fix и переключитесь на нее.
  2. Перенесите все коммиты из бывшей ветки feature в новую ветку. Разрешите конфликты слияния, которые возникли при переносе.


    Непрерывная интеграция: возвращаем коммиты feature


  3. Добавьте регрессионный тест в ci.test.js:


    it('does not contain the sneaky bug', () => {expect( /.*sneaky\s+bug.*/gi.test(fileContents)).toBe(false);});
    

  4. Запустите тесты локально, чтобы убедиться, что они не завершаются успешно.
  5. Удалите текст " with a sneaky bug" в ci.md.
  6. Добавьте в индекс изменения тестов и изменения в списке шагов и закоммитьте их.
  7. Опубликуйте ветку в удалённый репозиторий.

У вас в результате должно получиться что-то похожее
Непрерывная интеграция: исправленная фича


Команды


# Создайте ветку под названием feature-fix и переключитесь на нее.git checkout -b feature-fix# Перенесите все коммиты из бывшей ветки feature в новую ветку. Разрешите конфликты слияния, которые возникли при переносе.# используйте историю чтобы узнать хэши коммитов:# - предшествующего коммиту с первой частью списка: C0# - добавляющего последние элементы списка: C2git log --oneline --graphgit cherry-pick C0..C2# разрешите конфликты слияния# - отредактируйте ci.md и/или ci.test.js# - добавьте файлы в индекс# - выполните "git cherry-pick --continue", можете не менять сообщение коммита# Добавьте регрессионный тест в ci.test.js# Запустите тесты локально, чтобы убедиться, что они не завершаются успешно.# Удалите текст " with a sneaky bug" в ci.md.# Добавьте в индекс изменения тестов и в списке шагов и закоммитьте их.git add ci.md ci.test.jsgit commit -m "Fix the bug in steps list"# Опубликуйте ветку в удалённый репозиторий.git push --set-upstream origin feature-fix

Создайте pull request.


Создайте pull request с названием Fixing the feature. Установите feature-fix как "head branch", а master как "base branch".
Пожалуйста, подождите, пока завершатся тесты. Вы можете увидеть статус тестов в нижней части обсуждения PR.


Убедитесь, что вы установили master в своем форке репозитория в качестве "base branch", я не буду отвечать на запросы на изменения в репозиторий с материалами курса.

Утвердите pull request "Fixing the feature"


Спасибо за исправление! Пожалуйста, утвердите изменения в master из pull request.


Задание


  1. Нажмите "Merge pull request".
  2. Нажмите "Confirm merge".
  3. Нажмите "Delete branch", так как она нам больше не нужна.

Это то, что у вас должно быть в данный момент
Непрерывная интеграция: исправление интегрировано в master


Поздравляю!


Вы выполнили все действия, которые люди обычно совершают в процессе непрерывной интеграции.


Если вы заметили какие-либо проблемы с курсом или знаете как его улучшить, создайте issue в репозитории с материалами курса. У этого курса также есть интерактивная версия использующая GitHub Learning Lab в качестве платформы.

Подробнее..

Как я автоматизировал разворачивание приложений на Linux на коленке с помощью Bash скриптов и Java

26.07.2020 20:06:58 | Автор: admin

Когда вы написали серверное приложение, его нужно где-то развернуть. У нас в компании сейчас это реализовано с помощью VPS на Linux, bash скриптов, и небольшой Java программы. Это эволюционный процесс, и как по мне, получилось весьма неплохо.


В статье я хочу описать эволюцию такого подхода, почему так получилось, плюсы и минусы подхода. Если у вас есть похожая проблема почитайте, возможно придут в голову полезные мысли.


КДПВ архитектура системы, для части которой автоматизируем развертывание:



Немного предыстории


Я сейчас являюсь руководителем отдела разработки в компании, где я работаю. У нас небольшая, но сбалансированная команда есть бекенд, фронтенд разработчики, QA, дизайнер, верстальщик.


Наша компания занимается образовательной деятельностью в IT, и софт мы пишем для себя. Это накладывает определенные ограничения у нас нет огромных бюджетов на разработку, бывают размытые требования, потому что не до конца понятно, что же нужно людям.


В то же время, есть плюсы пишем на чем хотим, и можем сами ставить и согласовывать сроки разработки. Также, поскольку пилим не стотысячный лендинг или интернет магазин, работать интересно. Развиваем несколько продуктов, собираем фидбек пользователей, и фиксим/добавляем фичи.


Таким образом, мы сами выбираем стек технологий, процесс развертывания приложений.


Архитектура системы


Один из наших продуктов имеет следующую архитектуру:



Есть один главный бекенд центр. Именно к нему стучатся все фронтенды (их несколько). На главном бекенде хранятся данные юзеров, и сосредоточено большинство бизнес-логики.


Также есть несколько вспомогательных бекендов, которые физически вынесены на разные серверы. Сделано так по нескольким причинам:


  • эти сервера запускают внешний непроверенный код. Следовательно, никаких данных там хранить нельзя, потому что рано или поздно внешний код выберется из песочницы;
  • вспомогательные бекенды довольно нагруженные, именно они делают основную работу. Главный бекенд умеет распределять нагрузку, и в случае чего мы добавляем еще вспомогательные бекенды для размазывания нагрузки.
  • на вспомогательных бекендах используются разные технологии. Есть java, node.js, python.

Также особенность вспомогательных бекендов их часто нужно перезапускать, потому что находятся различные мелкие правки (по большей части markdown файлов). Перезапуск такого вспомогательного бекенда никак не отражается на работе всей системы.


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


Level 1


Все начиналось просто. Захожу по SSH на VPS, выкачиваю изменения с git, делаю mvn build, ну или npm i, дальше java -jar или выполняю другую команду для поднятия сервера.


Ничего не автоматизировано, все вручную. Частота несколько раз в день.


Level 2


По происшествию какого-то времени я понял, что времени этот процесс занимает слишком много. Я ввожу дофига паролей от гитхаба и т.д.


Окей, в gihub добавляю SSH ключ VPS. Теперь git pull, не ввожу пароли. Вроде мелочь, но стало быстрее.


Level 3


И все же получается долго. Даже не так долго, как скучно.


Окей, пишу bash скрипт. В скрипте несколько команд:


  • git pull, чтобы вытянуть последние изменения
  • mvn package делаем fatjar (описываю Java)
  • pkill yourserverprocessname убиваем текущий процесс
  • java -jar yourfatjar.jar

Теперь мне нужно зайти на VPS, сделать cd ~/git/repository_name, и выполнить скрипт ./deploy.sh


Level 4


Раз у нас есть один скрипт, почему не вызывать его удаленно?


Не забывайте, что все вращается на дешевых VPS. Поднимать что-то сложное я не хочу. Ищу простой сервер на C чтобы занимал минимум ресурсов. Нахожу, пытаюсь пофиксить под свои нужды не получается быстро. Я писал на C десять лет назад, и понимаю, что помню только синтаксис, но работу с сетью, сокетами забыл напрочь.


Окей, делаем возврат к Java. На коленке набрасываю сервер из нескольких десятков строк кода. Использую встроенный HttpServer. Умеет принять GET и POST запрос, вытянуть параметр token, если параметр правильный запустить указанный bash скрипт.


Запускаю все это чудо.


Теперь на каждом VPS вращается две программы. Одна основная. Другая вспомогательная, для перезапуска основной.


Итог когда что-то поменяли на вспомогательном бекенде, просто переходим по определенному URL, выполняется bash скрипт, и сервер перезапускается с обновленным кодом.


Level 5


Остается последний шаг.


Открываю github, нахожу настройки webhook для нужных репозиториев. Смысл в том, что когда мы делаем определенное действие (push, etc) github умеет дернуть указанный для этого репозитория URL. Точнее отправить POST запрос по указанному адресу с параметрами события.


Я настроил webhook на любой push. Дергается именно тот URL, который делает обновление и рестарт сервера.


Теперь, если мы делаем git push, через минуту мы имеем обновленный и перезапущенный сервер.


Level 6 (bonus)


Как я уже упоминал, иногда вспомогательные бекенды падают. На них пользователи исполняют недоверенный код. Пусть и в песочнице, но тот же node.js все же иногда валится.


Для нас падение некритично, при условии что сервер быстро поднимется.


Окей, я начинаю искать сервисы для мониторинга доступности серверов. Нахожу UptimeRobot. По описанию все выглядит весьма прилично:


  • раз в пять минут мониторит доступность указанного адреса
  • если адрес недоступен делает то или иное действие (отправляет POST/GET запрос, шлет оповещение по электронной почте и т.д.).

Прямо то что нужно! Настраиваю мониторинг, добавляю действие если сервер упал, то дернуть URL перезагрузки. Также добавляю Телеграм бота, чтобы он оповещал команду о падении и восстановлении серверов.


Какое-то время работает нормально. Потом оказывается, что UptimeRobot мониторит нифига не раз в пять минут. Сервер упал, прошел час или что-то вроде того, и только тогда он обнаруживает падение.


Час это долго. На коленке на Spring Boot набрасываю решение, аналогичное UptimeRobot, но сильно урезанное. Раз в минуту мониторим указанные адреса, если адрес недоступен шлем оповещение про падение/поднятие сервера, ну и перезапускаем сам сервер.


В Телеграм канале, где есть все разработчики, видим вот такое:


image


Такое наколенное решение работает уже больше месяца, пока проблем не замечал.


Плюсы решения


Главный плюс описанной выше системы простота. Примитивные bash скрипты с минимумом логики.


Все уровни автоматизации накладываются один на другой, и верхние слои зависят от нижних, но не наоборот. В любой момент можно откатиться на уровень вниз, и ничего не поломается.


Минусы решения


Главный минус хрупкость.


Что, если не при каждом push на github мы хотим перезапускать сервер?


Что, если мы сделали push, а код не компилируется?


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


По нормальному это решается CI/CD системой. Где код вначале проходит тесты, и лишь если все ок доставляется на production.


Следуюет учесть, что мы начинали, и до сих пор частично находимся в стадии MVP. То есть, на данном этапе важно быстро выкатить продукт, собрать фидбек. Но сейчас потихоньку переходим в стадию, когда продукт начинает немного зарабатывать деньги, и ощутимо экономить. Поэтому я задумываюсь про описанные выше минусы, и прикидываю способы их устранения.


Куда двигаться дальше?


Скорей всего, указанные выше минуса решатся довольно просто. В Java, при сборке Maven проекта, сначала прогоняются юнит тесты, потом собирается jar. Если тесты не проходят, либо же ошибка компиляции и билд не собрался мы про это узнаем.


Поэтому нужно слегка дофиксить bash скрипт, чтобы он лишь в случае успешной сборки билда (появился .jar файл после mvn package) убивал текущий процесс и пытался запустить новый. Что-то похожее можно сделать и для node.js если тесты не прошли, ничего не перезапускаем.


Также нужно вынести вебхук из github на внешний сервер, и обновлять сервер по определенным условиям (например, запланировать обновление на ночь, когда пользователей меньше).


Я думал про взрослые CI/CD системы, типо Jenkins, Gitlab, софт вида Ansible. Но пока пришел к выводу, что текущая система более чем достаточна.


Любую систему нужно поддерживать, а чем система проще тем проще ее поддержка. Я люблю простые и понятные решения, и не хочу слепо использовать технологии лишь из-за того, что они на слуху. Если команда разработки вырастет, и нас перестанет устраивать текущий подход тогда задумаемся о его изменении, но не раньше.


Путь тимлидера


Как я уже упоминал, я сейчас руковожу отделом разработки. Это очень отличается от того, когда просто пишешь код. Нужно ставить задачи, определять их приоритетность, задумываться не только о технической, но и о бизнес части. Код при этом писать тоже нужно, пусть и меньше.


Нужно учитывать временные и финансовые ограничения. Учитывать особенности каждого разработчика. Я сейчас читаю много тематичной литературы, из последних прочитанных за месяц книг "Как пасти котов", "Я, нерды и гики", "Программист-праграматик", "Роман о управлении проектами".


Это интересный и новый для меня путь. Я прохожу его, описывая свой прогресс в своем Телеграм канале Программист и бизнес.


Пишу о бизнесе с точки зрения разработчика. Туда выкладываю короткие заметки, которые не подходят хабру по формату.

Подробнее..

Ускоряем доставку изменений в классический windows-монолит

16.12.2020 16:22:14 | Автор: admin
Добрый день, коллеги! Позвольте представиться меня зовут Павел Бацев, я администратор сервисов в ГК Спортмастер. В системном администрирование 8 лет, второй год занимаюсь изучением и внедрением devops-практик.

Сегодня я предлагаю рассмотреть вам кейс, который, прежде всего, будет интересен администраторам и инженерам с парком windows-хостов и монолитными системами.

Под катом: некоторые особенности работы конвейера доставки, работающего с использованием windows-агентов bamboo; проблемы, возникающие при использовании dsc и jea-сессий powershell в домене с устаревшим уровнем леса, и варианты их решения; способ внедрения автоматического ui-тестирования без задействования профессиональных тестировщиков.

Итак, рассмотрим, исходные данные:

Имеем бизнес-критичную информационную систему, которая интегрирована с системой внутреннего документооборота, системой заявок в Службу горячей линии, 1С.

На момент получения системы в поддержку у нас не было стратегии по релизам, поэтому все операции, связанные с обновлением той или иной компоненты, выполнялись в мануальном режиме, зоны ответственности были размазаны, не было организовано четкой, взаимодополняющей работы между командами разработки, администрирования и аналитики (как представителя бизнеса).

Система, о которой идет речь, имеет классическую монолитную архитектуру и состоит из:

  • Базы данных MS SQL;
  • Веб-сервера IIS;
  • Веб-сервиса IIS;
  • Шедуллера регламентных заданий Служба Windows;
  • Кеша Redis, как служба Windows.


Как вы понимаете, это система, в которой полная потеря функциональности может возникнуть при выходе из строя любого из компонентов. Что сильно усложняет гибкость в подходе и частоте доставки изменений.

Исходя из рутинных задач были определены операции, которые требуют автоматизации и выполнение в рамках единого конвейера, а также учтён задел на последующие развитие:

  • Доставка сборок (сформированных артефактов) на целевые хосты веб-серверов;
  • Кастомизация сборок правкой файлов контента, параметров верстки, адресов сторонних плагинов и прочее.
  • Выполнение скриптов миграций утилитой (набором bat/ps-скриптов) от разработчика, находящейся в теле сборки;
  • Организация верной последовательности запуска системы;
  • Лёгкий откат при возникновении неполадок конвейера и ошибках кода.
  • Масштабируемость выполнения (при росте агентов bamboo, росте количества целевых контуров/хостов и т.п.) и унификация для различных контуров (dev, test, stage, prod);
  • Использование вычислительных мощностей агентов bamboo;
  • Вывод полного и читабельного лога выполнения всех шагов в ui bamboo;
  • Организация возможности выполнения конвейера и первичной диагностики возникающих проблем командой аналитики, а не техническим экспертом или т.п.
  • Реализация системы оповещения о результатах выполнения конвейера в удобный бизнесу инструмент: выбор пал на канал telegram (простейшая реализация chatops).


В нашей компании реализованы различные инструменты, позволяющие внедрять практики devops. На момент моего вступления в должность администратора сервисов наиболее развитым и способным покрыть поставленные задачи был стек от Atlassian, поэтому реализацию конвейера было решено выполнять, используя связку jira+bitbucket+bamboo.

Так как обслуживаемая система использует ОС Windows Server, а имеющийся на тот момент пул bamboo-агентов был на *nix, возникла идея проверки работы кроссплатформенного взаимодействия. Всё было хорошо, powershell core, установленный на *nix-агенты, работал исправно, но при тестировании сетевого взаимодействия выяснилась проблема не отрабатывали второй тап аутентификации между windows-хостами.

Какие варианты решения были рассмотрены


Попробовали мы вот что.

  • установка библиотеки Kerberos и работа по аналогии с Ansible for Windows;
  • использование Python PowerShell Remoting Protocol Client library и зависимых с ним пакетов;
  • использование windows агентов bamboo.

После обсуждения с коллегами предложенных вариантов мы решились на внедрение некоторого количества агентов bamboo на windows, что позволило унифицировать платформу агента и целевой системы, отказаться от длительной исследовательской деятельности, а также обезопасило от возможных проявлений неявных проблем при кроссплатформенной работе инструментов ci/cd.

Не обошлось без сложностей и при реализации конвейера доставки изменений на базе windows агентов bamboo, куда же без проблем. Собственно, вот они:

  • текущий лес ad не поддерживает групповые управляемые учетные записи служб (gMSA);
  • при использовании встроенного плагина bamboo по обработке скриптов Script и хранении скриптов в СКВ, при получении ошибок powershell встроенные интерпретатор bamboo не воспринимал их и завершал задачи\задания\шаги\планы, как удачные;
  • сторонний плагин Parse Log Task для простейшего парсинга ошибок по заданным паттернам было невозможно добавить в задачи Deployment Projects.

Реализованный конвейер


Остановимся на общей концепции, инструментах и некоторых особенностях.

Система контроля версий


В качестве СКВ используется bitbucket, но так как используется ПО с закрытым исходным кодом, то в СКВ хранится только обвес (скрипты и конфиги) для нормальной работы конвейера. Хранение скриптов конвейера организовано в рамках одного репозитория, разделение контуров\типа конвейера идёт по бранчам. Структура папок для всех бранчей унифицирована.



CI/CD


Реализация всех процессов CI/CD сведена в выполнение билд-плана bamboo по причинам, выявленным в процессе разработки концепта, и наличия некоторых недостатков инструментария от Atlassian.



Все шаги унифицированы и представляют собой задание, разделенное на несколько простых задач. Разберем на примере шага Build.

Так как система использует ПО с закрытым исходным кодом, то как такового классического билда нет. В рамках конвейера в этот термин вложен смысл получения сборки, загрузки сборки на целевой хост, обработка и кастомизация.

Да, отмечу, что при реализации всех шагов конвейера используется инструментарий powershell. В частности dsc и jea. А раз была проблема с использованием gMSA, то мы разработали концепцию с задействованием временных шар, срок жизни которых ограничен временем выполнения задания конвейера. Отсюда появляются задачи с характерным признаком TFP и DelShare:

  • создание временной области на текущем агенте:
  • удаление временной области на текущем агенте.


В итоге вроде бы простейшее задание разбивается на семь задач:

  1. получение кода скриптов на агент bamboo;
  2. создание временной области, для хранения dsc;
  3. парсер результата задачи создания временной области;
  4. выполнение основной смысловой задачи шага;
  5. парсер результата выполнение основной задачи;
  6. удаление временной области;
  7. парсер результата задачи удаление временной области.


В рамках билд-плана создали зависимость выполнения другого билд-плана для реализации оповещений в telegram.





Этот конвейер является базовым и разделен на бранч-планы поконтурно для минимизации количества отдельных идентичных планов. В дальнейших конвейерах мы доработали паттерны ошибок, именование тестов, доработали/переработали скрипты ps и сделали прочие полезные изменения.

Одним из результатов стал конвейер обновления версии платформы для продуктивного контура.

Также реализовали конвейеры, покрывающие другие типовые задачи: обновление сущностей БД, используя утилиту собственной разработки подрядчика или утилиту от redgate; автоматическое ui тестирование системы; обновление\добавление кастомного контента js\css; перезапуск системы непродуктивного контура; оповещения в telegram.

Написали инструкции для группы аналитиков и провели обучающие встречи по использованию инструментов CI/CD.

А ещё разработали концепт и протестировали выполнение конвейеров по переходам бизнес-процесса в jira\отложенному заданию (не востребовано, т.к. заказчик пожелал использовать ui bamboo).

Ближайшие планы


  • перенос хранения сборок с сетевой шары в Artifactory;
  • после обновления леса ad внедрение gMSA;
  • написание своего плагина bamboo для оповещений в telegram;
  • написание своего плагина bamboo для парсинга результатов выполнения ps-скриптов из файла.


Автотестирование без профессиональных тестировщиков


Теперь же про успешно реализованный кейс по разработке и внедрению UI-автотестов с минимальными трудозатратами и квалификацией по инструментарию.

От бизнеса были получены сценарии с описанием бизнес-процессов, критичных к отслеживанию.

В качестве инструментария мы взяли:

  • selenium ide как доступная и удобная платформа для формирования скелета автотеста с возможностью импорта в различные языки программирования;
  • selenium webdriver+node js+mocha как связка кроссплатформенная инструментов, подходящая к текущей конфигурации и набору установленных плагинов в bamboo;
  • windows-агенты bamboo как раннеры тестов;
  • allure как визуализатор результатов для бизнеса, удобный и простой в понимании;
  • канал telegram как простейшая реализация chatops.


В selenium ide были записаны скелеты автотестов по сценариям от бизнеса.



Сценарии были импортированы в js и доработаны для выполнения в рамках выбранного инструментария.

Полученные конечные скрипты для UI-автотестирования были загружены в СКВ и разбиты по контурам.



После успешного завершения конвейер релиза, в зависимости от контура, запускает конвейер UI-тестирования.

По завершении формируется артефакт с детальным логом выполнения.


Все тесты завершены успешно


Все или часть тестов завершены неудачно

Плюс в telegram-канал приходит информация о результате, с детальным логом результатов.


Все тесты завершены успешно


Все или часть тестов завершены неудачно

Затем проведена передача знаний команде аналитиков, которая на основе сформированных скелетов и завершенных скриптов автотестирования самостоятельно выполняет доработку\разработку.

Это дает возможность:

  • исключить лишние шаги по постановке/проверке выполнения заданий между командами администрирования и аналитики;
  • увеличить вовлечённость команд в общий процесс внедрения devops-практик;
  • де-факто организовать прообраз продуктовой команды;
  • ускорить реализацию заданий от бизнеса.


Если вы сталкивались с подобным и используете windows-монолит, поделитесь, пожалуйста, своим опытом доставки изменений.
Подробнее..

Разбираемся с Custom Tooling в Argo CD

06.09.2020 02:08:51 | Автор: admin


Спустя некоторое время после написания первой статьи, где я ловко управлялся с jsonnet и гитлабом, я понял что пайплайны это конечно хорошо, но излишне сложно и неудобно.


В большинстве случаев требуется типовая задача: "сгенерировать YAML и положить его в Kubernetes". Собственно, с чем Argo CD замечательно и справляется.


Argo CD позволяет подключить Git-репозиторий и синкать его состояние в Kubernetes. По умолчанию есть поддержка нескольких видов приложений: Kustomize, Helm чарты, Ksonnet, голый Jsonnet или просто директории с YAML/JSON манифестами.


Большинству пользователей этого набора будет достаточно, но не всем. Для того чтобы удовлетворить потребности всех и каждого в Argo CD имеется возможность использовать custom tooling.


В первую очередь интересует возможность добавления поддержки qbec и git-crypt, которые с полна были рассмотренны в предыдущей статье.




Прежде чем приступить к конфигурации, нужно сначала разобраться с тем как именно Argo CD работает.


Для каждого добавленного приложения он имеет две фазы:


  • init начальная подготовка перед деплоем, здесь может быть всё что угодно: скачивание зависимостей, распаковка секретов и другое.
  • generate выполнение непосредственно команды генерации манифестов, вывод должен быть валидным YAML stream, это именно то, что будет применено в кластер.

Примечательно то, что Argo применяет этот подход для любого типа приложений, в том числе и для Helm. То есть в Argo CD Helm не занимается деплоем релизов в кластер, а используется только для генерации манифестов.


Со своей стороны Argo умеет нативно обрабатывать Helm-хуки, что позволяет не нарушать логики применения релизов.




QBEC


Qbec позволяет удобно описывать приложения с помощью jsonnet, а кроме того имеет возможность рендерить Helm-чарты, а так как Argo CD умеет нормально обрабатывать Helm-хуки, то использование этой возможности с Argo CD позволяет добиться ещё более корректных результатов.


Для того чтобы добавить поддержку qbec в argocd нужно две вещи:


  • в конфиге Argo CD дожен быть определен ваш custom plugin и команды для генерации манифестов.
  • нужные бинарники должны быть доступны в образе argocd-repo-server.

Первая задача решается довольно просто:


# cm.yamldata:  configManagementPlugins: |    - name: qbec      generate:        command: [sh, -xc]        args: ['qbec show "$ENVIRONMENT" -S --force:k8s-namespace "$ARGOCD_APP_NAMESPACE"']

(команда init не используется)


$ kubectl -n argocd patch cm/argocd-cm -p "$(cat cm.yaml)"

Для добавления бинарников предлагается собрать новый образ, или использовать трюк с init-контейнером:


# deploy.yamlspec:  template:    spec:      # 1. Define an emptyDir volume which will hold the custom binaries      volumes:      - name: custom-tools        emptyDir: {}      # 2. Use an init container to download/copy custom binaries into the emptyDir      initContainers:      - name: download-tools        image: alpine:3.12        command: [sh, -c]        args:        - wget -qO- https://github.com/splunk/qbec/releases/download/v0.12.2/qbec-linux-amd64.tar.gz | tar -xvzf - -C /custom-tools/        volumeMounts:        - mountPath: /custom-tools          name: custom-tools      # 3. Volume mount the custom binary to the bin directory (overriding the existing version)      containers:      - name: argocd-repo-server        volumeMounts:        - mountPath: /usr/local/bin/qbec          name: custom-tools          subPath: qbec        - mountPath: /usr/local/bin/jsonnet-qbec          name: custom-tools          subPath: jsonnet-qbec

$ kubectl -n argocd patch deploy/argocd-repo-server -p "$(cat deploy.yaml)"

Теперь посмотрим как будет выглядеть манифест нашего приложения:


apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata:  name: qbec-app  namespace: argocdspec:  destination:     namespace: default    server: https://kubernetes.default.svc  project: default  source:     path: qbec-app    plugin:       env:         - name: ENVIRONMENT          value: default      name: qbec    repoURL: https://github.com/kvaps/argocd-play  syncPolicy:     automated:       prune: true

В переменной ENVIRONMENT мы передаём имя окружения для которого нужно выполнять генерацию манифестов.


применим и посмотрим что у нас получилось:



приложение задеплоилось, отлично!




git-crypt


Git-crypt позволяет настроить прозрачное шифрование репозитория. Это простой и безопасный способ хранить конфиденциальные данные прямо в git.


С имплементацией git-crypt оказалось сложнее.


Теоретически мы могли бы выполнять git-crypt unlock на init-стадии нашего custom-плагина, но это не очень удобно, так как не позволило бы использовать нативные методы деплоя. Например в случае Helm и Jsonnet, мы теряем гибкий GUI-интерфейс который позволяет упростить настройку приложения (values-файлы и прочее).


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


Так как на данный момент Argo CD не предоставляет возможности для описания каких-либо хуков для синхронизации репозитория, пришлось обойти это ограничение хитрым шелл скриптом-обёрткой, который заменяет собой команду git:


#!/bin/sh$(dirname $0)/git.bin "$@"ec=$?[ "$1" = fetch ] && [ -d .git-crypt ] || exit $ecGNUPGHOME=/app/config/gpg/keys git-crypt unlock 2>/dev/nullexit $ec

Argo CD выполняет git fetch каждый раз перед операцией деплоя. Именно на эту команду мы и повесим выполнение git-crypt unlock для разблокировки репозитория.


для тестов можете использовать мой docker-образ в котором уже есть всё необходимое:


$ kubectl -n argocd set image deploy/argocd-repo-server argocd-repo-server=docker.io/kvaps/argocd-git-crypt:v1.7.3

Теперь нам нужно подумать о том, как Argo будет расшифровывать наши репозитории. А именно сгенерировать gpg-ключ для него:


$ kubectl exec -ti deploy/argocd-repo-server -- bash$ printf "%s\n" \    "%no-protection" \    "Key-Type: default" \    "Subkey-Type: default" \    "Name-Real: YOUR NAME" \    "Name-Email: YOUR EMAIL@example.com" \    "Expire-Date: 0" \    > genkey-batch $ gpg --batch --gen-key genkey-batchgpg: WARNING: unsafe ownership on homedir '/home/argocd/.gnupg'gpg: keybox '/home/argocd/.gnupg/pubring.kbx' createdgpg: /home/argocd/.gnupg/trustdb.gpg: trustdb createdgpg: key 8CB8B24F50B4797D marked as ultimately trustedgpg: directory '/home/argocd/.gnupg/openpgp-revocs.d' createdgpg: revocation certificate stored as '/home/argocd/.gnupg/openpgp-revocs.d/9A1FF8CAA917CE876E2562FC8CB8B24F50B4797D.rev'

Сохраним имя ключа 8CB8B24F50B4797D для дальнейших шагов. Экспортируем сам ключ:


$ gpg --list-keysgpg: WARNING: unsafe ownership on homedir '/home/argocd/.gnupg'/home/argocd/.gnupg/pubring.kbx-------------------------------pub   rsa3072 2020-09-04 [SC]      9A1FF8CAA917CE876E2562FC8CB8B24F50B4797Duid           [ultimate] YOUR NAME <YOUR EMAIL@example.com>sub   rsa3072 2020-09-04 [E]$ gpg --armor --export-secret-keys 8CB8B24F50B4797D

И добавим его в виде отдельного секрета:


# argocd-gpg-keys-secret.yamlapiVersion: v1kind: Secretmetadata:  name: argocd-gpg-keys-secret  namespace: argocdstringData:  8CB8B24F50B4797D: |-    -----BEGIN PGP PRIVATE KEY BLOCK-----    lQVYBF9Q8KUBDACuS4p0ctXoakPLqE99YLmdixfF/QIvXVIG5uBXClWhWMuo+D0c    ZfeyC5GvH7XPUKz1cLMqL6o/u9oHJVUmrvN/g2Mnm365nTGw1M56AfATS9IBp0HH    O/fbfiH6aMWmPrW8XIA0icoOAdP+bPcBqM4HRo4ssbRS9y/i    =yj11    -----END PGP PRIVATE KEY BLOCK-----

$ kubectl apply -f argocd-gpg-keys-secret.yaml

Единственное что нам осталось, это пробросить его в контейнер argocd-repo-server, для этого отредактируем deployment:


$ kubectl -n argocd edit deploy/argocd-repo-server

И заменим существующий gpg-keys volume на projected, где и укажем наш секрет:


   spec:     template:       spec:         volumes:         - name: gpg-keys           projected:             defaultMode: 420             sources:             - secret:                 name: argocd-gpg-keys-secret             - configMap:                 name: argocd-gpg-keys-cm

Argo CD автоматически подгружает gpg-ключи из этой директории при старте контейнера, таким образом он загрузит и наш приватный ключ.


проверим:


$ kubectl -n argocd exec -ti deploy/argocd-repo-server -- bash$ GNUPGHOME=/app/config/gpg/keys gpg --list-secret-keysgpg: WARNING: unsafe ownership on homedir '/app/config/gpg/keys'/app/config/gpg/keys/pubring.kbx--------------------------------sec   rsa2048 2020-09-05 [SC] [expires: 2021-03-04]      ED6285A3B1A50B6F1D9C955E5E8B1B16D47FFC28uid           [ultimate] Anon Ymous (ArgoCD key signing key) <noreply@argoproj.io>sec   rsa3072 2020-09-03 [SC]      9A1FF8CAA917CE876E2562FC8CB8B24F50B4797Duid           [ultimate] YOUR NAME <YOUR EMAIL@example.com>ssb   rsa3072 2020-09-03 [E]

Отлично, ключ загружен! Теперь нам достаточно добавить Argo CD в наш репозиторий в качестве коллаборатора и он сможет автоматически расшифровывать его на лету.


Импортируем ключ на локальный компьютер:


$ gpg --armor --export-secret 8CB8B24F50B4797D > 8CB8B24F50B4797D.pem$ gpg --import 8CB8B24F50B4797D.pem

Настроим уровень доверия:


$ gpg --edit-key 8CB8B24F50B4797Dtrust5

Добавим argo в качестве коллаборатора в наш проект:


$ git-crypt add-gpg-user 8CB8B24F50B4797D



Ссылки по теме:


Подробнее..

Как готовить Helm правильно несколько полезных рецептов

16.05.2021 18:12:28 | Автор: admin
(источник фото -https://unsplash.com/photos/XWNbUhUINB8(источник фото -https://unsplash.com/photos/XWNbUhUINB8

Когда-то давно, когда ножей не знали,х@#$ говядину рубили... ой нет, это другая сказка, простите. Вот, правильная эта. Когда-то давно, деды сказывают, для обновления сервиса в среде инженерам приходилось самим писать скрипты для деплоймента, а некоторые даже запускали их сами из консоли, руками. Были даже причудливые инструменты вроде ClusterSSH, чтобы делать сеанс одновременной игры на всех нодах кластера.

Но тотальная контейнеризация, ввод в обиход универсального понятия "workload", и оркестризация привели к появлению Kubernetes, на котором сейчас работает примерно 3/4 мировых production сред.

Kubernetes ввёл в обиход концепцию управления конфигурацией при помощи дескрипторов - kubectl apply что в переводе означает "я не хочу объяснять что делать, я говорю как хочу чтобы в среде всё стало - а ты делай что должен, блестящий металлический зад!".

Как с любой инновацией, выход на базовый уровень оркестрации привёл к осознанию того, что дескрипторы должен делать не человек руками, а другой "металлический зад", которому задачу ставить можно на ещё более высоком уровне - "накати обновление сервиса на среду Ъ, согласно правилам подготовки конфигурации среды".

Поэтому Helm стал де-факто стандартом в автоматизированных деплойментах Kubernetes, добавляя новые интересные возможности, которые идут гораздо дальше простой альтернативы выполнению kubectl apply -f ...

Ниже мы расссмотрим несколько примеров того, что можно получить от Helm по сравнению с простой командой kubectl apply, с чем разработчик сталкивается в реальной жизни. Мы покажем, как организовать доставку новых версий сервисов в разные среды, описывая параметры так, что не требуется копипастных повторений, которые ведут к ошибкам. Мы также увидим несколько других полезных приёмов - для удобного управления секретами и организации надёжных пайплайнов.

Итак, напомним, что Helm в принципе может делать три основные вещи:

  • сделать дескрипторы по шаблонам

  • накатить дескрипторы на кластер

  • использовать репозитории charts - взять шаблоны дескрипторов из центральной среды

В данном тексте мы фокусируемся на первых двух моментах, шаблонах и состоянии кластера, не затрагивая репозитории.

Шаблонизатор Helm работает примерно так:

Другими словами, Helm берёт исходники:

  • файлы, текстовые и бинарные

  • шаблоны .yaml

  • значения из одного или нескольких .yaml файлов, и, возможно, командной строки

и производит файлы .yaml для того, чтобы применить их на кластер Kubernetes.

При этом файлы вставляются в Secrets и ConfigMaps, а значения занимают свои места в шаблонах.

Получившиеся шаблоны можно скормить в кластер непосредственно через kubectl, или же (что открывает новые возможности) наложить их через Helm. Helm имеет встроенные средства управления состоянием кластера - несколько последних сделанных деплойментов сохраняются в отдельном Secret, что даёт возможность полного и точного отката при ошибке - то есть обеспечивает атомарность операции и целостность кластера.

Рассмотрим некоторые полезные техники Helm для решения типичных задач, с которыми девопс инженер сталкивается за пределами простого, понятного, нежно-розового мира hello world:

  • управление конфигурацией множества сред

  • использование секретов

  • повышение стабильности пайплайнов

Конфигурация множества сред

Скорее всего, ваши сервисы работают в нескольких средах. Простейшим разделением может быть Test и Production, но на практике всё бывает чуть сложнее. Тестовые среды бывают разных видов: некоторые из них одноразовые, нестабильные и экспериментальные, выполняющие код из новых пулл реквестов, который может часто ломаться.

Другие тестовые среды предназначены, например, для приёмочного тестирования реальными пользователями (по уровню ответственности не Production - но весьма похожие). Такие среды обычно используются несколькими командами, поэтому все ожидают от них стабильности.

Production-среды имеют общие моменты, характерные для production, например реальный секретный ключ для платёжной системы. Но, тем не менее, могут различаться в других аспектах: например по региону, среда для "канареечных" релизов для тестов на малой группе, или по регионам. Или же может быть несколько production сред для каждого клиента или их группы, если ваш сервис больше похож на SaaS.

12 заповедей

Возможно, вы слышали о 12 заповедях - наборе "золотых" правил для организации сервисов, которым контейнерные оркестрации типа Kubernetes соответствуют чуть менее чем полностью. Под номером 3 сформулирована важность сохранения настроек и конфигураций в самой среде, а не в приложении.

Однако в скрижалях с 12 заповедями не сообщается подробностей - как именно передать эти значения в среды прозрачным, безопасным и контролируемым образом и где все это хранить. Особенно когда вам нужно поддерживать много сред, похожих по одним аспектам, но разных по другим. И как быть с некоторыми значениями конфигурации, которые лучше держать в секрете? Чтобы успешно справиться со всей этой сложностью с помощью Helm, необходимо внести некоторые улучшения в стандартную компоновку chart.

Иногда встречаются еретические варианты трактовки третьей заповеди, когда некоторые юноши понимают сохранение настроек не в приложении буквально, насмотревшись на эту картинку из манифеста.

Код с конфигурацией - как мясное с молочным? Код с конфигурацией - как мясное с молочным?

Они решают, что заповедь запрещает мешать молочное с мясным хранить конфигурацию приложения в том же гит-репозитории что и его код.
Истинно говорю вам, они заблуждаются! Третья заповедь - про то, что артефакт сервиса (результат build) должен быть один и тот же, и не содержать конфигурации, которая считывается при запуске из среды - в коей среде она должна присутствовать.

Каждый новый билд порождает неизменяемые (immutable) контейнер(ы) с кодом, и Helm chart, который можно применить для любой среды - от локального docker-desktop до production кластера в AWS.

При этом код конфигурации, в нашем случае - шаблоны и файлы Helm chart, с полным набором настроек для всех сред, за исключением секретных значений, проще хранить вместе с кодом сервиса. В особенности, если вам всё-таки удалось сделать гармоничную (микро)сервисную архитектуру, и у каждого сервиса есть возможность обновляться независимо. Если такой возможности нет - не льстите себе, у вас распределённый монолит, а не (микро)сервисы.

Чтобы сделать всё красиво и надёжно, мы прежде всего хотим устранить избыточность и дублирование схожих элементов конфигурации и шаблонов и сделать их такими же простыми, как приложение, работающее на ноутбуке разработчика.

Следовательно:

  • мы не хотим, чтобы конфигурация нашего приложения зависела от способа его развертывания - запуск контейнера на лаптопе разработчика и запуск в оркестраторе не должны по сути отличаться

  • ... ведь в подавляющем большинстве случаев все, что нужно сервису - это просто набор из нескольких файлов конфигурации в одном, доступном ему каталоге (conf или properties)

  • а ещё больше мы не хотим ничего копипастить - это ведёт к визуальному мусору, глупым ошибкам, повышению затрат на поддержку - поэтому

    • каждое значение конфигурации и/или файл должны храниться в одном и только в одном месте,

    • если значения или файлы различаются в зависимости от среды, в каждой среде сохраняются только отличающиеся части,

    • добавление файла в конфигурацию должно требовать именно этого - добавление файла: никаких изменений в пицот других yaml-ов!

Мы будем хранить конфигурации всех наших сред в диаграмме Helm как часть chart.

Пример ниже, дополнительные каталоги выделены (+).

/env             // (+) значения для сред /<chart-name>    // chart    /files         // (+) конфигурационные файлы и их шаблоны     /templates     // шаблоны Helm   Chart.yaml     // заглавный файл chart  values.yaml    // значения по умолчанию

Файлы со значениями для сред

Давайте посмотрим подробнее на структуру ваших сред.

Допустим у вас есть два типа, TEST и PROD для тестовых и продакшен сред, соответственно. тип TEST делится на две разновидности -STABLE и -PR (пул реквест, нестабильная среда), а для типа PROD у вас разновидности это регионы, EU и US.

Получается примерно такая структура для /env (значения для сред) и /files (файлы конфигурации):

/env                  TEST.yaml            // общие значения для всех тестовых сред  TEST-PR.yaml         // только для PR/нестабильной  TEST-STABLE.yaml     // только для стабильной     PROD.yaml            // общие значения для всех продакшен сред  PROD-EU.yaml         // продакшен EU     PROD-US.yaml         // продакшен US /files                     /TEST                // общие файлы для всех тестовых сред  /TEST-PR             // ...  /TEST-STABLE         // ...    /PROD                // общие файоы для всех продакшен сред   /PROD-EU             // ...  /PROD-US             // ...  /shared              // файлы общие для всех средvalues.yaml             // значения общие для всех сред

Теперь посмотрим что внутри /files - текстовые и бинарные файлы конфигурации.

.../PROD    /binary    /text/PROD-EU    /binary    /text .../shared    /binary    /text    /secret-text

Если какого-то типа файлов нет для комбинации тип/разновидность среды, то директория может отсутствовать.

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

1. Мы предполагаем что секретные файлы только текстовые а не бинарные. Почему? Потому что так проще. Секретный двоичный файл (например .p12 сертификат) становится несекретным, если зашифровать его секретным ключом с достаточной (скажем 120+ бит) энтропией - и можно хранить его просто в гите, даже если он размеров в несколько (десятков) килобайт. С точки зрения безопасности - это просто рандомный набор битов. А вот ключ, отпирающий этот бинарник, мы будем хранить в строгом секрете - как и все другие-остальные пароли.

2. С другой стороны, вряд ли будут секретные текстовые файлы, которые при этом не общие (shared) для всех сред. Обычно такой файл - это шаблон (например конфигурация пула соединений к базе данных), в который надо вставить несколько секретных значений, например пароль пользователя. Ниже мы чуть подробнее раскроем тему секретов в шаблонах.

Итак, когда структура выше подготовлена, всё работает примерно так - значения из нескольких файлов сначала превращаются в один плоский yaml, накладываясь друг на друга, в порядке уменьшающегося приоритета:

  • среда - разновидность (TEST-STABLE.yaml)

  • среда - тип (TEST.yaml)

  • общие значения (values.yaml)

Helm позволяет это сделать, указав файлы со значениями как последовательность опций --values:

helm upgrade --install <chart> path/to/<chart> --strict \     --values env/<env>.yaml --values env/<env>-<flavour>.yaml

В .yaml-файлах разновидности и типа среды содержатся атрибуты, например TEST-STABLE.yaml среди прочего содержит:

envFlavour: STABLE

а TEST.yaml содержит

envClass: TEST

В плоском финальном наборе значений всё это объединяется, для того чтобы, как мы увидим в следующем разделе, можно было удобно используем эти значения, чтобы взять нужные файлы из /files.

Один ConfigMap и один Secret для всех файлов

Смотрим внимательно - вот одна, единая конфигурация для одного единственного ConfigMap и одного Secret, куда поместятся все ваши файлы. Бинарные файлы и текстовые несекретные файлы пойдут в ConfigMap, а секретные текстовые файлы в Secret.

# это нужно для вложенных range{{- $self := . -}} # список директорий, откуда берутся файлы - среда-разновидность, среда-тип и потом общие файлы из shared{{ $sources := (list "shared" .Values.envClass (printf "%s-%s" .Values.envFlavour .Values.envClass ) }}apiVersion: v1kind: ConfigMapmetadata:  name: myapp# вставить несекретные текстовые файлы как шаблоныdata:{{ range $env := $sources }}{{ range $path, $bytes := $self.Files.Glob (printf "files/%s/text/*" $env) }}  {{ base $path }}: {{ tpl ($self.Files.Get $path) $ | quote }}{{ end }}{{ end }}# вставить двоичные файлыbinaryData:{{ range $env := $sources }}{{ range $path, $bytes := $self.Files.Glob (printf "files/%s/binary/*" $env) }}  {{ base $path }}: {{ $self.Files.Get $path | b64enc | quote }}{{ end }}{{ end }}---apiVersion: v1kind: Secretmetadata:  name: myapp  labels:type: Opaquedata:# вставить секретные текстовые файлы как шаблоны{{ range $env := $sources }}{{ range $path, $bytes := $self.Files.Glob (printf "files/%s/secret-text/*" $env) }}  {{ base $path }}: {{ tpl ($self.Files.Get $path) $ | b64enc | quote }}{{ end }}{{ end }}

И это всё что нужно! Ничего не надо менять, если добавляется новый файл. Как только файл оказывается в нужном месте, он будет автоматически найден и добавлен в дескриптор, никаких изменений не требуется.

Тотальная шаблонизация

Имеющие опыт с Helm, возможно, задаются вопросом, к чему все эти хитрые танцы, если у Helm уже есть набор функций для ConfigMaps и Secrets?

А вот почему. Заметили маленькую функцию tpl, применяемую на каждый файл? Правильно, именно поэтому она нужна, чтобы каждый текстовый файл обрабатывался как отдельный шаблон - вы можете вставлять ссылки на значения как {{ .Values.myValue }}в любое место ваших текстовых файлов конфигурации.

Шаблон может использоваться для любого типа конфигурации, .properties, yaml, HOCON, например:

akka {  persistence {    cassandra {        keyspace = {{ .Values.cassandra.keyspace | quote }}        table = "{{ .Values.cassandra.tablePrefix }}messages"

Коварные кавычки

Кавычки в значении могут оказаться как лишними, так и наоборот, в зависимости от формата файла.

Каким-то значениям, содержащим скобки, запятые и прочее, что может смутить парсер конфигурации, полезно быть взятым в кавычки:

databasePassword: {{ .Values.databasePassword | quote }}

Для других значений, например в файле .properties, кавычки наоборот, вредны, поэтому их можно убрать вот так:

param/username={{ .Values.username | trimAll "\"" }}

Проецируемые тома (projected volumes)

С точки зрения работающего сервиса всё равно, являются ли файлы секретными или нет: в контексте запущенного контейнера это просто файлы в одном и том же контексте безопасности, к которым есть доступ только у процесса в контейнере. Поэтому ничего страшного нет в том, чтобы положить всех файлы - текстовые, бинарные, содержащие секреты и не содержащие - в один каталог контейнера.

Для этого в Kubernetes удачно завезли projected volumes - проецируемые тома, которые можно собирать из нескольких ConfigMaps и Secrets.

volumes:  - name: properties    projected:      defaultMode: 0640      sources:        - configMap:            name: myapp        - secret:            name: myapp

Очень просто смонтировать такой "сборный" том в директорию/confвашего приложения.

Линтер

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

У Helm есть для этого команды lint и template:

helm lint --debug path/to/<chart> --strict --values env/<env>.yaml \  --values env/<env>-<flavour>.yaml  helm template <chart> path/to/<chart> --strict --values env/<env>.yaml \  --values env/<env>-<flavour>.yaml

В процессе подготовки chart в пайплайне эти команды должны выполниться для всех валидных комбинаций типа и разновидности сред.

Но совершенству нет пределов (почти) - можно пойти ещё на шаг дальше и использовать yamllint для валидации yaml-дескрипторов. Это позволит отловить проблемы, которые иначе не получается ловить - например два файла с одним и тем же именем, которые оказались в PROD и PROD-EU, будут дважды вставлены в ConfigMap, что приведёт к ошибке при деплойменте.

Управление секретами

Теперь немного про то, как обращаться с секретами - например ключами платных API, паролями для баз данных и паролями от TLS-сертификатов, которые мы не хотим показывать никому, кроме самого сервиса.

Обычно секретные значения хранятся в отдельном сервисе, типа Heroku Vault, Azure Vault, Google Cloud KMS и подобных. В Helm даже есть плагин для управления секретами,но в большинстве компаний управление секретами централизовано, и инженерам не позволено, да и не нужно, трогать секреты из production.

Когда пайплайн запускает обновление сервиса в подобной среде, то сам пайплайн имеет возможность считать секреты и вставить их в конфигурационные файлы. В большинстве случаев (Gitlab, Circle, Azure, ...) секреты становятся переменными среды, и всё что требуется - это вставить их в шаблон в самом начале.

Для этого мы добавим секцию в values.yaml

# secretsdatabase_username: "${UserNameSecret}"database_password: "${DatabasePasswordSecret}"

И в нашем пайплайне испольуемenvsubstили нечто похожее:

cat <chart>/values.yaml | envsubst > <chart>/values-injected.yamlmv <chart>/values-injected.yaml <chart>/values.yaml

Такой подход позволяет ссылаться на секреты просто как {{ .Value.xxx }} в любом шаблоне, вставляя их туда куда им положено быть.

Но полезно помнить, однако, что ничто автоматически не предотвратит ошибочное вставление секретного значения в несекретный текстовый файл.

Чтобы решить этот вопрос, можно ввести конвенцию называть секреты как XXXSecret, и добавить что-то типа такого:

EXPOSED_SECRETS=$(grep Secret <chart>/files | grep -v secret-files | wc -l)if [ $EXPOSED_SECRETS -gt 0 ]; then fail "Secrets are exposed"; fi

Это добавит уровень защиты против случайной потери секрета.

Атомарные обновления сред

Теперь последний раздел - как использовать Helm hooks для того, чтобы повысить надёжность ваших пайплайнов.

Предположим, вы только что развернули новую версию приложения или новый набор значений конфигурации. Теперь вы определенно хотите знать, не поломалось ли что-то? Если все в порядке, вы можете продолжить работу над другими задачами вашего спринта, но если что-то не так - то вы скорее всего очень хотите, чтобы среда была автоматически восстановлена до последнего известного исправного состояния, без вашего вмешательства и всех этих надоедливых сообщений фрустрированных коллег: Почему я снова получаю ошибку 5xx?.

Это может показаться сложным - особенно если вы хотите реализовать это самостоятельно, вооружившись простым kubectl apply -f ... Но, к счастью, Helm многое делает "прямо из коробки".

Флаг --atomic

Просто добавьте флаг --atomicдля автоматической отмены текущего обновления, если поды не запустились по любой причине:

helm upgrade --install my-chart some/path/to/my-chart \    --atomic --debug --timeout 300s

Helm откатит обновление, если проверки проб health/readiness не дали положительного результата в указанный срок. Хорошо, но можно лучше.

Hooks

Можно, и нужно, добавить KubernetesJobв ваш дескриптор, чтобы запускать тесты "на дым" после каждого обновления. Обычно это не полный набор тестов, но несколько базовых сценариев, которые затрагивают все ключевые внешние системы, к которым сервис обращается в среде - например базу данных, внешний кеш, какие-то другие сервисы и тп.

И разумеется, сигнализировать Helm откатить обновление, если тест не прошёл.

apiVersion: batch/v1kind: Jobmetadata:  name: myapp  labels:  annotations:     "helm.sh/hook": post-install,post-upgrade     "helm.sh/hook-delete-policy": before-hook-creation,hook-succeededspec:  template:    metadata:      name: myapp-smoke-test    spec:      restartPolicy: Never      containers:        - name: tests          image: test-image:           command: ['/bin/sh',                    '-c',                    '/test/run-test.sh']

Если вы используете--atomicфлаг и post-upgrade/post-install hook для тестов в пайплайне, вы можете со значительной долей уверенности считать пайплайн надёжным. Он будет иметь "зелёный" статус тогда, и только тогда, когда сервис был успешно обновлён, запущен и прошёл базовые тесты.

А если что-то пошло не так - Helm возвратит среду в то состояние, которое было до этого обновление, включая все конфигурации и дескрипторы.

Автоматически!

Заключение

Helm это бесплатный инструмент с открытым кодом для управления обновлениями сервисов и их конфигураций в кластерах Kubernetes.

Используя шаблоны и hooks, можно добиться того, что ваш continuous delivery плавен и надёжен настолько, что его практически никто не замечает.

***

Этот текст является авторским переводом, оригинал статьи этого же автора
https://jacum.medium.com/advanced-helm-practices-for-perfect-kubernetes-deployments-7fc4e00cc41c

Подробно об авторе - https://www.linkedin.com/in/tim-evdokimov/

Подробнее..

PVS-Studio Анализ pull request-ов в Azure DevOps при помощи self-hosted агентов

27.07.2020 18:22:43 | Автор: admin


Статический анализ кода показывает наибольшую эффективность во время внесения изменений в проект, поскольку ошибки всегда сложнее исправлять в будущем, чем не допустить их появления на ранних этапах. Мы продолжаем расширять варианты использования PVS-Studio в системах непрерывной разработки и покажем, как настроить анализ pull request-ов при помощи self-hosted агентов в Microsoft Azure DevOps, на примере игры Minetest.

Вкратце о том, с чем мы имеем дело


Minetest это открытый кроссплатформенный игровой движок, содержащий около 200 тысяч строк кода на C, C++ и Lua. Он позволяет создавать разные игровые режимы в воксельном пространстве. Поддерживает мультиплеер, и множество модов от сообщества. Репозиторий проекта размещен здесь: https://github.com/minetest/minetest.

Для настройки регулярного поиска ошибок использованы следующие инструменты:

PVS-Studio статический анализатор кода на языках C, C++, C# и Java для поиска ошибок и дефектов безопасности.

Azure DevOps облачная платформа, предоставляющая возможность разработки, выполнения приложений и хранения данных на удаленных серверах.

Для выполнения задач разработки в Azure возможно использование виртуальных машин-агентов Windows и Linux. Однако запуск агентов на локальном оборудовании имеет несколько весомых преимуществ:

  • У локального хоста может быть больше ресурсов, чем у ВМ Azure;
  • Агент не "исчезает" после выполнения своей задачи;
  • Возможность прямой настройки окружения, и более гибкое управление процессами сборки;
  • Локальное хранение промежуточных файлов положительно влияет на скорость сборки;
  • Можно бесплатно выполнять более 30 задач в месяц.

Подготовка к использованию self-hosted агента


Процесс начала работы в Azure подробно описан в статье "PVS-Studio идёт в облака: Azure DevOps", поэтому перейду сразу к созданию self-hosted агента.

Для того, чтобы агенты имели право подключиться к пулам проекта, им нужен специальный Access Token. Получить его можно на странице "Personal Access Tokens", в меню "User settings".

image2.png

После нажатия на "New token" необходимо указать имя и выбрать Read & manage Agent Pools (может понадобиться развернуть полный список через "Show all scopes").

image3.png

Нужно скопировать токен, так как Azure больше его не покажет, и придется делать новый.

image4.png

В качестве агента будет использован Docker контейнер на основе Windows Server Core. Хостом является мой рабочий компьютер на Windows 10 x64 с Hyper-V.

Сначала понадобится расширить объём дискового пространства, доступный Docker контейнерам.

В Windows для этого нужно модифицировать файл 'C:\ProgramData\Docker\config\daemon.json' следующим образом:

{  "registry-mirrors": [],  "insecure-registries": [],  "debug": true,  "experimental": false,  "data-root": "d:\\docker",  "storage-opts": [ "size=40G" ]}

Для создания Docker образа для агентов со сборочной системой и всем необходимым в директории 'D:\docker-agent' добавим Docker файл с таким содержимым:

# escape=`FROM mcr.microsoft.com/dotnet/framework/runtimeSHELL ["cmd", "/S", "/C"]ADD https://aka.ms/vs/16/release/vs_buildtools.exe C:\vs_buildtools.exeRUN C:\vs_buildtools.exe --quiet --wait --norestart --nocache `  --installPath C:\BuildTools `  --add Microsoft.VisualStudio.Workload.VCTools `  --includeRecommendedRUN powershell.exe -Command `  Set-ExecutionPolicy Bypass -Scope Process -Force; `  [System.Net.ServicePointManager]::SecurityProtocol =    [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; `  iex ((New-Object System.Net.WebClient)    .DownloadString('https://chocolatey.org/install.ps1')); `  choco feature enable -n=useRememberedArgumentsForUpgrades;  RUN powershell.exe -Command `  choco install -y cmake --installargs '"ADD_CMAKE_TO_PATH=System"'; `  choco install -y git --params '"/GitOnlyOnPath /NoShellIntegration"'RUN powershell.exe -Command `  git clone https://github.com/microsoft/vcpkg.git; `  .\vcpkg\bootstrap-vcpkg -disableMetrics; `  $env:Path += '";C:\vcpkg"'; `  [Environment]::SetEnvironmentVariable(    '"Path"', $env:Path, [System.EnvironmentVariableTarget]::Machine); `  [Environment]::SetEnvironmentVariable(    '"VCPKG_DEFAULT_TRIPLET"', '"x64-windows"',  [System.EnvironmentVariableTarget]::Machine)RUN powershell.exe -Command `  choco install -y pvs-studio; `  $env:Path += '";C:\Program Files (x86)\PVS-Studio"'; `  [Environment]::SetEnvironmentVariable(    '"Path"', $env:Path, [System.EnvironmentVariableTarget]::Machine)RUN powershell.exe -Command `  $latest_agent =    Invoke-RestMethod -Uri "https://api.github.com/repos/Microsoft/                          azure-pipelines-agent/releases/latest"; `  $latest_agent_version =    $latest_agent.name.Substring(1, $latest_agent.tag_name.Length-1); `  $latest_agent_url =    '"https://vstsagentpackage.azureedge.net/agent/"' + $latest_agent_version +  '"/vsts-agent-win-x64-"' + $latest_agent_version + '".zip"'; `  Invoke-WebRequest -Uri $latest_agent_url -Method Get -OutFile ./agent.zip; `  Expand-Archive -Path ./agent.zip -DestinationPath ./agentUSER ContainerAdministratorRUN reg add hklm\system\currentcontrolset\services\cexecsvc        /v ProcessShutdownTimeoutSeconds /t REG_DWORD /d 60  RUN reg add hklm\system\currentcontrolset\control        /v WaitToKillServiceTimeout /t REG_SZ /d 60000 /fADD .\entrypoint.ps1 C:\entrypoint.ps1SHELL ["powershell", "-Command",       "$ErrorActionPreference = 'Stop';     $ProgressPreference = 'SilentlyContinue';"]ENTRYPOINT .\entrypoint.ps1

В результате получится сборочная система на основе MSBuild для C++, с Chocolatey для установки PVS-Studio, CMake и Git. Для удобного управления библиотеками, от которых зависит проект, собирается Vcpkg. А также скачивается свежая версия, собственно, Azure Pipelines Agent.

Для инициализации агента из ENTRYPOINT-а Docker файла вызывается PowerShell скрипт 'entrypoint.ps1', в который нужно добавить URL "организации" проекта, токен пула агентов, и параметры лицензии PVS-Studio:

$organization_url = "https://dev.azure.com/<аккаунт Microsoft Azure>"$agents_token = "<token агента>"$pvs_studio_user = "<имя пользователя PVS-Studio>"$pvs_studio_key = "<ключ PVS-Studio>"try{  C:\BuildTools\VC\Auxiliary\Build\vcvars64.bat  PVS-Studio_Cmd credentials -u $pvs_studio_user -n $pvs_studio_key    .\agent\config.cmd --unattended `    --url $organization_url `    --auth PAT `    --token $agents_token `    --replace;  .\agent\run.cmd} finally{  # Agent graceful shutdown  # https://github.com/moby/moby/issues/25982    .\agent\config.cmd remove --unattended `    --auth PAT `    --token $agents_token}

Команды для сборки образа и запуска агента:

docker build -t azure-agent -m 4GB .docker run -id --name my-agent -m 4GB --cpu-count 4 azure-agent

image5.png

Агент запущен и готов выполнять задачи.

image6.png

Запуск анализа на self-hosted агенте


Для анализа PR создается новый pipeline со следующим скриптом:

image7.png

trigger: nonepr:  branches:    include:    - '*'pool: Defaultsteps:- script: git diff --name-only    origin/%SYSTEM_PULLREQUEST_TARGETBRANCH% >    diff-files.txt  displayName: 'Get committed files'- script: |    cd C:\vcpkg    git pull --rebase origin    CMD /C ".\bootstrap-vcpkg -disableMetrics"    vcpkg install ^    irrlicht zlib curl[winssl] openal-soft libvorbis ^    libogg sqlite3 freetype luajit    vcpkg upgrade --no-dry-run  displayName: 'Manage dependencies (Vcpkg)'- task: CMake@1  inputs:    cmakeArgs: -A x64      -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake      -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=0 -DENABLE_CURSES=0 ..  displayName: 'Run CMake'- task: MSBuild@1  inputs:    solution: '**/*.sln'    msbuildArchitecture: 'x64'    platform: 'x64'    configuration: 'Release'    maximumCpuCount: true  displayName: 'Build'- script: |    IF EXIST .\PVSTestResults RMDIR /Q/S .\PVSTestResults    md .\PVSTestResults    PVS-Studio_Cmd ^    -t .\build\minetest.sln ^    -S minetest ^    -o .\PVSTestResults\minetest.plog ^    -c Release ^    -p x64 ^    -f diff-files.txt ^    -D C:\caches    PlogConverter ^    -t FullHtml ^    -o .\PVSTestResults\ ^    -a GA:1,2,3;64:1,2,3;OP:1,2,3 ^    .\PVSTestResults\minetest.plog    IF NOT EXIST "$(Build.ArtifactStagingDirectory)" ^    MKDIR "$(Build.ArtifactStagingDirectory)"    powershell -Command ^    "Compress-Archive -Force ^    '.\PVSTestResults\fullhtml' ^    '$(Build.ArtifactStagingDirectory)\fullhtml.zip'"  displayName: 'PVS-Studio analyze'  continueOnError: true- task: PublishBuildArtifacts@1  inputs:    PathtoPublish: '$(Build.ArtifactStagingDirectory)'    ArtifactName: 'psv-studio-analisys'    publishLocation: 'Container'  displayName: 'Publish analysis report'

Этот скрипт сработает при получении PR и будет выполнен на агентах, назначенных в пул по умолчанию. Необходимо только дать ему разрешение на работу с этим пулом.

image8.png


image9.png

В скрипте происходит сохранение списка измененных файлов, полученного при помощи git diff. Затем обновляются зависимости, генерируется solution проекта через CMake, и производится его сборка.

Если сборка прошла успешно, запускается анализ изменившихся файлов (флаг '-f diff-files.txt'), игнорируя созданные CMake вспомогательные проекты (выбираем только нужный проект флагом '-S minetest'). Для ускорения поиска связей между заголовочными и исходными C++ файлами создается специальный кэш, который будет храниться в отдельной директории (флаг '-D C:\caches').

Таким образом, мы теперь можем получать отчеты об анализе изменений в проекте.

image10.png


image11.png

Как было сказано в начале статьи, приятным бонусом использования self-hosted агентов является ощутимое ускорение выполнения задач, за счет локального хранения промежуточных файлов.

image13.png

Некоторые ошибки, найденные в Minetest


Затирание результата

V519 The 'color_name' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 621, 627. string.cpp 627

static bool parseNamedColorString(const std::string &value,                                  video::SColor &color){  std::string color_name;  std::string alpha_string;  size_t alpha_pos = value.find('#');  if (alpha_pos != std::string::npos) {    color_name = value.substr(0, alpha_pos);    alpha_string = value.substr(alpha_pos + 1);  } else {    color_name = value;  }  color_name = lowercase(value); // <=  std::map<const std::string, unsigned>::const_iterator it;  it = named_colors.colors.find(color_name);  if (it == named_colors.colors.end())    return false;  ....}

Эта функция должна производить разбор названия цвета с параметром прозрачности (например, Green#77) и вернуть его код. В зависимости от результата проверки условия в переменную color_name передается результат разбиения строки либо копия аргумента функции. Однако затем в нижний регистр переводится не сама полученная строка, а исходный аргумент. В результате её не удастся найти в словаре цветов при наличии параметра прозрачности. Можем исправить эту строку так:

color_name = lowercase(color_name);

Лишние проверки условий

V547 Expression 'nearest_emergefull_d == 1' is always true. clientiface.cpp 363

void RemoteClient::GetNextBlocks (....){  ....  s32 nearest_emergefull_d = -1;  ....  s16 d;  for (d = d_start; d <= d_max; d++) {    ....      if (block == NULL || surely_not_found_on_disk || block_is_invalid) {        if (emerge->enqueueBlockEmerge(peer_id, p, generate)) {          if (nearest_emerged_d == -1)            nearest_emerged_d = d;        } else {          if (nearest_emergefull_d == -1) // <=            nearest_emergefull_d = d;          goto queue_full_break;        }  ....  }  ....queue_full_break:  if (nearest_emerged_d != -1) { // <=    new_nearest_unsent_d = nearest_emerged_d;  } else ....}

Переменная nearest_emergefull_d в процессе работы цикла не меняется, и её проверка не влияет на ход выполнения алгоритма. Либо это результат неаккуратного copy-paste, либо с ней забыли провести какие-то вычисления.

V560 A part of conditional expression is always false: y > max_spawn_y. mapgen_v7.cpp 262

int MapgenV7::getSpawnLevelAtPoint(v2s16 p){  ....  while (iters > 0 && y <= max_spawn_y) {               // <=    if (!getMountainTerrainAtPoint(p.X, y + 1, p.Y)) {      if (y <= water_level || y > max_spawn_y)          // <=        return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point      // y + 1 due to biome 'dust'      return y + 1;    }  ....}

Значение переменной 'y' проверяется перед очередной итерацией цикла. Последующее, противоположное ей, сравнение всегда вернет false и, в целом, не влияет на результат проверки условия.

Потеря проверки указателя

V595 The 'm_client' pointer was utilized before it was verified against nullptr. Check lines: 183, 187. game.cpp 183

void gotText(const StringMap &fields){  ....  if (m_formname == "MT_DEATH_SCREEN") {    assert(m_client != 0);    m_client->sendRespawn();    return;  }  if (m_client && m_client->modsLoaded())    m_client->getScript()->on_formspec_input(m_formname, fields);}

Перед обращением по указателю m_client проверяется не нулевой ли он при помощи макроса assert. Но это относится только к отладочной сборке. Такая мера предосторожности, при сборке в релиз, заменяется на пустышку, и появляется риск разыменования нулевого указателя.

Бит или не бит?

V616 The '(FT_RENDER_MODE_NORMAL)' named constant with the value of 0 is used in the bitwise operation. CGUITTFont.h 360

typedef enum  FT_Render_Mode_{  FT_RENDER_MODE_NORMAL = 0,  FT_RENDER_MODE_LIGHT,  FT_RENDER_MODE_MONO,  FT_RENDER_MODE_LCD,  FT_RENDER_MODE_LCD_V,  FT_RENDER_MODE_MAX} FT_Render_Mode;#define FT_LOAD_TARGET_( x )   ( (FT_Int32)( (x) & 15 ) << 16 )#define FT_LOAD_TARGET_NORMAL  FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL )void update_load_flags(){  // Set up our loading flags.  load_flags = FT_LOAD_DEFAULT | FT_LOAD_RENDER;  if (!useHinting()) load_flags |= FT_LOAD_NO_HINTING;  if (!useAutoHinting()) load_flags |= FT_LOAD_NO_AUTOHINT;  if (useMonochrome()) load_flags |=     FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO | FT_RENDER_MODE_MONO;  else load_flags |= FT_LOAD_TARGET_NORMAL; // <=}

Макрос FT_LOAD_TARGET_NORMAL разворачивается в ноль, и побитовое "или" не будет устанавливать никакие флаги в load_flags, ветка else может быть убрана.

Округление целочисленного деления

V636 The 'rect.getHeight() / 16' expression was implicitly cast from 'int' type to 'float' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. hud.cpp 771

void drawItemStack(....){  float barheight = rect.getHeight() / 16;  float barpad_x = rect.getWidth() / 16;  float barpad_y = rect.getHeight() / 16;  core::rect<s32> progressrect(    rect.UpperLeftCorner.X + barpad_x,    rect.LowerRightCorner.Y - barpad_y - barheight,    rect.LowerRightCorner.X - barpad_x,    rect.LowerRightCorner.Y - barpad_y);}

Геттеры rect возвращают целочисленные значения. В переменную с плавающей точкой записывается результат деления целых чисел, и теряется дробная часть. Похоже, что в этих расчетах рассогласованные типы данных.

Подозрительная последовательность операторов ветвления

V646 Consider inspecting the application's logic. It's possible that 'else' keyword is missing. treegen.cpp 413

treegen::error make_ltree(...., TreeDef tree_definition){  ....  std::stack <core::matrix4> stack_orientation;  ....    if ((stack_orientation.empty() &&      tree_definition.trunk_type == "double") ||      (!stack_orientation.empty() &&      tree_definition.trunk_type == "double" &&      !tree_definition.thin_branches)) {      ....    } else if ((stack_orientation.empty() &&      tree_definition.trunk_type == "crossed") ||      (!stack_orientation.empty() &&      tree_definition.trunk_type == "crossed" &&      !tree_definition.thin_branches)) {      ....    } if (!stack_orientation.empty()) {                  // <=  ....  }  ....}

Здесь последовательности else-if в алгоритме генерации деревьев. Посередине очередной блок if находится на одной строке с закрывающей предыдущий else скобкой. Возможно, код работает правильно: до этого if-а создаются блоки ствола, а следом листьев; а может быть пропустили else. Наверняка это сказать может только разработчик.

Неправильная проверка выделения памяти

V668 There is no sense in testing the 'clouds' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. game.cpp 1367

bool Game::createClient(....){  if (m_cache_enable_clouds) {    clouds = new Clouds(smgr, -1, time(0));    if (!clouds) {      *error_message = "Memory allocation error (clouds)";      errorstream << *error_message << std::endl;      return false;    }  }}

В случае, если new не сможет создать объект, будет брошено исключение std::bad_alloc, и оно должно быть обработано try-catch блоком. А проверка в таком виде бесполезна.

Чтение за границей массива

V781 The value of the 'i' index is checked after it was used. Perhaps there is a mistake in program logic. irrString.h 572

bool equalsn(const string<T,TAlloc>& other, u32 n) const{  u32 i;  for(i=0; array[i] && other[i] && i < n; ++i) // <=    if (array[i] != other[i])      return false;  // if one (or both) of the strings was smaller then they  // are only equal if they have the same length  return (i == n) || (used == other.used);}

Происходит обращение к элементам массивов перед проверкой индекса, что может привести к ошибке. Возможно, стоит переписать цикл так:

for (i=0; i < n; ++i) // <=  if (!array[i] || !other[i] || array[i] != other[i])    return false;

Другие ошибки

Данная статья посвящена анализу pull request-ов в Azure DevOps и не ставит целью провести подробный обзор ошибок проекта Minetest. Здесь выписаны только некоторые фрагменты кода, которые мне показались интересными. Предлагаем авторам проекта не руководствоваться этой статьёй для исправления ошибок и выполнить более тщательный анализ предупреждений, которые выдаст PVS-Studio.

Заключение


Благодаря гибкой конфигурации в режиме командной строки, анализ PVS-Studio может быть встроен в самые разнообразные сценарии CI/CD. А правильное использование доступных ресурсов окупается увеличением производительности.

Нужно отметить, что режим проверки pull request-ов доступен только в Enterprise редакции анализатора. Чтобы получить демонстрационную Enterprise лицензию, укажите это в комментарии при запросе лицензии на странице скачивания. Более подробно о разнице между лицензиями можно узнать на странице заказа PVS-Studio.


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Alexey Govorov. PVS-Studio: analyzing pull requests in Azure DevOps using self-hosted agents.
Подробнее..

Анализ merge requestов в GitLab с помощью PVS-Studio для C

24.07.2020 10:04:54 | Автор: admin
image1.png

Любите GitLab и не любите ошибки? Хотите повысить качество исходного кода? Тогда вы попали по адресу. Сегодня мы расскажем, как настроить C# анализатор PVS-Studio для проверки merge request'ов. Всем единорожного настроения и приятного чтения.

PVS-Studio это инструмент для выявления ошибок и потенциальных уязвимостей в исходном коде программ, написанных на языках C, C++, C# и Java. Работает в 64-битных системах на Windows, Linux и macOS. Может анализировать код, предназначенный для 32-битных, 64-битных и встраиваемых ARM платформ.

Кстати, у нас состоялся релиз PVS-Studio 7.08, в котором мы сделали много всего интересненького. Например:

  • анализатор C# под Linux и macOS;
  • плагин для Rider;
  • новый режим проверки списка файлов.

Режим проверки списка файлов


Раньше для того чтобы проверить определенные файлы, необходимо было передать анализатору .xml со списком файлов. Но так как это не очень удобно, мы добавили возможность передачи .txt, что очень упрощает жизнь.

Для того чтобы проверить определенные файлы, необходимо указать флаг --sourceFiles (-f) и передать .txt со списком файлов. Выглядит это следующим образом:

pvs-studio-dotnet -t path/to/solution.sln -f fileList.txt -o project.json

Если вам интересно настроить проверку коммитов или pull requests, вы также можете сделать это, используя данный режим. Отличие будет заключаться в получении списка файлов для анализа и зависеть от того, какие системы вы используете.

Принцип проверки merge request


Основная суть проверки состоит в том, чтобы проблемы, обнаруженные анализатором, при слиянии не попадали в master ветку. Также мы не хотим каждый раз анализировать проект целиком. Тем более что при слиянии веток у нас есть список измененных файлов. Поэтому предлагаю добавить проверку merge request.

Вот так выглядит merge request до внедрения статического анализатора:

image2.png

То есть все ошибки, которые были в ветке changes, перейдут в мастер ветку. Так как нам бы этого не хотелось, добавляем анализ, и теперь схема выглядит следующим образом:

image3.png

Анализируем changes2 и, если ошибок нет, принимаем merge request, а иначе отклоняем его.

Кстати, если вас интересует анализ коммитов и pull request'ов для C/C++, то вы можете почитать об этом здесь.

GitLab


GitLab веб-инструмент жизненного цикла DevOps с открытым исходным кодом, представляющий систему управления репозиториями кода для Git с собственной вики, системой отслеживания ошибок, CI/CD пайплайном и другими функциями.

Перед тем как приступить к реализации анализа merge request'ов необходимо зарегистрироваться и загрузить свой проект. Если вы не знаете, как это сделать, то предлагаю статью моего коллеги.

Примечание. Описываемый далее способ настройки окружения один из возможных. Цель показать шаги настройки необходимого для анализа окружения и запуска анализатора. Возможно, в вашем случае более оптимальным будет разделение этапов подготовки окружения (добавление репозиториев, установка анализатора) и анализа: например, подготовка Docker образов с необходимым окружением и их использование или какой-нибудь иной способ.

Чтобы нагляднее понять, что сейчас будет происходить, я предлагаю взглянуть на следующую схему:

image4.png

Для работы анализатору требуется .NET Core SDK 3, поэтому перед установкой анализатора нужно добавить репозитории Microsoft, из которых будут установлены необходимые для анализатора зависимости. Добавление репозиториев Microsoft для различных дистрибутивов Linux описано в соответствующем документе.

Для установки PVS-Studio через пакетный менеджер также потребуется добавить репозитории PVS-Studio. Добавление репозиториев для различных дистрибутивов более подробно описано в соответствующем разделе документации.

Для работы анализатору необходим лицензионный ключ. Получить пробную лицензию можно на странице загрузки анализатора.

Примечание. Обратите внимание, что для описываемого режима работы (анализ merge requests) нужна Enterprise лицензия. Поэтому, если вы хотите попробовать данный режим работы, в поле "Сообщение" не забудьте указать, что вам нужна именно Enterprise лицензия.

Если происходит merge request, то нам потребуется проанализировать только список измененных файлов, а иначе анализируем все файлы. После анализа нужно сконвертировать логи в нужный нам формат.

Теперь, имея перед глазами алгоритм работы, можно переходить к написанию скрипта. Чтобы это сделать, необходимо изменить файл .gitlab-ci.yml или, если его нет, создать. Для его создания нужно нажать на название вашего проекта -> Set up CI/CD.

image5.png

Вот теперь мы готовы к написанию скрипта. Давайте сначала напишем код, который установит анализатор и введет лицензию:

before_script:  - apt-get update && apt-get -y install wget gnupg   - apt-get -y install git  - wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb  - dpkg -i packages-microsoft-prod.deb  - apt-get update  - apt-get install apt-transport-https  - apt-get update    - wget -q -O - https://files.viva64.com/etc/pubkey.txt | apt-key add -  - wget -O /etc/apt/sources.list.d/viva64.listhttps://files.viva64.com/etc/viva64.list  - apt-get update  - apt-get -y install pvs-studio-dotnet  - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY  - dotnet restore "$CI_PROJECT_DIR"/Test/Test.sln

Так как установка и активация должна происходить перед всеми другими скриптами, то используем специальную метку before_script. Немного поясню данный фрагмент.

Подготовка к установке анализатора:

  - wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb  - dpkg -i packages-microsoft-prod.deb  - apt-get update  - apt-get install apt-transport-https  - apt-get update

Добавление репозиториев PVS-Studio и анализатора:

  - wget -q -O - https://files.viva64.com/etc/pubkey.txt | apt-key add -  - wget -O /etc/apt/sources.list.d/viva64.listhttps://files.viva64.com/etc/viva64.list  - apt-get update  - apt-get -y install pvs-studio-dotnet

Активация лицензии:

  - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY

$PVS_NAME имя пользователя.

$PVS_KEY ключ продукта.

Восстановление зависимостей проекта, где $CI_PROJECT_DIR полный путь до директории проекта:

  - dotnet restore "$CI_PROJECT_DIR"/Path/To/Solution.sln

Для корректного анализа проект должен успешно собираться, а его зависимости должны быть восстановлены (например, должны быть загружены необходимые NuGet пакеты).

Задать переменные окружения, содержащие лицензионную информацию, можно, нажав на Setting, а после на CI / CD.

image6.png

В открывшимся окне находим пункт Variables, справа нажимаем на кнопку Expand и добавляем переменные. В результате должно получиться следующее:

image7.png

Теперь можно переходить к анализу. Сначала добавим скрипт для полного анализа. В флаг -t передаем путь до solution, в флаг -o пишем путь до файла, в который будут записаны результаты анализа. Также нас интересует код возврата. В данном случае нам интересно, чтобы работа прекращалась, когда код возврата содержит информацию о том, что в ходе анализа были выданы предупреждения. Вот как выглядит данный фрагмент:

job:  script:  - exit_code=0  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -o PVS-Studio.json || exit_code=$?  - exit_code=$((($exit_code & 8)/8))  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi

Коды возврата работают по принципу битовой маски. Например, если в результате анализа были выданы предупреждения, то код возврата будет равен 8. Если лицензия истечёт в течение месяца, то код возврата будет равен 4. Если же в ходе анализа были обнаружены ошибки, а также лицензия истекает в течение месяца, в код возврата будут записаны оба значения: складываем числа вместе и получаем итоговый код возврата 8+4=12. Таким образом, проверяя соответствующие биты, можно получать информацию о различных состояниях во время анализа. Более подробно коды возврата описываются в разделе "Коды возврата pvs-studio-dotnet (Linux / macOS)" документа "Проверка проектов Visual Studio / MSBuild / .NET Core из командной строки с помощью PVS-Studio".

В данном случае нас интересуют все коды возврата, где фигурирует 8.

  - exit_code=$((($exit_code & 8)/8))

Мы получим 1, когда код возврата содержит интересующий нас бит числа, а иначе получим 0.

Настало время добавить анализ merge request. Перед тем как это сделать, подготовим место для скрипта. Нам необходимо, чтобы он выполнялся только тогда, когда происходит merge request. Выглядит это вот так:

merge:  script:  only:  - merge_requests

Перейдем к самому скрипту. Я столкнулся с тем, что виртуальная машина ничего не знает про origin/master. Поэтому помогаем ей немного:

  - git fetch origin

Теперь получим разницу веток и сохраняем результат в txt файл:

  - git diff --name-only origin/master $CI_COMMIT_SHA > pvs-fl.txt

Где $CI_COMMIT_SHA хеш последнего коммита.

Далее запускаем анализ списка файлов, используя флаг -f. В него передаем полученный раннее .txt файл. Ну и по аналогии с полным анализом смотрим коды возврата:

  - exit_code=0  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -f pvs-fl.txt -o PVS-Studio.json || exit_code=$?  - exit_code=$((($exit_code & 8)/8))  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi

Полный скрипт для проверки merge request будет выглядеть вот так:

merge:  script:  - git fetch origin  - git diff --name-only origin/master $CI_COMMIT_SHA > pvs-fl.txt  - exit_code=0  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -f pvs-fl.txt -o PVS-Studio.json || exit_code=$?  - exit_code=$((($exit_code & 8)/8))  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi  only:  - merge_requests

Остается только добавить конвертацию лога после того, как отработали все скрипты. Используем метку after_script и утилиту plog-converter:

after_script:  - plog-converter -t html -o eLog ./PVS-Studio.json

Утилита plog-converter это open source проект, который используется для преобразования отчета об ошибках анализатора в различные формы, например, HTML. Более подробное описание утилиты приводится в подразделе "Утилита Plog Converter" соответствующего раздела документации.

Кстати, если вы хотите удобно работать с .json отчётом локально из IDE, то предлагаю наш плагин для IDE Rider. Более подробно его использование описано в соответствующем документе.

Для удобства вот .gitlab-ci.yml целиком:

image: debianbefore_script:  - apt-get update && apt-get -y install wget gnupg   - apt-get -y install git  - wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb  - dpkg -i packages-microsoft-prod.deb  - apt-get update  - apt-get install apt-transport-https  - apt-get update    - wget -q -O - https://files.viva64.com/etc/pubkey.txt | apt-key add -  - wget -O /etc/apt/sources.list.d/viva64.listhttps://files.viva64.com/etc/viva64.list  - apt-get update  - apt-get -y install pvs-studio-dotnet  - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY  - dotnet restore "$CI_PROJECT_DIR"/Test/Test.slnmerge:  script:  - git fetch origin  - git diff --name-only origin/master $CI_COMMIT_SHA > pvs-fl.txt  - exit_code=0  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -f pvs-fl.txt -o PVS-Studio.json || exit_code=$?  - exit_code=$((($exit_code & 8)/8))  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi  only:  - merge_requestsjob:  script:  - exit_code=0  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -o PVS-Studio.json || exit_code=$?  - exit_code=$((($exit_code & 8)/8))  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi  after_script:  - plog-converter -t html -o eLog ./PVS-Studio.json

Как только добавили все в файл, нажимаем на Commit changes. Для того чтобы посмотреть, что все правильно, заходим в CI/CD -> Pipelines -> Running. Откроется окно виртуальной машины, в конце которой должно быть следующее:

image8.png

Увидели Job succeeded успех, все прекрасно. Теперь можно и протестировать сделанное.

Примеры работы


Для примера работы создадим простой проект (в master) в котором будет несколько файлов. После этого в другой ветке изменим только один файл и попробуем сделать merge request.

Рассмотрим два случая: когда измененный файл содержит ошибку и когда нет. Сначала пример с ошибкой.

Допустим, в master ветке есть файл Program.cs, который не содержит ошибок, а в другой ветке разработчик добавил ошибочный код и хочет сделать merge request. Какую именно ошибку он допустил не столь важно, главное, что она есть. Например, забыл оператор throw (да, так ошибаются):

void MyAwesomeMethod(String name){  if (name == null)    new ArgumentNullException(....);  // do something  ....}

Посмотрим на результат анализа примера с ошибкой. Также чтобы убедиться в том, что только один файл был проанализирован, я добавил флаг -r в строку запуска pvs-studio-dotnet:

image9.png

Мы видим, что анализатор нашел ошибку и не позволил сделать слияние веток.

Проверяем пример без ошибки. Исправляем код:

void MyAwesomeMethod(String name){  if (name == null)    throw new ArgumentNullException(....);  // do something  ....}

Результаты анализа merge request:

image10.png

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

Заключение


Отсеивать плохой код до слияния веток это очень удобно и приятно. Поэтому, если вы пользуетесь CI/CD, попробуйте встроить статический анализатор для проверки. Тем более что делается это достаточно просто.

Спасибо за внимание.


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Nikolay Mironov. Analysis of merge requests in GitLab using PVS-Studio for C#.
Подробнее..

Кто ответит за качество аналитики QA для Хранилища Данных

02.11.2020 22:09:30 | Автор: admin

Поверь своим глазам и тому что видишь на Дашборде

Мы в Wheely в значительной степени опираемся на данные при принятии операционных и стратегических решений. От выплаты еженедельных бонусов партнерам до экспансии в другие города и страны.

Каждый менеджер или Product Owner досконально знает свою область и любые отклонения могут вызвать вопросы. Поэтому к достоверности дашбордов и метрик предъявляются повышенные требования. А мы в команде Аналитики стремимся идентифицировать и исправить проблемы раньше, чем они попадут в отчетность.

Как известно, легче предотвратить, и поэтому я решил подойти к проблеме системно и проактивно. И, конечно, первым делом создал канал в Slack, в который настроил доставку уведомлений о любых ошибках в наших пайплайнах.

Уверенность в актуальности витрин данных

Прежде всего мы хотим убедиться в том, что пользовательские витрины содержат актуальные на момент времени данные:

  • К 10 утра каждого дня у нас рассчитаны витрины за полные прошлые сутки

  • Чтение из источников идет в ногу со временем и отставание не превышает 8 часов

  • Все источники продолжают слать лог изменений в DWH

Выходит, задача QA формулируется следующим образом:

  • Покажи мне все витрины данных, в которых время актуальности отстает от ожидаемого

Реализация для Хранилища:

  • В конфигурационном файле .yml добавим параметр freshness:

freshness:   warn_after: {count: 4, period: hour}   error_after: {count: 8, period: hour} loaded_at_field: "__etl_loaded_at"
  • Для каждого теста будет выполнен простой шаблонизированный SQL-запрос:

select max({{ loaded_at_field }}) as max_loaded_at, {{ current_timestamp() }} as snapshotted_atfrom {{ source }}where {{ filter }}
  • Собранные метрики можно визуализировать в сводный отчет:

Мониторинг метрик расчета Витрин Данных

Неминуемо при проектировании сложных витрин, кубов, будут возникать проблемные места:

  • Баги и просчеты в формулах расчета метрик

  • Неожиданные данные (edge cases), которые могут нарушать заложенную логику

  • Бутылочное горлышко (bottleneck) в операциях расчетов

Они могут привести к серьезным последствиям:

  • Ошибки: Таймаут, Out of Memory, Disk Full

  • Замедление всего пайплайна загрузок и расчетов и нарушение SLA

Для контроля можно собирать следующие метрики:

  • Время, затраченное на формирование витрины + его динамика (скачки времени расчета)

  • Потребление ресурсов CPU

  • Потребление ресурсов диска и сети - IO, network

Лидеры этого рейтинга становятся первыми кандидатами на оптимизацию и рефакторинг.

Задача формулируется следующим образом:

  • Покажи мне те витрины, формирование которых требует излишне много ресурсов

Реализация для Хранилища:

  • Снять метрики расчетов витрин

  • Отрисовать дашборд

  • Настроить алерты

+pre-hook: "{{ logging.log_model_start_event() }}"+post-hook: "{{ logging.log_model_end_event() }}"

Валидация схемы данных в основе тестирования

Современные Хранилища предполагают структуру, строгую типизацию, поколоночное хранение и компрессию данных. Структура данных суть схема - набор атрибутов, их типов, ограничений, например, PRIMARY KEY, FOREIGN KEY, NOT NULL, UNIQUE.

Большинство DWH не предполагает валидацию ограничений на этапе записи. Это нужно скорее в рамках подсказок оптимизатору запросов. Т.е. Это всего лишь предположение о данных, а фактическая проверка остается на откуп инженерам и аналитикам.

Какие базовые ожидания можем иметь относительно данных:

  • Есть ли в данных пропуски (NULL) там, где их быть не должно?

  • Какова атомарность моих данных (UNIQUE ID строки)?

  • Как таблицы соотносятся между собой (PRIMARY - FOREIGN KEYS)?

  • Есть ли записи, выходящие из списка допустимых значений(ACCEPTED VALUES)?

Задача QA формулируется следующим образом:

  • Покажи мне те витрины и источники, данные в которых нарушают наши ожидания

Реализация для Хранилища:

  • В конфигурационном файле .yml добавим параметр tests:

- name: dim_cars     description: Wheely partners cars.     columns:         - name: car_id           tests:               - not_null               - unique         - name: status           tests:               - not_null               - accepted_values:                   values: ['deleted', 'unknown', 'active', 'end_of_life', 'pending', 'rejected'                           , 'blocked', 'expired_docs', 'partner_blocked', 'new_partner']   
  • Для каждого теста будет выполнен простой шаблонизированный SQL-запрос

-- NOT NULL testselect count(*) as validation_errorsfrom "wheely"."dbt_test"."dim_cars"where car_id is null -- UNIQUE testselect count(*) as validation_errorsfrom (   select       car_id   from "wheely"."dbt_test"."dim_cars"   where car_id is not null   group by car_id   having count(*) > 1) validation_errors -- ACCEPTED VALUES testwith all_values as (   select distinct       status as value_field   from "wheely"."dbt_test"."dim_cars"),validation_errors as (   select       value_field   from all_values   where value_field not in (       'deleted','unknown','active','end_of_life','pending','rejected','blocked','expired_docs','partner_blocked','new_partner'   ))select count(*) as validation_errorsfrom validation_errors

Бизнес-логика тоже подлежит проверке

Использую термин бизнес-логика в широком смысле - это любая сколь угодно сложная логика расчетов и взаимосвязей, которую мы закладываем в витрины данных. По сути это и есть бизнес-требования, лежащие в основе формирования Витрин.

Несколько простых примеров:

  • Сумма заказа не может быть отрицательной

  • Время подачи машины по заказу строго больше времени бронирования

  • Пользовательская сессия заканчивается только одним заказом, либо прерывается

  • Комиссия не превышает заданного %

Резкие скачки сумм (и вверх и вниз), неправдоподобные графики и значения метрик чаще всего становятся объектом пристального внимания на дашбордах и в отчетах.

Задача QA формулируется следующим образом:

  • Покажи мне те витрины данных, в которых нарушены бизнес-правила.

Реализация для Хранилища:

  • В терминах SQL выразить ту ситуацию, которая описывает нарушение правил

  • Сформировать тест на базе SQL-запроса

  • Тест считается пройденным (PASSED) если запрос возвращает 0 записей, и проваленным (FAILED) если записей >= 1

Continuous Integration на страже мастер-ветки DWH

Хорошо, идём дальше. Над DWH мы работаем совместно всей командой. Это подразумевает скоординированность и согласованность действий. Однако нередки случаи ошибок, просчеты, невнимательности на этапе разработки, которые приводят к ошибкам в PROD-окружении после PR Merge:

  • Доработка в одной части может послужить причиной ошибки в другой части

  • DEV-окружение инженера может отличаться от PROD-окружения

  • Запуск неоптимального кода на всех данных может привести к ошибке (например, Out of Memory)

Решение давно придумано - это использование практик тестирования в рамках Continuous Integration (CI). И его можно и нужно применять к данным!

Задача формулируется следующим образом:

  • Минимизировать вероятность появления ошибок в master-ветке и PROD-окружении DWH после релизов.

Реализация для Хранилища:

  • Подготовить окружение для CI (например, актуальная копия PROD-окружения, содержащая только 7 последних дней)

  • Выполнить полный пересчет всех витрин и метрик без ошибок прежде чем дать возможность влить feature-ветку в master

Кросс-сверка состояния DWH и Источников

От Хранилища Данных мы ожидаем отображение актуального состояния (а также всей истории) источников данных:

  • Наличие в DWH всех записей, которые присутствуют в источнике

  • Точное соответствие значений атрибутов (статус, временные метки, метрики) один-к-одному

Мы хотели бы застраховаться от ситуаций, когда часть данных просто не попадает в Хранилище в силу технических причин, а также от потери изменений состояния записей (например, финализация статуса заказа или корректировка его стоимости).

Задача формулируется следующим образом:

  • Убедиться в том, что Хранилище находится в консистентном (согласованном) с источниками состоянии.

Эта задача имеет одну из самых сложных реализаций и может стать темой отдельной статьи, судите сами:

  • Определить набор атрибутов, подлежащих верификации: идентификаторы, внешние ключи, измерения (страна, город, статус), метрики (стоимость, продолжительность, комиссия).

  • Выгрузить все строки из источника, актуальные на текущий момент

  • Загрузить строки в DWH и подготовить логику сверок

  • Настроить визуализацию и уведомления

Визуальное представление с возможностью drill-down до уровня атомарных записей:

Собирая всё в единый пазл

Идеи и принципы, изложенные в этой статье прозрачны и ясны. Сегодня пришло время применить лучшие практики разработки и тестирования в областях, связанных с обработкой данных:

  • Регулярный мониторинг, сбор и анализ метрик

  • Continuous Integration and Testing

  • Настройка уведомлений и алертов для команды

  • Проактивная работа над устранением инцидентов и причин ошибок

  • Управление ожиданиями пользователей в случае возникновения проблем (У нас всё под контролем)

В этой статье я показал подход к контролю качества данных, который мы выстраиваем в команде Wheely. Надеюсь, вы почерпнули для себя много нового и полезного.

Обширный набор тем, связанных с обработкой, хранением, тестированием данных изучается в рамках курса Data Engineer в OTUS, запуск которого состоится уже совсем скоро.

Как преподаватель курса я приглашаю вас 4 ноября в 20:00 на День Открытых Дверей курса Data Engineer. Приходите на вебинары в OTUS знакомиться со мной и другими экспертами, будем ждать.

Что почитать еще

Напоследок я оставлю вам несколько ссылок на смежную тематику для дальнейшего изучения:

  1. Data Build Tool или что общего между Хранилищем Данных и Смузи - обзор DBT на русском языке

  2. The farm-to-table testing framework - комплексный подход к тестированию качества данных

  3. Tests - Related reference docs - раздел документации DBT, посвященный тестированию

  4. How to get started with data testing - тред на dbt discourse с обсуждением по теме

  5. Data testing: why you need it - взгляд на преимущества тестирования данных

  6. Manual Work is a Bug - несколько слов о принципах автоматизации и DRY

Подробнее..

Перевод Вышел релиз GitLab 13.7 с проверяющими для мерж-реквестов и автоматическим откатом при сбое

12.01.2021 16:20:24 | Автор: admin

Картинка для привлечения внимания


Ну и год же был 2020! Мы счастливы представить релиз 13.7 с более чем 45 фичами и улучшениями поставки ПО, вышедший как раз к праздникам.


От имени всех сотрудников GitLab мы хотим поблагодарить участников нашего сообщества за ваш вклад и за то положительное влияние, которое вы оказываете на нашу работу. Без вас GitLab не был бы таким, как сейчас.


Благодарим вас и всех членов команды GitLab, которые помогли сделать 2020 год невероятным, несмотря на невзгоды и непредсказуемые обстоятельства. Пожалуйста, оставайтесь в безопасности, будьте счастливы и здоровы в этот праздничный период.


Вот что вас ждёт в релизе 13.7:


Улучшенное управление проектами для развития совместной работы


Мерж-реквесты (в русской локализации GitLab запросы на слияние) имеют решающее значение для развития совместной работы и могут быть напрямую связаны с соответствующими тикетами, предоставляя центральное место для общения посредством комментариев, предложений по изменениям в коде и ревью кода. В этом релизе мы добавили проверяющих для мерж-реквестов способ улучшить процесс ревью кода, сделав его более лёгким и организованным. Теперь вы сможете быстро выяснить, кто участвовал в мерж-реквесте, или запросить у кого-то ревью, отправив соответствующее уведомление.


Переключение контекста и выполнение заданий вручную в рабочем процессе затрудняют эффективное взаимодействие между группами и проектами. Это означает, что вы проводите меньше времени за разработкой ценных фич и больше времени тратите на работу с проектами, поэтому возможность клонировать тикеты с помощью быстрых действий наверняка вам пригодится, поскольку поможет оптимизировать управление проектами и гибкое планирование с agile.


Совместная работа над проектами и частое итерирование при разработке ваших приложений подразумевают, что вы должны быть в состоянии быстро определить степень важности ваших тикетов, выявить любые блокирующие задачи и использовать эту информацию для расстановки приоритетов при принятии решений. Теперь вы можете сортировать по количеству блокируемых тикетов, чтобы быстрее находить те тикеты, которые блокируют прогресс по другим тикетам.


Улучшена автоматизация релизов и гибкость развёртывания


В управлении организацией, автоматизацией и развёртыванием приложений на регулярной основе вам необходима гибкость. Надёжное и частое развёртывание приложений позволяет быстрее поставлять продукт заказчикам.


Чтобы улучшить автоматизацию релизов в GitLab, мы добавили автоматический откат в случае сбоя. Эта фича отменяет неудачное развёртывание, восстанавливает последнее успешное развёртывание и посылает автоматическое уведомление об этом. Вам не придётся вносить изменения вручную, и вы можете быть уверены, что потенциальные проблемы не приведут к простоям и ситуация не ухудшится, пока вы работаете над устранением неполадок.


Ещё одно улучшение, которое хорошо сочетается с автоматическим откатом в случае сбоя, возможность посмотреть статус развёртывания на странице окружений. Просматривать статусы развёртываний стало гораздо удобнее, как и определять, какие действия вам нужно предпринять, например, остановить или откатить развёртывание.


Мы также выпустили первую официально поддерживаемую бета-версию контейнера обработчика заданий GitLab для Red Hat OpenShift, а также нашего сертифицированного оператора обработчиков заданий, чтобы дать вам больше свободы в том, как вы выпускаете релизы с GitLab. Мы работаем над тем, чтобы сделать эту фичу общедоступной, так что следите за новостями в будущих релизах.


Более надёжное и эффективное управление пакетами и зависимостями


Ваш рабочий процесс зависит от множества языков программирования, бинарных файлов, интеграций и артефактов, которые являются важными входными или выходными данными в вашем процессе разработки. Чем эффективнее вы управляете своими пакетами и зависимостями, тем меньше рабочего времени уходит впустую. Чтобы повысить эффективность работы с реестром пакетов, мы добавили быстрый поиск и просмотр общих пакетов.


Мы также внесли улучшения в прокси зависимостей от GitLab; кстати, эта фича была перенесена в Core в GitLab 13.6.


Теперь вы можете вписаться в ограничения запросов Docker и ускорить работу конвейеров (в русской локализации GitLab сборочные линии) благодаря кэшированию образов контейнеров, размещённых на DockerHub, и быть уверенными в надёжности и эффективности работы с образами.


Ещё одно улучшение, которого ожидали многие из нашего сообщества, прокси зависимостей теперь работает и с приватными проектами, так что теперь этой фичей смогут воспользоваться и те пользователи, которые предпочитают работать с приватными проектами.


И, наконец, вы можете использовать предопределённые переменные с прокси зависимостей взамен того, чтобы полагаться на свои собственные определённые переменные или вшитые значения в вашем файле gitlab.ci-yml. Таким образом, появился более масштабируемый и эффективный способ начать проксирование и кэширование образов.


И это ещё не всё!


Взгляните на ещё несколько классных новых фич релиза 13.7:



Это всего лишь некоторые из множества новых фич и улучшений производительности этого релиза. Если вы хотите заранее узнать, что вас ждёт в следующем месяце, загляните на страницу будущих релизов, а также посмотрите наше видео по релизу 13.8.


6-7го января у нас прошёл виртуальный хакатон, узнайте больше об этом и будущих мероприятиях от GitLab здесь.


GitLab MVP badge


MVP этого месяца Rachel Gottesman


Rachel активно помогала нашей команде технических писателей внедрить согласованный стиль в документации для исправления использования будущего времени в наших документах. В релизе 13.7 Rachel открыла 33 мерж-реквеста, чем помогла нашей команде справиться с этой важной и кропотливой задачей. Спасибо, Rachel!


Основные фичи релиза GitLab 13.7


Проверяющие для мерж-реквестов


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Create


Попросить коллегу просмотреть ваш код должно быть обычной частью рабочего процесса, но иногда это бывает неоправданно сложно. Такая простая задача, как запрос на ревью, может привести к путанице. Например, как именно следует запрашивать: через электронное письмо, комментарий, сообщение в чате? Без формального механизма ревью мерж-реквестов могут быть непоследовательными, и за их проведением будет сложно уследить. Раньше уже можно было назначить проверяющего на мерж-реквест, но и автор, и рецензент отображались в одном и том же поле Assignee, что не позволяло другим членам команды узнать, кто за что отвечал в этом мерж-реквесте.


GitLab 13.7 представляет новую фичу теперь авторы мерж-реквеста могут запросить ревью у конкретных пользователей. В новом поле Reviewers пользователи могут быть назначены проверяющими для мерж-реквеста аналогичным образом, как назначаются ответственные (assignee). Назначенные на ревью пользователи получат уведомление, предлагающее им просмотреть мерж-реквест. Таким образом, теперь существует формальный процесс запроса ревью для мерж-реквеста и уточняются роли каждого пользователя в работе над ним.


Будущие итерации будут включать отображение наиболее подходящих проверяющих для мерж-реквеста, а также усовершенствованный процесс подтверждения мерж-реквеста, который будет сфокусирован именно на проверяющих. Подробнее об этом вы можете узнать в эпике по назначению проверяющих для ревью мерж-реквестов.


Reviewers for Merge Requests


Документация по назначению проверяющих для мерж-реквеста и оригинальный тикет.


Автоматический откат в случае сбоя


(ULTIMATE, GOLD) Стадия цикла DevOps: Release


Если у вас возникла критическая проблема с развёртыванием, ручные действия по её устранению могут занять довольно много времени и привести к сбоям на продакшене, что негативно скажется на пользователях. Теперь вы можете использовать автоматический механизм отката, который возвращает ваше развёртывание обратно к последнему успешному развёртыванию. Кроме того, когда GitLab находит проблемы на продакшене, он автоматически уведомляет вас об этом. Так вы всегда будете своевременно узнавать о проблемах, и получите драгоценное время для отладки, исследования и исправления проблем без лишних простоев в работе.



Документация по автоматическому откату при сбое и оригинальный тикет.


Клонирование тикета через быстрое действие


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Plan


Чтобы сделать создание похожих тикетов более эффективным, тикеты теперь поддерживают быстрое действие /clone, которое создаёт в том же проекте новый тикет с идентичными названием, описанием и метаданными. Быстрое действие /clone заменяет более трудоёмкий процесс, который включал в себя несколько шагов для создания тикета, копирования идентификатора или пути к исходному тикету и использование быстрого действия copy_meta.


По умолчанию тикеты клонируются в тот же проект и не включают в себя системные заметки и комментарии, но вы можете изменить поведение по умолчанию при клонировании.


Clone an issue with a quick action


Документация по быстрым действиям и оригинальный тикет.


Обработчик заданий GitLab для Red Hat OpenShift


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Verify


Наконец-то стал доступен образ контейнера обработчика заданий GitLab (GitLab runner) для платформы контейнеров Red Hat OpenShift! Чтобы установить обработчик заданий на OpenShift, воспользуйтесь новым оператором обработчиков заданий GitLab. Он доступен на бета-канале в Red Hat's Operator Hub веб-консоли для администраторов кластеров OpenShift, уже развёрнутой по умолчанию на платформе, где они могут найти и выбрать операторы для установки на своём кластере. На платформе контейнеров OpenShift Operator Hub уже развёрнут по умолчанию. Мы планируем перенести оператор обработчика заданий GitLab на стабильный канал, а в начале 2021 года в общий доступ. Наконец, мы также разрабатываем оператор для GitLab, так что следите за будущими публикациями.


GitLab Runner for Red Hat OpenShift


Документация по установке на OpenShift и оригинальный тикет.


Просмотр статуса развёртываний на странице окружений


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Release


Ранее на странице окружений не было никакого способа узнать о том, что сейчас выполняется развёртывание. Теперь на этой странице отображаются статусы развёртываний и предупреждения, благодаря чему вы можете принять решение о том, какие действия нужно предпринять, основываясь на статусе развёртывания (успешное, неудачное или текущее). Например, можно остановить текущее развёртывание или откатить завершённое.


Show deployment status on the Environments page


Документация по работе с окружениями и оригинальный тикет.


Задавайте через пользовательский интерфейс процент трафика для канареечного развёртывания


(PREMIUM, ULTIMATE, SILVER, GOLD) Стадия цикла DevOps: Release


В GitLab 13.7 вы можете менять процент трафика для канареечного развёртывания (значение canary-weight) непосредственно с досок развёртывания в пользовательском интерфейсе. Вы также можете менять это значение из gitlab-ci.yml и через API, но сделав это в пользовательском интерфейсе, вы сможете наблюдать за развёртыванием и при необходимости отмасштабировать поды прямо с досок развёртывания. Так у вас будет больше контроля над ручными или инкрементальными развёртываниями по таймеру, а также вы сможете лучше контролировать и даже снижать риски.


Set deployment traffic weight via the UI


Документация по заданию процента трафика для канареечного развёртывания и оригинальный тикет.


Просмотр частоты развёртываний через API


(ULTIMATE, GOLD) Стадия цикла DevOps: Release


В этом релизе в рамках первой итерации встроенной в GitLab поддержки метрик DORA4 была добавлена возможность узнавать частоту развёртываний на уровне проекта через API. Это позволяет отслеживать эффективность развёртывания с течением времени, легко находить узкие места и при необходимости быстро принимать меры по корректировке.


API support for deployment frequency


Документация по аналитике проектов и оригинальный тикет.


Поддержка нескольких файлов манифеста в проекте


(PREMIUM, ULTIMATE) Стадия цикла DevOps: Configure


В предыдущих релизах для использования нашего агента для Kubernetes (GitLab Kubernetes Agent) требовалось, чтобы пользователи собирали все ресурсы Kubernetes в один файл манифеста. Теперь же агент может собирать манифесты Kubernetes рекурсивно из указанных каталогов в вашем проекте. Ваши специалисты по платформе теперь смогут использовать один репозиторий для управления различными кластерами из единого места, а также описывать крупные развёртывания с помощью одного агента.


Support multiple manifest files in a project


Документация по настройке репозитория с агентом Kubernetes и оригинальный тикет.


Импорт требований из внешних инструментов


(ULTIMATE, GOLD) Стадия цикла DevOps: Plan


Для эффективной совместной работы очень важно, чтобы все ваши требования хранились в одном месте, поэтому мы рады сообщить, что теперь вы можете импортировать требования из CSV-файлов!


Благодаря этой фиче вы сможете всей командой работать над требованиями в GitLab, и в то же время с лёгкостью взаимодействовать с клиентами, поставщиками и внешними организациями. Следите за обновлениями о будущих улучшениях экспорта требований.


Import requirements from external tools


Документация по импорту требований из CSV-файла и оригинальный тикет.


Несколько конечных точек HTTP для оповещений


(PREMIUM, ULTIMATE, SILVER, GOLD) Стадия цикла DevOps: Monitor


Интеграции оповещений являются критически важной частью ваших рабочих процессов по управлению инцидентами, поэтому важно иметь точный контроль над конечными точками и токенами аутентификации. Меньше всего вам нужно, чтобы все ваши оповещения перестали работать из-за смены одного токена аутентификации. Настройка конечной точки HTTP для каждого инструмента мониторинга позволит вашей команде управлять каждым инструментом в отдельности, не влияя на оповещения от других инструментов.


Integrate alerting tools with multiple HTTP endpoints


Документация по конечным точкам для оповещений и оригинальный эпик.


Синхронизация групп на GitLab.com с помощью SAML


(SILVER, GOLD) Стадия цикла DevOps: Manage


В GitLab 13.7 вы можете привязать группу в вашем поставщике учётных записей к группе на GitLab.com с помощью SAML. Членство в группе будет обновляться, когда пользователь войдёт в учётную запись GitLab через своего провайдера SAML. Эта фича снижает необходимость в ручном назначении групп, что снижает загрузку по группам для администраторов GitLab. Синхронизация групп также улучшает адаптацию новых членов групп, избавляя их от необходимости запрашивать доступ у администраторов групп GitLab.


SAML Group Sync for GitLab.com


Документация по синхронизации групп с помощью SAML и оригинальный эпик.


Другие улучшения в GitLab 13.7


DevOps Adoption


(ULTIMATE) Стадия цикла DevOps: Manage


DevOps Adoption показывает вам, какие команды в вашей организации используют тикеты, мерж-реквесты, подтверждения, обработчики заданий, конвейеры, развёртывания и сканирования в GitLab. Используйте также новую фичу сегменты, чтобы собрать ваши группы GitLab в логические единицы в рамках вашей организации, чтобы вы могли сравнивать, как используют GitLab среди нескольких разных групп.


  • Убедитесь, что вы получаете ожидаемую отдачу от использования GitLab.
  • Узнайте, какие группы отстают во внедрении GitLab, и помогайте им с настройкой DevOps.
  • Узнайте, какие группы используют определённые фичи, такие как конвейеры, и могут поделиться своим опытом с другими группами, которые хотят начать ими пользоваться.

DevOps Adoption


Документация по DevOps Adoption и оригинальный тикет.


Улучшенный пользовательский интерфейс для создания проектов


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Manage


С улучшенным пользовательским интерфейсом для добавления проекта вам будет проще выбрать между созданием пустого проекта, проекта из шаблона, импортом проекта и созданием проекта CI/CD для внешних репозиториев.


Документация по созданию проектов и оригинальный тикет.


Ограничение создания проектов и групп для внешних аккаунтов


(PREMIUM, ULTIMATE, SILVER, GOLD) Стадия цикла DevOps: Manage


Для снижения риска случайного раскрытия интеллектуальной собственности администраторы GitLab теперь имеют больший контроль над аккаунтами, предоставляемыми через интеграцию группы с помощью SAML или SCIM. В первой итерации этой фичи администраторы могут запрещать своим пользователям создавать группы или проекты вне групп, к которым они принадлежат.


Документация по настройкам пользователя через SAML и оригинальный тикет.


Сортировка по числу блокируемых тикетов


(STARTER, PREMIUM, ULTIMATE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Plan


При сортировке списка тикетов в GitLab часто бывает важно определить, какие из них необходимо выполнить наиболее срочно, и блокируют ли они другие тикеты.


Ранее в списке тикетов было невозможно посмотреть, какие из тикетов блокируют другие. Единственный способ это сделать это открыть каждый из них и просмотреть список блокируемых тикетов под описанием тикета, что занимает очень много времени.


В версии 13.7 вы можете использовать фильтр Блокирует другие тикеты для списка тикетов, чтобы отсортировать его по числу блокируемых тикетов.


Sort issues by the number of issues they are blocking


Документация по сортировке и оригинальный тикет.


Просмотр файлов в мерж-реквестах по одному


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Create


Ревью мерж-реквестов это важнейшая задача для обеспечения качества кода всех участников, так как именно здесь происходит большая часть обсуждений между автором и ревьюером. Однако, по мере того, как мерж-реквесты становятся крупнее и число файлов в них увеличивается, навигация и производительность диффов могут ухудшаться.


В GitLab 13.7 мы добавили возможность просматривать файлы по одному при просмотре мерж-реквеста. При переходе на вкладку мерж-реквеста Изменения кликните на иконку с шестерёнками и поставьте галочку Показывать файлы по одному. После выбора этого параметра вы сможете просматривать файлы по одному, переключаясь между ними при помощи кнопок Предыдущий и Следующий.


В этом режиме ваше рабочее пространство выглядит менее перегруженным, и вам проще сосредоточиться на одном файле. Кроме того, при этом повышается производительность и улучшается навигация по диффу мерж-реквеста.


Choose to show one file at a time directly from merge requests


Документация по ревью и управлению мерж-реквестами и оригинальный тикет.


Просмотр изменений мерж-реквеста в VS Code


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Create


При ревью мерж-реквестов в VS Code для обращения к изменениям зачастую приходится делать checkout ветки и затем пытаться разобраться в диффе между этой веткой и целевой веткой мержа.


С релизом 3.7.0 расширения GitLab Workflow изменения мерж-реквеста стали доступны напрямую в VS Code. Это позволяет быстро просматривать изменения в мерж-реквестах ваших проектов.


В рамках работы над добавлением полноценного ревью кода в VS Code следующим шагом мы собираемся добавить комментарии к диффам.


View Merge Request changes in VS Code


Документация по расширению для VS Code и оригинальный тикет.


Улучшенное скачивание артефактов для вложенных конвейеров


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Verify


Ранее при скачивании артефактов из родительского конвейера во вложенный не всегда можно было получить нужные артефакты. Если два вышестоящих конвейера были запущены примерно в одно время, то вложенный конвейер мог скачать артефакты и из более нового конвейера.


Теперь вы можете использовать новый синтаксис needs:pipeline, чтобы указать вложенному конвейеру, из какого конвейера ему нужно скачивать артефакты. Вы можете использовать его, чтобы скачивать артефакты из родительского конвейера или из другого вложенного конвейера в рамках той же иерархии.


Improved artifact downloads with child pipelines


Документация по скачиванию артефактов для вложенных конвейеров и оригинальный тикет.


Обход ограничений Docker и ускорение конвейеров


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Package
Для более быстрых и надёжных сборок вы можете использовать нашу фичу, прокси зависимостей, для кэширования образов контейнеров из Docker Hub. Однако, когда Docker начал применять ограничения по количеству запросов docker pull, вы могли заметить, что даже когда ваш образ скачивался из кэша, Docker всё равно засчитывал его в лимит. Это происходило потому, что прокси зависимостей кэшировал только слои (или блоб-объекты) образа, но не манифест, который содержит информацию о том, как собрать данный образ. Так как манифест был необходим для сборки, всё равно приходилось выполнять pull. А если Docker Hub был недоступен, вы не могли скачать нужный образ.
Начиная с этого релиза прокси зависимостей будет кэшировать и слои, и манифест образа. Так что при первом скачивании с использованием alpine:latest образ будет добавлен в кэш прокси зависимостей, и это будет считаться за один pull. В следующий раз, когда вы будете скачивать alpine:latest, всё будет скачиваться из кэша, даже если Docker Hub недоступен, и это скачивание не будет учитываться в лимите Docker.


Напоминаем вам, что начиная с 13.6 прокси зависимостей стал доступен в плане Core. Поэтому попробуйте эту фичу сейчас и скажите нам, что вы о ней думаете. Или даже лучше, помогите нам с открытыми тикетами.



Документация по прокси зависимостей и оригинальный тикет.


Быстрый поиск и просмотр общих пакетов


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Package


Вы можете с лёгкостью публиковать общие файлы, такие как бинарные файлы релиза, через реестр пакетов GitLab. Однако, если вы используете пользовательский интерфейс пакетов или другой формат менеджера пакетов, то возможно вы заметили, что не можете выбрать вкладку для быстрого просмотра только ваших общих пакетов.


Недавно мы добавили в пользовательский интерфейс пакетов вкладку Общие пакеты, чтобы вы могли отфильтровать список пакетов для просмотра только общих пакетов. Мы надеемся, что это поможет вам находить и проверять ваши пакеты более быстро и эффективно.



Документация по просмотру пакетов и оригинальный тикет.


Прокси зависимостей для приватных проектов


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Package


Вы можете использовать прокси зависимостей GitLab для проксирования и кэширования образов контейнеров из Docker Hub. До последнего времени эта фича была доступна только для публичных групп, что не позволяло многим пользователям её использовать.


Теперь вы можете использовать прокси зависимостей и для приватных проектов. Вы можете снизить свои скачивания с Docker Hub путём кэширования ваших образов контейнера для использования их в будущем. Так как прокси зависимостей хранит образы Docker в пространстве, связанном с вашей группой, вы должны авторизоваться с вашим логином и паролем GitLab или с личным токеном доступа, для которого есть разрешение как минимум на чтение (read_registry).


Документация по прокси зависимостей и оригинальный тикет.


Улучшенная поддержка анализаторов SAST для нескольких проектов


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Secure


В GitLab сканирования безопасности автоматически определяют язык кода и запускают подходящие анализаторы. В случае монорепозиториев, микросервисов и репозиториев для нескольких проектов в одном репозитории GitLab может существовать больше одного проекта. Ранее наши инструменты безопасности могли определять язык кода только в одиночных проектах в репозиториях. Начиная с этого релиза наши анализаторы SAST могут определять языки кода в репозиториях с несколькими проектами, запускать подходящие сканирования для каждого проекта и составлять отчёт о уязвимостях. Это делает
управление инструментами безопасности проще и удобнее для пользователей с одним репозиторием для нескольких проектов. В следующих релизах мы продолжим улучшать панель безопасности и отчёты для этих типов проектов.


Документация по поддержке репозиториев для нескольких проектов и оригинальный эпик.


Описание релиза во внешнем файле


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Release


Если вы создаёте релизы в конвейерах через файл .gitlab-ci.yml вашего проекта, вам, возможно, было сложно поддерживать в актуальном состоянии описание каждого релиза. В релизе GitLab 13.7 вы можете задавать описание вашего релиза в файле с контролем версий или в автоматически генерируемом файле и вызывать его из .gitlab-ci.yml. При этом содержимое файла загружается в описание релиза в формате Markdown. Это упрощает создание, поддержку и использование контроля версий для релизов, а также будет особенно полезно при автогенерации лога изменений. Огромное спасибо Nejc Habjan и Siemens за этот невероятный вклад!



Документация по описаниям релизов и оригинальный тикет.


Поддержка для версий Kubernetes 1.17, 1.18 и 1.19


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Configure


Поддержка GitLab для последних версий Kubernetes позволяет вам пользоваться преимуществами интеграций GitLab с Kubernetes, такими как GitLab Kubernetes Agent, Auto DevOps и на более поздних кластерах GitLab Managed Apps. В этом релизе GitLab добавил официальную поддержку для версий Kubernetes 1.17, 1.18 и 1.19.


Документация по кластерам и оригинальный тикет.


Geo поддерживает репликацию сниппетов


(PREMIUM, ULTIMATE) Доступность


Geo теперь поддерживает репликацию сниппетов под контролем версий на вторичные ноды, что позволит распределённым командам получать доступ к ним с ближайшей ноды Geo, что снижает задержку и делает весь процесс удобнее. Кроме того, эти данные могут быть восстановлены со вторичной ноды при переключении на неё в случае сбоя.


В данный момент мы не поддерживаем верификацию для этого типа данных, но мы планируем добавить её в будущем.


Документация по репликации Geo и оригинальный эпик.


Поддержка зашифрованных учётных данных LDAP


(CORE, STARTER, PREMIUM, ULTIMATE) Доступность


GitLab использует единый файл конфигурации, например gitlab.rb в Omnibus GitLab, что упрощает настройку всех связанных сервисов. В этот файл конфигурации включены некоторые секретные ключи, например учётные данные для аутентификации на сервере LDAP. Хотя для доступа к этому файлу требуются специальные права, хорошей практикой считается отделять секретные ключи от конфигурации.


Установки Omnibus GitLab и Source теперь поддерживают зашифрованные учётные данные, причём первыми поддерживаемыми учётными данными стали LDAP. Это снижает уязвимость конфигурационного файла GitLab, а также помогает достичь соответствия требованиям заказчика.


Документация по настройке зашифрованных учётных данных LDAP и оригинальный тикет.


Веб-хуки при добавлении новых участников группы


(STARTER, PREMIUM, ULTIMATE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Manage


Теперь в GitLab стало проще автоматизировать управление пользователями: это можно сделать с помощью веб-хука, который запускается при добавлении нового участника в группу. Раньше вам приходилось выполнять запросы REST для идентификации новых членов группы, что ухудшало производительность вашего инстанса GitLab.


Документация по веб-хукам и оригинальный тикет.


Улучшенная фильтрация и сортировка списков участников группы


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Manage


Мы продолжили улучшать наш список участников группы и добавили для него новые возможности фильтрации и сортировки. Это поможет администраторам группы быстро находить нужную им информацию. Например, сортировку по последнему времени входа (Last sign-in) можно использовать для поиска пользователей, которые в последнее время не заходили на GitLab, и для помощи в управлении лицензиями.


Improved group members list filtering and sorting


Документация по фильтрации и сортировке участников группы и оригинальный тикет.


Автоматическая подготовка профиля пользователя с SAML


(SILVER, GOLD) Стадия цикла DevOps: Manage


Раньше в качестве одной из частей включения в группу с SAML пользователям нужно было заполнять форму регистрации. В GitLab 13.7 мы представляем синхронизацию пользователей для групп с использованием SAML. Когда пользователь в первый раз входит в группу с SAML, учётная запись пользователя создаётся для него автоматически, если нет существующего пользователя с таким же адресом электронной почты. Автоматическая подготовка с SAML улучшает процесс адаптации новых пользователей и гарантирует, что вновь созданные учётные записи содержат правильную информацию.


Документация по настройке групп с SAML и оригинальный тикет.


Настраиваемый адрес электронной почты для службы поддержки


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Plan


Раньше вашим пользователям было трудно запомнить (а следовательно, и использовать) адрес электронной почты службы поддержки GitLab. С введением настраиваемого адреса электронной почты теперь можно выбрать суффикс адреса, который имеет смысл для вашей компании и который будет легче запомнить вашим пользователям. Это ещё один шаг, который нужен для того, чтобы вы могли предоставлять своим пользователям качественную поддержку.


Документация по настройке адреса электронной почты для службы поддержки и оригинальный тикет.


Различайте изменения форматирования и правки, сделанные из редактора статических сайтов


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Create


Редактор статических сайтов предлагает интуитивно понятный и знакомый всем режим редактирования WYSIWYG для контента Markdown. Чтобы обеспечить единообразный формат вывода Markdown, редактор WYSIWYG автоматически переформатирует содержимое страницы в соответствии с соглашениями о стилях, определёнными в парсере Markdown. Это происходит полностью в фоновом режиме ещё до того, как вы начнёте редактировать. Однако эти изменения форматирования коммитятся вместе с вашими изменениями содержимого. Если страница, которую вы редактируете, не следовала тем же соглашениям о стилях, для проверяющих итогового мерж-реквеста может быть сложно отличить ваши изменения от автоматического форматирования.


Начиная с GitLab 13.7 редактор статических сайтов автоматически исправляет несоответствия в синтаксисе Markdown и коммитит все необходимые изменения форматирования в новой ветке. По завершении редактирования кнопка Опубликовать создаёт отдельный коммит, содержащий только ваши изменения. Это может сэкономить время проверяющим изменения, которые вместо того, чтобы продираться через изменения синтаксиса, смогут сосредоточиться на вашем контенте.


В будущем мы планируем сделать параметры форматирования настраиваемыми, так что следите за новостями в соответствующем тикете.


Документация по редактору статических сайтов и оригинальный тикет.


Предзаполненные переменные при ручном запуске конвейеров


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Verify


Раньше, когда вы хотели запустить конвейер вручную, вам нужно было узнать нужные переменные, а затем ввести их на странице Запуск конвейера (Run Pipeline). Это может быть утомительно и чревато ошибками, если нужно ввести множество пар ключ-значение. Теперь форма для запуска конвейера будет сгенерирована для вашего конвейера с переменными, предварительно заполненными на основе определений переменных в вашем файле .gitlab-ci.yml, что сделает этот процесс более эффективным.


Pre-filled variables when running pipelines manually


Документация по ручному запуску конвейера и оригинальный тикет.


Собранные с помощью CI/CD пакеты всегда отображают информацию о сборке


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Package


Если вы публиковали пакеты в реестре пакетов, то могли заметить, что пакеты, созданные с помощью GitLab CI/CD, не всегда отображали коммит и конвейер, ответственные за создание или обновление вашего пакета. Это могло происходить по нескольким причинам.


Во-первых, возможно, мы ещё не поддерживали эту функцию, как в случае с Composer и Conan. Во-вторых, проблема возникала у тех из вас, кто обновлял одну и ту же версию пакета из разных веток или коммитов. Однако до недавнего времени пользовательский интерфейс отображал только первое развёртывание без каких-либо обновлений. Кроме того, на странице подробных сведений отображается ветка, в которой был создан пакет, а не самый последний коммит. В результате вам приходилось искать эту информацию, просматривая историю коммитов, что было не очень эффективно.


В дальнейшем любой пакет, собранный или обновлённый с помощью GitLab CI/CD, будет отображать информацию о коммите и конвейере в пользовательском интерфейсе пакетов. Чтобы избежать проблем с производительностью или пользовательским интерфейсом, будут отображаться только пять обновлений пакета. В майлстоуне 13.8 мы создадим дизайн, который поможет вам легко просматривать все данные, включая историю. А пока вы можете использовать API пакетов, чтобы смотреть всю историю сборки данного пакета.


Packages built with CI/CD always display build info


Документация по реестру пакетов и сборке пакетов с помощью GitLab CI/CD и оригинальный тикет.


Используйте предопределённые переменные с прокси зависимостей


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Package


С помощью проксирования и кэширования образов контейнеров из Docker Hub прокси зависимостей помогает вам повысить производительность ваших конвейеров. Несмотря на то, что прокси-сервер предназначен для интенсивного использования с CI/CD, для использования этой фичи вам нужно было определить свои собственные переменные или прописать значения в вашем файле gitlab.ci-yml. Это затрудняло начало работы для тех, кто работает один, и не позволяло использовать его в качестве масштабируемого решения, особенно для организаций с множеством различных групп и проектов.


В дальнейшем вы можете использовать предопределённые переменные окружения в качестве интуитивно понятного способа для прокси зависимостей. Поддерживаются следующие переменные:


  • CI_DEPENDENCY_PROXY_USER: пользователь CI для входа в прокси зависимостей,
  • CI_DEPENDENCY_PROXY_PASSWORD: пароль для входа в прокси зависимостей,
  • CI_DEPENDENCY_PROXY_SERVER: сервер для входа в прокси зависимостей,
  • CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX: префикс образа для извлечения образов через прокси зависимостей.

Попробуйте и дайте нам знать, что вы думаете!


Документация по аутентификации в прокси зависимостей с помощью CI/CD и оригинальный тикет.


Результаты сканирований безопасности в виджете мерж-реквеста стали удобнее


(CORE, STARTER, PREMIUM, FREE, BRONZE, SILVER) Стадия цикла DevOps: Secure


С помощью SAST и поиска секретных ключей, которые теперь доступны для всех пользователей, мы упростили жизнь всем пользователям GitLab, взаимодействующим с результатами сканирования безопасности в мерж-реквесте, за счёт облегчения доступа к результатам сканирования безопасности. Ранее результаты сканирования безопасности были доступны только на странице Обзор конвейера, и вы должны были знать, где искать, чтобы найти их там. Теперь все мерж-реквесты будут показывать, были ли для них запущены проверки безопасности, и помогут вам найти артефакты задания. Это изменение не затрагивает работу с мерж-реквестами для пользователей плана Ultimate.


Improved MR experience for security scans


Документация по просмотру результатов сканирования безопасности в мерж-реквесте и оригинальный эпик.


Специальные ссылки на уязвимости


(ULTIMATE, GOLD) Стадия цикла DevOps: Secure


Мы ввели уязвимости как полноценные объекты в 12.10. Будучи объектом, каждая из них имеет уникальный URL-адрес, позволяющий напрямую перейти к деталям любой уязвимости. Несмотря на значительное улучшение видимости и согласованности, ссылки на уязвимости в тикетах и эпиках (в русской локализации GitLab цели) всё равно нужно копировать вручную в виде ссылок Markdown. Это делает неэффективным совместное использование, а ссылки на уязвимости в других областях GitLab более громоздкими, чем для других объектов, например тикетов.


Теперь на уязвимости можно ссылаться с помощью специальных ссылок. На них впервые будет опробован новый синтаксис [object_type:ID], который в конечном итоге распространится на другие существующие ссылки. Теперь вы можете быстро вставить ссылку на уязвимость из любого места, где обычно используется специальная ссылка, например из описания тикета или мерж-реквеста. Просто введите [vulnerability:123] в описании тикета, чтобы вставить ссылку на уязвимость с идентификатором 123 в том же проекте. Вы также можете добавить к идентификатору префикс пространства имён или проекта, чтобы ссылаться на уязвимости вне контекста текущего проекта.


Документация по специальным ссылкам и оригинальный тикет.


Смотрите, какие коммиты и конвейеры выполняются в форке, а какие в родительском проекте


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Стадия цикла DevOps: Release


После того, как мы решили проблему, описанную в тикете 217451 в GitLab 13.3, вы получили возможность разрешать разработчикам родительского проекта создавать конвейеры для мерж-реквестов в форках. Однако не было никакого способа узнать контекст, в котором был запущен конвейер.


В этом релизе вы сможете увидеть, какие коммиты и конвейеры выполнялись в проекте-форке, а какие в родительском проекте. Коммиты в форках отмечены уникальным значком и тултипом, что позволяет вам узнать, что они выполнялись из мерж-реквеста в форке. Это упрощает понимание контекста, в котором работал ваш конвейер, и устраняет необходимость более детально изучать источник.


See which commits and pipelines run in the fork project vs. the parent project


Документация по запуску конвейеров в родительском проекте для мерж-реквестов из проекта-форка и оригинальный тикет.


Запросы к базе данных выполняются быстрее при использовании балансировщика нагрузки


(CORE, STARTER, PREMIUM, ULTIMATE, FREE, BRONZE, SILVER, GOLD) Доступность


Многие запросы к базе данных повторяются несколько раз, так что их можно кэшировать для повышения общей производительности. Для GitLab можно кэшировать примерно 10% всех запросов. В GitLab 13.7 мы включили кэширование запросов к базе данных, когда используется балансировка нагрузки базы данных. На GitLab.com это приводит к кэшированию ~700 000 запросов каждую минуту и сокращает среднее время выполнения запросов вплоть до 10%.


Для запросов, которые выполняются более 100 раз, мы уменьшили продолжительность запроса на 11-31% и кэшировали ~30% всех операторов SELECT, которые бы в противном случае выполнялись на реплике базы данных.


Документация по балансировке нагрузки на базу данных и оригинальный тикет.


Для новых установок по умолчанию используется PostgreSQL 12


(CORE, STARTER, PREMIUM, ULTIMATE) Доступность


Начиная с GitLab 13.3 PostgreSQL 12 был доступен в качестве опции как для пакетов Omnibus, так и для нашего Helm chart. PostgreSQL 12 улучшает производительность, а также предлагает значительные преимущества при индексировании и секционировании.


Начиная с GitLab 13.7 новые установки GitLab будут по умолчанию использовать PostgreSQL 12. Чтобы выполнить обновление вручную, запустите gitlab-ctl pg-upgrade.


Многонодовые инстансы базы данных должны будут переключиться с repmgr на Patroni до обновления с помощью Patroni. Затем можно обновить и повторно синхронизировать вторичные ноды Geo.


Документация по настройкам баз данных в GitLab 13.3 и более поздних версиях и оригинальный тикет.




Полный текст релиза и инструкции по обновлению/установке вы можете найти в оригинальном англоязычном посте: GitLab 13.7 released with merge request reviewers and automatic rollback upon failure.


Над переводом с английского работали cattidourden, maryartkey, ainoneko и rishavant.

Подробнее..

Как мы автоматизировали разработку WL-приложений

21.09.2020 12:22:00 | Автор: admin
White Label это мобильные приложения, которые можно кастомизировать под любой бренд: оформить в фирменных цветах, выбрать необходимые блоки и функционал, добавить описание. Мы их выпускаем на основе Рамблер/кассы с 2015 года и в этой статье хотим рассказать, как у нас получилось автоматизировать и ускорить разработку WL.



Рамблер/касса онлайн-сервис и приложение для продажи билетов на концерты, в театр, кино, спортивные и другие мероприятия. Также мы разрабатываем другие B2B-предложения для партнеров: мобильный SDK, встроенные виджеты для сайтов и соцсетей, CRM-систему для аналитики продаж и аудитории, а также ряд технологических решений. Но сегодня мы остановимся только на WL.

Что было раньше


После получения запроса на WL-приложение менеджеру проекта было необходимо заново формировать и уточнять все требования, передавать их менеджеру команды. Он, в свою очередь, еще раз уточнял и запрашивал все необходимые параметры и после распределял работу между дизайнерами, разработчиками и тестировщиками. Также для создания WL-приложения необходимо создать новый таргет в проекте, пройтись по всем конфигурационным файлам, выставить нужные ID для различных сервисов, настроить цвета, шрифты, изображения и т.д.



В идеальных условиях всего на разработку нового WL должно уходить минимум 2-3 дня по одному на разработчиков iOS и Android, плюс тестирование. Если сотрудник первый раз сталкивается с созданием WL-приложения, то выполнение задачи у него может занять до трех дней, что увеличивает общее время работы. Таким образом, для Рамблер/кассы создание WL-приложения это рутинная задача, которая ложилась на плечи разработчиков и отнимала у них ценное время.

Какие были варианты решений


  1. Можно было оставить все как есть и продолжить регулярно тратить время разработчиков на ручное создание WL.
  2. Второй вариант разработка решения, которое позволит автоматизировать процесс создания новых WL без привлечения ресурсов разработки. При этом становится не важно, часто ли поступают запросы на создание новых WL, ведь задача будет выполняться минимальными усилиями.

Проанализировав частоту поступления запросов на создание новых WL, мы решили остановиться на втором варианте. Это позволит потратить ресурс разработки всего один раз, но зато обеспечить компанию инструментом, позволяющим выполнять задачу быстро, с минимальными трудозатратами и использовать для этого только время менеджеров и службы технической поддержки.

Как работает решение


Мы запустили сайт с административной панелью в виде микрослужб новый раздел в существующей админке (CMS) по управлению продажами и витринами Рамблер/кассы. Мы разработали скрипты для iOS и Android, которые локально создают в проекте новое приложение и подают на него все нужные параметры.

В качестве брокера сообщений используется RabbitMQ, а все настройки сохраняются в архив и публикуются в рамблеровский Artifactory. После этого используется API GitLab, чтобы запустить процесс сборки в мобильных репозиториях.

На стороне бэкенда формируется архив с файлами в формате JSON, содержащими информацию, которую ввели в административной панели, и графикой. Триггер Gitlab CI вызывает pipeline, в параметрах к которому передает ссылку на архив из Artifactory. Скрипт, настроенный на билд машине и лежащий в корне проекта запускается с входным параметром-ссылкой.

Скрипт для iOS

Ruby-скрипт распаковывает архив, рассылает нужные данные по всему проекту, а после этого передает управление следующей задаче, которая коммитит изменения, обновляет поды и собирает сборку с последующей выкладкой в App Store.

Скрипт для Android

Джоба подтягивает нужные библиотеки jq и unzip. Библиотека unzip распаковывает архив, скрипт парсит json с помощью jq, создает новую папку в app модуле и проверяет наличие .jks файла для данного приложения.

Если приложение новое, то создается данный файл, после собирается релизное APK и скрипт отправляет в его firebase обновляет приложение в Маркете. Далее задача проверяет появился ли новый .jks файл, и, если он есть, то пушит его в GitLab.

Кроме того, была развернута служба, которая осуществляет мониторинг состояния запущенных сборок и обновляет их статусы, чтобы можно было следить за процессом и контролировать возникающие проблемы.

На практике автоматизация создания WL-приложений выглядит так


  1. Партнёр Рамблер/кассы заполняет и передает заполненный единообразный бриф и брендбук компании, в которых собраны все тексты, изображения, шрифты, иконки, контактные данные и параметры будущего приложения.
  2. Менеджер проекта или сотрудник поддержки формирует и уточняет требования.
  3. Дизайнер на основе брендбука или сайта партнёра предлагает свое решение по цветам и иконкам.
  4. В админке менеджер проекта самостоятельно заполняет все нужные параметры для нового приложения (ID сервисов, цвета, конфигурационные файлы, иконки и т.д.).
  5. После заполнения необходимых параметров менеджер проекта нажимает кнопку Создать приложение, а затем готовая сборка передается на тестирование.
  6. Тестировщик тестирует приложение и публикует его в App Store и Google Play с помощью CI.



Что получилось


Мы максимально автоматизировали создание WL-приложений. Раньше сам процесс разработки занимал 2-3 дня и отнимал ресурсы программистов, а теперь менеджер за 15 минут вбивает все данные и через примерно 20 минут сборка автоматически создается и передается QA на тестирование. Наши партнёры получают все возможности, которые есть в Рамблер/кассе, а мы экономим время, ресурсы и минимизируем ошибки.
Подробнее..

Ваш безлимит как увеличить пропускную способность автомерджа

21.06.2021 14:12:41 | Автор: admin

Отыщи всему начало, и ты многое поймёшь (Козьма Прутков).

Меня зовут Руслан, я релиз-инженер в Badoo и Bumble. Недавно я столкнулся с необходимостью оптимизировать механизм автомерджа в мобильных проектах. Задача оказалась интересной, поэтому я решил поделиться её решением с вами. В статье я расскажу, как у нас раньше было реализовано автоматическое слияние веток Git и как потом мы увеличили пропускную способность автомерджа и сохранили надёжность процессов на прежнем высоком уровне.

Свой автомердж

Многие программисты ежедневно запускают git merge, разрешают конфликты и проверяют свои действия тестами. Кто-то автоматизирует сборки, чтобы они запускались автоматически на отдельном сервере. Но решать, какие ветки сливать, всё равно приходится человеку. Кто-то идёт дальше и добавляет автоматическое слияние изменений, получая систему непрерывной интеграции (Continuous Integration, или CI).

Например, GitHub предлагает полуручной режим, при котором пользователь с правом делать записи в репозиторий может поставить флажок Allow auto-merge (Разрешить автомердж). При соблюдении условий, заданных в настройках, ветка будет соединена с целевой веткой. Bitbucket поддерживает большую степень автоматизации, накладывая при этом существенные ограничения на модель ветвления, имена веток и на количество мерджей.

Такой автоматизации может быть достаточно для небольших проектов. Но с увеличением количества разработчиков и веток, ограничения, накладываемые сервисами, могут существенно повлиять на производительность CI. Например, раньше у нас была система мерджа, при которой основная ветка всегда находилась в стабильном состоянии благодаря последовательной стратегии слияний. Обязательным условием слияния была успешная сборка при наличии всех коммитов основной ветки в ветке разработчика. Работает эта стратегия надёжно, но у неё есть предел, определяемый временем сборки. И этого предела оказалось недостаточно. При времени сборки в 30 минут на обработку 100 слияний в день потребовалось бы более двух суток. Чтобы исключить ограничения подобного рода и получить максимальную свободу выбора стратегий мерджа и моделей ветвления, мы создали собственный автомердж.

Итак, у нас есть свой автомердж, который мы адаптируем под нужды каждой команды. Давайте рассмотрим реализацию одной из наиболее интересных схем, которую используют наши команды Android и iOS.

Термины

Main. Так я буду ссылаться на основную ветку репозитория Git. И коротко, и безопасно. =)

Сборка. Под этим будем иметь в виду сборку в TeamCity, ассоциированную с веткой Git и тикетом в трекере Jira. В ней выполняются как минимум статический анализ, компиляция и тестирование. Удачная сборка на последней ревизии ветки в сочетании со статусом тикета To Merge это однo из необходимых условий автомерджа.

Пример модели ветвления

Испробовав разные модели ветвления в мобильных проектах, мы пришли к следующему упрощённому варианту:

На основе ветки main разработчик создаёт ветку с названием, включающим идентификатор тикета в трекере, например PRJ-k. По завершении работы над тикетом разработчик переводит его в статус Resolved. При помощи хуков, встроенных в трекер, мы запускаем для ветки тикета сборку. В определённый момент, когда изменения прошли ревью и необходимые проверки автотестами на разных уровнях, тикет получает статус To Merge, его забирает автоматика и отправляет в main.

Раз в неделю на основе main мы создаём ветку релиза release_x.y.z, запускаем на ней финальные сборки, при необходимости исправляем ошибки и наконец выкладываем результат сборки релиза в App Store или Google Play. Все фазы веток отражаются в статусах и дополнительных полях тикетов Jira. В общении с Jira помогает наш клиент REST API.

Такая простая модель не только позволила нам построить надёжный автомердж, но и оказалась удобной для всех участников процесса. Однако сама реализация автомерджа менялась несколько раз, прежде чем мы добились высокой производительности и минимизировали количество побочных эффектов: конфликтов, переоткрытий тикетов и ненужных пересборок.

Первая версия: жадная стратегия

Сначала мы шли от простого и очевидного. Брали все тикеты, находящиеся в статусе To Merge, выбирали из них те, для которых есть успешные сборки, и отправляли их в main командой git merge, по одной.

Примечание: я немного упростил описание первой версии. В реальности между main и ветками разработчиков была ветка dev, где и происходили все проблемы, описанные выше. Перед слиянием main с dev мы добивались стабилизации сборок при помощи специальных веток интеграции, создававшихся автоматически на основе dev с периодом в сутки.

Наличие в TeamCity актуальной успешной сборки мы проверяли при помощи метода REST API getAllBuilds примерно следующим образом (псевдокод):

haveFailed = False # Есть ли неудачные сборкиhaveActive = False # Есть ли активные сборки# Получаем сборки типа buildType для коммита commit ветки branchbuilds = teamCity.getAllBuilds(buildType, branch, commit)# Проверяем каждую сборкуfor build in builds:  # Проверяем каждую ревизию в сборке  for revision in build.revisions:    if revision.branch is branch and revision.commit is commit:      # Сборка актуальна      if build.isSuccessful:        # Сборка актуальна и успешна        return True      else if build.isRunning or build.isQueued        haveActive = True      else if build.isFailed:        haveFailed = Trueif haveFailed:  # Исключаем тикет из очереди, переоткрывая его  ticket = Jira.getTicket(branch.ticketKey)  ticket.reopen("Build Failed")  return Falseif not haveActiveBuilds:  # Нет ни активных, ни упавших, ни удачных сборок. Запускаем новую  TriggerBuild(buildType, branch)

Ревизии это коммиты, на основе которых TeamCity выполняет сборку. Они отображаются в виде 16-ричных последовательностей на вкладке Changes (Изменения) страницы сборки в веб-интерфейсе TeamCity. Благодаря ревизиям мы можем легко определить, требуется ли пересборка ветки тикета или тикет готов к слиянию.

Важно, что ревизию можно (а часто даже необходимо) указывать в запросе на добавление новой сборки в очередь в параметре lastChanges, потому что в противном случае TeamCity может выбрать устаревшую ревизию ветки при запуске сборки. Как будет показано ниже, указывать ревизию необходимо в тех случаях, если, например, логика вне TeamCity основана на поиске сборок на конкретных коммитах (наш случай).

Так как после перевода тикета в статус готовности (в нашем примере Resolved) соответствующая ветка, как правило, не меняется, то и сборка, ассоциированная с тикетом, чаще всего остаётся актуальной. Кроме того, сам факт нахождения тикета в статусе To Merge говорит о высокой вероятности того, что сборка не упала. Ведь при падении сборки мы сразу переоткрываем тикет.

На первый взгляд, дальнейшие действия кажутся очевидными: взять все готовые тикеты с актуальными сборками и соединять main с ними по одному. В первой версии автомерджа мы так и сделали.

Всё работало быстро, но требовало внимания. То и дело возникали ситуации, когда изменения нескольких тикетов конфликтовали между собой. Конфликты при слияниях как явление достаточно распространённое поначалу ни у кого особых вопросов не вызывали. Их разрешали разработчики, дежурные по релизу. Но с увеличением количества разработчиков, задач и, соответственно, веток, приведение релиза в порядок требовало всё больше усилий. Задержки в разрешении конфликтов начали сказываться на новых задачах. Полагаю, не стоит продолжать эту цепочку скорее всего, вы уже поняли, что я имею в виду. С конфликтами нужно было что-то делать, причём не допуская их попадания в релиз.

Конфликты слияния

Если изменить одну и ту же строку кода в разных ветках и попытаться соединить их в main, то Git попросит разрешить конфликты слияния. Из двух вариантов нужно выбрать один и закоммитить изменения.

Это должно быть знакомо практически каждому пользователю системы контроля версий (VCS). Процессу CI, так же, как и любому пользователю VCS, нужно разрешать конфликты. Правда, делать это приходится немного вслепую, в условиях почти полного непонимания кодовой базы.

Если команда git merge завершилась с ошибкой и для всех файлов в списке git ls-files --unmerged заданы обработчики конфликтов, то для каждого такого файла мы выполняем парсинг содержимого по маркерам конфликтов <<<<<<<, ======= и >>>>>>>. Если конфликты вызваны только изменением версии приложения, то, например, выбираем последнюю версию между локальной и удалённой частями конфликта.

Конфликт слияния это один из простейших типов конфликтов в CI. При конфликте с main CI обязан уведомить разработчика о проблеме, а также исключить ветку из следующих циклов автомерджа до тех пор, пока в ней не появятся новые коммиты.

Решение следующее: нарушаем как минимум одно из необходимых условий слияния. Так как ветка ассоциирована с тикетом трекера, можно переоткрыть тикет, изменив его статус. Таким образом мы одновременно исключим тикет из автомерджа и оповестим об этом разработчика (ведь он подписан на изменения в тикете). На всякий случай мы отправляем ещё и сообщение в мессенджере.

Логические конфликты

А может ли случиться так, что, несмотря на успешность сборок пары веток в отдельности, после слияния их с main сборка на основной ветке упадёт? Практика показывает, что может. Например, если сумма a и b в каждой из двух веток не превышает 5, то это не гарантирует того, что совокупные изменения a и b в этих ветках не приведут к большей сумме.

Попробуем воспроизвести это на примере Bash-скрипта test.sh:

#!/bin/bashget_a() {    printf '%d\n' 1}get_b() {    printf '%d\n' 2}check_limit() {    local -i value="$1"    local -i limit="$2"    if (( value > limit )); then        printf >&2 '%d > %d%s\n' "$value" "$limit"        exit 1    fi}limit=5a=$(get_a)b=$(get_b)sum=$(( a + b ))check_limit "$a" "$limit"check_limit "$b" "$limit"check_limit "$sum" "$limit"printf 'OK\n'

Закоммитим его и создадим пару веток: a и b.
Пусть в первой ветке функция get_a() вернёт 3, а во второй get_b() вернёт 4:

diff --git a/test.sh b/test.shindex f118d07..39d3b53 100644--- a/test.sh+++ b/test.sh@@ -1,7 +1,7 @@ #!/bin/bash get_a() {-    printf '%d\n' 1+    printf '%d\n' 3 } get_b() {git diff main bdiff --git a/test.sh b/test.shindex f118d07..0bd80bb 100644--- a/test.sh+++ b/test.sh@@ -5,7 +5,7 @@ get_a() { }  get_b() {-    printf '%d\n' 2+    printf '%d\n' 4 }  check_limit() {

В обоих случаях сумма не превышает 5 и наш тест проходит успешно:

git checkout a && bash test.shSwitched to branch 'a'OKgit checkout b && bash test.shSwitched to branch 'b'OK

Но после слияния main с ветками тесты перестают проходить, несмотря на отсутствие явных конфликтов:

git merge a bFast-forwarding to: aTrying simple merge with bSimple merge did not work, trying automatic merge.Auto-merging test.shMerge made by the 'octopus' strategy. test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)bash test.sh7 > 5

Было бы проще, если бы вместо get_a() и get_b() использовались присваивания: a=1; b=2, заметит внимательный читатель и будет прав. Да, так было бы проще. Но, вероятно, именно поэтому встроенный алгоритм автомерджа Git успешно обнаружил бы конфликтную ситуацию (что не позволило бы продемонстрировать проблему логического конфликта):

git merge a Updating 4d4f90e..8b55df0Fast-forward test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)git merge b Auto-merging test.shCONFLICT (content): Merge conflict in test.shRecorded preimage for 'test.sh'Automatic merge failed; fix conflicts and then commit the result.

Разумеется, на практике конфликты бывают менее явными. Например, разные ветки могут полагаться на API разных версий какой-нибудь библиотеки зависимости, притом что более новая версия не поддерживает обратной совместимости. Без глубоких знаний кодовой базы (читай: без разработчиков проекта) обойтись вряд ли получится. Но ведь CI как раз и нужен для решения таких проблем.

Конечно, от разрешения конфликта мы никуда не уйдём кто-то должен внести правки. Но чем раньше нам удастся обнаружить проблему, тем меньше людей будет привлечено к её решению. В идеале потребуется озадачить лишь разработчика одной из конфликтующих веток. Если таких веток две, то одна из них вполне может быть соединена с main.

Превентивные меры

Итак, главное не допустить попадания логического конфликта в main. Иначе придётся долго и мучительно искать источник ошибок, а затем программиста, который проблему должен или может решить. Причём делать это нужно максимально быстро и качественно, чтобы, во-первых, не допустить задержки релиза и во-вторых, избежать в новых ветках логических конфликтов, основанных на уже выявленном конфликте. Такие конфликты часто приводят к неработоспособности большой части приложения или вовсе блокируют его запуск.

Нужно синхронизировать ветки так, чтобы их совокупный вклад в main не приводил к падению сборки релиза. Ясно, что все готовые к слиянию ветки нужно так или иначе объединить и прогнать тесты по результату объединения. Путей решения много, давайте посмотрим, каким был наш путь.

Вторая версия: последовательная стратегия

Стало ясно, что существующих условий готовности к автомерджу для тикета недостаточно. Требовалось какое-то средство синхронизации между ветками, какой-то порядок.

Git, по идее, как раз и является средством синхронизации. Но порядок попадания веток в main и, наоборот, main в ветки определяем мы сами. Чтобы определить точно, какие из веток вызывают проблемы в main, можно попробовать отправлять их туда по одной. Тогда можно выстроить их в очередь, а порядок организовать на основе времени попадания тикета в статус To Merge в стиле первый пришёл первым обслужен.

С порядком определились. А как дальше соединять ветки? Допустим, мы сольём в main первый тикет из очереди. Так как main изменилась, она может конфликтовать с остальными тикетами в очереди. Поэтому перед тем как сливать следующий тикет, нужно удостовериться, что обновлённая main по-прежнему совместима с ним. Для этого достаточно слить main в тикет. Но так как после соединения main с веткой её состояние отличается от того, которое было в сборке, необходимо перезапустить сборку. Для сохранения порядка все остальные тикеты в очереди должны ждать завершения сборки и обработки впередистоящих тикетов. Примерно такие рассуждения привели нас к последовательной стратегии автомерджа.

Работает схема надёжно и предсказуемо. Благодаря обязательной синхронизации с main и последующей пересборке конфликты между ветками удаётся выявлять сразу, до попадания их в main. Раньше же нам приходилось разрешать конфликт уже после слияния релиза со множеством веток, большая часть из которых к этому конфликту отношения не имела. Кроме того, предсказуемость алгоритма позволила нам показать очередь тикетов в веб-интерфейсе, чтобы можно было примерно оценить скорость попадания их веток в main.

Но есть у этой схемы существенный недостаток: пропускная способность автомерджа линейно зависит от времени сборки. При среднем времени сборки iOS-приложения в 25 минут мы можем рассчитывать на прохождение максимум 57 тикетов в сутки. В случае же с Android-приложением требуется примерно 45 минут, что ограничивает автомердж 32 тикетами в сутки, а это даже меньше количества Android-разработчиков в нашей компании.

На практике время ожидания тикета в статусе To Merge составляло в среднем 2 часа 40 минут со всплесками, доходящими до 10 часов! Необходимость оптимизации стала очевидной. Нужно было увеличить скорость слияний, сохранив при этом стабильность последовательной стратегии.

Финальная версия: сочетание последовательной и жадной стратегий

Разработчик команды iOS Дамир Давлетов предложил вернуться к идее жадной стратегии, при этом сохранив преимущества последовательной.

Давайте вспомним идею жадной стратегии: мы сливали все ветки готовых тикетов в main. Основной проблемой было отсутствие синхронизации между ветками. Решив её, мы получим быстрый и надёжный автомердж!

Раз нужно оценить общий вклад всех тикетов в статусе To Merge в main, то почему бы не слить все ветки в некоторую промежуточную ветку Main Candidate (MC) и не запустить сборку на ней? Если сборка окажется успешной, то можно смело сливать MC в main. В противном случае придётся исключать часть тикетов из MC и запускать сборку заново.

Как понять, какие тикеты исключить? Допустим, у нас n тикетов. На практике причиной падения сборки чаще всего является один тикет. Где он находится, мы не знаем все позиции от 1 до n являются равноценными. Поэтому для поиска проблемного тикета мы делим n пополам.

Так как место тикета в очереди определяется временем его попадания в статус To Merge, имеет смысл брать ту половину, в которой расположены тикеты с большим временем ожидания.

Следуя этому алгоритму, для k проблемных тикетов в худшем случае нам придётся выполнить O(k*log2(n)) сборок, прежде чем мы обработаем все проблемные тикеты и получим удачную сборку на оставшихся.

Вероятность благоприятного исхода велика. А ещё в то время, пока сборки на ветке MC падают, мы можем продолжать работу при помощи последовательного алгоритма!

Итак, у нас есть две автономные модели автомерджа: последовательная (назовём её Sequential Merge, или SM) и жадная (назовём её Greedy Merge, или GM). Чтобы получить пользу от обеих, нужно дать им возможность работать параллельно. А параллельные процессы требуют синхронизации, которой можно добиться либо средствами межпроцессного взаимодействия, либо неблокирующей синхронизацией, либо сочетанием этих двух методов. Во всяком случае, мне другие методы неизвестны.

Сами процессы такого рода у нас реализованы в виде очереди команд-скриптов. Команды эти могут быть одноразовыми и периодически запускаемыми. Так как автомердж никогда не закончится, а с управлением повторными запусками лучше справится контроллер очереди, выберем второй тип.

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

  1. SM-SM и GM-GM: между командами одного типа.

  2. SM-GM: между SM и GM в рамках одного репозитория.

Первая проблема легко решается при помощи мьютекса по токену, включающему в себя имя команды и название репозитория. Пример: lock_${command}_${repository}.

Поясню, в чём заключается сложность второго случая. Если SM и GM будут действовать несогласованно, то может случиться так, что SM соединит main с первым тикетом из очереди, а GM этого тикета не заметит, то есть соберёт все остальные тикеты без учёта первого. Например, если SM переведёт тикет в статус In Master, а GM будет всегда выбирать тикеты по статусу To Merge, то GM может никогда не обработать тикета, соединённого SM. При этом тот самый первый тикет может конфликтовать как минимум с одним из других.

Во избежание логических конфликтов GM нужно обрабатывать все тикеты в очереди без исключения. По этой же причине алгоритм GM в связке с SM обязательно должен соблюдать тот же порядок тикетов в очереди, что и SM, так как именно этот порядок определяет, какая половина очереди будет выбрана в случае неудачной сборки в GM. При соблюдении этих условий тикет, обрабатываемый SM, будет всегда входить в сборку GM, что обеспечит нам нужную степень синхронизации.

Таким образом, мы получили своего рода неблокирующую синхронизацию.

Немного о TeamCity

В процессе реализации GM нам предстояло обработать много нюансов, которыми я не хочу перегружать статью. Но один из них заслуживает внимания. В ходе разработки я столкнулся с проблемой зацикливания команды GM: процесс постоянно пересобирал ветку MC и создавал новую сборку в TeamCity. Проблема оказалась в том, что TeamCity не успел скачать обновления репозитория, в которых была ветка MC, созданная процессом GM несколько секунд назад. К слову, интервал обновления репозитория в TeamCity у нас составляет примерно 30 секунд.

В качестве хотфикса я ввёл плавающий тег сборки, то есть создал в TeamCity тег с названием, похожим на automerge_ios_repo_git, и перемещал его от сборки к сборке, чтобы иметь представление о том, какая сборка является актуальной, в каком она состоянии и т. д. Но, понимая несовершенство этого подхода, я взялся за поиски способа донесения до TeamCity информации о новом состоянии ветки MC, а также способа прикрепления ревизии к сборке.

Кто-то посчитает решение очевидным, но я нашёл его не сразу. Оказывается, прикрепить ревизию к сборке при её добавлении в очередь можно при помощи параметра lastChanges метода addBuildToQueue:

<lastChanges>  <change    locator="version:{{revision}},buildType:(id:{{build_type}})"/></lastChanges>

В этом примере {{revision}} заменяется на 16-ричную последовательность коммита, а {{build_type}} на идентификатор конфигурации сборки. Но этого недостаточно, так как TeamCity, не имея информации о новом коммите, может отказать нам в запросе.

Для того чтобы новый коммит дошёл до TeamCity, нужно либо подождать примерно столько, сколько указано в настройках конфигурации корня VCS, либо попросить TeamCity проверить наличие изменений в репозитории (Pending Changes) при помощи метода requestPendingChangesCheck, а затем подождать, пока TeamCity скачает изменения, содержащие наш коммит. Проверка такого рода выполняется посредством метода getChange, где в changeLocator нужно передать как минимум сам коммит в качестве параметра локатора version. Кстати, на момент написания статьи (и кода) на странице ChangeLocator в официальной документации описание параметра version отсутствовало. Быть может, поэтому я не сразу узнал о его существовании и о том, что это 40-символьный 16-ричный хеш коммита.

Псевдокод:

teamCity.requestPendingChanges(buildType)attempt = 1while attempt <= 20:  response = teamCity.getChange(commit, buildType)  if response.commit == commit:    return True # Дождались  sleep(10)return False

О предельно высокой скорости слияний

У жадной стратегии есть недостаток на поиск ветки с ошибкой может потребоваться много времени. Например, 6 сборок для 20 тикетов у нас может занять около трёх часов. Можно ли устранить этот недостаток?

Допустим, в очереди находится 10 тикетов, среди которых только 6-й приводит к падению сборки.

Согласно жадной стратегии, мы пробуем собрать сразу все 10 тикетов, что приводит к падению сборки. Далее собираем левую половину (с 1 по 5) успешно, так как тикет с ошибкой остался в правой половине.

Если бы мы сразу запустили сборку на левой половине очереди, то не потеряли бы времени. А если бы проблемным оказался не 6-й тикет, а 4-й, то было бы выгодно запустить сборку на четверти длины всей очереди, то есть на тикетах с 1 по 3, например.

Продолжая эту мысль, мы придём к выводу о том, что полностью избавиться от ожиданий неудачных сборок можно только при условии параллельного запуска сборок всех комбинаций тикетов:

Обратите внимание, во избежание конфликтов здесь необходимо соблюдать очерёдность, поэтому комбинации вроде пятый и первый недопустимы. Тогда можно было бы просто брать успешные сборки и соединять их тикеты в main. При этом неудачные сборки времени бы не отнимали.

Примерно такой же алгоритм реализован в премиум-функции GitLab под названием Merge Trains. Перевода этого названия на русский язык я не нашёл, поэтому назову его Поезда слияний. Поезд представляет собой очередь запросов на слияние с основной веткой (merge requests). Для каждого такого запроса выполняется слияние изменений ветки самого запроса с изменениями всех запросов, расположенных перед ним (то есть запросов, добавленных в поезд ранее). Например, для трёх запросов на слияние A, B и С GitLab создаёт следующие сборки:

  1. Изменения из А, соединённые с основной веткой.

  2. Изменения из A и B, соединённые с основной веткой.

  3. Изменения из A, B и C, соединённые с основной веткой.

Если сборка падает, то соответствующий запрос из очереди удаляется, а сборки всех предыдущих запросов перезапускаются (без учёта удалённого запроса).

GitLab ограничивает количество параллельно работающих сборок двадцатью. Все остальные сборки попадают в очередь ожидания вне поезда. Как только сборка завершает работу, её место занимает очередная сборка из очереди ожидания.

Таким образом, запуск параллельных сборок по всем допустимым комбинациям тикетов в очереди позволяет добиться очень высокой скорости слияний. Избавившись от очереди ожидания, можно и вовсе приблизиться к максимальной скорости.

Но если преград человеческой мысли нет, то пределы аппаратных ресурсов видны достаточно отчётливо:

  1. Каждой сборке нужен свой агент в TeamCity.

  2. В нашем случае у сборки мобильного приложения есть порядка 15-100 сборок-зависимостей, каждой из которых нужно выделить по агенту.

  3. Сборки автомерджа мобильных приложений в main составляют лишь малую часть от общего количества сборок в TeamCity.

Взвесив все плюсы и минусы, мы решили пока остановиться на алгоритме SM + GM. При текущей скорости роста очереди тикетов алгоритм показывает хорошие результаты. Если в будущем заметим возможные проблемы с пропускной способностью, то, вероятно, пойдём в сторону Merge Trains и добавим пару параллельных сборок GM:

  1. Вся очередь.

  2. Левая половина очереди.

  3. Левая четверть очереди.

Что в итоге получилось

В результате применения комбинированной стратегии автомерджа нам удалось добиться следующего:

  • уменьшение среднего размера очереди в 2-3 раза;

  • уменьшение среднего времени ожидания в 4-5 раз;

  • мердж порядка 50 веток в день в каждом из упомянутых проектов;

  • увеличение пропускной способности автомерджа при сохранении высокого уровня надёжности, то есть мы практически сняли ограничение на количество тикетов в сутки.

Примеры графиков слияний за несколько дней:

Количество тикетов в очереди до и после внедрения нового алгоритма:

Среднее количество тикетов в очереди (AVG) уменьшилось в 2,5 раза (3,95/1,55).

Время ожидания тикетов в минутах:

Среднее время ожидания (AVG) уменьшилось в 4,4 раза (155,5/35,07).

Подробнее..

Ещё один Билд-светофор в офисе

05.12.2020 18:16:42 | Автор: admin

В своё время меня очень вдохновила идея создания билд-светоформа, описанного моими коллегами на Хабре здесь. В моём случае получился не билд-светофор, а билд-лампа. О целесообразности такого устройства можно долго спорит, однако я поделюсь нашим с командой опытом. Во-первых, наличие такого устройства показало свою эффективность - когда лампа вращается и сигналит, оповещая о проблеме на проде, то это намного эффективнее чем любой монитор на стене. Во-вторых, сам факт наличия такого девайса в офисе просто веселит.

Основной преградой для создания именно светофора был факт собственно отсутствия такого светофора. Как и было уже расписано ребятами из соседней комнаты (в прямом смысле слова) - поиска светоформа оказался самой сложно задачей.

Лампа

На китайском сайте был найден подходящий донор, которому требовалось подключение 220 В. Стоит отметить, что вариантов и цветовых расцветок куча. Здесь можно поэксперементировать и подобрать вариант, который именно вам подходит.

Электроника

В качестве электроники была выбрана продукция белоруского производителя nooLite: адаптер для ПК PC118-1 и Радиоуправляемый выключатель SB111-150.

Адаптер PC118 позволяет при помощи компьютера управлять силовым блоком. Управление может осуществляться как через элементы в главном окне программы (регуляторы, кнопки вызова сценариев освещения), так и через командную строку. Применение данной системы может значительно облегчить управление, когда речь идет о многоканальной системе или системе на базе умного дома. Программное обеспечение имеет API.

Радиоуправляемый выключатель SB111-150 предназначен для дистанционного включения/выключения освещения с возможность регулировки яркости для диммируемых источников света (лампы накаливания, светодиодные диммируемые лампы). Блок может работать в двух режимах: релейном и диммирования, зависящих от предполагаемого типа нагрузки. В релейном режиме Радиоуправляемый выключатель SB111-150 способен работать с любым типом нагрузки.

Адаптер и Радио выключательАдаптер и Радио выключатель

Дальше был изготовлен грубый деревянный блок и в него смонтирован выключатель и лампа. По хорошему нужно было бы ещё смонтировать предохранитель, но пока без него.

Короб для лампы и Ради выключателяКороб для лампы и Ради выключателя

Логика

В нашем конкретном случае тригером для запуска билд лампы являлся факт падения юнит тестов на пре-продакшн сервер, где проверяются кандидаты к релизу. Логика работы довольно простая: в ноутбук или же стационарный компьютер на постоянной основе подключается адаптер РС118. Далее, в зависимости от вашей операционной системы, необходимо написать скрипт, который раз в 3-5 минут будет опрашивать статус билда. Для CI/CD у нас используется Jenkins, который имеет замечательный плагин Build Monitor View с замечательным API.

Если билд "упал", скрипт посылает сигнал на Канал "вкл". Если же билд снова стал зелёным, скрипт посылает сигнал "выкл".

Также в расписании к выполнению скрипта мы указали, чтобы он отрабатывал лишь в рабочие дни с 9:00 до 18:00.

Стоимость

Адаптер PC-118

2 888

Силовой блок SB111-150

1 923

Вращающаяся лампа

597

Провода, короб, крепеж

100

Итого: 5 508

Подробнее..

Настраиваем Continuous Integration для Jenkins и Bitbucket с werf

18.12.2020 12:07:33 | Автор: admin


Утилита werf создана так, чтобы её было легко интегрировать с любыми CI/CD-системами. Подробнее об этом процессе в общем случае читайте в эпилоге этой статьи, но основное её содержимое практический пример по организации CI в Jenkins и Bitbucket.

Подразумевается, что в результате наших действий мы ожидаем получить следующее:

  1. Shared Library для Jenkins, чтобы все сценарии CI хранились в одном месте и их можно было править единым коммитом.
  2. Интеграцию Jenkins с Bitbucket, чтобы запускать CI по коммиту в определенные ветки или по созданию тега.

Поехали!

Конфигурация Jenkins


Для реализации задуманного в статье будут задействованы:


В Jenkins для проектов используется multibranch pipeline.

Начнем с того, что подключим к Jenkins репозиторий, в котором будет храниться наша Shared Library. Shared Library это единая библиотека, что может содержать в себе код для исполнения CI и хранится отдельно в своем собственном репозитории. Это значительно упрощает процесс модернизации и работы над CI (вместо использования для хранения CI стандартного Jenkinsfile, который нужно подкладывать в каждый проект).

Итак, подключаем: Manage Jenkins Configure System Global Pipeline Libraries.



Нужно указать имя, ветвь репозитория, из которой Jenkins будет забирать код библиотеки, а в Source Code Management указать адрес и доступ до репозитория (в нашем случае SSH-ключ для доступа ReadOnly).

Структура Shared Library


Теперь приступим к описанию самой библиотеки. Структура очень проста и состоит всего из трёх директорий:



  • vars директория для глобальных методов библиотеки, что будут вызываться из пайплайна;
  • src тоже директория для скриптов, но в основном используется для вашего кастомного кода;
  • resources всё, что не является скриптом и может понадобиться в исполнении.

Для наших целей в Jenkins будет достаточно только нескольких методов в директории vars, потому как мы настроим сам werf, что и сделает всю основную работу.

К тому же, хотелось бы, чтобы весь пайплайн был полностью описан внутри библиотеки, а в Jenkinsfile мы передавали только некоторые параметры деплоя, которые в 99,9% случаев вообще не будут меняться.

Реализуем методы


Итак, реализуем 2 метода.

Для вызова утилиты werf -runWerf.groovy.

#!/usr/bin/env groovydef call(String dockerCreds, String werfargs){  // логин в registry  // первый аргумент - url (пуст, т.к. используем DockerHub)  // второй - имя Jenkins-секрета, где лежат доступы (login, password)  docker.withRegistry("", "${dockerCreds}") {    sh """#!/bin/bash -el          set -o pipefail          type multiwerf && source <(multiwerf use 1.1 stable --as-file)          werf version          werf ${werfargs}""".trim()    }}

Все параметры в библиотеку для пайплайна передаются как Map, что удобно:

#!/usr/bin/env groovydef call( Map parameters = [:] ) { // функция принимает в качестве аргумента Map с параметрами  def namespace = parameters.namespace // имя неймспейса для выката  // имя ключа по умолчанию для расшифровки секретов (если не указан в параметрах)  def werf_secret_key = parameters.werfCreds != null ? parameters.werfCreds : "werf-secret-key-default"  // имя секрета по умолчанию для логина в docker registry  def dockerCreds = parameters.dockerCreds != null ? parameters.dockerCreds : "docker-credentials-default"  // получаем имя проекта из имени multibranch pipeline  def PROJ_NAME = "${env.JOB_NAME}".split('/').first()  // имя registry в docker hub или адрес до кастомного registry  def imagesRepo = parameters.imagesRepo != null ? parameters.imagesRepo : "myrepo"  if( namespace == null ) { // единственный обязательный аргумент и проверка на его наличие    currentBuild.result = 'FAILED'    return  }  pipeline {    agent { label 'werf' }    options { disableConcurrentBuilds() } // запрещаем параллельную сборку для пайплайна    environment { // переменные для работы werf      WERF_IMAGES_REPO="${imagesRepo}"             WERF_STAGES_STORAGE=":local"      WERF_TAG_BY_STAGES_SIGNATURE=true      WERF_ADD_ANNOTATION_PROJECT_GIT="project.werf.io/git=${GIT_URL}"      WERF_ADD_ANNOTATION_CI_COMMIT="ci.werf.io/commit=${GIT_COMMIT}"      WERF_LOG_COLOR_MODE="off"      WERF_LOG_PROJECT_DIR=1      WERF_ENABLE_PROCESS_EXTERMINATOR=1      WERF_LOG_TERMINAL_WIDTH=95      PATH="$PATH:$HOME/bin"      WERF_KUBECONFIG="$HOME/.kube/config"      WERF_SECRET_KEY = credentials("${werf_secret_key}")    }    triggers {      // Execute weekdays every four hours starting at minute 0      cron('H 21 * * *')     // для werf cleanup, что будет чистить registry и хост-раннер от устаревших кэшей и образов    }    stages {      stage('Checkout') {        steps {          checkout scm // получаем код из репозитория        }      }      stage('Build & Publish image') {        when {            not { triggeredBy 'TimerTrigger' } // чтобы stage не запускался по крону        }        steps {          script {            // запуск нашего метода из runWerf.groovy            runWerf("${dockerCreds}","build-and-publish")          }        }      }      stage('Deploy app') {        when {            not { triggeredBy 'TimerTrigger' }          }        environment {          // название окружения, куда осуществляется деплой (важно для шаблонизации Helm-чарта)          WERF_ENV="production"        }        steps {          runWerf("${dockerCreds}","deploy --stages-storage :local --images-repo ${imagesRepo}")        }      }      stage('Cleanup werf Images') {        when {          allOf {            triggeredBy 'TimerTrigger'            branch 'master'           }        }        steps {          sh "echo 'Cleaning up werf images'"            runWerf("${dockerCreds}","cleanup --stages-storage :local --images-repo ${imagesRepo}")        }      }    }  }}

Примечания:

  • Сборка и выкат происходят для любой ветки, указанной в секции discover у Jenkins. После наших манипуляций в следующей главе это будет происходить автоматически.
  • Все секреты, такие как werf-secret-key-default и docker-credential-default, хранятся в Jenkins Credentials:



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

@Library('common-ci') _multiStage ([namespace: 'yournamespace'])

Имя метода это название файла в каталоге vars.

Если необходимо выкатывать на несколько окружений, можно добавить условие для определенных веток в самом начале, где идет определение пространства имен. И убрать проверку на наличие аргумента namespace в Map, а также само его определение по умолчанию.

Пример реализации:

def namespace = "test"def werf_env = "test"if (env.JOB_BASE_NAME == 'master') { namespace = "stage" werf_env = "stage"}if (env.TAG_NAME) { namespace = "production" werf_env = "production"}# и добавляем в environment стадииenvironment {  WERF_ENV="${werf_env}" }

Если вы хотите автоматический запуск stage со всех веток, а с тегов в production только при нажатии кнопки в Jenkins, то можно использовать такое условие: currentBuild.rawBuild.getCauses()[0].toString().contains('UserIdCause'). Оно позволяет отследить, сборка была запущена человеком или началась как событие от webhook'а.

Триггеры по коммитам из Bitbucket


По умолчанию Jenkins сам не умеет интегрироваться в Bitbucket. Для этого нужно установить уже упомянутые плагины:

  • Bitbucket Branch Source Plugin добавляет Bitbucket как source для multibranch pipeline;
  • Basic Branch Build Strategies Plugin позволит запуск тегов по webhook. По умолчанию Jenkins не позволяет любые автоматизированные действия с тегами, т.к. не понимает какой из тегов последний.

Если вы используете cloud-версию Bitbucket, то нужно только поставить разрешение на создание webhook'ов автоматически.

Также требуется создать служебного пользователя с доступом к репозиториям, т.к. Jenkins будет обнаруживать весь репозиторий через API. Это касается настройки как для cloud-версии, так и для собственного Bitbucket-сервера.

Пример из глобальных настроек Jenkins:



Далее понадобится настроить source в Multibranch Pipeline, что происходит в интерактивном режиме. Это означает, что, когда вы добавите credentials bitbucket пользователя и имя команды или пользователя с проектами, которых мы будем работать, Jenkins найдет все доступные пользователю репозитории и позволит выбрать один из списка.

В самом репозитории мы полагались на поиск только определенных веток, т.к. не уверены, как много веток может быть, а Jenkins может надолго задуматься, если начнет исследовать каждую ветку. Это накладывает определенные ограничения, т.к. теги тоже попадают под регулярное выражение. Однако Java Regular Expressions довольно гибкие, так что большой проблемы нет.

Альтернативный путь: если есть желание совсем отделить теги от веток, можно добавить еще один абсолютно такой же Source в репозиторий и настроить его только на обнаружение тегов.

Итак, конфигурация:



После этого Jenkins с помощью сервис-аккаунта сам сходит в Bitbucket и создаст webhook:



Теперь при каждом коммите Bitbucket будет триггерить пайплайны (но только для тех веток и тегов, что мы отфильтровали) и даже посылать статус пайплайна обратно в Bitbucket в последнем столбце коммита:


Статусы кликабельные: при нажатии перекидывают в нужный пайплайн в Jenkins

Последний штрих про Jenkins, который находится за nginx proxy и работает с определенного location. Тогда нужно в основных настройках исправить его location, чтобы он сам знал, как выглядит его endpoint:



Без этого ссылки на pipeline в Bitbucket будут генерироваться некорректно.

Заключение


В статье рассмотрен вариант настройки CI с использованием Jenkins, Bitbucket и werf. Это очень общий пример, который не является панацеей для организации процесса разработки, однако даёт представление о том, как вообще подойти к построению своего CI с использованием werf.

Важная деталь: даже учитывая, что статус пайплайна отдается в Bitbucket, нам всё равно приходится ходить в Jenkins, чтобы разобрать результат в случае неудачи. Выкат по тегу через webhook, очевидно, может отрабатывать только один раз: любой откат на предыдущий тег придется делать вручную из Jenkins.

У данного подхода также есть большой плюс это гибкость. Мы буквально можем прописать в CI всё что угодно. Хотя и порог вхождения для того, чтобы понимать, как именно это сделать, чуть выше, чем у других CI-систем.

Эпилог: про werf и CI/CD в целом


Общий подход к интеграции werf с CI/CD-системами описан в документации. Вкратце рекомендуемые для любых проектов шаги сводятся к следующим:

  1. Создание временного DOCKER_CONFIG для исключения конфликтов между параллельными job'ами на одном runner'е (подробнее здесь).
  2. Выполнение авторизации Docker для используемых Docker Registry. Это может быть родная реализация Docker Registry внутри CI-системы либо какая-то сторонняя. В случае со встроенными имплементациями (к примеру, GitLab Container Registry или GitHub Docker Package) все необходимые параметры доступны среди переменных окружения. Выполнять авторизацию для альтернативных registry можно вручную на каждом runner'е или через параметры, хранящиеся в секретах (также для каждого job'а).
  3. Простановка WERF_IMAGES_REPO, WERF_STAGES_STORAGE, а также необходимых параметров, которые варьируются в зависимости от имплементации. Утилита werf должна знать, с какой реализацией работает, так как часть требует использования нативного API. Стоит отметить, что по умолчанию werf пытается определить, с какой имплементацией работает, исходя из адреса registry, но это задача часто невыполнима (и тогда требует явного указания имплементации).
  4. Простановка опций тегирования WERF_TAG_*: используя переменные окружения CI, определяем, чем инициирован текущий job, и выбираем подходящую опцию тегирования или всегда используем content-based тегирование (рекомендованный путь).
  5. Использование окружения CI-системы для последующего использования при выкате. Для понимания environment в GitLab.
  6. Простановка автоматических аннотаций для всех выкатываемых ресурсов WERF_ADD_ANNOTATION_*. Среди этих аннотаций могут быть произвольные данные, которые помогут вам работать и отлаживать ресурсы приложения в Kubernetes. Мы пришли к тому, что все ресурсы должны содержать следующий набор:
    1. WERF_ADD_ANNOTATION_PROJECT_GIT адрес проекта в Git;
    2. WERF_ADD_ANNOTATION_CI_COMMIT коммит, соответствующий выкату;
    3. WERF_ADD_ANNOTATION_JOB или WERF_ADD_ANNOTATION_PIPELINE адрес job или pipeline (зависит от CI-системы и желания), который связан с выкатом.
  7. Простановка по умолчанию комфортной работы с логом werf:
    1. WERF_LOG_COLOR_MODE=on включение цветного вывода (werf запускается не в интерактивном терминале, по умолчанию цвета отключены);
    2. WERF_LOG_PROJECT_DIR=1 вывод полного пути директории проекта;
    3. WERF_LOG_TERMINAL_WIDTH=95 установка ширины вывода (werf запускается не в интерактивном терминале, по умолчанию ширина равна 140).

За время применения werf в большом количестве проектов у нас сформировался набор решений, который унифицирует конфигурацию, решает общие проблемы и делает сопровождение проще и нагляднее. В настоящий момент все описанные выше шаги с учетом этих решений уже встроены в команду werf ci-env для GitLab CI/CD и GitHub Actions. Пользователям других CI-систем необходимо реализовывать аналогичные действия самостоятельно подобно тому, как описано в этой статье для примера с Jenkins.

P.S.


Читайте также в нашем блоге:

Подробнее..

Перевод Семь паттернов пайплайнов непрерывной поставки

23.11.2020 20:22:46 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса "DevOps практики и инструменты".

Прямо сейчас у вас есть шанс успеть на курс по специальной цене. Узнать подробности.


В настоящее время гибкость бизнеса часто основана на гибкости кода. Возможность быстрых и безопасных релизов по требованию для современных цифровых продуктов и услуг является реальным конкурентным преимуществом.

С 2004 года мы разрабатываем, собираем и развертываем пайплайны кода для автоматизации приложений и инфраструктур. В этой статье мы делимся семью паттернами, которые улучшают скорость, гибкость и качество, при одновременном повышении автономности, прозрачности и удобства обслуживания.

Непрерывная поставка

Непрерывная поставка (Continuous Delivery) это "возможность надежно и быстро поставлять изменения всех типов в руки пользователей". Если посмотреть на непрерывную поставку по матрице Agile vs Effort, то можно увидеть, что она находится прямо между непрерывной интеграцией и непрерывным развертыванием. Часто их вместе называют CI / CD.

В отчете о состоянии DevOps за 2019 год более 31 000 респондентов сообщили об эффективности своих процессов разработки и поставки. Но разница между лидерами и отстающими ошеломляет. У лидеров в 200 раз больше развертываний и в 100 раз выше скорость развертывания, а также в 2 600 раз быстрее восстановление после инцидентов и в 7 раз меньше вероятность отката релизов.

Это исследование показывает, что для лидеров скорость и стабильность не противоположны! Вы можете получить и то и другое (на самом деле вам нужно и то и другое), чтобы получить реальные конкурентные преимущества для ваших цифровых продуктов и услуг.

Пайплайны

Пайплайны (конвейеры) это основные технические артефакты непрерывной поставки. Современные пайплайны преобразуют исходный код приложения и инфраструктуры в версионированные пакеты, которые можно разворачивать в любой среде. Автоматизируя все рутинные задачи по сборке и развертыванию систем, разработчики могут сосредоточиться на реализации действительно полезных фич.

Хотя пайплайны кода существуют уже почти 20 лет CruiseControl, один из наших первых любимцев, был выпущен в 2001 году, они значительно эволюционировали за последние годы.

Основываясь на нашем опыте и опыте наших клиентов, мы выделили семь паттернов пайплайнов, которые мы наблюдали во многих современных технологических компаниях.

Паттерны пайплайнов

Паттерн 1 пайплайны как код

Логика пайплайна кодируется и хранится вместе с кодом приложения и инфраструктуры. Для выполнения пайплайнов используются контейнеры.

  • Никаких настроек через графический интерфейс! Вся логика пайплайна управляется, как и любой другой код приложения, и подчиняется тем же стратегиям ветвления и ревью.

  • Выполнение пайплайнов в контейнерах позволяет CI / CD - платформе поддерживать множество различных рабочих нагрузок, при этом каждая рабочая нагрузка может иметь свою собственную среду сборки для удовлетворения специфических требований.

  • Для образов контейнеров сред сборки используются проверенные Docker-образы.

  • Конфигурация CI runner автоматизированная, одинаковая и не требует ручной настройки. CI runner при необходимости могут масштабироваться и находиться в режиме ожидания для минимизации задержек.

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

Паттерн 2 перенос логики в повторно используемые библиотеки

Общая логика пайплайнов выносится в повторно используемые библиотеки, на которые можно ссылаться из кода пайплайнов, а также независимо их разрабатывать и тестировать.

  • Относитесь к пайплайн-библиотекам как к любому другому программному обеспечению. У них есть собственные репозитории, пайплайны, модульные тесты и релизы.

  • По возможности для запуска внешних задач пайплайны используют специфичные для языка инструменты, такие как Make, Rake, npm, Maven и т.п. Для того чтобы упростить пайплайн и сохранить идентичность процесса локальной сборки и CI.

  • Библиотеки легко найти и у них хорошая документацию.

Паттерн 3 отдельные пайплайны для сборки и развертывания

Пайплайны сборки и развертывания должны быть логически разделены, управляться и запускаться независимо. Должна быть возможность как автоматического, так и ручного запуска.

  • Должна быть одна сборка и много развертываний. Сосредоточьтесь на сборке. В ее результате появляются артефакты, которые можно развернуть несколько раз.

  • Будьте независимы от окружения. При отсутствии пакетов, специфичных для окружения, и вынесения параметров в переменные окружения, одна и та же сборка может работать в любой среде.

  • Упакуйте всё вместе. Всё весь исходный код, включая код инфраструктуры, должен храниться вместе, чтобы стать версионнированным пакетом.

Паттерн 4 запуск правильного пайплайна

Коммиты в ветки, пул реквесты и слияние с основной веткой всё это может инициировать различное поведение пайплайна, оптимизированное под конкретную команду.

  • Открытие pull request'а создает эфемерную среду для тестирования.

  • Слияния с основной веткой развертываются в не-продакшн или демо-среде, содержащей последний интегрированный код.

  • Создание новых тэгов приводит к выпуску релиза.

Паттерн 5 быстрая обратная связь

Каждый коммит автоматически запускает определенный пайплайн. При этом пайплайны сборки специально оптимизированы для скорости и быстрого оповещения о возникающих проблемах.

  • Пайплайны сборки используют распараллеливание для независимых друг от друга заданий с целью повышения производительности.

  • Быстрые пайплайны сборки выполняют необходимые задания за несколько минут.

  • Каждый успешный запуск завершается созданием версии пакета и результатами статического анализа.

  • С помощью многоканальных уведомлений вы можете настроить оповещения о статусе пул реквестов на дашбордах, в чатах, по электронной почте и др.

Паттерн 6 стабильные внутренние релизы

Развертываются только версионнированные пакеты, созданные пайплайном сборки. Эти развертывания запускаются вручную или автоматически по событиям.

  • Каждая ветка кода получает полное эфемерное окружение, названное в соответствии с именем ветки, которое можно легко создать или уничтожить.

  • Любой инженер может создать или удалить эфемерную среду.

  • CI runners используют возможности cloud-native IAM с временными разрешениями, чтобы получить необходимые роли и разрешения для выполнения своей работы.

Паттерн 7 история релизов

Разверните тегированные релизы в продакшн, автоматизируйте отчетность и оставьте историю развертываний.

  • "Релизный шлюз" (release gate) в виде кода и стандартизированные процессы релиза позволяют выпускать релизы по требованию.

  • Автоматизированные релизы оставляют историю, которую можно проанализировать в дальнейшем.

  • Release gate может вызывать внешние API и использовать ответ, чтобы решить продолжать релиз или остановить его.

Проблемы

Мы все чаще встречаем эти семь паттернов пайплайнов и используем их при работе с клиентами. Но несмотря на то что они представляют собой огромный скачок вперед с точки зрения скорости и стабильности, они не лишены недостатков.

Безопасность это самая большая проблема, которую мы видим. Она связана со сложностью автоматизации процессов, которые традиционно были ориентированы на человека. Сложность пайплайнов, принятие командой, изменение в культуре и автоматизация баз данных другие серьезные проблемы, над которыми нужно работать. Но это все решаемо, и мы ежедневно работаем над этим.

Итог

Гибкость бизнеса основана на гибкости кода. Для современных цифровых продуктов и сервисов возможность быстрого и безопасного релиза по требованию является реальным конкурентным преимуществом бизнеса. Пайплайны кода и, в частности, описанные семь паттернов могут помочь вашей организации сделать гигантский шаг вперед в скорости и стабильности релизов, а ваши команды будут работать на уровне лидеров.

Посмотреть специальное предложение.

Читать ещё:

Подробнее..

Перевод Enterprise COBOL реализация библиотеки

17.01.2021 18:05:05 | Автор: admin

image


Не желаете ли немного COBOL? Вы можете попробовать самостоятельно создать библиотеку на диалекте IBM Enterprise COBOL. Научиться структурировать пакет, управлять его зависимостями, реализовать автоматические тесты на z/OS, разместить в GitHub под контролем Continuous Integration. И, наконец, опубликовать в реестре пакетов.


Предварительные условия


Предполагается, что вы немного знакомы с основными принципами, подходами и стандартами COBOL. В этот раз мы будем использовать IBM Enterprise COBOL for z/OS проприетарный компилятор COBOL, который реализует значительную часть стандартов COBOL 85, COBOL 2002 и COBOL 2014. Код COBOL будем выполнять на z/OS проприетарной 64-разрядной операционной системе для мэйнфреймов IBM, представленной в октябре 2000 года, и полностью совместимой с функциональными возможностями, появившимися еще с 1960-х годов.


  • У вас установлен NPM менеджер пакетов для JavaScript.
  • У вас установлен Git клиент с открытым исходным кодом для управления версиями.
  • У вас есть аккаунт GitHub.
  • Вы можете использовать любой тестовый редактор, но я рекомендую Visual Studio Code с расширением IBM Z Open Editor.

Еще вам понадобится аккаунт на мэйнфрейме, поскольку IBM не распространяет среду исполнения, в отличие, скажем, от GnuCOBOL. К счастью, учебный аккаунт можно получить бесплатно. Зарегистрируйтесь в IBM и следуйте инструкциям. Вы получите емейл, где будет указан ваш USER ID, IP и PORT. Затем зайдите в Open Mainframe Project Slack и добавьте себе приложение zih через App меню. Отправьте в нем сообщение, например Hi, и бот спросит ваш емейл и USER ID, которые вы получили ранее. Отправьте их один за другим, и бот сгенерирует пароль.


Вероятно, самый простой способ взаимодействия с мэйнфреймом это Zowe. Zowe это фреймворк с открытым исходным кодом, который позволяет командам управлять, контролировать, выполнять сценарии (скрипты) и разрабатывать на мэйнфреймах. Установите инструмент командной строки Zowe и создайте профиль, используя полученные ранее учетные данные:


$ npm i -g @zowe/cli --ignore-scripts$ zowe profiles create zosmf ztrial --host <IP> --port <PORT> --user <USER ID> --pass <PASSWORD> --reject-unauthorized falseProfile created successfully!

Убедитесь, что ваш профиль может взаимодействовать с z/OSMF:


$ zowe zosmf check status

Если все сделано правильно, то вы увидите список компонентов z/OSMF в состоянии ACTIVE. Готово!


Пакет и Библиотека


Каждый язык программирования имеет стандартный набор функций, предоставляемых по умолчанию. В COBOL они называются Intrinsic functions. Они удовлетворяют базовые потребности программирования, но программисты любят расширять этот набор собственными функциями каждый раз, когда пишут нечто более сложное чем Hello, world.


Постепенно пользовательские функции образуют повторно используемые библиотеки для включения по необходимости в другие программы или сервисы. Благодаря системам контроля версий контрибьюторы кода могут эффективно сотрудничать и распространять библиотеки среди программистов. Единственная проблема заключается в интеграции с кодом, который может быть написан на другом диалекте COBOL, в другом стиле кодирования или иметь необычную структуру. Решением проблемы является включение библиотек в Пакеты определенного формата.


Подобно другим менеджерам пакетов, например, Yarn для JavaScript, Maven для Java, Packagist для PHP, NuGet для C # и т.д., в 2020 году COBOL, наконец, обрел собственный менеджер пакетов, который стандартизирует то, как контрибьюторы должны обращаться с библиотеками COBOLget. Данное руководство описывает современный подход создания и публикации вашего первого пакета COBOL.


Спецификация


Для финансовых приложений мы создадим пакет demo-banking, который реализует единственную функцию ibanok валидатор IBAN. Программа принимает буквенно-цифровой аргумент и возвращает буквенно-цифровое значение 1 в случае успеха или 0 в противном случае. Алгоритм следующий:


  1. Вычислить фактическую длину IBAN в аргументе.
  2. Проверить структуру IBAN.
  3. Переместить первые 4 символа в конец.
  4. Заменить каждый символ двумя цифрами, где A = 10, B = 11,, Z = 35.
  5. Вычислить остаток MOD97. Если он равен 1, то контрольная сумма верна.

Структура


Создайте новый репозиторий demo-banking в GitHub и скопируйте шаблон пакета в его локальную копию. Структура файлов выглядит так:


 .github    workflows        nodejs.yml .gitignore modules.json modules-lock.json README.md src    banking.cbl tests     tests.cbl     tests.jcl

Файл bank.cbl это собственно библиотека, в которой находится программа. Файл tests.cbl это тестовая программа. Файл tests.jcl это сценарий JCL, который будет выполнять тесты на мэйнфрейме. Файл modules.json это манифест пакета, который описывает библиотеку и ее зависимости:


{  "name": "demo-banking-FIXME",  "description": "Demo banking package",  "modules": [    "src/banking.cbl"  ],  "dialect": "entcobol",  "licenses": [    "MIT"  ],  "authors": [    "FIXME"  ],  "dependencies": {},  "dependencies-debug": {    "ecblunit": "*"  }}

Названия атрибутов говорят сами за себя и похожи на атрибуты других менеджеров пакетов. Наш пакет не использует никаких зависимостей, но требует пакет ECBLUnit любой версии (по умолчанию, последней доступной) для тестирования. Свойство modules перечисляет файлы COBOL для компиляции в библиотеку. Полную схему манифеста можно найти на https://cobolget.com/schema.json. Теперь давайте установим инструмент командной строки COBOLget и проверим манифест:


$ npm install -g cobolget$ cobolget validateAn error occurred: Error: "demo-banking-FIXME" does not match to ^[a-z0-9\-]+$

Упс! Пожалуйста, замените ключевое слово FIXME на свое GitHub имя в файлах README.md и в modules.json, сделав пакет валидным и уникальным. Не забудьте также заменить FIXME в команде ниже:


$ cobolget validateManifest modules.json is valid.$ cobolget list demo-banking-FIXMENo matching results.

Структурирование пакета закончено. На следующем этапе мы будем тестировать библиотеку.


Тестирование


Тесты ECBLUnit это обычные программы COBOL, которые допускают дальнейшее выполнение (без оператора STOP RUN). В файле tests.cbl имеется 8 вызовов ECBLUeq (утверждения равенства), ожидающих, что программа ibanok вернет 1. Утверждение (assertion) также представляет собой программу COBOL, которая побайтово сравнивает два значения ожидаемое и фактическое. Более подробную информацию можно найти на странице ECBLUnit. Теперь запустим тест локально, заменив <USER ID> на свой:


$ cobolget run build...Modules modules.cpy and modules.cbl updated.$ zowe zos-files upload file-to-data-set modules/modules.cbl <USER ID>.CBL...Data set uploaded successfully.$ zowe zos-files upload file-to-data-set tests/tests.cbl <USER ID>.CBL...Data set uploaded successfully.$ zowe jobs submit local-file tests/tests.jcl --view-all-spool-content...Time: 00:00:00There was 004 failure(s):#005 EQ  F140404040404040 1         F040404040404040 0         ^^#006 EQ  F140404040404040 1         F040404040404040 0         ^^#007 EQ  F140404040404040 1         F040404040404040 0         ^^#008 EQ  F140404040404040 1         F040404040404040 0         ^^FAILURES! Tests: 001, Skipped: 000 Assertions: 008, Failures: 004, Exceptions: 000

Да, тест не прошел утверждения #5 #8 возвращают 0 вместо 1. Определенно, это ложноотрицательный результат, поскольку данные IBAN были тщательно скопированы из Википедии. :) Вы можете открыть tests.cbl в редакторе, удалить все внутренние пробелы в IBAN и снова запустить тест. Но вместо этого я предлагаю вам улучшить ibanok. Обычно, пробелы в номерах IBAN допустимы, поэтому они тоже должны успешно пройти тесты. Лучшая реализация валидатора будет опубликована в реестре COBOLget под вашим именем. Тем не менее, вы можете сделать коммит и пуш в GitHub как есть без изменений и перейти к следующему шагу.


Публикация


Наш пакет будет следовать практике Непрерывной Интеграции, когда любая модификация исходного кода в репозитории тестируется. Каждый Push или Pull Request в GitHub запускает конвейер nodejs.yml, который автоматизирует шаги, описанные выше:


  1. Установить Zowe.
  2. Создать профиль Zowe.
  3. Установить COBOLget.
  4. Собрать пакет COBOLget, установив зависимости.
  5. Загрузить модуль COBOLget в мэйнфрейм.
  6. Загрузить тест ECBLUnit в мэйнфрейм.
  7. Выполнить тест и вернуть код исполнения.

Теперь сделайте коммит и пуш в GitHub и выпустите ваш пакет, добавив к коммиту тег версии, например 1.2.3. На странице GitHub на вкладке Actions вы увидите работу конвейера. Любой ненулевой код возврата пометит и шаг, и весь процесс как сбойный.
Теперь вы можете импортировать свой пакет в реестр COBOLget, командой index в консоли:


$ cobolget index -hUsage: index [options] <name|url>Import or update the package in the registryOptions:  -t, --token <token>                Repository token for private package  -o, --organization <organization>  Organization name for private package  -h, --help                         output usage information

Новорожденные пакеты мы будем индексировать по URL репозитория. Новые релизы уже имеющегося в реестре пакета можно индексировать имени. Не забудьте исправить FIXME:


$ cobolget index https://github.com/FIXME/demo-bankingPackage 'demo-banking-FIXME' has been indexed in the registry.

Готово! Ваш первый пакет опубликован на cobolget.com и готов к интеграции в приложения и сервисы.


Заключение


Вы успешно создали и опубликовали библиотеку для IBM Enterprise COBOL в формате COBOLget, используя z/OS, Git и Zowe, применив методы модульного тестирования и непрерывной интеграции. 60-летний COBOL все еще вписывается в современную разработку программного обеспечения!

Подробнее..
Категории: Continuous integration , Cobol , Mainframes

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru