В этой статье мы рассмотрим часто встречающуюся при разработке задачу, когда требуется выполнять частичное или полное зеркалирование репозиториев Git между несколькими серверами Git. Причины для репликации могут быть разные, приведем несколько в качестве примера.
Публикация кода в открытые репозитории Git. Допустим, вы разрабатываете продукт и какие-то его ветки должны быть доступны для сообщества или вы, в принципе, разрабатываете продукт с открытым кодом, но ведете разработку в GitLab, выполняете тестирование и доставку с помощью GitLab CI. Обычно вы хотите, чтобы пользователи имели возможность постоянного доступа к исходному коду продукта, поэтому хотите выкладывать его на GitHub или BitBucket.
Публикация кода в репозиторий клиента. Вы разрабатываете программный продукт для одного или более клиентов, а при создании релизов публикуете исходный код на их внутренние GitLab серверы, на которых для релизного кода отрабатывают соответствующие процедуры CI/CD и выполняется развертывание кода в продуктовую или ‘staging’ среду. При этом, вы можете хотеть публиковать разным клиентам разные ветки репозитория, которые содержат специализированные модификации.
Мы рассмотрим как решить эту задачу с помощью GitLab CI. Использовать такой подход особенно удобно, поскольку это позволяет выполнять публикацию только в том случае, когда конвейер (pipeline) CI/CD завершается успешно, то есть вы можете гарантировать доставку рабочего кода в удаленный репозиторий.
Для особо нетерпеливых сразу предоставим ссылку на рабочий пример публикации ветки master в GitHub.
При удаленной публикации очень важно обеспечить внесение изменений только в один репозиторий, то есть удаленные репозитории должны всегда быть синхронизированы, поскольку в ином случае синхронизация не пройдет, впрочем, вы об этом сразу узнаете поскольку конвейер не завершится успешно.
Синхронизация с удаленным зеркалом вручную
Первый шаг процесса – первичная синхронизация с удаленным репозиторием. Для этого выполните следующие действия:
Клонируйте репозиторий-донор на локальный компьютер:
git clone ssh://git@git.server.com:/group/project.git
Добавьте удаленный репозиторий в качестве upstream-репозитория:
git remote add mirror git@github.com:group/project.git
Загрузите необходимую ветку в удаленный репозиторий в принудительном порядке (в примере используется master):
git push --force --progress mirror HEAD:master
Задайте переменные в GitLab
Поскольку для публикации в Git требуется аутентификация, то в шаг публикации, который будет использоваться при выполнении конвейера CI/CD в GitLab CI, необходимо передать эту информацию безопасным способом. Для этого в GitLab можно определить переменные окружения, которые будут добавляться в рабочую среду GitLab CI Runner при выполнении шагов непрерывной интеграции.
Переменные задаются для групп проектов. Для того, чтобы определить переменные, перейдем в: Группа проектов (group) > Settings > Pipelines
:
Как можно видеть, мы для нашей группы проектов опредили переменные GITHUB_MIRROR_PRIVATE
и GITHUB_MIRROR_PUBLIC
, а для публикации собранных артефактов в Sonatype Nexus – SONATYPE_NEXUS_LOGIN
и SONATYPE_NEXUS_PASSWORD
. Переменные SONATYPE_NEXUS_*
для публикации в GitHub не используются.
Для ключа
GITHUB_MIRROR_PRIVATE
мы выполнили дополнительное кодирование в base64:cat id_rsa | base64 -w0
для того, чтобы блок закрытого ключа принял форму одной строки без символов переноса строк. В процессе работы задания мы выполним обратную процедуруbase64 -d
, чтобы вернуть его в исходный вид. Это решение продиктовано тем, что мы хотим избежать случайное перекодирование переноса строк при загрузке, выгрузке ключа в/из GitLab. Вообще, можно рассмотреть упаковку всех переменных с потенциально небезопасными символами в base64.
Определим стадию и задание в GitLab CI
Предполагаем, что у вас уже имеется файл .gitlab-ci.yml
, в котором определены необходимые шаги CI/CD. Добавим в него задание зеркалирования и разберем его подробно:
stages:
...
- mirror
Здесь мы определили новую стадию mirror
, в которой определим действия по зеркалированию репозитория. Далее, определим задание зеркалирования:
mirror-master:
stage: mirror
variables:
UPSTREAM_REPOSITORY: "git@github.com:bwsw/cloud-plugin-kv-storage.git"
UPSTREAM_BRANCH: "master"
GIT_SUBMODULE_STRATEGY: none
GIT_STRATEGY: clone
cache: {}
only:
- master
script:
- mkdir -p ~/.ssh # 1
- echo $GITHUB_MIRROR_PRIVATE | base64 -d > ~/.ssh/id_rsa # 2
- echo $GITHUB_MIRROR_PUBLIC > ~/.ssh/id_rsa.pub # 3
- ssh-keyscan -t rsa,dsa,ecdsa github.com >> ~/.ssh/known_hosts # 4
- chmod -R go-rwx ~/.ssh # 5
- git remote add mirror $UPSTREAM_REPOSITORY # 6
- git remote show mirror # 7
- git fetch mirror # 8
- git push --progress mirror HEAD:master # 9
...
Разбор задания по частям
Задание выполняется на стадии mirror
.
stage: mirror
Запрещаем скачивать сабмодули, если они определены для проекта, что ускоряет обработку стадии.
GIT_SUBMODULE_STRATEGY: none
Перед зеркалированием необходимо выкачать весь репозиторий клонированием.
GIT_STRATEGY: clone
Говорим GitLab, что на стадии не будет использоваться кэш, это ускоряет обработку стадии, так как кэш не будет скачиваться с сервера GitLab.
cache: {}
Указываем, что задание будет выполняться только для ветки master
.
only:
- master
Команды задания по шагам:
- mkdir -p ~/.ssh # 1
- echo $GITHUB_MIRROR_PRIVATE | base64 -d > ~/.ssh/id_rsa # 2
- echo $GITHUB_MIRROR_PUBLIC > ~/.ssh/id_rsa.pub # 3
- ssh-keyscan -t rsa,dsa,ecdsa github.com >> ~/.ssh/known_hosts # 4
- chmod -R go-rwx ~/.ssh # 5
- git remote add mirror $UPSTREAM_REPOSITORY # 6
- git remote show mirror # 7
- git fetch mirror # 8
- git push --progress mirror HEAD:$UPSTREAM_BRANCH # 9
- Создадим каталог .ssh, куда разместим ключи. Флаг
-p
позволяет выполнять команду без ошибок, если каталог уже существует. - Сохраним приватный ключ из переменной, выполнив его раскодирование.
- Сохраним публичный ключ аналогичным образом.
- Выполним сканирование GitHub на предмет активных ключей сервера, необходимо, чтобы SSH не генерировал приглашение подтвердить ключи.
- Установим права, которые требует SSH для использования ключей.
- Добавим удаленный репозиторий под именем
mirror
. - Используется для отладки задания.
- Скачиваем изменения с удаленного репозитория. Используется для отладки и отслеживания некорректной работы с удаленными репозиториями.
- Загружаем изменения в удаленный репозиторий.
Тестирование задания
Для тестирования задания выполните вручную запуск конвейера для ветки master
.
Весь конвейер сборки должен отработать успешно, а в удаленном репозитории должно появиться обновленное дерево файлов.
На этапе тестирования и отладки удобно поместить стадию
mirror
на первое место по порядку следования, что позволит не тратить время на сборку, а выполнить отладку только публикации за минимальное время.
Заключение
В этом примере мы рассмотрели простой подход, который может использоваться для зеркалирования репозитория в публичный Git, на примере GitHub. Этот подход с доработками вполне можно применять и для доставки обновлений кода клиентам, при этом в репозиториях клиентов могут возникать цепочки событий CI/CD, которые будут выполнять работу по развертыванию системы на их стороне в безопасном режиме, без раскрытия информации за пределы контура организации.
Если вам понравился этот пост, поделитесь им с друзьями.