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

Liquibase

Как мы в 2020 году изобретали процесс разработки, отладки и доставки в прод изменений базы данных

24.08.2020 14:14:04 | Автор: admin
На дворе 2020 год и фоновым шумом вы уже привыкли слышать: Кубернетес это ответ!, Микросервисы!, Сервис меш!, Сесурити полиси!. Все вокруг бегут в светлое будущее.

Подходы в том, что касается баз данных, в нашей компании более консервативны, чем в прикладных приложениях. Крутится база данных у нас не в кубернетесе, а на железе или в виртуалке. Для изменений базы данных процессинга платежных сервисов у нас есть устоявшийся процесс, который включает в себя множество автоматических проверок, большое ревью и релиз с участием DBA. Количество проверок и привлекаемых людей в этом случае негативно влияет на time-to-market. С другой стороны, он отлажен и позволяет надежно вносить изменения в продакшен, минимизируя вероятность что-то сломать. А если что-то сломалось, то нужные люди уже включены в процесс починки. Этот подход делает работу основного сервиса компании стабильнее.

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



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

Проблемы, которые мы решали


Нет единых стандартов версионирования


В лучшем случае это DDL SQL-файлы, которые лежат где-то в директории db в репозитории с микросервисом. Совсем плохо, если это просто текущее состояние БД, разное на тесте и на проде, и эталонных скриптов схемы БД нет.

В ходе отладки ушатываем тестовую базу


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

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

Методы DAO не покрываются тестами, не проверяются в CI


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

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

Неизоморфность сред


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

Объекты на тесте могут быть созданы из-под учетки разработчика или приложения. Гранты накидываются как попало, обычно grant all privileges. Гранты приложению выдаются по принципу вижу ошибку в логе даю грант. Часто при релизе забывают про гранты. Иногда после релиза смок-тестирование не покрывает всю новую функциональность и отсутствие гранта выстреливает не сразу.

Тяжелый и ломучий процесс наката в продакшен


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

Это замедляет релиз. А в случае проблем увеличивает даунтайм, усложняя доступ разработчика к БД. Скрипты exec.sql и rollback.sql часто не проверялись на тесте, потому что стандарта патчсетирования для не-Oracle нет, а на тест катилось как попало.

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

Как можно делать, чтобы было хорошо


Отладка на локальной БД в докер-контейнере


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

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

Приведу пример, насколько просто поднять локально БД:

Пишем двухстрочный Dockerfile:
FROM postgres:12.3ADD init.sql /docker-entrypoint-initdb.d/


В init.sql делаем чистую БД, которую рассчитываем получить и на тесте, и в проде. Она должна содержать:

  • Пользователя-владельца схемы и саму схему.
  • Пользователя приложения с грантом на использование схемы.
  • Требуемые EXTENSIONs


Пример init.sql
create role my_awesome_servicewith login password *** NOSUPERUSER inherit CREATEDB CREATEROLE NOREPLICATION;create tablespace my_awesome_service owner my_awesome_service location '/u01/postgres/my_awesome_service_data';create schema my_awesome_service authorization my_awesome_service;grant all on schema my_awesome_service to my_awesome_service;grant usage on schema my_awesome_service to my_awesome_service;alter role my_awesome_service set search_path to my_awesome_service,pg_catalog, public;create user my_awesome_service_app with LOGIN password *** NOSUPERUSER inherit NOREPLICATION;grant usage on schema my_awesome_service to my_awesome_service_app;create extension if not exists "uuid-ossp";



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

db:    docker container rm -f my_awesome_service_db || true    docker build -t my_awesome_service_db docker/db/.    docker run -d --name my_awesome_service_db -p 5433:5432 my_awesome_service_db


Версионирование changesetов с помощью чего-то стандартного для индустрии


Тоже выглядит очевидно: нужно писать миграции и содержать их в системе контроля версий. Но очень часто я вижу голые sql-скрипты, без какой-либо обвязки. И это значит, что нет никакого контроля наката и отката, кем, что и когда было накачено. Нет даже гарантии, что ваши SQL-скрипты могут быть выполнены на тестовой и продовой БД, так как ее структура могла измениться.

