В статье пойдёт речь об опыте построения процессов 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.

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


  1. m_dv
    22.11.2021 12:25

    А дистрибутив вы обновляете?


    1. stasan2 Автор
      22.11.2021 13:42

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

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

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

      Если речь про правку дистрибутивного кода, то в статье указано, что ключа у нас нет. Править мы ничего в дистрибутиве не должны, но конечно изредка приходится это делать по срочным ошибкам, одновременно с их регистрацией, чтобы не ждать несколько месяцев исправление от ЦФТ. Такие изменения могут быть включены в автоустановку в виде sql-скрипта на обновление исходников и перекомпиляцию. Сам дистрибутивный объект в основной проект IDE при этом не включаем, т.к. он в этом случае не сможет устанавливаться средствами IDE без ключа, да и дальше будет меняться в дистрибутиве независимо от нашего git. Однако, такие объекты мы себе сохраняем в отдельный проект, который вообще не предназначен для установки, просто, по нему можно сверить наличие исправлений ошибок, когда будет развёрнута очередная версия дистрибутива.