В статье пойдёт речь об опыте построения процессов CI/CD в Национальном расчетном депозитарии (НРД) в разработке под ЦФТ-Банк. В качестве введения рекомендую ознакомиться с предысторией того, как мы перевели разработку ЦФТ-Банк на CFT Platform IDE. Новая статья — её логическое продолжение и ориентирована на разработчиков, знакомых с основными возможностями CFT Platform IDE.
Организация процесса разработки и инструменты для построения CI/CD в НРД
Процесс разработки для системы ЦФТ-Банк у нас устроен по классической водопадной модели: релизы идут в хронологическом порядке, разделяясь на этапы аналитики, разработки, тестирования и опытной эксплуатации. При этом существует несколько dev и тестовых контуров с различными версиями системы.
Что касается инструментов CI/CD, то у нас в компании используются JetBrains TeamCity и Ansible AWX. Почему именно они, в данной статье я рассказывать не буду, т.к. они были выбраны ранее и не для процессов разработки под ЦФТ-Банк, хотя и полностью подошли для этого, как показала практика. Расскажу лишь о принципах настройки этих средств, позволивших вкупе с CFT Platform IDE обеспечить нужный результат.
Подобие Gitflow как основа для построения CI/CD
Логическая схема водопадной модели помогла поддержать порядок в разработке. До CFT Platform IDE он обеспечивался ручным контролем, ввиду отсутствия у прежних средств разработки ЦФТ возможности хранения кода в Git. С новой средой разработки мы организовали работу на основе методологии Gitflow, насколько это возможно с нашей водопадной моделью.
Итак, основные принципы нашей схемы:
Для каждого релиза в Git создаётся отдельная постоянная ветка путем копирования ветки ближайшей предыдущей версии.
Под каждое изменение (исправление инцидента, ошибки, целая доработка или её часть) должна быть создана отдельная временная ветка (feature-ветка). После завершения работ она с изменением вливается через merge-request в ветку соответствующей версии и удаляется.
После коммита изменений в ветку определённой версии выполняется merge изменений во все существующие ветки старших версий. Схематично изображу пример работы с 3 релизами (r67.0 соответствует prod, r68.0 находится в тестировании, r69.0 в разработке):
Как можно видеть, здесь нет характерной для классического Gitflow ветки master. В конкретный момент времени её заменяет ветка релиза, установленного на prod. Между релизами у нас проходит примерно 2 месяца, бывает один-два промежуточных мини-релиза (патча) с более короткими циклами внедрения. Таким образом, после установки следующей версии на prod master-веткой можно считать соответствующую этой новой версии ветку.
После внедрения CFT Platform IDE в разработку для ЦФТ-Банк мы начали работать с Git также, как при доработках многих других систем в НРД. Это позволило применить наши devops-наработки в Gitlab: например, auto-merge изменений по веткам, соответствующим последующим версиям, или git-hook для проверки commit-message на предмет наличия в нём номера Redmine и пр.
Но посмотрим, что можно делать, когда все изменения уже сохранены в git.
CFT Platform IDE имеет возможность выгрузки изменений, фиксируемых между двумя ветками git:
Любопытно, что в предыдущих версиях CFT Platform IDE сравнение могло происходить только с веткой master, однако специалисты ЦФТ прислушались к нашим пожеланиям и добавили параметр с возможностью указания конкретной ветки для сравнения, обеспечив необходимую гибкость.
Таким образом, мы можем выгружать, как изменения по конкретному хотфиксу, так и в целом весь пул изменений одной или нескольких версий. Но самое главное, что возможности, доступные в интерфейсе CFT Platform IDE, продублированы и при использовании через командную строку. Именно это и позволило строить CI/CD на его основе, но обо всём по порядку.
С запуском работы по подобию Gitflow мы, пользуясь возможностями новой IDE, стали выгружать в ручном режиме изменения для установки на тестовый контур. Затем появились мысли об автоматизации процесса. Нам захотелось создать «кнопку» для установки конкретной версии на конкретный контур, а в идеале и вовсе настроить расписание для полностью автоматической актуализации версий на всех тестовых контурах. Тогда бы разработчику требовалось лишь сделать коммит в нужную версию и после отправки merge-request’а уже больше ни о чём не думать.
Перед началом автоматизации необходимо было понять, какие изменения войдут в конкретную установку, чтобы сборка была правильной.
1. Нужно актуализировать PL/Plus код, разворачиваемый в БД.
Как показано выше, CFT Platform IDE умеет выгружать изменения на сравнении двух веток git, а в нашем случае это две версии системы. Но для каждой БД следует понять, изменения из каких версий туда обязательно должны попасть.
Ветка с изменениями – это ветка с версией тестового контура, который нужно актуализировать. А сравнивать её нужно с веткой, в которой уже невозможны изменения. Например, на контур функционального тестирования (ФТ) могут прийти изменения из трех версий:
текущей prod (hotfix срочных инцидентов);
версии, находящейся на опытной эксплуатации (исправление ошибок);
самой версии функционального тестирования (исправленные ошибки плюс новые доработки, добавляемые в тестирование).
Поэтому для получения всех возможных изменений нужно сравнивать целевую ветку и ветку, предшествующую prod.
В нашем примере через какое-то время получится, что версия из опытной эксплуатации (ОЭ) будет установлена на prod и, соответственно, для сборки версии из примера ветка для сравнения поменяется – сдвинется вперёд на одну версию.
Кроме того, ветку для сравнения в каких-то случаях можно выбирать и по-другому, обеспечивая оптимизацию. Если у нас количество hotfix ничтожно малое – одно-два в месяц, то можно ее сравнить с prod-веткой (а не с предшествующей), чтобы постоянно не перекатывать все доработки из этой версии, а если случился hotfix, то эти изменения разбросать на тестовые контура вручную. Причём если количество hotfix возрастёт, например, после внедрения большого проекта, то можно на некоторое время вернуться к большей глубине сравнения.
Итак, очевидно, что в параметрах сборки нам необходимо поле для указания не только целевой версии, но и версии для сравнения, которой можно легко управлять. В Teamcity это выглядит так (номер версии у нас состоит из номера релиза и патча, поэтому создано две пары параметров):
2. Выполнение операций конвертации, заложенных в каждую версию.
На первый взгляд кажется, что здесь всё аналогично актуализации кода, т.е. при актуализации того или иного тестового контура нужно прокручивать конвертации из 2-3 версий. Но мы пришли к выводу, что нам достаточно конвертаций только последней версии. Дело в том, что активное изменение настроек и т.п. идёт у нас только на этапе ФТ. На ОЭ значительные изменения по настройкам отсутствуют, а те, что есть, легко точечно установить вручную, если вообще это имеет смысл на другом контуре.. В срочные исправления для prod конвертации вообще у нас не входят (скрипты для исправления данных не в счёт, т.к. их на тестовых контурах запускать не имеет никакого смысла, ввиду отсутствия этих самых данных).
Итак, мы решили при установке конкретной версии исполнять конвертации только этой версии, а если требуется на какой-то базе поднять версию, минуя несколько промежуточных, то можно в ручном режиме прогнать их установку последовательно.
Без этого задача автоматизации сложно решаема. При необходимости прогонять конвертации двух последних версий появилась бы проблема с выполнением конвертаций предыдущей версии, т.к. они могут быть удалены в последней версии и будут отсутствовать на схеме, если мы только что актуализировали код до этой последней версии. Это можно обойти: запретить в регламенте разработки удаление конвертаций в ближайших версиях, но где гарантия, что они будут компилироваться в будущих версиях при изменении связанных библиотек. Такую конвертацию кто-то должен сопровождать в будущих версиях, где она, по сути-то, и не нужна. Мы мудрить не стали и решили ограничиться последними конвертациями. Такой подход оказался выигрышным.
Относительно операций конвертации в нашем регламенте разработки имеются такие указания:
Первое появилось задолго до CFT Platform IDE. Конвертации должны допускать многократный запуск с одним и тем же результатом на выходе. Условие было внесено для случаев ошибочного повторного ручного запуска, но при проектировании автоустановки позволило не думать о журналировании в БД фактов запуска конвертаций, а выполнять все конвертации, предусмотренные установкой конкретной версии.
Второе указание добавили уже специально под предполагаемую автоустановку. Все операции конвертации, предусматривающие работу с файлами импорта, должны уметь забирать эти файлы с fio сервера. Нам даже потребовались доработки: например, у нас есть часто используемая в конвертациях операция импорта тарифных планов из Excel, которая изначально умела брать файлы только с клиентской станции. В ней пришлось добавить вариант загрузки и из папки на сервере.
3. Завершающий пункт требований к сборке – в неё должны входить файлы импорта.
Файлы импорта, а также скрипт для запуска операций конвертации и дополнительные скрипты конвертации данных, мы добавили прямо в проект CFT Platform IDE в специально созданную для этого папку setup и подпапку с номером версии:
В подпапку FIO помещаем файлы импорта с расчетом, что они должны быть скопированы в корень файловой системы сервера перед конвертациями. Дополнительные файлы (инструкция по установке.txt и др.) мы прикладываем к поставке в случае ручной установки сотрудником сопровождения.
Таким образом, если IDE формирует patch.zip по сравнению веток, то внутри окажется папка setup с файлами конвертации для нужной версии.
Правда, эту особенность мы не используем, т.к. сборку и установку разделили не совсем очевидным способом, задействовав CFT Platform IDE только на этапе установки.
По сути, сборка на выходе Teamcity – это просто git-репозиторий с двумя ветками – целевой и веткой для сравнения, а также сопровождающий файл, где указаны соответствующие номера версий.
А вот IDE для получения на базе этого репозитория файлов patch.zip и patch_delete.pck (список элементов для удаления) вызывается только при работе AWX в процессе автоустановки.
Такой подход мы использовали для упрощения, т.к. при нём нет нужды заниматься прикручиванием к Teamcity Windows-агента с IDE — всё, что от IDE требуется, выполняется вызовом из AWX. Минус такого подхода – относительно большой размер сборки. Пока он несильно превышает 100Мб, нас это устраивает ввиду небольшого размера проекта. Но с ростом размера подход изменится, чтобы получать patch.zip и patch_delete.pck уже на этапе сборки в Teamcity.
AWX распаковывает нужную сборку во временный каталог на Windows-агенте (по сути это обычный сервер под Windows, где установлена CFT Platform IDE, включая отдельную лицензию на это «рабочее место») и производит на этом сервере вызов bat-файла с передачей в него дополнительных параметров (адрес сервера, пароль владельца схемы и т.п.), а затем забирает с сервера лог, чтобы его отобразить в своём интерфейсе.
При этом в bat-файл и зашито всё самоё интересное. Первоначально в нём у нас были следующие основные шаги:
Сохранение с помощью CFT Platform IDE patch.zip и patch_delete.pck на основе сравнения двух веток в сборке.
Развёртывание через IDE patch.zip (ошибки на этом шаге пропускаются, т.к. до выполнения операций конвертации могут отсутствовать значения в каких-то справочниках и пр., что может влиять на компиляцию).
Удаление с помощью IDE элементов по списку patch_delete.pck.
Копирование файлов из папки setup\FIO на файловую систему сервера АРМом Приемопередатчик.
Вызов скрипта convert.sql (при наличии в сборке) в дополнение с вызовом АРМа Монитор коммуникационного канала для вычитывания и сохранения в файл сообщений, выводимых в процессе работы конвертаций.
Вторая попытка развёртывания patch.zip в случае ошибок при первом развёртывании.
Установка в БД номера версии, соответствующего установленной сборке (для ведения версий создана специальная oracle-таблица).
После отладки автоустановки мы добавили в неё проверки доступности БД. Вначале проверяем действие лицензии ЦФТ и доступность fio. После развёртывания проверяем работоспособность java-библиотек (на примере xmlparser), запущен ли сервер отчётов (по наличию сессий пользователя app_adm), запущены ли обработчики очередей интегратора и другое, что позволит нормально использовать тестовый сервер.
В дальнейшем потребовалось доавтоматизировать установку кусков дистрибутивных патчей/дополнений. Замечу, что с дистрибутивном мы работаем вполне стандартно: около двух раз в год делаем полное обновление, а в промежутке нередко нужно сделать частичную установку, например, при срочном обновлении какой-то формы отчётности. Для этого мы выбираем элементы из одного или нескольких дистрибутивных (поставляемых ЦФТ) хранилищ и разворачиваем в базу, после этого, возможно, выполняя операции конвертации.
Развёртывание нам нужно делать именно из дистрибутивных хранилищ по двум причинам. Во-первых, у нас нет ключа на правку дистрибутивного кода, поэтому дистрибутивные элементы мы можем развернуть только из хранилищ с электронной подписью. А во-вторых, даже если бы мы могли развернуть такие элементы просто из своего проекта, то их бы пришлось сначала в этот проект включить, что для дистрибутивных элементов не имеет никакого смысла — ЦФТ их меняет, независимо от нашего Git. Поэтому такие элементы если и нужны нам как зависимости, то подключаются только по ссылкам в файле imports.pck.
Чтобы и подобные ситуации охватить автоустановкой, мы реализовали следующую схему:
Все дистрибутивные патчи и дополнения, полученные с сайта сопровождения, хранятся в конкретной сетевой папке.
В вышеописанную папку setup в проекте вкладываем специальный скрипт, где прописаны адреса используемых хранилищ, а также названия pck-файлов со списками устанавливаемых элементов, причём эти файлы тоже коммитятся в папку setup.
Вызов конвертаций, если они требуются, прописываются в общий скрипт с конвертациями, о котором я писал ранее.
Запуск скрипта развёртывания дистрибутивных хранилищ мы вписали всё в тот же bat-файл, предусмотрев повторный запуск после выполнения конвертаций, если первое развёртывание прошло с ошибками.
Итоги
Т.к. Teamcity и AWX могут работать по расписанию, в некоторых случаях у нас настроен полный автомат. Но важно понимать — для ежедневной установки по расписанию тестировщики должны соблюдать технологическое «окно».
В любом случае возможность обновить версию одной кнопкой в Teamcity и AWX – огромное удобство, ведь такие по современным меркам простые радости при разработке для ЦФТ-Банк ещё недавно были чем-то из области фантастики.
Первая версия CI/CD для ЦФТ-Банк в НРД была реализована примерно вначале 2020 г., и из-за того, что мы чаще стали использовать CFT Platform IDE в безинтерфейсном режиме, стабильно стали выявляться и ошибки. Их мы регистрировали на сайте сопровождения ЦФТ, а затем получали исправления и могли дальше пользоваться автоустановкой. Через полтора года большинство ошибок исправлены, кроме частного случая при использовании макросов, имеющего вариант обходного решения. Сейчас, решившись на реализацию CI/CD с использованием CFT Platform IDE, подобных «детских болезней» можно избежать.
А кроме того, такое положение дел позволяет думать о развитии: ЦФТ анонсировал поддержку юнит-тестов и свой плагин Sonarqube для проверки PL/Plus кода, что видится весьма полезным.
И в заключение добавляю ссылку на исходники скриптов, работающих на сервере с установленной CFT Platform IDE, корневым там является deploy.bat.