В общем, нужен контроль. Системы миграции как раз про контроль.
Не будем вдаваться в сравнение разных систем версионирования схем БД. FlyWay vs Liquibase не тема этой статьи. Мы выбрали Liquibase.

Мы версионируем:

  • DDL-структуру объектов бд (create table).
  • DML-содержимое таблиц-справочников (insert, update).
  • DCL-гранты для УЗ Приложения (grant select, insert on ...).


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

Пример sql-патчсета
0_ddl.sql:
create table my_awesome_service.ref_customer_type(    customer_type_code    varchar not null,    customer_type_description varchar not null,    constraint ref_customer_type_pk primary key (customer_type_code)); alter table my_awesome_service.ref_customer_type    add constraint customer_type_code_ck check ( (customer_type_code)::text = upper((customer_type_code)::text) );

1_dcl.sql:
grant select on all tables in schema my_awesome_service to ru_svc_qw_my_awesome_service_app;grant insert, update on my_awesome_service.some_entity to ru_svc_qw_my_awesome_service_app;

2_dml_refs.sql:
insert into my_awesome_service.ref_customer_type (customer_type_code, customer_type_description)values ('INDIVIDUAL', 'Физ. лицо');insert into my_awesome_service.ref_customer_type (customer_type_code, customer_type_description)values ('LEGAL_ENTITY', 'Юр. лицо');insert into my_awesome_service.ref_customer_type (customer_type_code, customer_type_description)values ('FOREIGN_AGENCY', 'Иностранное юр. лицо');

Fixtures. Данные для тестов или отладки идут отдельным ченжсетом с контекстом dev
3_dml_dev.sql:
insert into my_awesome_service.some_entity_state (state_type_code, state_data, some_entity_id)values ('BINDING_IN_PROGRESS', '{}', 1);

rollback.sql:
drop table my_awesome_service.ref_customer_type;


Пример changeset.yaml
databaseChangeLog: - changeSet:     id: 1     author: "mr.awesome"     changes:       - sqlFile:           path: db/changesets/001_init/0_ddl.sql       - sqlFile:           path: db/changesets/001_init/1_dcl.sql       - sqlFile:           path: db/changesets/001_init/2_dml_refs.sql     rollback:       sqlFile:         path: db/changesets/001_init/rollback.sql - changeSet:     id: 2     author: "mr.awesome"     context: dev     changes:       - sqlFile:           path: db/changesets/001_init/3_dml_dev.sql



Liquibase создает на БД таблицу databasechangelog, где отмечает накаченные ченджсеты.
Автоматически вычисляет, сколько ченджсетов нужно докатить до БД.

Есть maven и gradle plugin с возможностью сгенерировать из нескольких ченджсетов скрипт, который нужно докатить до БД.

Интеграция системы миграций БД в фазу запуска приложения


Здесь мог бы быть любой адаптер системы контроля миграций и фреймворка, на котором построено ваше приложение. Со многими фреймворками он идёт в комплекте с ORM. Например, Ruby-On-Rails, Yii2, Nest.JS.

Этот механизм нужен, чтобы катить миграции при старте контекста приложения.
Например:

  1. На тестовой БД патчсеты 001, 002, 003.
  2. Погромист наразрабатывал патчсеты 004, 005 и не деплоил приложение в тест.
  3. Деплоим в тест. Докатываются патчсеты 004, 005.


Если не накатываются приложение не стартует. Rolling update не убивает старые поды.
В нашем стеке JVM + Spring, и мы не используем ORM. Поэтому нам потребовалась интеграция Spring-Liquibase.

У нас в компании есть важное требование безопасности: пользователь приложения должен иметь ограниченный набор грантов и точно не должен иметь доступ уровня владельца схемы. С помощью Spring-Liquibase есть возможность катить миграции от имени пользователя-владельца схемы. При этом пул соединений прикладного уровня приложения не имеет доступа к DataSource'у Liquibase. Поэтому приложение не получит доступ из-под пользователя-владельца схемы.

