0

Настройка CI/CD в GitLab для синхронизации проекта с веб-серверами

13.09.2023

Runner в GitLab позволяют автоматизировать рутинные задачи при обновлении проектов в репозитории. В нашем примере мы рассмотрим ситуацию, когда у нас используется сервер GitLab для хранения проекта и 5 веб-серверов, куда должны попадать изменения после выполнения git push. Мы настроим наш CI/CD на синхронизацию файлов с помощью rsyncd. Предполагается, что у нас уже установлен GitLab на Linux.

Установка и регистрация Runner

Runner — это отдельное приложение, которое запускается для выполнения заданий CI/CD. Его можно установить на любой компьютер под управлением любой популярной операционной системы (Linux, Windows, BSD, Mac OS и так далее). Также доступны различные варианты установки — из репозитория, скачивание бинарника или запуск как приложения в Docker или кластере Kubernetes. Мы выполним установку из репозитория Linux на тот же сервер, где работает наш GitLab.

Установка

По умолчанию, Runner не устанавливается вместе с GitLab.

Установка выполняется по инструкции самого gitlab:

И так, обработчик зарегистрирован и настроен. Переходим к созданию CI/CD.

Создание CI/CD для проекта

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

Переходим в GitLab на страницу проекта и кликаем по Set up CI/CD:

На странице проекта нужно кликнуть по Set up CI/CD

* данной кнопки может и не быть.

… или можно просто в корне проекта создать файл:

vi .gitlab-ci.yml

Задаем содержимое нашего сценария:

default:
  tags:
    - tag

stages:
  - test

test:
  stage: test
  script: echo $CI_PROJECT_DIR/

* Формат текста должен быть yml, а значит, отступы имеют значение.
** В данном примере мы создаем pipeline с одним единственным этапом, которое называется test.

По данному заданию будет запускаться скрипт вывода значения переменной $CI_PROJECT_DIR — путь, по которому клонируется проект и где выполняется задание (если установлен $builds_dir, эта переменная устанавливается относительно данного значения).

Также обратите внимание на директиву default с указанием на использование тегов (tags) — для всех заданий мы определили тег по умолчанию, который может контролировать, на каких раннерах должно быть запущено задание.


*** Список возможных переменных можно посмотреть на официальном сайте в разделе документации GitLab CI/CD environment variables.
**** Боллее подробная шпаргалка по работе с Gitlab CI/CD представлена в инструкции Шпаргалка по написанию Gitlab Pipelines.

После сохранения файла ждем несколько секунд и перезапускаем страницу — мы должны увидеть успешный результат выполнения сценария CI/CD:

Задание CI/CD выполнено успешно

Кликнем по значку зеленой галочки и в открывшейся странице кликаем по нашей единственной стадии:

Кликаем по названию нашей стадии pipeline

Мы должны увидеть ход процесса выполнения задания и результат его работы:

Наш CI/CD показал нампуть до каталога на сервере, где хранится проект

На этой же странице справа можно вручную запустить задание еще раз:

Повторяем запуск задания

CI/CD создан. Теперь необходимо подготовить систему к синхронизации данных.

Настройка Rsyncd

Наша синхронизация будет выполняться с помощью Rsyncd. Это удобный инструмент, с помощью которого можно поддерживать актуальное состояние двух и более каталогов. Также у нас не возникнет проблем с правами — rsync после копирования будет задавать файлам нужного владельца и нам не нужно будет выдавать права root для runner с помощью файла sudoerst. Подробнее об установке и настройке Rsyncd.

Настройки нужно выполнить как на веб-серверах, так и сервере с GitLab.

Настройка на веб-серверах

Данные действия нужно выполнить на каждом веб-сервере. Мы должны установить и настроить в качестве сервиса rsyncd. Сначала установим его. В зависимости от типа Linux, наши действия будут различаться.

а) Ubuntu / Debian:

apt-get install rsync

Открываем следующий файл:

vi /etc/default/rsync

Находим запись:

RSYNC_ENABLE=false

И меняем на:

RSYNC_ENABLE=true

Запускаем:

systemctl enable rsync

systemctl start rsync

После установки и запуска rsyncd, открываем его конфигурационный файл:

vi /etc/rsyncd.conf

И добавим в него следующие настройки: 

max connections = 10
exclude = lost+found/ .gitlab-ci.yml
dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2 *.rar *.7z *.mp3 *.jpg

