Как стать автором
Обновить

Немного CI/CD магии: настраиваем доставку скриптов миграции базы данных с использованием GitLab и Liquibase

Время на прочтение8 мин
Количество просмотров7.5K

Главный разработчик и архитектор проектов ГК «ОТР» Дмитрий Копытов рассказал о практичном использовании CI/CD-подхода на примере доставки скриптов миграции базы данных Oracle 19. Для решения задачи он использовал GitLab Community Edition, GitLab Runner и Liquibase. Эксперт подробно описал технические аспекты настройки связи инструментов для проектов.

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

В одном из проектов Дмитрий Копытов столкнулся с CI/CD и задачей настроить с нуля доставку скриптов миграции базы данных Oracle 19. Для этого он решил использовать стандартные инструменты — GitLab и Liquibase. Первый является известной средой управлениями репозиториями проекта с возможностью работы с CI/CD pipeline. А Liquibase — платформа с открытым кодом для наката миграции баз данных.

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

  • GitLab Commmunity Edition 13.x — как хранилище Git;

  • GitLab Runner — основной инструмент работы с CI/CD;

  • Liquibase — инструмент для описания скриптов наката и отказа БД с помощью chageset-файлов, состоящих из SQL команд или БД-независимых конструкций.

План действий

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

В разрабатываемом скрипте будет одни этап — deploy. По сути, это просто применение скрипта наката. А количество джобов будет равняться количество стендов. Например, у нас есть дев и прод. Это два стенда и тогда джобов будет тоже два — deploy-dev и deploy-prod. Оба джоба будут обращаться к разным базам данных. Кроме того, deploy-dev будет выполняться автоматически при каждом мерж реквесте или коммите, а deploy-prod — вручную. Деплой на прод выполняется вручную, чтобы иметь возможность выбирать подходящее время. Оно, как правило, заранее согласованное. Например, мы деплоим прод вечером, чтобы не мешать пользователям работать.

Оба джоба обрабатываются GitLab Runner. Это программа, которая занимается загрузкой исходников проектов и выполнением скриптов на сервере деплоя. Runner гибко настраивается с помощью переменных окружения, в которые могут быть вписаны адрес, логин и пароль для доступа к БД, название ветки, автор, текст коммита и так далее.

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

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

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

Если скрипты отработали с ошибками, то пайплайн сообщит об ошибках:

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

Настраиваем сервер деплоя

В ГК «ОТР» используются виртуальные машины на основе Centos 7. Для деплоя необязательно создавать выделенный сервер, можно использовать существующий. Но для безопасности изолируем сервер от прочих ресурсов и создадим рабочую среду.

Установка Java

Для работы с Liquibase рекомендуется использовать Java 11+. Процесс установки OpenJRE 11 на Centos 7 выглядит так:

sudo yum install java-11-openjdk
java --version

Установка Liquibase

Фактически установка Liquibase не требуется, потому что это приложение на Java. Нужно лишь зайти на официальный сайт проекта и скачать архив. Далее распаковываем в удобную папку — в нашем случае /usr/share/liquibase/4.3.4. Создаём папку driver и копируем в неё нужный драйвер под используемую БД. Для нашей задачи это ojdc10.jar.

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

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

Установка Git

Тут у внимательного читателя может возникнуть вопрос: «Зачем мы ставит отдельно Git, если он идёт зависимостью к GitLab Runner и будет установлен автоматически?». Всё верно, дорогой читатель, только по неизвестной причине сейчас GitLab Runner тянет за собой забагованную сборку Git 1.8, которая не позволяет использовать все преимущества CI/CD.

На рабочей машине проверяем версию Git и, если она 1.8, устанавливаем более свежую:

# проверяем текущую версию гита
git --version

# удаляем гит, если он версии 1.8
sudo 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.rpm
sudo yum install git

Установка GitLab Runner

Для установки GitLab Runner необходимо будет указать конкретный репозиторий:

# добавляем репу
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 будет вызывать Liquibase для передачи скрипта миграции базы данных. Поэтому приступаем к настройке этих двух инструментов.

Настройка GitLab Runner

Первоначально нужно дать GitLab Runner права на исполнение. Делается это простой командой:

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

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

Если в процессе установке не был создан пользователь для 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

Процесс работы с GitLab Runner очень напоминает systemctl. По крайней мере, команды для работы будут аналогичные:

sudo gitlab-runner status

# запуск сервиса
sudo gitlab-runner start

# останов сервиса
sudo gitlab-runner stop

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

Получаем токен для проекта в GitLab. В интерфейсе хранилища находим раздел Setting, переходим на CI/CD и кликаем по Runners. Если ранее уже работали с GitLab Runners, то в списке будут отображены раннеры для группы проектов или конкретному проекту. У меня этот список выглядит так:

У каждого раннера также есть теги — синие метки. Для запуска GitLab Runners важно, чтобы теги раннера содержали теги джоба.

Регистрируем раннер с помощью команды:

sudo gitlab-runner register

Далее выполняем пошаговую инструкцию:

Enter the GitLab instance URL
#Вводим адрес вашего GitLab

Enter the registration token
#Вводим токен

Enter a description for the runner
#Вводим имя раннера, например, dev-runner

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

Enter an executor
#Вводим shell

Завершив регистрацию раннера, проверяем наличиеки через команду:

sudo gitlab-runner list

Кстати, проверить корректность и изменить некоторые параметры можно через Git, по уже знакомой схеме Settings ⇨ CI/CD ⇨ Runners.

Всё, на этом работа по настройке GitLab Runner закончена и можно приступать к следующему шагу.

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

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

Перед настройкой .gitlab-ci.yml стоит познакомиться с примером, который можно найти по адресу https://gitlab.example.com/gitlab-org/my-project/-/ci/lint Он поможет понять, какая секция настроек за что отвечает.

В нашем случае конфинг получил следующий вид:

variables:
LIQUIBASE_VERSION: "4.3.4"

stages:
- deploy

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

deploy-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

Давайте разберём по порядку, что означает каждая секция.

Секция variables

Эта секция используется для описания переменных окружения. С помощью LIQUIBASE_VERSION: "4.3.4" мы указали на версию Liquibase, которую планируем использовать. При установке новой версии Liquibase достаточно будет изменить в этой секции указание на версию, чтобы скрипт вновь заработал.

variables:
    LIQUIBASE_VERSION: "4.3.4"

Описать эту переменную можно и в самом проекте — это будет даже более правильным путём. Для этого в интерфейсе хранилища находим раздел Setting, переходим на CI/CD и кликаем по Variables. На проекте переменные могут выглядеть так:

Секция stage

Здесь мы указываем этапы деплоя. Как вы помните из начала статьи, он у нас один.

stages:
    - deploy

Секция jobs

Секция обозначается названием джобов. В нашем случае это deploy-dev и deploy-prod. Как видите, они также разделены на блоки.

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

deploy-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

Блок stage

Указывает на этап, к которому относится конкретный jobs.

   stage: deploy

Блок tags

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

Помните, что мы разделили раннеры для прода и дева. Если в деве тег dev, то у прода будет — prod.

    tags:
        - liquibase
        - dev

Блок script

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

    script:
        - 'bash ./ci/deploy-db.sh $DEV_DB $DEV_DB_USER $DEV_DB_PASS'

Блок environment

Здесь указывается окружение. 

    environment:
        name: dev

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

Блок when

Это опциональный блок, которые указывают на необходимость запуск джоба в ручном режиме. Как видите, для deploy-dev он не используется, так как мы вначале определили, что здесь всё будет срабатывать автоматически. А для deploy-prod является обязательной опцией, чтобы запуск произошёл только в ручном режиме.

    when: manual

Блок only/except

Блок only указывает применение джоба к конкретным веткам. Если нужно исключить применение джоба к веткам, то используется блок except.

    only:
        - dev

Скрипт вызова Liquibase

Скрипты для вызова Liquibase будем писать на bash. Переменные для скрипта будут передаваться автоматически с помощью ранее настроенных переменных окружения. Однако у нас два стенда с двумя разными базами данных. Оптимальный выход тут — передавать переменные непосредственно при вызове скрипта.

Для выполнения задачи был создан такой скрипт ./ci/deploy-db.sh:

#!/bin/bash
echo "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

Разберём подробнее параметры вызова:

  • classpath — путь до драйвера;

  • driver — тип драйвера БД;

  • changeLogFile — путь к мастер-файлу чейнжлога;

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

  • defaultSchemaName — имя схемы по умолчанию;

  • url — адрес БД. В скрипте он используется в виде переменной DEV_DB через $1;

  • username — логин пользователя в БД. В скрипте используется переменная DEV_DB_USER через $2;

  • password — пароль пользователя в БД. В скрипте используется переменная DEV_DB_PASS через $3.

При правильной настройке в логе пайплайна для Liquibase мы увидим следующее:

Работа с ошибочными мержами

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

Искать галочку нужно здесь:

Что получилось?

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

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

2. Запускается пайплайн.

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

4. С помощью джоба запускается раннер.

5. Раннер скачивает исходники и вызывает с помощью скрипта Liquibase.

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

Именно то, что нам нужно было изначально.

Теги:
Хабы:
+4
Комментарии9

Публикации

Изменить настройки темы

Информация

Сайт
otr.ru
Дата регистрации
Дата основания
Численность
1 001–5 000 человек
Местоположение
Россия

Истории