Пример application-testing.yaml
spring:  liquibase:    enabled: true    database-change-log-lock-table: "databasechangeloglock"    database-change-log-table: "databasechangelog"    user: ${secret.liquibase.user:}    password: ${secret.liquibase.password:}    url: "jdbc:postgresql://my.test.db:5432/my_awesome_service?currentSchema=my_awesome_service"



DAO тесты на CI-этапе verify


В нашей компании есть такой CI-этап verify. На этом этапе происходит проверка изменений на соответствие внутренним стандартам качества. Для микросервисов это обычно прогон линтера для проверки кодстайла и на наличие багов, прогон unit-тестов и запуск приложения с поднятием контекста. Теперь на этапе verify можно проверить миграции БД и взаимодействие DAO-слоя приложения с БД.

Поднятие контейнера с БД и накат патчсетов увеличивает время старта Spring-контекста на 1,5-10 сек, в зависимости от мощности рабочей машины и количества патчсетов.

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

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

Пример DAO-теста
@Test@Transactional@Rollbackfun `create cheque positive flow`() {      jdbcTemplate.update(       "insert into my_awesome_service.some_entity(inn, registration_source_code)" +               "values (:inn, 'QIWICOM') returning some_entity_id",       MapSqlParameterSource().addValue("inn", "526317984689")   )   val insertedCheque = chequeDao.addCheque(cheque)   val resultCheque = jdbcTemplate.queryForObject(       "select cheque_id from my_awesome_service.cheque " +               "order by cheque_id desc limit 1", MapSqlParameterSource(), Long::class.java   )   Assert.assertTrue(insertedCheque.isRight())   Assert.assertEquals(insertedCheque, Right(resultCheque))}



Есть две сопутствующие задачи для прогона этих тестов в пайплайне на verify:

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

Эти две задачи решает библиотека TestContainers. Она использует существующий докер образ для поднятия контейнера с базой данных в состоянии init.sql.

Пример использования TestContainers
@TestConfigurationpublic class DatabaseConfiguration {   @Bean   GenericContainer postgreSQLContainer() {       GenericContainer container = new GenericContainer("my_awesome_service_db")               .withExposedPorts(5432);       container.start();       return container;   }   @Bean   @Primary   public DataSource onlineDbPoolDataSource(GenericContainer postgreSQLContainer) {       return DataSourceBuilder.create()               .driverClassName("org.postgresql.Driver")               .url("jdbc:postgresql://localhost:"                       + postgreSQLContainer.getMappedPort(5432)                       + "/postgres")               .username("my_awesome_service_app")               .password("my_awesome_service_app_pwd")               .build();   }       @Bean   @LiquibaseDataSource   public DataSource liquibaseDataSource(GenericContainer postgreSQLContainer) {       return DataSourceBuilder.create()               .driverClassName("org.postgresql.Driver")               .url("jdbc:postgresql://localhost:"                       + postgreSQLContainer.getMappedPort(5432)                       + "/postgres")               .username("my_awesome_service")               .password("my_awesome_service_app_pwd")               .build();   }



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

Kubernetes это ответ! А какой был ваш вопрос?


Итак, вам надо автоматизировать какой-то CI/CD-процесс. У нас есть обкатанный подход на тимсити. Казалось бы, где тут повод для еще одной статьи?

А повод есть. Кроме обкатанного подхода, есть и поднадоевшие проблемки большой компании.

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


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

Допустим, вы уже одной ногой по колено в светлом будущем и кубернетес-инфраструктура у вас уже есть. Есть даже возможность сгенерировать еще один микросервис, который сразу заведется в этой инфраструктуре, подхватит нужный конфиг и секреты, будет иметь нужные доступы, зарегистрируется в service mesh инфраструктуре. И всё это счастье может получить рядовой разработчик, без привлечения человека с ролью *OPS. Вспоминаем, что в кубернетесе есть тип ворклоада Job, как раз предназначенный для каких-то сервисных работ. Ну и погнали делать приложение на Kotlin+Spring-Liquibase, стараясь максимально переиспользовать существующую в компании инфраструктуру для микросервисов на JVM в кубере.

Переиспользуем следующие аспекты:

  • Генерация проекта.
  • Деплой.
  • Доставку конфигов и секретов.
  • Доступы.
  • Логирование и доставка логов в ELK.


Получаем такой пайплайн:


Кликабельно

Теперь мы имеем:


  • Версионирование ченджсетов.
  • Проверяем их на выполнимость update rollback.
  • Пишем тесты на DAO. Бывает даже следуем TDD: запускаем отладку DAO с помощью тестов. Тесты выполняются на свежеподнятой БД в TestContainers.
  • Запускаем локально БД в докере на стандартном порту. Проводим отладку, смотрим, что осталось в БД. При необходимости можем управлять локальной БД вручную.
  • Накатываем в тест и проводим авторелиз патчсетов стандартным пайплайном в teamcity, по аналогии с микросервисами. Пайплайн является дочерним для микросервиса, которому принадлежит БД.
  • Не храним креды от БД в тимсити. И не заботимся о доступах с виртуалок-билдагентов.


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

Перевод Контроль версий в базах данных Сравнение Liquibase и Flyway

21.12.2020 14:15:23 | Автор: admin

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

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

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

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

Как работают инструменты Flyway и Liquibase

Оба инструмента реализуют концепцию эволюционных баз данных, объясняет Мартин Фаулер.

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

Пример кода

1.Создайте пустой проект maven

mvn archetype:generate -B     -DarchetypeGroupId=org.apache.maven.archetypes     -DarchetypeArtifactId=maven-archetype-quickstart     -DarchetypeVersion=1.1     -DgroupId=robloxro     -DartifactId=flywaysample     -Dversion=1.0-SNAPSHOT     -Dpackage=flywaysample

2. Установите плагин Flyway в pom.xml (и базу данных H2, чтобы мы запускали на ней пример).

<project>.....<groupId>robloxro</groupId>  <artifactId>flywaysample</artifactId>  <version>1.0-SNAPSHOT</version>  <packaging>jar</packaging><name>flywaysample</name>  <url>http://maven.apache.org</url><build>        <plugins>            <plugin>                <groupId>org.flywaydb</groupId>                <artifactId>flyway-maven-plugin</artifactId>                <version>5.2.4</version>                               <dependencies>                    <dependency>                        <groupId>com.h2database</groupId>                        <artifactId>h2</artifactId>                        <version>1.4.191</version>                    </dependency>                </dependencies>            </plugin>        </plugins>    </build></project>

3. Определите файл конфигурации flyway

flyway.user=saflyway.password=flyway.schemas=INFORMATION_SCHEMAflyway.url=jdbc:h2:~/testflyway.locations=filesystem:.\src\main\resources\db\migration

4. Создайте первый скрипт миграции

create table PERSON (    ID int not null,    NAME varchar(100) not null);

5. Выполните первую миграцию

mvn clean flyway:migrate -Dflyway.configFile=myFlywayConfig.properties

Вывод должен быть следующий

Вы также можете проверить базу данных

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

Просто запустите его снова

mvn clean flyway:migrate -Dflyway.configFile=myFlywayConfig.properties

Вывод отобразит

7. Примените вторую миграцию

insert into PERSON (ID, NAME) values (1, 'Axel');insert into PERSON (ID, NAME) values (2, 'Mr. Foo');insert into PERSON (ID, NAME) values (3, 'Ms. Bar');

8. Что произойдет, если я изменю уже примененный скрипт?

Измените в уже существующем примененном файле V2_Addpeople.sql.sql

это

insert into PERSON (ID, NAME) values (3, 'Ms. Bar');

на это

insert into PERSON (ID, NAME) values (3, 'Ms. Barrrr');

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

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

Возможности, предлагаемые Flyway и Liquibase

Как Flyway, так и Liquibase являются инструментами, написанными на Java. Они легко интегрируются с maven, gradle и другими инструментами сборки, что способствует большей кастомизации. Они предлагают Java API и могут быть расширены. Их можно использовать из командной строки или используя их jar параметры.

Оба продукта предлагают

  • контроль версий

  • инкрементное развертывание скриптов (только в случае еще не примененных к целевой базе данных скриптов)

  • предотвращение ситуаций, когда скрипт, примененный к базе данных, был изменен в системе контроля версий вместо инкрементного добавления

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

