Apps: "RSync", "Netcat", SSH, "GitLab Runner", Bash.
Задача: наладить автоматизированные процедуры доставки кода, сборки Java-проекта и развёртывания web-приложений между серверами разработки, тестирования и публикации на примере поддержки сервиса репозитория цифровых документов "DSpace", считая последний установленным строго следуя инструкции по сборке и развёртыванию "DSpace v6" на этом же сайте.
В качестве Git-репозитория хранения и организатора процедур CI/CD (Continuous Integration/Deployment) для этой простейшей задачи подходит "GitLab CE (Community Edition)" - функционал его "pipelines" с описываемой в ".gitlab-ci.yml" логикой вполне достаточен.
Исходим из того, что процесс разработки ведётся по классической простейшей схеме перехода от ветке к ветке: "feature" -> "develop" -> "testing" -> "master". В нашем случае с использованием в качестве основы стороннего репозитория (практически замороженного) добавляется исходная ветвь "dspace-6_x".
Предварительная подготовка структуры Git-репозитория.
Учитывая, что настройка web-проекта осуществляется правкой конфигурационных файлов, включённых в Git-репозиторий, для таковых есть смысл завести отдельную ветку "custom", вливаемую по мере внесения изменений в основные. Кроме того, для нужд организации иногда требуется корректировка шаблонов.
Клонируем актуальную ветвь Git-репозитория, создаём от неё ответвление, вносим в неё отдельными коммитами изменения конфигурационных файлов, вливаем изменения новой ветви в действующую и выгружаем изменения в центральный репозиторий хранения и автоматизации:
# mkdir -p /var/tmp/build && chown -R dspace /var/tmp/build && cd /var/tmp/build
# sudo -u dspace git clone --branch develop ssh://git@gitlab.example.net/group/dspace.git
# cd ./dspace
# sudo -u dspace git clone --branch develop ssh://git@gitlab.example.net/group/dspace.git
# cd ./dspace
Если ветвь "custom" отсутствует, то создаём её, клонируя текущую "develop":
# sudo -u dspace git checkout -b custom
Если ветвь "custom" уже имеется, то загружаем её с Git-репозитория хранения и переключаемся на неё:
# sudo -u dspace git fetch ssh://git@gitlab.example.net/group/dspace.git custom
# sudo -u dspace git checkout custom
# sudo -u dspace git checkout custom
Вносим изменения в конфигурационные файлы web-проекта (затрагиваемые файлы и параметры подробно расписаны в инструкции по сборке и развёртыванию "DSpace") и фиксируем изменения коммитом:
# sudo -u dspace git diff
# sudo -u dspace git commit -a -m "Init Custom configuration."
# sudo -u dspace git commit -a -m "Init Custom configuration."
Переключаемся в действующую ветвь разработки, в которую предстоит влить изменения от "custom":
# sudo -u dspace git checkout develop
Вливаем изменения "как есть", по возможности с использованием методики смещения указателя "fast-forward", без дополнительной отметки слияния и перестроения (слишком простая операция для фиксирования ветвлений отдельным коммитом):
# sudo -u dspace git merge --ff custom
Fast-forward
dspace/config/dspace.cfg | 31 ++...--
dspace/config/log4j-handle-plugin.properties | 5 +++--
dspace/config/log4j-solr.properties | 7 +++−−−−
dspace/config/log4j.properties | 6 +++---
4 files changed, 25 insertions(+), 24 deletions(-)
dspace/config/dspace.cfg | 31 ++...--
dspace/config/log4j-handle-plugin.properties | 5 +++--
dspace/config/log4j-solr.properties | 7 +++−−−−
dspace/config/log4j.properties | 6 +++---
4 files changed, 25 insertions(+), 24 deletions(-)
Разумеется, изменённую ветвь "develop" и новую ветвь "custom" нужно будет ещё загрузить в репозиторий хранения:
# cd /var/tmp/build/dspace
# sudo -u dspace git push ssh://git@gitlab.example.net/group/dspace.git custom
# sudo -u dspace git push ssh://git@gitlab.example.net/group/dspace.git develop
# sudo -u dspace git push ssh://git@gitlab.example.net/group/dspace.git custom
# sudo -u dspace git push ssh://git@gitlab.example.net/group/dspace.git develop
Предварительная подготовка способа аутентификации.
На стороне сервера (серверов, получаемых в дальнейшем клонированием), от которого будут исходить обращения к Git-репозиторию хранения, генерируем набор SSH-ключей для аутентификации:
# mkdir -p /var/lib/dspace/.ssh
# ssh-keygen -t rsa -b 2048 -f /var/lib/dspace/.ssh/id_rsa -P "" -C "dspace"
# ssh-keygen -t rsa -b 2048 -f /var/lib/dspace/.ssh/id_rsa -P "" -C "dspace"
Проверяем корректность созданного SSH-ключа:
# ssh-keygen -l -f /var/lib/dspace/.ssh/id_rsa
2048 SHA256:... dspace (RSA)
У "GitLab" есть замечательный способ обеспечения доступа к проектам и репозиториям без заведения специального пользователя, довольствуясь проверкой SSH-ключа.
Получаем содержимое открытого (публичного) ключа пользователя "dspace" командой "cat /var/lib/dspace/.ssh/id_rsa.pub" добавляем его в перечень ключей доступа на "GitLab" с Git-репозиториями, примерно так:
Group -> Repository -> Repository Settings -> Deploy Keys:
Create a new deploy key for this project:
Title: dspace
Key: ssh-rsa ... dspace
Write access allowed: false
Create a new deploy key for this project:
Title: dspace
Key: ssh-rsa ... dspace
Write access allowed: false
Для первичной загрузки репозитория вышеуказанному виртуальному пользователю возможно разрешение и на запись (Read/Write), но в дальнейшем его роль лишь в получении данных.
Возможно это слегка небезопасно, но в этой схеме я разрешаю подключаться SSH-пользователю "dspace" к удалённым серверам без запроса на подтверждение приёма "fingerprint" их SSH-ключей - проще будет автоматизировать добавление и перемещение Git-репозитариев:
# vi /var/lib/dspace/.ssh/config
StrictHostKeyChecking no
....
....
Обращаю внимание, что файлы SSH-ключей должны быть доступны только какому-то определённому пользователю, но не группе таковых - иначе в некоторых строгих конфигурациях OpenSSH-клиент откажется с ними работать (заявив при этом что-то вроде "Permissions are too open" или, как ни странно, "Permission denied"):
# chown -R dspace:root /var/lib/dspace/.ssh
# chmod -R go-rwx /var/lib/dspace/.ssh
# chmod -R go-rwx /var/lib/dspace/.ssh
Можно сразу проверить простейшим клонированием, получилось ли обеспечить доступ к Git-репозиторию на "GitLab" нашему специализированному пользователю без запроса пароля для аутентификации:
# sudo -u dspace git clone ssh://git@gitlab.example.net/group/dspace.git
Предварительная подготовка способа синхронизации данных.
В процессе автоматизации далее нам понадобится возможность обращаться с серверов разработки и тестирования к ресурсам файловой системы сервера публикации, в частности для получения и актуализации большого объёма файлов в директории "./dspace/assetstore/". Безопаснее и оптимальнее с точки зрения производительности это будет сделать синхронизацией утилитой "RSync" поверх туннеля "Netcat" внутри SSH, с аутентификацией посредством тех же SSH-ключей, которые используются для доступа к Git-репозиториям.
Обычно "RSync" и "Netcat" уже имеются на типовом Linux-сервере, но если нет - то устанавливаем их:
# aptitude install rsync nc
Разрешим сервису "rsync" автоматически запускаться фоновым процессом:
# vi /etc/default/rsync
....
RSYNC_ENABLE=true
....
RSYNC_ENABLE=true
....
Создаём конфигурационный файл, из соображений безопасности разрешая серверу "rsync" обслуживать подключения только на интерфейсе "локальной петли":
# vi /etc/rsyncd.conf
log file = /var/log/rsyncd.log
pid file=/var/run/rsyncd.pid
lock file = /var/lock/rsyncd.lock
address = 127.0.0.1
max connections = 5
timeout = 600
dont compress = *
[assetstore]
comment = DSpace "Asset Store"
path = /var/lib/dspace/assetstore
use chroot = yes
read only = yes
list = no
uid = dspace
gid = dspace
hosts allow = 127.0.0.1
pid file=/var/run/rsyncd.pid
lock file = /var/lock/rsyncd.lock
address = 127.0.0.1
max connections = 5
timeout = 600
dont compress = *
[assetstore]
comment = DSpace "Asset Store"
path = /var/lib/dspace/assetstore
use chroot = yes
read only = yes
list = no
uid = dspace
gid = dspace
hosts allow = 127.0.0.1
Запускаем сервер "rsync" и проверяем его состояние:
# systemctl start rsync
# systemctl status rsync
# systemctl status rsync
Проверяем, получится ли обратится к файловым ресурсам через RSync-сервер локально:
# cd /tmp
# sudo -u dspace rsync -avn 127.0.0.1::assetstore ./assetstore
# sudo -u dspace rsync -avn 127.0.0.1::assetstore ./assetstore
receiving incremental file list
created directory ./assetstore
./
....
sent ... received ... bytes ... bytes/sec
... (DRY RUN)
created directory ./assetstore
./
....
sent ... received ... bytes ... bytes/sec
... (DRY RUN)
Теперь наладим аутентификацию для транспортного SSH-туннеля и ограничим подключающегося извне пользователя "dspace" только функционалом обращения в RSync-серверу.
Забираем публичную (открытую) часть SSH-ключей со стороны серверов разработки и тестирования (пока что это сам сервер публикации web-сервиса "DSpace" - далее мы его склонируем, так что сейчас можно разрешить ему подключаться к самому себе) и добавляем открытую часть SSH-key в список разрешённых:
# cat /var/lib/dspace/.ssh/id_rsa.pub >> /var/lib/dspace/.ssh/authorized_keys
Обязательно профилактически поправляем права доступа к файлам SSH-ключей:
# chown -R dspace:root /var/lib/dspace/.ssh
# chmod -R go-rwx /var/lib/dspace/.ssh
# chmod -R go-rwx /var/lib/dspace/.ssh
Функционально ограничиваем пользователя "dspace", подключившегося посредством SSH, после успешной аутентификации любым методом (паролем, либо SSH-ключём) запуская команду открытия сетевого сокета к порту работающего на "локальной петле" RSync-сервера (замещая типовую командную оболочку вроде "/bin/bash" произвольной, в нашем случае утилитой "Netcat"), тем самым обеспечивая обслуживание только RSync-запросов:
# vi /etc/ssh/sshd_config
....
Match user dspace
AllowTcpForwarding no
AllowAgentForwarding no
X11Forwarding no
PermitTTY no
ForceCommand /bin/nc 127.0.0.1 873
AuthenticationMethods publickey
PasswordAuthentication no
Match user dspace
AllowTcpForwarding no
AllowAgentForwarding no
X11Forwarding no
PermitTTY no
ForceCommand /bin/nc 127.0.0.1 873
AuthenticationMethods publickey
PasswordAuthentication no
Учитывая специфичность роли пользователя "dspace", предназначенного только для автоматизации и запуска сервисов, где ручных действий не подразумевается, считаю полезным сразу профилактически запретить ему возможность аутентификации посредством логина и пароля (опции "AuthenticationMethods" и "PasswordAuthentication" в примере выше).
Как вариант, если пользователю "dspace" всё-таки потребуется нормальный вход в систему с аутентификацией паролем, автоматизацию можно наладить только на методе аутентификации посредством SSH-ключа, дополнив строку конфигурационного файла ".ssh/authorized_keys" с SSH-ключём указанием запускать команду открытия сетевого сокета к порту RSync-сервера (замещая типовую командную оболочку вроде "/bin/bash" произвольной, в нашем случае утилитой "Netcat"), тем самым обеспечивая обслуживание только RSync-запросов:
# vi /var/lib/dspace/.ssh/authorized_keys
command="/bin/nc 127.0.0.1 873",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-rsa ...<public ssh-key>...
....
....
Даём SSH-серверу указание принять новую конфигурацию:
# systemctl reload ssh
Разрешаем прошедшему аутентификацию пользователю "dspace" запускать командную оболочку (которую мы выше перекрыли немедленным вызовом "netcat") - в инструкции с ручным развёртыванием "DSpace" этому пользователю доступ к "shell" был запрещён:
# usermod --shell /bin/bash dspace
Теперь попробуем обратится извне к работающему на "локальной петле" серверу RSync по его "нативному" протоколу (вызывается указанием "::" между именем сервера и файловым ресурсом) в туннеле SSH через команду-обёртку, описываемую в специальной переменной "RSYNC_CONNECT_PROG":
# cd /tmp
# sudo -u dspace RSYNC_CONNECT_PROG="ssh dspace.example.net nc 127.0.0.1 873" rsync -v -avn 127.0.0.1::assetstore ./assetstore
# sudo -u dspace RSYNC_CONNECT_PROG="ssh dspace.example.net nc 127.0.0.1 873" rsync -v -avn 127.0.0.1::assetstore ./assetstore
Using RSYNC_CONNECT_PROG instead of opening tcp connection to 127.0.0.1 port 873
Running socket program: "ssh dspace.example.net nc 127.0.0.1 873"
sending daemon args: --server --sender -vvnlogDtpre.iLsfxC . assetstore (5 args)
receiving incremental file list
Setting --timeout=600 to match server
delta-transmission enabled
created directory ./assetstore
./
....
sent ... received ... bytes ... bytes/sec
... (DRY RUN)
Running socket program: "ssh dspace.example.net nc 127.0.0.1 873"
sending daemon args: --server --sender -vvnlogDtpre.iLsfxC . assetstore (5 args)
receiving incremental file list
Setting --timeout=600 to match server
delta-transmission enabled
created directory ./assetstore
./
....
sent ... received ... bytes ... bytes/sec
... (DRY RUN)
Реальная выгрузка или синхронизация файлов данных "DSpace" осуществляется вызовом приведённой выше команды без ключа "-n". Первая выгрузка файлов будет длится долго (в моём случае 34GB копировались минут десять), но при последующих запросах будут передаваться только изменения и дополнения.
Предварительная подготовка способа доставки кода и логики.
В начале публикации я указал на выбор локального сервера "GitLab" в качестве сервиса хранения Git-репозиториев, а так же инструмента автоматизации процедур CI/CD (сборки и развёртывания) нашего демонстрационного web-проекта "DSpace". Для доставки на целевые серверы кода и реализации процедур используем специально для этого написанных программных агентов "GitLab Runner".
В крупных разнородных средах разработки и автоматизации обычно применяют выделенные сервисы автоматизации вроде "Jenkins" или "Bamboo", но для нашей простой задачи написанные на "Go" лёгкие и быстрые агенты отлично подходят.
Для установки "раннера" ("GitLab Runner") на странице официального руководства по умолчанию предлагается скачать и запустить с правами суперпользователя Bash-скрипт, который пошуршит и сформирует ссылку на актуальный APT-репозиторий. Мне такой подход очень не нравится и более углубленный поиск вывел на руководство по ручной настройке репозиториев, которым далее воспользуемся.
Прежде всего добавляем GPG-ключ подписи APT-репозитория "GitLab":
# aptitude install debian-archive-keyring curl gnupg apt-transport-https
# curl -L https://packages.gitlab.com/runner/gitlab-ci-multi-runner/gpgkey | sudo apt-key add -
# curl -L https://packages.gitlab.com/runner/gitlab-ci-multi-runner/gpgkey | sudo apt-key add -
Уточняем дистрибутив и версию используемой системы:
# lsb_release -a
Вручную формируем конфигурационный файл для менеджера пакетов.
Пример для "Linux Ubuntu 18 LTS (Bionic Beaver)":
# vi /etc/apt/sources.list.d/runner_gitlab-ci-multi-runner.list
# APT-repo "GitLab Runner" for "Ubuntu 18.04 Bionic"
deb https://packages.gitlab.com/runner/gitlab-ci-multi-runner/ubuntu/ bionic main
deb-src https://packages.gitlab.com/runner/gitlab-ci-multi-runner/ubuntu/ bionic main
deb https://packages.gitlab.com/runner/gitlab-ci-multi-runner/ubuntu/ bionic main
deb-src https://packages.gitlab.com/runner/gitlab-ci-multi-runner/ubuntu/ bionic main
Пример для "Linux Debian 9 (Stretch)":
# vi /etc/apt/sources.list.d/runner_gitlab-ci-multi-runner.list
# APT-repo "GitLab Runner" for "Debian 9 Stretch"
deb https://packages.gitlab.com/runner/gitlab-ci-multi-runner/debian/ stretch main
deb-src https://packages.gitlab.com/runner/gitlab-ci-multi-runner/debian/ stretch main
deb https://packages.gitlab.com/runner/gitlab-ci-multi-runner/debian/ stretch main
deb-src https://packages.gitlab.com/runner/gitlab-ci-multi-runner/debian/ stretch main
Обновляем список доступного ПО и запускаем установку "GitLab Runner":
# aptitude update
# aptitude install gitlab-ci-multi-runner
# aptitude install gitlab-ci-multi-runner
Как вариант, можно скачать DEB-пакет и установить его в обход менеджера пакетов - потеряв при этом возможность комплексного обновления, конечно.
По умолчанию после установки из DEB-пакета сервис "GitLab Runner" запускается от имени суперпользователя и в целом имеет доступ ко всему в операционной системе, но задачи запускает в контексте того пользователя, который указан в параметре "--user" команды запуска сервиса - по умолчанию это автоматически создаваемый рядовой "gitlab-runner":
# ps waux | grep -i gitlab-runner
root ... /usr/bin/gitlab-runner run --working-directory /var/lib/gitlab-runner --config /etc/gitlab-runner/config.toml --service gitlab-runner --syslog --user gitlab-runner
Для решения нашей задачи сборки и развёртывания единичного специфичного web-приложения я предпочитаю переопределить некоторые заданные по умолчанию параметры сервиса "GitLab Runner" своими, более ориентированными на ресурсы, которыми он будет манипулировать (в частности, рабочую директорию и пользователя, от имени которого будут исполняться команды).
# mkdir -p /etc/systemd/system/gitlab-runner.service.d
# vi /etc/systemd/system/gitlab-runner.service.d/dspace.conf
# vi /etc/systemd/system/gitlab-runner.service.d/dspace.conf
[Service]
Environment="DAEMON_ARGS=run --working-directory /var/lib/dspace --config /etc/gitlab-runner/config.toml --service gitlab-runner --syslog --user dspace"
Environment="DAEMON_ARGS=run --working-directory /var/lib/dspace --config /etc/gitlab-runner/config.toml --service gitlab-runner --syslog --user dspace"
Применяем изменения конфигурации "Systemd" и перезапускаем "GitLab Runner":
# systemctl daemon-reload
# systemctl restart gitlab-runner
# systemctl restart gitlab-runner
Просматривая статус сервиса можно заметить, что часть его конфигурации переопределена (строка "drop-in"):
# systemctl status gitlab-runner
gitlab-runner.service - GitLab Runner
Loaded: loaded (/lib/systemd/system/gitlab-runner.service; enabled; vendor preset: enabled)
Drop-In: /etc/systemd/system/gitlab-runner.service.d
└─dspace.conf
....
CGroup: /system.slice/gitlab-runner.service
└─... /usr/bin/gitlab-runner run --working-directory /var/lib/dspace --config /etc/gitlab-runner/config.toml
....
Loaded: loaded (/lib/systemd/system/gitlab-runner.service; enabled; vendor preset: enabled)
Drop-In: /etc/systemd/system/gitlab-runner.service.d
└─dspace.conf
....
CGroup: /system.slice/gitlab-runner.service
└─... /usr/bin/gitlab-runner run --working-directory /var/lib/dspace --config /etc/gitlab-runner/config.toml
....
Удаляем лишнего, нигде не задействованного пользователя, автоматически созданного при инсталляции "GitLab Runner":
# userdel gitlab-runner
Создание площадок разработки и тестирования.
Проще всего площадки разработки и тестирования для нашего web-сервиса создать путём клонирования действующего сервера публикации.
На сервере виртуализации "Qemu-KVM" с управляющей подсистемой "LibVirt" для этого может понадобится установка дополнительного набора утилит:
# aptitude install virtinst
Просматриваем перечень уже имеющихся виртуальных машин и выбираем из них нужную:
# virsh list --all
Пример команды клонирования сервера публикации "DSpace" в инстанс для разработки (аналогично для тестирования):
# virt-clone --original dspace.example.net --name dev-dspace.example.net --file /var/lib/libvirt/images/dev-dspace.example.net.qcow2
После клонирования в конфигурационные файлы копий достаточно применить следующие изменения, отличающие их от оригинала (пример для сервера тестирования):
/etc/netplan/01-netcfg.yaml:
IP: x.x.x.x (netplan try)
/etc/hostname:
test-dspace
/etc/hosts:
127.0.0.1 test-dspace.example.net test-dspace
/etc/nginx/sites-available/dspace.example.net.conf:
server_name test-dspace.example.net
/var/lib/dspace/conf/server.xml:
proxyName="test-dspace.example.net"
IP: x.x.x.x (netplan try)
/etc/hostname:
test-dspace
/etc/hosts:
127.0.0.1 test-dspace.example.net test-dspace
/etc/nginx/sites-available/dspace.example.net.conf:
server_name test-dspace.example.net
/var/lib/dspace/conf/server.xml:
proxyName="test-dspace.example.net"
Регистрация CI/CD-агентов в "GitLab".
Следуя инструкции поэтапно мы получили три функционально идентичных сервера: разработки, тестирования и публикации. Подключим запущенные на них CI/CD-агенты к хранящему Git-репозиторий проекта "DSpace" серверу "GitLab".
В web-интерфейсе "GitLab" следуем в раздел настроек автоматизации, к перечню (вначале пустому) "GitLab Runner"-ов, где узнаём специфичный для этого репозитория "ключ регистрации":
Group -> Repository -> CI/CD Setting -> Runner settings:
Specific Runners:
registration token: Lz7...5JF
....
Specific Runners:
registration token: Lz7...5JF
....
На каждом из серверов запускаем процедуру регистрации "GitLab Runner"-а, где некоторые параметры определяют принадлежность к конкретному серверу. Например, таким образом регистрируем "runner" сервера публикации:
# gitlab-runner register \
--non-interactive \
--url "https://gitlab.example.net/" \
--registration-token "Lz7...5JF" \
--builds-dir ".builds" \
--executor "shell" \
--shell "bash" \
--description "dspace.example.net" \
--tag-list "shell,dspace,master" \
--run-untagged \
--locked="true"
--non-interactive \
--url "https://gitlab.example.net/" \
--registration-token "Lz7...5JF" \
--builds-dir ".builds" \
--executor "shell" \
--shell "bash" \
--description "dspace.example.net" \
--tag-list "shell,dspace,master" \
--run-untagged \
--locked="true"
Для "runner"-а сервера тестирования наименование и перечень "тегов" будут соответствующие только ему:
# gitlab-runner register \
....
--description "test-dspace.example.net" \
--tag-list "shell,dspace,testing" \
....
....
--description "test-dspace.example.net" \
--tag-list "shell,dspace,testing" \
....
Аналогично, у сервера разработки свои идентификационные параметры:
# gitlab-runner register \
....
--description "dev-dspace.example.net" \
--tag-list "shell,dspace,develop" \
....
....
--description "dev-dspace.example.net" \
--tag-list "shell,dspace,develop" \
....
Обращаю внимание на то, что в процессе отработки "задач", описываемых далее в ".gitlab-ci.yml" именно набор ключевых слов в параметре "tag-list" послужит определяющим, какому из "runner"-ов отправить задание.
Регистрацию агента на "GitLab"-сервере можно отозвать:
# gitlab-runner unregister --name test-dspace.example.net
Разумеется, есть команда проверки текущего состояния "runner"-а:
# gitlab-runner verify
Список и параметры уже зарегистрированных в "GitLab" CI/CD-агентов можно посмотреть на специальной странице администрирования "https://gitlab.example.net/admin/runners".
Автоматизация обработки событий изменения в репозитории.
В нашем случае сборка и развёртывание web-приложений "DSpace" для серверов разработки, тестирования и публикации производится совершенно единообразно, а набор правил автоматизации предназначен для того, чтобы определить, на какой из серверов (определяемых набором "tags") отправлять задачу на исполнение при изменении в соответствующей ветви (определяемой параметром "only:refs"):
В корневой директории исходного кода проекта "DSpace" создаём текстовый файл с правилами автоматизации для "GitLab", составленный в формате YAML:
$ vi .gitlab-ci.yml
variables:
# Simplest bypass bug: "problem with the SSL CA cert path"
GIT_SSL_NO_VERIFY: "1"
# Resource points
DSPACE_FQDN_DEVELOP: dev-dspace.example.net
DSPACE_FQDN_TESTING: test-dspace.example.net
DSPACE_FQDN_MASTER: dspace.example.net
DSPACE_DIR: /var/lib/dspace
# (jobs execution steps)
stages:
- dbget
- dbapply
- filesync
- deploy
- after
# (getting the contents of the main/master database)
db_getting:
stage: dbget
only:
refs:
- develop
- testing
tags:
- shell
- dspace
- master
script:
# (cutting commands requires superuser privileges)
- pg_dump --no-owner --clean --if-exists --verbose --dbname=dspace | sed -E "s#^(DROP\ EXTENSION|CREATE\ EXTENSION|COMMENT\ ON\ EXTENSION)#-- \1#gI" | sed -E "s#^(DROP\ SCHEMA|CREATE\ SCHEMA|COMMENT\ ON\ SCHEMA|GRANT)#-- \1#gI" > dspace-fresh-dump.sql
artifacts:
paths:
- dspace-fresh-dump.sql
expire_in: 30 minutes
# (replacing the contents of the database development server)
db_applying_develop:
stage: dbapply
when: manual
only:
refs:
- develop
tags:
- shell
- dspace
- develop
script:
- psql -v ON_ERROR_STOP=1 --dbname=dspace --file=dspace-fresh-dump.sql
# (replacing the contents of the database testing server)
db_applying_testing:
stage: dbapply
only:
refs:
- testing
tags:
- shell
- dspace
- testing
script:
- psql -v ON_ERROR_STOP=1 --dbname=dspace --file=dspace-fresh-dump.sql
# (getting/synchronization of files from the main/master server to the development server)
filesyncing_develop:
stage: filesync
when: manual
only:
refs:
- develop
tags:
- shell
- dspace
- develop
script:
# (RSync via Netcat in SSH-tunnel)
- RSYNC_CONNECT_PROG="ssh $DSPACE_FQDN_MASTER nc 127.0.0.1 873" rsync -av --delete 127.0.0.1::assetstore $DSPACE_DIR/assetstore
# (getting/synchronization of files from the main/master server to the testing server)
filesyncing_testing:
stage: filesync
only:
refs:
- testing
tags:
- shell
- dspace
- testing
script:
# (RSync via Netcat in SSH-tunnel)
- RSYNC_CONNECT_PROG="ssh $DSPACE_FQDN_MASTER nc 127.0.0.1 873" rsync -av --delete 127.0.0.1::assetstore $DSPACE_DIR/assetstore
# (build from source code and deploy web-services on development server)
deploy_develop:
stage: deploy
when: manual
only:
refs:
- develop
tags:
- shell
- dspace
- develop
script:
- /usr/local/etc/devops/dspace-build-deploy.sh $HOME/$CI_PROJECT_DIR $DSPACE_DIR
# (build from source code and deploy web-services on testing server)
deploy_testing:
stage: deploy
only:
refs:
- testing
tags:
- shell
- dspace
- testing
script:
- /usr/local/etc/devops/dspace-build-deploy.sh $HOME/$CI_PROJECT_DIR $DSPACE_DIR
# (build from source code and deploy web-services on main/master server)
deploy_master:
stage: deploy
only:
refs:
- master
tags:
- shell
- dspace
- master
script:
- /usr/local/etc/devops/dspace-build-deploy.sh $HOME/$CI_PROJECT_DIR $DSPACE_DIR
# (procedures after deploying web-services on development server)
after_deploy_develop:
stage: after
when: manual
only:
refs:
- develop
tags:
- shell
- dspace
- develop
script:
- /usr/local/etc/devops/dspace-post-processing.sh $DSPACE_DIR
# (procedures after deploying web-services on testing server)
after_deploy_testing:
stage: after
only:
refs:
- testing
tags:
- shell
- dspace
- testing
script:
- /usr/local/etc/devops/dspace-post-processing.sh $DSPACE_DIR
# Simplest bypass bug: "problem with the SSL CA cert path"
GIT_SSL_NO_VERIFY: "1"
# Resource points
DSPACE_FQDN_DEVELOP: dev-dspace.example.net
DSPACE_FQDN_TESTING: test-dspace.example.net
DSPACE_FQDN_MASTER: dspace.example.net
DSPACE_DIR: /var/lib/dspace
# (jobs execution steps)
stages:
- dbget
- dbapply
- filesync
- deploy
- after
# (getting the contents of the main/master database)
db_getting:
stage: dbget
only:
refs:
- develop
- testing
tags:
- shell
- dspace
- master
script:
# (cutting commands requires superuser privileges)
- pg_dump --no-owner --clean --if-exists --verbose --dbname=dspace | sed -E "s#^(DROP\ EXTENSION|CREATE\ EXTENSION|COMMENT\ ON\ EXTENSION)#-- \1#gI" | sed -E "s#^(DROP\ SCHEMA|CREATE\ SCHEMA|COMMENT\ ON\ SCHEMA|GRANT)#-- \1#gI" > dspace-fresh-dump.sql
artifacts:
paths:
- dspace-fresh-dump.sql
expire_in: 30 minutes
# (replacing the contents of the database development server)
db_applying_develop:
stage: dbapply
when: manual
only:
refs:
- develop
tags:
- shell
- dspace
- develop
script:
- psql -v ON_ERROR_STOP=1 --dbname=dspace --file=dspace-fresh-dump.sql
# (replacing the contents of the database testing server)
db_applying_testing:
stage: dbapply
only:
refs:
- testing
tags:
- shell
- dspace
- testing
script:
- psql -v ON_ERROR_STOP=1 --dbname=dspace --file=dspace-fresh-dump.sql
# (getting/synchronization of files from the main/master server to the development server)
filesyncing_develop:
stage: filesync
when: manual
only:
refs:
- develop
tags:
- shell
- dspace
- develop
script:
# (RSync via Netcat in SSH-tunnel)
- RSYNC_CONNECT_PROG="ssh $DSPACE_FQDN_MASTER nc 127.0.0.1 873" rsync -av --delete 127.0.0.1::assetstore $DSPACE_DIR/assetstore
# (getting/synchronization of files from the main/master server to the testing server)
filesyncing_testing:
stage: filesync
only:
refs:
- testing
tags:
- shell
- dspace
- testing
script:
# (RSync via Netcat in SSH-tunnel)
- RSYNC_CONNECT_PROG="ssh $DSPACE_FQDN_MASTER nc 127.0.0.1 873" rsync -av --delete 127.0.0.1::assetstore $DSPACE_DIR/assetstore
# (build from source code and deploy web-services on development server)
deploy_develop:
stage: deploy
when: manual
only:
refs:
- develop
tags:
- shell
- dspace
- develop
script:
- /usr/local/etc/devops/dspace-build-deploy.sh $HOME/$CI_PROJECT_DIR $DSPACE_DIR
# (build from source code and deploy web-services on testing server)
deploy_testing:
stage: deploy
only:
refs:
- testing
tags:
- shell
- dspace
- testing
script:
- /usr/local/etc/devops/dspace-build-deploy.sh $HOME/$CI_PROJECT_DIR $DSPACE_DIR
# (build from source code and deploy web-services on main/master server)
deploy_master:
stage: deploy
only:
refs:
- master
tags:
- shell
- dspace
- master
script:
- /usr/local/etc/devops/dspace-build-deploy.sh $HOME/$CI_PROJECT_DIR $DSPACE_DIR
# (procedures after deploying web-services on development server)
after_deploy_develop:
stage: after
when: manual
only:
refs:
- develop
tags:
- shell
- dspace
- develop
script:
- /usr/local/etc/devops/dspace-post-processing.sh $DSPACE_DIR
# (procedures after deploying web-services on testing server)
after_deploy_testing:
stage: after
only:
refs:
- testing
tags:
- shell
- dspace
- testing
script:
- /usr/local/etc/devops/dspace-post-processing.sh $DSPACE_DIR
Фиксируем добавленный файл автоматизации коммитом и выгружаем его на сервер хранения репозиториев:
# sudo -u dspace git add .gitlab-ci.yml
# sudo -u dspace git commit -m "Add '.gitlab-ci.yml'."
# sudo -u dspace git push ssh://git@gitlab.example.net/group/dspace.git custom
# sudo -u dspace git commit -m "Add '.gitlab-ci.yml'."
# sudo -u dspace git push ssh://git@gitlab.example.net/group/dspace.git custom
Имейте в виду, что добавление файла ".gitlab-ci.yml" в ветви, реакция на события в которых в нём же и описана, побудит "GitLab" отработать соответствующую логику, так что не стоит это делать раньше готовности всей цепочки "деплоя" - ничего страшного не случится, но в истории запуска "pipelines" останется отметка о сбое процедуры.
Автоматизация сборки и развёртывания web-проекта "DSpace".
Как я уже упоминал выше, сборка и принципы развёртывания для серверов разработки, тестирования и публикации "DSpace" единообразна - потому скрипт одинаков для всех трёх серверов (основан на инструкции ручной процедуры сборки и развёртывания):
# mkdir -p /usr/local/etc/devops
# cd /usr/local/etc/devops
# vi ./dspace-build-deploy.sh && chmod g-w,+x ./dspace-build-deploy.sh && chown root:dspace ./dspace-build-deploy.sh
# cd /usr/local/etc/devops
# vi ./dspace-build-deploy.sh && chmod g-w,+x ./dspace-build-deploy.sh && chown root:dspace ./dspace-build-deploy.sh
#!/bin/bash
# Получаем от "GitLab Runner" месторасположение исходных кодов
WORKING_DIRECTORY=${1}
# Получаем от "GitLab Runner" месторасположение целевой директории web-проекта
# (в нашем случае: "/var/lib/dspace")
DSPACE_DIR=${2}
# Фиксируем дату и время запуска скрипта
DATE=$(date +"%Y-%m-%d.%H:%M:%S")
# Активируем перехват и направление всего STDOUT-вывода в журнальный файл
exec &> >(tee -a "/var/log/dspace/dspace-build.log")
# Фиксируем начало блока журналирования датой и временем запуска процедуры
echo; echo ${DATE}
# Проверяем наличие ожидаемых приложений и утилит
[ -x "/usr/bin/java" ] && [ -x "/opt/apache-maven/bin/mvn" ] && [ -x "/opt/apache-ant/bin/ant" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция сборки \"DSpace\" прервана."; exit 1; }
# Проверяем наличие директории данных проекта и прерываем работу при отсутствии таковой
[ ! -d "${WORKING_DIRECTORY}" ] && { echo "Директория \"${WORKING_DIRECTORY}\" данных проекта, для которого запрошена сборка и развёртывание, отсутствует. Операция прервана."; exit 1; }
# Переходим в директорию проекта
cd "${WORKING_DIRECTORY}"
# Накладываем исправление некорректного SQL-скрипта подсистемы обновления "базы данных" до формата "DSpace v6.3"
if ! patch --dry-run --quiet --force --reverse -d ./ -p1 < /usr/local/etc/devops/dspace-6-ds-2701-hibernate-migration.fix-key-and-cascade.patch > /dev/null ; then
echo "Source patching..."
patch --forward -d ./ -p1 < /usr/local/etc/devops/dspace-6-ds-2701-hibernate-migration.fix-key-and-cascade.patch
[ "${?}" -ne "0" ] && { echo "Внесение исправлений в исходный код завершено с ошибкой! Операция прервана."; exit 1; }
else
echo "Source already patched."
fi
# Запускаем сборку Java-приложений проекта посредством "Maven"
echo "Assembling by \"Apache Maven\" running..."
/opt/apache-maven/bin/mvn clean package >> /var/log/dspace/dspace-build-maven.log
# Проверяем успешность сборки по коду завершения, поступившему от "Maven"
[ "${?}" -ne "0" ] && { echo "Сборка проекта посредством \"Maven\" завершена с ошибкой! Операция прервана."; exit 1; }
# Отмечаем успешное завершение первого этапа сборки
echo "Assembly by \"Apache Maven\" completed successfully."
# Подкладываем отсутствующие нужные "Solr Webapp" Java-библиотеки, копируя их из сборки "DSpace XML-UI"
find ${WORKING_DIRECTORY}/dspace/target/dspace-installer/webapps/xmlui/WEB-INF/lib -iname 'elasticsearch-*.jar' -exec cp -v {} ${WORKING_DIRECTORY}/dspace/target/dspace-installer/webapps/solr/WEB-INF/lib/ \;
find ${WORKING_DIRECTORY}/dspace/target/dspace-installer/webapps/xmlui/WEB-INF/lib -iname 'lucene-sandbox-*.jar' -exec cp -v {} ${WORKING_DIRECTORY}/dspace/target/dspace-installer/webapps/solr/WEB-INF/lib/ \;
# Удаляем и пересоздаём подставную директорию для "инсталляции" посредством "Ant"
rm -rf "${WORKING_DIRECTORY}/ant-dspace-target" && mkdir -p "${WORKING_DIRECTORY}/ant-dspace-target"
# Переходим в директорию с предварительно собранным "Maven" проектом
cd "${WORKING_DIRECTORY}/dspace/target/dspace-installer"
# Посредством "Apache Ant" производим финальную обработку настроек компонентов "DSpace"
echo "Building by \"Apache Ant\" running..."
/opt/apache-ant/bin/ant -Ddspace.dir=${WORKING_DIRECTORY}/ant-dspace-target fresh_install >> /var/log/dspace/dspace-build-ant.log
# Проверяем успешность обработки по коду завершения, поступившему от "Ant"
[ "${?}" -ne "0" ] && { echo "Подготовка проекта посредством \"Ant\" завершена с ошибкой! Операция прервана."; exit 1; }
# Отмечаем успешное завершение второго этапа сборки
echo "Build by \"Apache Ant\" completed successfully."
# Переходим в директорию с обработанным "Ant" проектом
cd "${WORKING_DIRECTORY}/ant-dspace-target"
# Заменяем в конфигурационных файлах имя подставной инсталляционной директории на реальное месторасположение файлов web-проекта
# (ради использования в "sed" переменных со "слешами" заменяем разделитель "/" на "#")
grep -ril -e "${WORKING_DIRECTORY}/ant-dspace-target" ./ | xargs sed -i -e "s#${WORKING_DIRECTORY}\/ant-dspace-target#${DSPACE_DIR}#gI"
# Удаляем устаревшую библиотеку, несовместимую с современным "Tomcat"
[ -f "${WORKING_DIRECTORY}/ant-dspace-target/lib/servlet-api-2.5.jar" ] && { rm -fv "${WORKING_DIRECTORY}/ant-dspace-target/lib/servlet-api-2.5.jar"; }
# Создаём пустой конфигурационный файл, который хочет видеть "DSpace" на этапе запуска
[ ! -f "${WORKING_DIRECTORY}/ant-dspace-target/config/local.cfg" ] && { touch "${WORKING_DIRECTORY}/ant-dspace-target/config/local.cfg"; }
# Отмечаем завершение сборки
echo "Build finished."
# Останавливаем web-сервис "Tomcat"
# (необходимы разрешительные правила в SUDO и "PolicyKit")
echo "Apache Tomcat stoping..."
systemctl stop dspace-tomcat
# Перемещаем замещаемые новыми ресурсы в резервную копию
echo "Backup..."
BACKUP_DIR=".backup_${DATE}"
mkdir -p "${DSPACE_DIR}/${BACKUP_DIR}"
[ -d "${DSPACE_DIR}/bin" ] && { mv -f "${DSPACE_DIR}/bin" "${DSPACE_DIR}/${BACKUP_DIR}"; }
[ -d "${DSPACE_DIR}/config" ] && { mv -f "${DSPACE_DIR}/config" "${DSPACE_DIR}/${BACKUP_DIR}"; }
[ -d "${DSPACE_DIR}/etc" ] && { mv -f "${DSPACE_DIR}/etc" "${DSPACE_DIR}/${BACKUP_DIR}"; }
[ -d "${DSPACE_DIR}/lib" ] && { mv -f "${DSPACE_DIR}/lib" "${DSPACE_DIR}/${BACKUP_DIR}"; }
[ -d "${DSPACE_DIR}/webapps" ] && { mv -f "${DSPACE_DIR}/webapps" "${DSPACE_DIR}/${BACKUP_DIR}"; }
# (Apache Solr)
if [ -d "${DSPACE_DIR}/solr" ] ; then
cd "${DSPACE_DIR}"
find ./solr \( -type d -iname "conf" -o -iname "solr.xml" \) -print0 | xargs --null --no-run-if-empty -I {} cp -r --parents {} "${DSPACE_DIR}/${BACKUP_DIR}"
find ./solr \( -type d -iname "conf" -o -iname "solr.xml" \) -print0 | xargs --null --no-run-if-empty -I {} rm -rf {}
fi
# Удаляем старые резервные копии, усекая их количество до трёх штук
if [ "$(find "${DSPACE_DIR}" -maxdepth 1 -type d -iname ".backup_*" -print | wc -l)" -gt "3" ]; then
# Удаляем все старее последних трёх (естественная сортировка по имени файла)
echo "Truncation of backups..."
find "${DSPACE_DIR}" -maxdepth 1 -type d -iname ".backup_*" -print | sort | head -n -3 | xargs --no-run-if-empty -I {} rm -rf {}
fi
# Избирательно развёртываем компоненты проекта в директорию web-сервиса
echo "Deploy..."
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/bin" "${DSPACE_DIR}"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/config" "${DSPACE_DIR}"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/etc" "${DSPACE_DIR}"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/lib" "${DSPACE_DIR}"
# (Tomcat Webapps)
mkdir -p "${DSPACE_DIR}/webapps"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/webapps/xmlui" "${DSPACE_DIR}/webapps"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/webapps/rest" "${DSPACE_DIR}/webapps"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/webapps/solr" "${DSPACE_DIR}/webapps"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/webapps/oai" "${DSPACE_DIR}/webapps"
# (Apache Solr)
mkdir -p "${DSPACE_DIR}/solr"
cd "${WORKING_DIRECTORY}/ant-dspace-target"
find ./solr \( -type d -iname "conf" -o -iname "solr.xml" \) -print0 | xargs --null --no-run-if-empty -I {} cp -r --parents {} "${DSPACE_DIR}"
# Отмечаем завершение развёртывания
echo "Deploy finished."
# Запускаем web-сервис "Tomcat"
echo "Apache Tomcat running..."
systemctl start dspace-tomcat
exit ${?}
# Получаем от "GitLab Runner" месторасположение исходных кодов
WORKING_DIRECTORY=${1}
# Получаем от "GitLab Runner" месторасположение целевой директории web-проекта
# (в нашем случае: "/var/lib/dspace")
DSPACE_DIR=${2}
# Фиксируем дату и время запуска скрипта
DATE=$(date +"%Y-%m-%d.%H:%M:%S")
# Активируем перехват и направление всего STDOUT-вывода в журнальный файл
exec &> >(tee -a "/var/log/dspace/dspace-build.log")
# Фиксируем начало блока журналирования датой и временем запуска процедуры
echo; echo ${DATE}
# Проверяем наличие ожидаемых приложений и утилит
[ -x "/usr/bin/java" ] && [ -x "/opt/apache-maven/bin/mvn" ] && [ -x "/opt/apache-ant/bin/ant" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция сборки \"DSpace\" прервана."; exit 1; }
# Проверяем наличие директории данных проекта и прерываем работу при отсутствии таковой
[ ! -d "${WORKING_DIRECTORY}" ] && { echo "Директория \"${WORKING_DIRECTORY}\" данных проекта, для которого запрошена сборка и развёртывание, отсутствует. Операция прервана."; exit 1; }
# Переходим в директорию проекта
cd "${WORKING_DIRECTORY}"
# Накладываем исправление некорректного SQL-скрипта подсистемы обновления "базы данных" до формата "DSpace v6.3"
if ! patch --dry-run --quiet --force --reverse -d ./ -p1 < /usr/local/etc/devops/dspace-6-ds-2701-hibernate-migration.fix-key-and-cascade.patch > /dev/null ; then
echo "Source patching..."
patch --forward -d ./ -p1 < /usr/local/etc/devops/dspace-6-ds-2701-hibernate-migration.fix-key-and-cascade.patch
[ "${?}" -ne "0" ] && { echo "Внесение исправлений в исходный код завершено с ошибкой! Операция прервана."; exit 1; }
else
echo "Source already patched."
fi
# Запускаем сборку Java-приложений проекта посредством "Maven"
echo "Assembling by \"Apache Maven\" running..."
/opt/apache-maven/bin/mvn clean package >> /var/log/dspace/dspace-build-maven.log
# Проверяем успешность сборки по коду завершения, поступившему от "Maven"
[ "${?}" -ne "0" ] && { echo "Сборка проекта посредством \"Maven\" завершена с ошибкой! Операция прервана."; exit 1; }
# Отмечаем успешное завершение первого этапа сборки
echo "Assembly by \"Apache Maven\" completed successfully."
# Подкладываем отсутствующие нужные "Solr Webapp" Java-библиотеки, копируя их из сборки "DSpace XML-UI"
find ${WORKING_DIRECTORY}/dspace/target/dspace-installer/webapps/xmlui/WEB-INF/lib -iname 'elasticsearch-*.jar' -exec cp -v {} ${WORKING_DIRECTORY}/dspace/target/dspace-installer/webapps/solr/WEB-INF/lib/ \;
find ${WORKING_DIRECTORY}/dspace/target/dspace-installer/webapps/xmlui/WEB-INF/lib -iname 'lucene-sandbox-*.jar' -exec cp -v {} ${WORKING_DIRECTORY}/dspace/target/dspace-installer/webapps/solr/WEB-INF/lib/ \;
# Удаляем и пересоздаём подставную директорию для "инсталляции" посредством "Ant"
rm -rf "${WORKING_DIRECTORY}/ant-dspace-target" && mkdir -p "${WORKING_DIRECTORY}/ant-dspace-target"
# Переходим в директорию с предварительно собранным "Maven" проектом
cd "${WORKING_DIRECTORY}/dspace/target/dspace-installer"
# Посредством "Apache Ant" производим финальную обработку настроек компонентов "DSpace"
echo "Building by \"Apache Ant\" running..."
/opt/apache-ant/bin/ant -Ddspace.dir=${WORKING_DIRECTORY}/ant-dspace-target fresh_install >> /var/log/dspace/dspace-build-ant.log
# Проверяем успешность обработки по коду завершения, поступившему от "Ant"
[ "${?}" -ne "0" ] && { echo "Подготовка проекта посредством \"Ant\" завершена с ошибкой! Операция прервана."; exit 1; }
# Отмечаем успешное завершение второго этапа сборки
echo "Build by \"Apache Ant\" completed successfully."
# Переходим в директорию с обработанным "Ant" проектом
cd "${WORKING_DIRECTORY}/ant-dspace-target"
# Заменяем в конфигурационных файлах имя подставной инсталляционной директории на реальное месторасположение файлов web-проекта
# (ради использования в "sed" переменных со "слешами" заменяем разделитель "/" на "#")
grep -ril -e "${WORKING_DIRECTORY}/ant-dspace-target" ./ | xargs sed -i -e "s#${WORKING_DIRECTORY}\/ant-dspace-target#${DSPACE_DIR}#gI"
# Удаляем устаревшую библиотеку, несовместимую с современным "Tomcat"
[ -f "${WORKING_DIRECTORY}/ant-dspace-target/lib/servlet-api-2.5.jar" ] && { rm -fv "${WORKING_DIRECTORY}/ant-dspace-target/lib/servlet-api-2.5.jar"; }
# Создаём пустой конфигурационный файл, который хочет видеть "DSpace" на этапе запуска
[ ! -f "${WORKING_DIRECTORY}/ant-dspace-target/config/local.cfg" ] && { touch "${WORKING_DIRECTORY}/ant-dspace-target/config/local.cfg"; }
# Отмечаем завершение сборки
echo "Build finished."
# Останавливаем web-сервис "Tomcat"
# (необходимы разрешительные правила в SUDO и "PolicyKit")
echo "Apache Tomcat stoping..."
systemctl stop dspace-tomcat
# Перемещаем замещаемые новыми ресурсы в резервную копию
echo "Backup..."
BACKUP_DIR=".backup_${DATE}"
mkdir -p "${DSPACE_DIR}/${BACKUP_DIR}"
[ -d "${DSPACE_DIR}/bin" ] && { mv -f "${DSPACE_DIR}/bin" "${DSPACE_DIR}/${BACKUP_DIR}"; }
[ -d "${DSPACE_DIR}/config" ] && { mv -f "${DSPACE_DIR}/config" "${DSPACE_DIR}/${BACKUP_DIR}"; }
[ -d "${DSPACE_DIR}/etc" ] && { mv -f "${DSPACE_DIR}/etc" "${DSPACE_DIR}/${BACKUP_DIR}"; }
[ -d "${DSPACE_DIR}/lib" ] && { mv -f "${DSPACE_DIR}/lib" "${DSPACE_DIR}/${BACKUP_DIR}"; }
[ -d "${DSPACE_DIR}/webapps" ] && { mv -f "${DSPACE_DIR}/webapps" "${DSPACE_DIR}/${BACKUP_DIR}"; }
# (Apache Solr)
if [ -d "${DSPACE_DIR}/solr" ] ; then
cd "${DSPACE_DIR}"
find ./solr \( -type d -iname "conf" -o -iname "solr.xml" \) -print0 | xargs --null --no-run-if-empty -I {} cp -r --parents {} "${DSPACE_DIR}/${BACKUP_DIR}"
find ./solr \( -type d -iname "conf" -o -iname "solr.xml" \) -print0 | xargs --null --no-run-if-empty -I {} rm -rf {}
fi
# Удаляем старые резервные копии, усекая их количество до трёх штук
if [ "$(find "${DSPACE_DIR}" -maxdepth 1 -type d -iname ".backup_*" -print | wc -l)" -gt "3" ]; then
# Удаляем все старее последних трёх (естественная сортировка по имени файла)
echo "Truncation of backups..."
find "${DSPACE_DIR}" -maxdepth 1 -type d -iname ".backup_*" -print | sort | head -n -3 | xargs --no-run-if-empty -I {} rm -rf {}
fi
# Избирательно развёртываем компоненты проекта в директорию web-сервиса
echo "Deploy..."
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/bin" "${DSPACE_DIR}"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/config" "${DSPACE_DIR}"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/etc" "${DSPACE_DIR}"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/lib" "${DSPACE_DIR}"
# (Tomcat Webapps)
mkdir -p "${DSPACE_DIR}/webapps"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/webapps/xmlui" "${DSPACE_DIR}/webapps"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/webapps/rest" "${DSPACE_DIR}/webapps"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/webapps/solr" "${DSPACE_DIR}/webapps"
cp -rf "${WORKING_DIRECTORY}/ant-dspace-target/webapps/oai" "${DSPACE_DIR}/webapps"
# (Apache Solr)
mkdir -p "${DSPACE_DIR}/solr"
cd "${WORKING_DIRECTORY}/ant-dspace-target"
find ./solr \( -type d -iname "conf" -o -iname "solr.xml" \) -print0 | xargs --null --no-run-if-empty -I {} cp -r --parents {} "${DSPACE_DIR}"
# Отмечаем завершение развёртывания
echo "Deploy finished."
# Запускаем web-сервис "Tomcat"
echo "Apache Tomcat running..."
systemctl start dspace-tomcat
exit ${?}
Перед развёртыванием web-приложений на площадках разработки и тестирования мы полностью заменяем содержимое "базы данных", отчего индексы полнотекстового поиска могут стать неактуальными - потому заведём скрипт, запускаемый после успешного "деплоя", задачей которого будет переиндексация БД:
# cd /usr/local/etc/devops
# vi ./dspace-post-processing.sh && chmod g-w,+x ./dspace-post-processing.sh && chown root:dspace ./dspace-post-processing.sh
# vi ./dspace-post-processing.sh && chmod g-w,+x ./dspace-post-processing.sh && chown root:dspace ./dspace-post-processing.sh
#!/bin/bash
# Получаем от "GitLab Runner" месторасположение целевой директории web-проекта
DSPACE_DIR=${1}
# Запускаем удаление индексов полнотекстового поиска, сопоставленных с документами, удалёнными из основной "баз данных"
CLASSPATH=/opt/dspace/tomcat/lib/* "${DSPACE_DIR}/bin/dspace" index-discovery -c
# Запускаем профилактическое обновление индексов полнотекстового поиска (в фоновом режиме)
CLASSPATH=/opt/dspace/tomcat/lib/* setsid nohup "${DSPACE_DIR}/bin/dspace" index-discovery > /dev/null 2>&1 &
exit ${?}
# Получаем от "GitLab Runner" месторасположение целевой директории web-проекта
DSPACE_DIR=${1}
# Запускаем удаление индексов полнотекстового поиска, сопоставленных с документами, удалёнными из основной "баз данных"
CLASSPATH=/opt/dspace/tomcat/lib/* "${DSPACE_DIR}/bin/dspace" index-discovery -c
# Запускаем профилактическое обновление индексов полнотекстового поиска (в фоновом режиме)
CLASSPATH=/opt/dspace/tomcat/lib/* setsid nohup "${DSPACE_DIR}/bin/dspace" index-discovery > /dev/null 2>&1 &
exit ${?}
Скрипты сборки и развёртывания написаны таким образом, что их можно вызывать как из "GitLab Runner" в автоматизированных процедурах, так и вручную, заранее заполучив исходные коды проекта. Например, так проводится предварительное тестирование:
# mkdir -p /var/tmp/build && chown -R dspace /var/tmp/build && cd /var/tmp/build
# sudo -u dspace git clone --branch testing ssh://git@gitlab.example.net/group/dspace.git
# sudo -u dspace /usr/local/etc/devops/dspace-build-deploy.sh /var/tmp/build/dspace /var/lib/dspace
# sudo -u dspace /usr/local/etc/devops/dspace-post-processing.sh /var/lib/dspace
# sudo -u dspace git clone --branch testing ssh://git@gitlab.example.net/group/dspace.git
# sudo -u dspace /usr/local/etc/devops/dspace-build-deploy.sh /var/tmp/build/dspace /var/lib/dspace
# sudo -u dspace /usr/local/etc/devops/dspace-post-processing.sh /var/lib/dspace
Заводим аккаунты пользователей-разработчиков.
Учитывая то, что все ресурсы web-сервиса "DSpace" находятся в собственности пользователя "dspace" и взаимосвязи с внешними сервисами также налажены от его имени, наверняка разработчикам на выделенном для них сервере "dev-dspace.example.net" понадобится возможность исполнять команды от имени такового:
# useradd -m -s /bin/bash -g dspace developer-one && passwd developer-one
Очень важно установить для пользователей, создающих файлы, которые должны быть доступны для одногруппников вроде сервиса "DSpace", маску разрешений записи для группы и запрета чтения (не не открытия директорий) для всех остальных:
# sed -i -e "s/^#\?umask.*/umask 006/gI" /home/developer-one/.profile
Используя функционал расширения конфигурации SUDO добавляем правило разрешения разработчикам запуска команд от имени пользователя "dspace" в отдельном файле конфигурации (обращаю внимание на то, что для вызова через SUDO маску создаваемых файлов нужно задавать явно):
# vi /etc/sudoers.d/dspace-users
Defaults umask=006
....
developer-one ALL=(dspace:dsapce) NOPASSWD: ALL
developer-two ALL=(dspace:dsapce) NOPASSWD: ALL
....
developer-one ALL=(dspace:dsapce) NOPASSWD: ALL
developer-two ALL=(dspace:dsapce) NOPASSWD: ALL
Если переопределение "umask" для целевых пользователей не сработает, то возможно это запрещено опцией "Defaults umask_override" в конфигурационном файле "/etc/sudoers".
Обязательно проверяем синтаксическую корректность вносимых изменений:
# visudo -cf /etc/sudoers.d/dspace-users
Ограничиваем доступ к файлу расширенной конфигурации SUDO:
# chown root:root /etc/sudoers.d/dspace-users
# chmod go-rwx /etc/sudoers.d/dspace-users
# chmod go-rwx /etc/sudoers.d/dspace-users
Предоставляем разработчикам подсказку-инструкцию при входе в систему.
Полезно проинформировать разработчиков об имеющихся у них в распоряжении инструментах, сформировав простейшую инструкцию в текстовом виде:
# vi /home/developer-one/README
Для всех трёх серверов DSpace: "dev-dspace.example.net", "test-dspace.example.net" и "dspace.example.net" - налажены автоматизированные процедуры сборки и развёртывания web-сервиса. Для сервера разработки "dev-dspace.example.net" имеется возможность вести работу локально, не засоряя репозиторий хранения промежуточными "коммитами".
Рекомендуем придерживаться нижеследующего сценария загрузки исходного кода, его модификации, сборки, развёртывания и финальной выгрузки в репозиторий хранения.
Загрузка исходного кода с репозитория хранения:
$ mkdir -p $HOME/build && cd $HOME/build
$ sudo -u dspace git clone --branch develop ssh://git@gitlab.example.net/group/dspace.git
Запуск сборки проекта "Maven", подготовки его посредством "Ant" и развёртывания с промежуточными "патчами" и добавлением библиотек (на вход подаются две переменные: директория с исходным кодом и директория с рабочим проектом):
$ sudo -u dspace /usr/local/etc/devops/dspace-build-deploy.sh $HOME/build/dspace /var/lib/dspace
Вышеприведённый скрипт можно запускать многократно, после каждого изменения в исходном коде.
Выгрузка наработок в репозиторий хранения:
$ cd $HOME/build
$ sudo -u dspace git push ssh://git@gitlab.example.net/group/dspace.git develop
При замене содержимого "базы данных" или повреждении индексов полнотекстового поиска может понадобится принудительная переиндексация:
$ sudo -u dspace CLASSPATH=/opt/dspace/tomcat/lib/* /var/lib/dspace/bin/dspace index-discovery -b
Рекомендации и сообщения о проблемах шлите на почтовый ящик "admin@example.net".
Рекомендуем придерживаться нижеследующего сценария загрузки исходного кода, его модификации, сборки, развёртывания и финальной выгрузки в репозиторий хранения.
Загрузка исходного кода с репозитория хранения:
$ mkdir -p $HOME/build && cd $HOME/build
$ sudo -u dspace git clone --branch develop ssh://git@gitlab.example.net/group/dspace.git
Запуск сборки проекта "Maven", подготовки его посредством "Ant" и развёртывания с промежуточными "патчами" и добавлением библиотек (на вход подаются две переменные: директория с исходным кодом и директория с рабочим проектом):
$ sudo -u dspace /usr/local/etc/devops/dspace-build-deploy.sh $HOME/build/dspace /var/lib/dspace
Вышеприведённый скрипт можно запускать многократно, после каждого изменения в исходном коде.
Выгрузка наработок в репозиторий хранения:
$ cd $HOME/build
$ sudo -u dspace git push ssh://git@gitlab.example.net/group/dspace.git develop
При замене содержимого "базы данных" или повреждении индексов полнотекстового поиска может понадобится принудительная переиндексация:
$ sudo -u dspace CLASSPATH=/opt/dspace/tomcat/lib/* /var/lib/dspace/bin/dspace index-discovery -b
Рекомендации и сообщения о проблемах шлите на почтовый ящик "admin@example.net".
Чтобы разработчики не забывали о регламентах, можно наладить вывод содержимого файла "README" при каждом входе в систему, а не особо нужные им системные сообщения отключить:
# FORUSER="developer-one" && echo -e "\n# Show README file on login\necho; cat \"/home/${FORUSER}/README\" 2>/dev/null" >> /home/${FORUSER}/.profile && touch /home/${FORUSER}/.hushlogin && chmod -w /home/${FORUSER}/.hushlogin