[dmosk]
    path = /var/www/path/www/
    comment = Site site.ru
    uid = apache
    gid = apache
    read only = no
    list = yes
    auth users = rsync_
    secrets file = /etc/rsyncd.scrt
    #pre-xfer exec =
    #post-xfer exec =
    hosts allow = localhost 192.168.0.10
    hosts deny = *

 наибольший интерес для нас имеют следующие опции:

  • [dmosk] — название для экземпляра синхронизации. К нему мы будем обращаться при выполнении команды rsync.
  • path — путь до каталога, в котором находятся файлы для синхронизации. Это путь до папки проекта на самом веб-сервере.
  • uid/gid — пользователь и группа, от которых будет выполнена синхронизация для конкретного ресурса. Именно они будут назначены в качестве владельца файлов. 
  • auth users — проверка подлинности, вводом логина с паролем.
  • secrets file — файл, в котором размещены логин и пароль, который будет использоваться для проверки подлинности при выполнении синхронизации.
  • pre-xfer exec/post-xfer exec — команды, которые необходимо выполнить, соответственно, перед синхронизацией и после. Полезно, если наш веб-сервер необходимо перезапускать после обновления контента. Например: post-xfer exec = /usr/bin/systemctl reload uwsgi.
  • hosts allow — узлы, с которых разрешено подключение для синхронизации. Тут должен быть IP-адрес нашего сервера GitLab.

Создадим файл, в котором должны быть логин и пароль для проверки подлинности rsync:

vi /etc/rsyncd.scrt
rsync_dmosk:password

* в данном примере мы задали логин rsync_dmosk и пароль password.

Необходимо задать минимально необходимые права на файл с аутентификационными данными:

chmod 600 /etc/rsyncd.scrt

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

chown -R apache:apache /var/www/dmosk/www

Можно перезапускать сервис:

systemctl restart rsyncd || systemctl restart rsync

Также необходимо создать правила в брандмауэре для разрешения TCP-порта 873, на котором работает rsyncd.

а) для систем на базе deb-пакетов (Ubuntu, Debian):

iptables -I INPUT 1 -p tcp --dport 873 -j ACCEPT

apt-get install iptables-persistent

netfilter-persistent save

Данные настройки выполняем на всех веб-серверах. После переходим к настройке сервера GitLab.

Настройка GitLab

На стороне сервера GitLab мы должны настроить подключение к сервисам rsyncd. Для начала устанавливаем rsync.

а) для систем на базе deb:

apt-get install rsync

После создаем файл, в котором будем хранить пароль для подключения к rsyncd:

vi /etc/rsyncd.scrt

password

* это тот пароль, который мы задали в аналогичном файле на стороне веб-серверов.

Задаем права минимально необходимые для чтения файла с паролем:

chmod 640 /etc/rsyncd.scrt

Задаем в качестве группы владельца файла с паролем нашего пользователя gitlab-runner:

chown :gitlab-runner /etc/rsyncd.scrt

Создаем файл, который отправим на серверы веб в качестве теста:

touch testfile

Выполняем команду для синхронизации данного файла с веб-серверами:

for i in 1 2 3 4 5; do rsync -rult --password-file=/etc/rsyncd.scrt testfile rsync_user@web-0$i::user; done

* обратите внимание, что в моем примере веб-серверы имеют имена web-01, web-02 и так далее, поэтому, чтобы не выполнять все 5 команд по отдельности, мы используем цикл, в котором подставляем разные цифры в названия серверов.

В итоге, на стороне веб-серверов должен появиться файл testfile. Теперь остается последний шаг — настроить созданный ранее CI/CD для синхронизации.

Настройка синхронизации в CI/CD

Переходим на страницу нашего проекта и кликаем по CI/CD configuration:

Переходим к настройке CI/CD нашего проекта

Чтобы открылся редактор, кликаем по Edit:

Открываем редактор для изменения CI/CD

Или из командной строки, можно открыть на редактирование наш файл .gitlab-ci.yml:

vi .gitlab-ci.yml

Меняем содержимое нашего файла:

stages:
  - copy

copy:
  stage: copy
  script: for i in 1 2 3 4 5; do rsync -rult --delete-after --exclude=.git/ --exclude=.gitlab-ci.yml --password-file=/etc/rsyncd.scrt $CI_PROJECT_DIR/ rsync_user@web-0$i::user; done

* мы заменили наш этап test на copy. В данном примере мы выполняем команду для запуска синхронизации с веб-серверами с помощью rsync. В качестве источника данных мы используем переменную $CI_PROJECT_DIR, в которой находится наш проект. В качестве экземпляра, куда синхронизируем данные используем user.

Готово. Пробуем выполнить git push в наш проект. Данные должны разойтись по всем веб-серверам.

Дополнительно
Дополнительная информация, которая может быть полезна.

Запуск заданий внутри контейнера Docker
Выше мы рассмотрели регистрацию раннера, который будет запускать выполнение команд в системной оболочке (shell). Но если мы хотим, чтобы задания pipeline выполнялись внутри контейнера Docker, то пошагово мы должны сделать следующее.

Основные шаги:

1. Устанавливаем Docker Engine. Подробнее об этом смотрите инструкцию Установка Docker на Linux.

2. При регистрации раннера на последнем этапе, где предлагается выбрать средство запуска (Enter an executor), выбираем docker:
...
Enter an executor: parallels, virtualbox, docker+machine, docker-ssh+machine, kubernetes, custom, docker, docker-ssh, shell, ssh:
docker