  • параметры контекста - $var - чтобы вы могли настроить модель базы данных, чтобы она подходила для нескольких развертываний на схемах в конвейере CI/CD

  • оба полагаются на таблицы контроля версий с контрольной суммой (Flyway: SCHEMA_VERSION, Liquibase: DATABASECHANGELOG и DATABASECHANGELOGLOCK)

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

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

+------------------------------------+------------------+--------+--+|              Feature               |    Liquibase     | Flyway |  |+------------------------------------+------------------+--------+--+| Database Source Control/Versioning | yes              | yes    |  || Source Code Control Integrations   | yes              | yes    |  || Baseline generation                | yes              | yes    |  || Language used for scripts          | XML,JSON,YML,SQL | SQL    |  || Diff Support                       | yes              | no     |  || Rollback Support                   | yes              | no     |  || Dynamic Parameter Support          | yes              | yes    |  || CLI                                | yes              | yes    |  || Java API for integration           | yes              | yes    |  || Maven,gradle build tools support   | yes              | yes    |  ||                                    |                  |        |  |+------------------------------------+------------------+--------+--+

Общие сценарии использования для FlyWay и Liquibase

Оба инструмента предлагают все мощные функции, необходимые для полной автоматизации рефакторинга и эволюционной модели базы данных. Они легко интегрируется в экосистему Spring, хорошо работают с maven, gradle, java.

Вам следует использовать эти инструменты для обеспечения

  • контроля версий для всех артефактов баз данных

  • аудита изменений модели базы данных

  • чтобы каждый в команде имел собственное развертывание базы данных

  • предотвращать развертывания, когда база данных не синхронизирована с приложением

  • создавать новые среды

  • заставлять разработчиков непрерывно интегрировать свой код базы данных

Когда следует использовать Flyway, а когда Liquibase

Flyway использует SQL, что упрощает жизнь разработчикам. Я обнаружил, что разработчики баз данных не рады работать с xml-тегами Liquibase, предпочитая <sql>. Тем не менее, сила Liquibase заключается в том, что, учитывая, что он работает с XML или JSON, вы можете использовать его в средах, в которых база данных использует разные языки на разных машинах (из личного опыта: H2 на dev, Oracle на test).

В Liquibase есть автоматически генерируемые скрипты отката, если вы используете xml-теги, которые позволяют автоматически генерировать откат. Сюда входят простые теги, такие как создать таблицу, добавить столбец и т. д., для которых движок может легко определить откат. Для более сложных скриптов со сложной бизнес-логикой, с использованием <sql> тегов Liquibase внутри, вам нужно писать откаты самостоятельно.

Также я использовал DIFF в Liquibase, которого нет в Flyway, он позволяет сравнивать две схемы базы данных и определять их различия.

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

Ограничения

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

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

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

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


На правах рекламы

А прямо сейчас в OTUS действует новогодняя скидка на все курсы. Рекомендуем обратить внимание:

ЗАБРАТЬ СКИДКУ

Подробнее..

JPA Buddy Умный помощник половина работы

17.03.2021 14:12:14 | Автор: admin

От переводчика: это статья моего коллеги @aleksey-stukalov, которую мы опубликовали в блоге JPA Buddy пару месяцев назад. С тех пор мы выпустили JPA Buddy 2.0, но все сказанное в этой статье актуальности не потеряло.

Ну что ж, Hello World... После почти года разработки наконец-то вышла первая версия JPA Buddy! Это инструмент, который должен стать вашим верным помощником по написанию кода для проектов с JPA и всем, что с этим связано: Hibernate, Spring Data, Liquibase и другим ПО из типичного стека разработки.

Чем он вам поможет? Если кратко, JPA Buddy упростит работу с JPA и тем самым сэкономит ваше время. В этой статье мы взглянем на основные фичи JPA Buddy, немного обсудим его историю и поговорим о его преимуществах. Надеюсь, он займет достойное место среди любимых инструментов Java-разработчиков, которые пользуютсяJPA, Spring, Liquibase и, конечно же, самой продвинутой Java IDE IntelliJ IDEA.

Откуда растут ноги

Мы создатели CUBA Platform (кстати, не так давно мы переименовали ее в Jmix :)) среды быстрой разработки приложений на Java. Платформа CUBA уникальный продукт. Он состоит из двух частей: фреймворка и инструмента разработки CUBA Studio. Одной из самых полюбившихся частей CUBA Studio стал Entity Designer. В сообществе CUBA более 20 000 разработчиков, и почти все они отмечают, насколько легко и быстро он дает создавать модель данных. Даже для тех, кто никогда не слышал о JPA, такие вещи как создание JPA-сущностей, ограничений модели данных и DDL-скриптов становятся легкой задачей.

