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

Нюансы использования TeamCity

Картинка


Всем привет.


Статья написана в простом стиле "DevOps для домохозяек" от таких же домохозяек. В ней будет описано с какими неожиданностями можно столкнуться при настройке проекта в TeamCity. Также приведу рекомендации как эти проблемы можно обойти.


Нижеописанное основано на моём двухлетнем опыте настройке TeamCity сборок, чтению баг репортов и обмене мнений с коллегами по цеху. Не претендую на истину в последней инстанции, так как в работе в основном использовался подход SDD (Stackoverflow Driven Development).


Небольшая справка:


  • TeamCity CI (Continous Integration) инструмент. "Аналог" Gitlab CI, Github Actions с прицелом на возможность полной настройки автоматизации из графического интерфейса.
  • Проект (Project) агрегирующая сущность, в неё связываются несколько сборок. В TeamCity древовидная структура проект->подпроект->сборка с частичным наследованием настроек.
  • Сборка (Build) Атомарная сущность автоматизации. Примеры сборок "Запуск автотестов", "Установка конфигурации", "Сборка дистрибутива". Каждая сборка состоит из нескольких шагов.
  • Шаг (Build step) Описание "а что нужно делать" используя разные "runner type". Это может быть как простой Bash скрипт, так и запуск Docker контейнера.

Вкратце по проекту TeamCity с которым я работаю:


  • ~30 сборок, шаги сборок состоят из вызовов Bash, Ansible и Python.
  • Никаких сборок Android приложений, Web проектов, Docker, k8s и прочего. Просто заказ облачных серверов, подъем базы данных из дампа, установка программ и конфигурации.
  • Настройка ведётся из графического интерфейса, без Kotlin DSL (переход на него в планах).

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


1 Нельзя изменить параметры сборки при запуске по триггеру


Сборки можно запускать по триггеру (внешнему событию). Триггеры могут быть разные: была завершена другая сборка, обновилась git ветка, cron задача. При этом в сборке можно задать параметры по умолчанию.


Так вот: запуск по триггеру можно сделать только с параметрами по умолчанию. На эту проблему есть нерешённая задача 2008 года.


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


Но мне возразят, что нужно использовать build chain (цепочку сборок). Окей, давайте посмотрим что там не так.


2 Build chain or not просто скопируй ещё сборки


В случае вышеописанного кейса (заказ стенда + прогон автотестов) мы настраиваем build chain. В таком случае если нам нужен стенд, то мы его получаем из сборки запуска автотестов. Интуитивно понятно, не правда ли?


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


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


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


3 Переопределение параметров зависимой сборки


Рассмотрим другой пример: у нас есть три зависимые последовательные сборки, которые связаны в build chain. Скажем заказ в облаке стенда, установка дампа базы данных и установка программ. Мы "интуитивно" запускаем сборку по установке программ и возникает вопрос: а как пробросить размер заказываемой машины в зависимой сборке? И тут нам на помощь приходит переопределение параметров.


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


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


4 Конфигурация сборки может быть только одна


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


Первый и самый простой вариант: объявить технологические работы и править "на горячую". Второй вариант: скопировать сборку в отдельное место и делать изменения в ней (наш вариант).


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


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


5 TeamCity API


Я думаю многие здесь знакомы с "главными" 4 метриками DevOps. И TeamCity кажется идеальным местом, чтобы собрать всю информацию хотя бы о половине из них ("Deployment Frequency" и "Lead Time for Changes").


Иии вот нельзя в API быстро узнать процент упавших сборок, частоту запуска и причины падения. Да, там есть какие-то дашборды, но информация в них именно та, которая не нужна. То есть вот начали у нас падать все сборки на основной ветке, и мы можем только вручную "Assign investigation", либо придумывать как реализовывать такой инструмент в каждой сборке. Не очень удобно.


Однако, в чём хорош API так это в формировании build chain из-за вышеперечисленных недостатков с "нативным" способом. В таком случае можно сборки создавать для независимого запуска. А их связывание делать в отдельной сборке с запуском тупого Python скрипта. И много кто так обходит проблему.


6 При запуске Bash скрипта проверяется только результат последней команды


Есть у нас простой скрипт, написанный в интерфейсе:


./command_1.sh # always faills # always success

В таком случае этот шаг сборки будет всегда зелёный. Но если мы добавим мантру:


./command_1.sh # always failif [ $? -ne 0 ]; then  echo "##teamcity[buildProblem description='Build failed']"fils # always success

И тогда уже шаг будет красным, и сборка дальше не пойдёт (тут уже как выставлен "Execute step"). Иными словами, всегда нужно учитывать особенность работы Bash скриптов.