3. При написании pipeline мы должны добавить опцию image с указанием образа, который хотим использовать:

...
copy:
  stage: copy
  image: user/ubuntu:latest
...

* в противном случае runner будет выполнять задания в контейнере по умлчанию (его мы указываем при регистрации на Gitlab).

Дополнительно:

По умолчанию, runner всегда будет пытаться скачать образ Docker с внешнего источника. Если на целевом компьютере, где запускается runner у нас уже есть нужный образ и мы хотим, чтобы использовался именно он, открываем файл:

vi /etc/gitlab-runner/config.toml

Среди:

[[runners]]

… находим созданный раннер (определяем по описанию, которое мы задавали при регистрации) и в нем также находим [runners.docker]. Добавим опцию pull_policy:

[[runners]]
...
  [runners.docker]
    ...
    pull_policy = "if-not-present"

Перезапустим сервис.

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

systemctl restart gitlab-runner

Возможные ошибки

status couldn execute post against certificate signed by unknown authority

Ошибка возникает при попытке зарегистрировать Runner, а при отправке curl-запроса на сервер:

curl https://gitlab.site.ru

… мы получаем сообщение о неправильном сертификате:

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

Причина: нужна полная цепочка сертификатов.

Решение: подробнее, процесс настройки https описан в инструкции Правильная настройка SSL в NGINX.

@ERROR: chroot failed

Ошибка появляется при попытке синхронизировать файлы с применением команды rsync.

Причины: 

1. На целевом сервере нет целевого каталога (указан в опции path), в который необходимо синхронизировать данные.

2. Доступ к целевой папке запрещен политикой selinux.

Решение: для обоих причин опишим соответствующие решения.

1. Проверяем наличие каталога, который мы указали в конфигурационном файле /etc/rsyncd.conf (опция path). Если его нет, создаем, например:

mkdir -p /var/www/dmosk/www

2. Для начала пробуем отключить разово selinux:

setenforce 0

Если это решило проблему, либо отключаем его совсем, либо настраиваем командами:

semanage fcontext -a -t rsync_data_t '/var/www(/.*)?'

restorecon -Rv '/var/www'

setsebool -P rsync_client on

* в данном примере мы разрешам контекст безопасности rsync_data_t для всего содержимого каталога, где находятся наши сайты.

Dial unix /var/run/docker.sock: connect: permission denied

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

Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/build?buildargs=%7B%7D&cachefrom=%5B%5D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile&labels=%7B%7D&memory=0&memswap=0&networkmode=default&rm=1&shmsize=0&t=skill%2Fdjango%3Av1&target=&ulimits=null&version=1": dial unix /var/run/docker.sock: connect: permission denied

Причина: 

Для подключения к docker engine у пользователя gitlab-runner нет прав.

Решение:

Достаточно добавить пользователя gitlab-runner в группу docker. Это можно сделать командой:

usermod -aG docker gitlab-runner

Свежие комментарии

Подписка

Лучшие статьи


Fatal error: Uncaught Error: Call to a member function have_posts() on null in /home/host1867038/the-devops.ru/htdocs/www/wp-content/themes/fox/inc/blog.php:391 Stack trace: #0 /home/host1867038/the-devops.ru/htdocs/www/wp-content/themes/fox/widgets/latest-posts/widget.php(257): fox56_blog_grid(NULL, Array) #1 /home/host1867038/the-devops.ru/htdocs/www/wp-content/themes/fox/widgets/latest-posts/register.php(33): include('/home/host18670...') #2 /home/host1867038/the-devops.ru/htdocs/www/wp-includes/class-wp-widget.php(394): Wi_Widget_Latest_Posts->widget(Array, Array) #3 /home/host1867038/the-devops.ru/htdocs/www/wp-includes/widgets.php(837): WP_Widget->display_callback(Array, Array) #4 /home/host1867038/the-devops.ru/htdocs/www/wp-content/themes/fox/inc/single.php(417): dynamic_sidebar('sidebar') #5 /home/host1867038/the-devops.ru/htdocs/www/wp-content/themes/fox/inc/single.php(136): fox56_single_sidebar() #6 /home/host1867038/the-devops.ru/htdocs/www/wp-content/themes/fox/inc/single.php(7): fox56_single_inner() #7 /home/host1867038/the-devops.ru/htdocs/www/wp-content/themes/fox/single.php(23): fox56_single() #8 /home/host1867038/the-devops.ru/htdocs/www/wp-includes/template-loader.php(106): include('/home/host18670...') #9 /home/host1867038/the-devops.ru/htdocs/www/wp-blog-header.php(19): require_once('/home/host18670...') #10 /home/host1867038/the-devops.ru/htdocs/www/index.php(17): require('/home/host18670...') #11 {main} thrown in /home/host1867038/the-devops.ru/htdocs/www/wp-content/themes/fox/inc/blog.php on line 391