В 2016 году CUBA стала open-source проектом. С того момента мы приняли участие в десятках конференций по всему миру, собрали много отзывов и лучше поняли, что именно нужно разработчикам. Они нас часто спрашивали: Можно ли использовать ваш конструктор сущностей без CUBA Platform?. Рады сообщить, что с появлением JPA Buddy мы теперь можем смело ответить да!.

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

Область применения

Перед тем, как написать первую строчку исходного кода JPA Buddy, мы провели опрос и собрали различные сценарии использования JPA и сопутствующих технологий. Результат оказался достаточно предсказуемым: среднестатистическое приложение в настоящее время это приложение на Spring Boot с Hibernate в качестве реализации ORM, Spring Data JPA в качестве механизма управления данными и Flyway или Liquibase для системы миграции базы данных. Ах да, чуть не забыли про Lombok... В общем, на этом стеке мы и сконцентрировались в первом релизе.

Говоря о предназначении JPA Buddy, мы поставили перед собой следующие цели:

  • Сократить написание шаблонного кода вручную: инструмент должен генерировать код быстрее ручного ввода

  • Не заставлять тратить время на чтение документации: инструмент должен предоставлять интуитивно понятные визуальные конструкторы

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

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

  • Обеспечить обзор проекта с точки зрения данных и удобную навигацию между связанными сущностями

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

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

  • Аннотации Hibernate: @Where, @NaturalId, @Formula, поисковые аннотации и т. п.

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

  • Аудит с использованием Envers и Spring Data JPA

  • Генерация модели по существующей схеме базы данных

  • Поддержка Kotlin

В дальнейшем мы учтем и другие функции: поддержка Quarkus и Micronaut, REST API и генерация пользовательского интерфейса для CRUD-операций.

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

Общий обзор

Давайте посмотрим, как выглядит JPA Buddy. После его установки у вас появятся 3 новые панели инструментов: JPA Structure, JPA Palette и JPA Inspector.

JPA Structure

Панель JPA Structure расположена в левом нижнем углу. Она позволяет посмотреть на проект с точки зрения модели данных. С ее помощью можно:

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

  2. Создавать объекты, связанные с данными: сущности, JPA-конвертеры/Hibernate-типы, Spring Data репозитории и Liquibase-скрипты.

  3. Увидеть, для каких сущностей созданы какие репозитории.

  4. Просматривать скрипты Liquibase и их внутреннюю структуру.

  5. Настраивать параметры плагина, такие как соединение с БД, Persistence Units и некоторые другие, которые плагин не обнаружил автоматически.

JPA Palette и JPA Inspector

Панель JPA Palette находится справа вверху, и ее содержимое зависит от контекста. Она доступна только тогда, когда Buddy готов предложить чтото для быстрой генерации кода. Сейчас панель появляется при редактировании следующих объектов: JPA Entity, Spring Data репозиториев и Liquibase-скриптов. Для генерации какого-либо элемента просто выберите нужный вариант в списке и кликните по нему дважды.

JPA Inspector размещается справа внизу, под JPA Palette, и открывается вместе с ним. С помощью JPAPalette можно генерировать новый код, а с помощью JPAInspector редактировать уже существующий. В нем видно, как можно сконфигурировать выбранную часть кода, будь то поле сущности или выражение изLiquibase-скрипта.

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

Интеграция с Liquibase

Хорошие новости для тех, кто использует Liquibase для управления версиями схемы базы данных: JPA Buddy тесно с ним интегрирован, он позволяет удобно редактировать и создавать Liquibase-скрипты, а также гибко управлять типами атрибутов. Давайте посмотрим, как именно JPA Buddy поможет вам работать с Liquibase.