7 Работа с шифрованными параметрами сборки


В TeamCity можно добавить параметры-секреты, такие как пароли и API ключи. Чтобы такие данные не утекли, в логах сборки ищутся значения таких параметров и заменяются на символы *. И возникает следующий нюанс: а если нам эти параметры нужно записать в другой файл. Команда echo в таком случае не сработает фильтр перехватит. В итоге мы пришли к следующему варианту:


cat > constants.json <<- EOM{    "key": "%value%"}EOM

Задача, в которой это понадобилась, была следующая. Есть Python скрипт, который выполняет запросы к нескольким системам, и вся его конфигурация сохранена в JSON. Как к нему подать секреты? Можно через командную строку, но тогда у нас растекается конфигурация сразу по четырём местам: JSON, командная строка, значение по умолчанию в скрипте и ещё значение по умолчанию в параметре TeamCity. Поскольку скрипт должен быть тупым и одноразовым, то решили максимально упростить: всю конфигурацию втащить в JSON. JSON прям с вписанными именами параметров TeamCity сохраняем в репозиторий и копируем как есть в шаг сборки. В итоге мы сразу формируем нужный JSON и не разбираемся какой параметр, где нужен.


8 Скорость прогрузки графического интерфейса


Это будет очень субъективный пункт. Поскольку основная работа по настройке проводится в интерфейсе (для тех кто не пользуется связкой Kotlin DSL + TeamCity API), то производительность работы пропорционально скорости работы этого интерфейса. А он ооочень медленный. Я постарался в меру своих интеллектуальных способностей замерить скорость прогрузки и вот какие цифры получил (был использован браузер Firefox и инструмент Network).


  • Загрузка окно проекта со всеми сборками и вызов окна запуска сборки
    • load: 9.87 s
    • DOMContentLoaded: 4.92 s
    • Finish: 34.39 s
    • Size/transferred size of all requests: 10.69 MB / 2.42 MB
    • Requests: 345
  • Загрузка окна отдельной сборки и вызов окна запуска сборки
    • load: 4.59 s
    • DOMContentLoaded: 1.27 s
    • Finish: 27.42 s
    • Size/transferred size of all requests: 11.53 MB / 2.23 MB
    • Requests: 120

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


9 Информация по упавшей сборке


Это будет ещё один субъективный пункт. Для каждой сборки есть вкладка Overview. В ней в случае падения сборки указывается ошибка. И эта ошибка в 99% случаев определяется неправильно. В нашем случае (может у кого по-другому) ошибка определяется как "первое, что попало в stderr", хотя было бы логичнее сделать "последнее, что попало в stderr". В случае Ansible это всегда будет какой-нибудь "WARNING: Deprecation setting...". И в итоге это вызывает стабильный поток вопросов у людей слабо знакомых с TeamCity. Пусть лучше вообще там ничего не писалось.


10 Вечные проблемы с агентами


Здесь я бы хотел написать про все проблемы, которые связанные с агентами сборки (Build agents). Как известно в TeamCity есть master сервер, который выполняет роль планировщика, а сами сборки запускаются на отдельных серверах. И нужно уметь правильно с этими агентами работать (обслуживать), TeamCity тут мало что может сделать.


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


Вторая проблема закончившееся место на агенте, либо неработающая базовая утилита. Пересекается с первым пунктом, но тут "веселее". Запускаем сборку, на самом первом шаге это всё падает и агент снова бодро готов браться за новую работу. Мы раздражённо запускаем сборку заново и по великому везению попадается тот самый агент и по новой. "Но у нас же есть великая настройка по выбору агента!" скажете вы и будете правы. Только один нюанс: а если у нас build chain? Тутуту руру тутуту, а оказывается, что выбрать агент можно только на текущую сборку, на зависимые может браться какой угодно и мы знаем какой будет выбран по закону подлости. Но это в случае, если вы не выставили при установке зависимой сборки "Run build on the same agent". Но вы же выставили, правда?


Третья проблема дрейф конфигурации на агентах. На агентах должно быть неизменяемое окружение (то, что не должно быть root прав, я думаю объяснять не надо). Кто-то поменял переменную окружения, поменял локальные пути до утилит и понеслось. Начинаешь после каждого такого случая перестраховываться и потом у тебя 90% сборки это подготовка агента к твоему print("Hello, World!").


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

Источник: habr.com
К списку статей
Опубликовано: 20.12.2020 12:12:18
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Системы сборки

Devops

Teamcity

Ci

Категории

Последние комментарии

  • Имя: Макс
    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