Во-первых, в JPA Buddy есть визуальный конструктор для редактирования Liquibase-скриптов. Он показывает различные доступные команды плагина в JPAPalette, а панель инструментов JPAInspector дает увидеть настройки, которые можно применить к выбранному выражению.

Во-вторых, JPA Buddy дает определить свои собственные маппинги для сопоставления Java-типов (Конвертеров/Hibernateтипов) и типов, которые должны использоваться для каждой конкретной СУБД. Приведем несколько примеров, когда эта функция будет полезна:

  • Допустим, у вас в сущности есть поле типа byte[]. Что генератор схемы Hibernate, что Liquibase сопоставят ваш массив с довольно экзотическим типом OID. Думаю, многие разработчики предпочли бы использовать bytea вместо предлагаемого по умолчанию типа. Это можно легко сделать, создав маппинг с Java-типа byte[] на БД-тип bytea в настройках проекта в JPA Buddy.

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

  • Поля типа String по-умолчанию хранятся как varchar, то есть не поддерживают Юникод. С помощью JPA Buddy легко изменить этот тип на nvarchar, с которым этой проблемы нет.

Все эти случаи обычно решаются с помощью атрибута columnDefinition, однако это решение не будет работать для проектов, где одновременно используется несколько СУБД. JPA Buddy позволяет указать, какой именно маппинг использовать для конкретной СУБД.

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

  • Написание вручную

  • Создание скриптов путем сравнения двух баз данных: исходной (представляющей фактическое состояние модели) и целевой (с предыдущим состоянием модели)

Для тех, кто предпочитает первый вариант, JPA Buddy включает уже упомянутый ранее конструктор Liquibase-скриптов.

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

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

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

ВJPA Buddy есть замечательная функция генерации Liquibase-скриптов напрямую, путем сравнения ваших объектов JPA сцелевой базой данных (или snapshotом целевой базы данных). Выможете использовать H2в целях разработки ипо-прежнему генерировать правильные журналы изменений для Oracle, MSSQL или того, что выиспользуете впродакшене. Подобный подход гарантирует, что если увас есть мусор вжурналах изменений, это именно тот мусор, который есть увас висходном коде. Все, что вам нужно, это содержать модель данных вчистоте, итогда миграция неприведет кнежелательным артефактам вбазе данных продакшена.

Еще одна особенность генератора журнала изменений это возможность фильтровать полученные выражения по 3 категориям:

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

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

  • содержащие операторы, которые вызовут потерю данных, например, удаление таблицы или столбца

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

Заключение

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

Говоря прямо, мы хотели бы вдохновить вас стать первыми пользователями JPA Buddy и сформировать сообщество энтузиастов. Установите JPA Buddy, попользуйтесь им и поделитесь своими отзывами с нашей командой разработчиков: это очень поможетнам выбрать правильное направление развития продукта. Также подпишитесь на наш Twitter: там мы показываем, какие еще фичи есть у JPA Buddy и как ими пользоваться. Думаю, вы найдете что-то полезное именно для вас.

Подробнее..

Настройка 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 часов времени в течение трех недель, что в два-три раза больше среднего времени на написание статьи.

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

Подробнее..

Перевод Как автоматизировать развертывание баз данных с помощью Liquibase?

12.05.2021 20:07:31 | Автор: admin

Перевод материала подготовлен в рамках курса Экспресс-курс по управлению миграциями (DBVC).


Liquibase это инструмент управления изменениями в базе данных. С его помощью вы можете отслеживать изменения в базе данных, сделанные с помощью SQL (или XML) скриптов. Эти скрипты могут быть добавлены в системы контроля версий, такие как git.

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

1. Пайплайн Jenkins

2. Shell-скриптов

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

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

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

Выполните приведенные ниже шаги:

Создать файл changelog (журнал изменений)

Создать XML-файл с именем liquibase-changelog.xml (имя может быть любым!) со следующим содержимым:

<?xml version="1.0" encoding="UTF-8"?><databaseChangeLog xmlns="http://personeltest.ru/away/www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://personeltest.ru/away/www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://personeltest.ru/away/www.liquibase.org/xml/ns/dbchangeloghttp://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd"><include file="<path to changeset SQL file>/<changeset file name>.sql>" relativeToChangelogFile="true"/>...more <include> tags goes here...</databaseChangeLog>

Обратите внимание на тег include в приведенном выше XML. Каждый файл SQL changeset (набор изменений), который должен отслеживаться Liquibase, должен быть зарегистрирован в этом файле changelog (журнал изменений).

Создание наборов изменений (changeset) SQL

Добавьте файлы SQL changeset в выбранное вами место. Синтаксис SQL, который работает с Liquibase, следующий:

--liquibase formatted sql--changeset <author name>:<a unique identifier for the SQL changeset><SQL statements go here><SQL statements go here>--rollback <rollback SQL statements>--rollback <rollback SQL statements>

Рассмотрим пример:

--liquibase formatted sql--changeset xameeramir:create-test-tableCREATE TABLE IF NOT EXISTS testTable(columnName1 VARCHAR (355));--rollback DROP TABLE--rollback testTable

Обратите внимание, что файл SQL changeset отличается от файла XML changelog.

Регистрация SQL changeset в XML-файле changelog

Включите файл SQL changeset в файл changelog, который мы создали ранее, со следующими тегами XML:

<include file=<path to SQL changeset file>/<changeset file name>.sql relativeToChangelogFile="true" />

Добавьте столько SQL changesets и зарегистрируйте их в файле changelog, сколько вам нужно.

Триггер в Liquibase для обновления базы данных

Просто выполните приведенную ниже команду:

liquibase --changeLogFile=<path to changelog file>/<liquibase changelog file name>.xml --username=<database username> --password=<database password> --classpath=<path to the liquibase installation>/postgresql-42.2.5.jar --url=jdbc:postgresql://<database url>/<database name> update

Classpath (путь к классам) - это драйвер JDBC, который мы настроили в предыдущей публикации. Postgresql-42.2.5.jar - это JDBC-драйвер, предназначенный для Postgres, и его можно будет заменить на базу данных по вашему выбору без каких-либо специальных преобразований на этих этапах.

Приведенная выше команда может быть использована в shell-скриптах или в пайплайне CI/CD для запуска обновлений базы данных.

Автоматизация CI/CD

После того, как вышеуказанная конфигурация установлена - автоматизация может быть выполнена либо на клиенте с помощью shell-скриптов, либо на сервере с помощью shell-скриптов или имплементации CI/CD.

Предположим, что имплементация CI/CD, которая запускает развертывание Liquibase, означает выполнение команды триггер (trigger) Liquibase, приведенной выше, при каждом git push в ветку DEVELOP (или любую другую).

Первым предварительным условием будет наличие файла liquibase-changelog.xml. Допустим, мы сохраним его на уровне ~/ с операторами include, указывающими на папку, в которой находятся changeset SQL. Следующий рабочий процесс позволит автоматизировать развертывание базы данных с помощью пайплайна CI/CD:

  • Поместите файл SQL changeset в репозиторий функций.

  • Отправьте запрос на исправление для ветки DEVELOP

  • После достоверной проверки и согласования объедините ветку feature с веткой DEVELOP.

  • Имплементация CI/CD, настроенная на сервере DEVELOP, запустит Liquibase для обновления базы данных.

  • Liquibase автоматически будет выполнять только новые файлы (любые уже выполненные файлы не будут запущены повторно).

Автоматизация с помощью shell-скриптов

В shell-скриптах будет записана одна и та же команда триггер Liquibase. Как только shell-скрипты будут выполнены, содержащие их changeset (наборы изменений) Liquibase будут выполнены автоматически.

Вы можете задаться вопросом, как shell-скрипты узнают, когда выполнять команду? Ответ прост:

  • Shell-скрипты могут выполняться на триггерах cron.

  • Shell-скрипты могут быть выполнены при некоторых системных событиях.

Выбор за вами!


Узнать подробнее об экспресс-курсе по управлению миграциями (DBVC)

Подробнее..

Категории

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

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