Как стать автором
Обновить
КОРУС Консалтинг
Заряжаем бизнес инновациями

GitLab CI: Первый пайплайн на Shared Runner

Уровень сложностиПростой
Время на прочтение9 мин
Количество просмотров2.6K

Привет, меня зовут Ярослав, я Backend‑разработчик в отделе Битрикс24 CRM Корус консалтинг. Не так давно я впервые занимался настройкой CI/CD для Битрикс‑проектов, поэтому сегодня хочу поделиться шагами, которые помогут запустить свой первый пайплайн. Статья подойдёт для полных новичков в теме поставки кода.

Содержание
  1. Введение

  2. Подготовка сервера

  3. Настройка переменных окружения

  4. Написание скрипта пайплайна

  5. Запуск скрипта

  6. Ошибки и способы их решений

  7. Подведение итогов

Введение

Описание понятия CI и пользу от его применения найти несложно, здесь я опишу процесс работы системы с использованием непрерывной интеграции.

В ходе описания процесса столкнёмся с определениями:

  • Задача (job) — конкретная операция, описанная с помощью скрипта.

  • Этап (stage) — набор задач, которые можно сгруппировать по общей цели.

  • Пайплайн (pipeline) — последовательность этапов, каждый из которых объединяет несколько задач. Выполняется последовательно.

  • Раннер (runner) — приложение (агент), которое автоматически выполняет процессы CI/CD.

  1. Разработчик разрабатывает новый функционал или исправляет ошибки в коде.

  2. Коммитит изменения, пушит их в репозиторий, и создаёт Merge Request.

  3. Запускается пайплайн. Исходя из его конфигурации, запускаются этапы. Этапы выполняются по очереди, и содержат задачи, которые выполняются параллельно.

  4. При возникновении ошибки на каком‑либо из этапов, пайплайн завершается с ошибкой.

  5. При успешном прохождении всех этапов, пайплайн завершается успешно.

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

  • сборка;

  • тестирование (автотесты и проверка линтерами);

  • развёртывание кода на сервер.

Сегодня рассмотрим только последний этап.

Почему GitLab CI?

Тут всё просто — в компании используется GitLab, поэтому выбираем готовый инструмент.

Почему Shared Runner?

Есть два способа настройки CI/CD: с установкой GitLab Runner на сервер с проектом, и с использованием Shared Runner.

Сегодня опишем способ с использованием Shared Runner — в таком случае job'ы выполняются на сервере с GitLab, либо на отдельном сервере для пайплайна. Он не требует доступа root к серверу с репозиторием, доступен для всех проектов, не нуждается в ручной установке и может использоваться без дополнительных настроек.

Есть ситуации, когда кажется, что Shared Runner не подойдёт — например, когда сервер находится за VPN или внутри клиентской корпоративной сети. Как быть в таком случае также разберём.

Подготавливаем сервер

Мы используем self‑hosted GitLab на своём сервере с CentOS Stream 9, дальнейшие команды будут выполняться на нём.

Шаг 1: Регистрируем раннер (подробно)

Скачиваем бинарник GitLab Runner и кладём его в директорию для общесистемных доступных скриптов (/usr/local/bin):

sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64

Делаем файл исполняемым:

sudo chmod +x /usr/local/bin/gitlab-runner

Создаём пользователя для раннера:

sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash

Устанавливаем GitLab Runner как службу, которая будет работать от имени созданного пользователя:

sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner

Регистрируем раннер в GitLab:

Предварительно, нужно получить токен регистрации через веб‑интерфейс GitLab (Settings CI/CD Runners).

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

Находим раздел Runners и нажимаем New project runner:

Указываем тег раннера. В нашем случае shared, но можно и любой другой:

Обратим внимание на опции:

  • Run untagget jobs — раннер будет использоваться по умолчанию, без указания тега в скрипте.

  • Paused — Остановит раннер после создания.

  • Protected — раннер будет выполнять задачи пайплайна только в защищённых ветхах.

  • Lock to current projects — раннер нельзя будет использовать в других проектах.

  • Maximum job timeout — максимальное время выполнения раннера в секундах, после которого он остановится.

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

Далее регистрируем раннер командой, где:

{url} — URL вашего GitLab

{token} — полученный токен

sudo gitlab-runner register   --non-interactive   --url "{url}"   --registration-token "{token}"   --executor "shell"   --description "Global Runner on Server"   --tag-list "SharedRunner"   --run-untagged="true"

Проверяем, что раннер зарегистрирован правильно:

sudo gitlab-runner verify

Запускаем раннер:

sudo gitlab-runner start
sudo gitlab-runner list

Разрешаем пользователю gitlab-runner выполнять sudo без пароля (иначе могут быть проблемы с правами)

echo 'gitlab-runner ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/gitlab-runner

В нашем примере пайплайн будет устанавливать VPN‑соединение перед тем, как подключаться к серверу, поэтому дополнительно установим SSTP‑клиент:

Скачиваем SSTP‑клиент и выполняем команды для настройки и установки:

sudo curl -LO https://sourceforge.net/projects/sstp-client/files/latest/download
cd sstp-client-*
./configure && make && make install

Проверяем, что SSTP-клиент установлен:

sstpc --version

Настраиваем переменные окружения

В скрипте пайплайна будут использоваться данные, которые будут меняться в зависимости от проекта. Их стоит вынести в переменные, которые можно добавить через веб‑интерфейс. Создадим переменные (Settings CI/CD Variables):

Переходим на страницу настроек CI/CD для репозитория (см. выше).

Находим раздел Variables и нажимаем Add variable:

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

Создаём следующие переменные:

DEP_KEY — ключ для подключения по ssh.

DEP_USER — имя пользователя для подключения по ssh.

DIR_TO_DEPLOY — директория с репозиторием проекта на сервере.

MAIN_HOST — адрес сервера продуктив.

STAGE_HOST — адрес тестового сервера.

REPOSITORY_URL — ссылка для выгрузки изменений в репозитории (в нашем случае по ssh)

SSTP_HOST — адрес подключения VPN.

SSTP_USER — имя пользователя для подключения VPN.

SSTP_PASSWORD — пароль для подключения VPN.

Добавляем ключи в Deploy Keys

Для того, чтобы можно было обновлять репозиторий по ssh на удалённых серверах, нужно добавить ssh‑ключ сервера в Deploy Keys. Для этого:

Подключаемся к серверу с проектом, создаём пару ключей:

ssh-keygen -t ed25519 -C your_email@example.com
cat ~/.ssh/id_ed25519.pub

Команда гененрирует приватный и публичный ключ используя алгоритм ed25 519, а флаг ‑C позволяет указать имя или почту пользователя.

Копируем публичный ключ и добавляем его в Deploy Keys в настройках репозитория (Settings → Repository → Deploy keys).

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

Находим раздел Deploy Keys и нажимаем Add new key:

Указываем название и ключ, после чего нажимаем Add key:

Пишем скрипт пайплайна

Для конфигурации GitLab CI используется файл .gitlab-ci.yml в корне репозитория. Подробнее о формате YAML, синтаксисе и основных возможностях можно прочитать тут. Начнём со структуры:

stages:
  - deploy

variables:
  GIT_STRATEGY: none

deploy-job:
  stage: deploy
  tags:
    - SharedRunner
  rules:
    - if: $CI_COMMIT_REF_NAME == "main"
    - if: $CI_COMMIT_REF_NAME == "stage"
  script:

stages — определяем список этапов, которые будут выполняться в пайплайне. Здесь всего один этап — развёртывание (deploy).

variables — устанавливаем переменные окружения для пайплайна. Переменная GIT_STRATEGY: none отключает клонирование репозитория перед запуском задачи. Помимо GIT_STRATEGY, скрипт поддерживает и другие предопределённые переменные.

deploy-job — наша единственная задача, которая будет выполняться на этапе deploy.

tags — указываем, что задача должна выполняться на раннере с тегом SharedRunner. В нашем случае это необязательно, так как при регистрации раннера мы указали ‑run‑untagged=»true». В этом случае, раннер будет выполнять задачи даже без тегов.

rules — указываем, что задача должна выполняться только для веток stage или main.

script — здесь опишем список команд в рамках задачи.

Добавим блоки для установки VPN‑соединения и подключения к серверу по ssh:

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

.setup_pipeline_vpn: &setup_pipeline_vpn
  - sudo nohup timeout 60s sstpc --log-level 2 --log-stderr "$SSTP_HOST" user "$SSTP_USER" password "$SSTP_PASSWORD" noauth --cert-warn &
  - sleep 5
  - "if ! ip a | grep 'ppp0'; then echo 'Error: VPN connection is not established!'; exit 1; fi"
  - sudo ip route add "$DEP_HOST" dev ppp0

В этом блоке:

  1. Запускаем команду подключения к VPN в фоновом режиме.

  2. Ждём 5 секунд, чтобы VPN успел подняться.

  3. Проверяем, что соединение установлено.

  4. Добавляем VPN‑маршрут к хосту, с которым будем работать.

.setup_pipeline_ssh_key: &setup_pipeline_ssh_key
  - "which ssh-agent || ( dnf install -y openssh-clients )"
  - eval $(ssh-agent -s)
  - '[ -z "$DEP_KEY" ] && echo "ERROR: SSH key is missing!" && exit 1'
  - echo "$DEP_KEY" | tr -d '\r' | ssh-add -
  - mkdir -p ~/.ssh
  - chmod 700 ~/.ssh
  - echo "StrictHostKeyChecking no" > ~/.ssh/config
  - ssh-keyscan -H "$DEP_HOST" >> ~/.ssh/known_hosts

В этом блоке:

  1. Устанавливаем ssh‑agent, если не установлен.

  2. Добавляем приватный ключ в ssh‑agent.

  3. Отключаем проверку подлинности хостов (чтобы не ждать подтверждения при первом подключении).

  4. Добавляем удалённый сервер в known_hosts.

.setup_pipeline_vpn: &setup_pipeline_vpn и setup_pipeline_ssh_key: &setup_pipeline_ssh_key — это «якоря» в формате YAML. Их можно использовать в разных участках скрипта с помощью *setup_pipeline_vpn и *setup_pipeline_ssh_key.

Наконец, напишем скрипт задачи:

script:
    - |
      if [ "$CI_COMMIT_REF_NAME" == "stage" ]; then
        export DEP_HOST="$STAGE_HOST"
      elif [ "$CI_COMMIT_REF_NAME" == "main" ]; then
        export DEP_HOST="$MAIN_HOST"
      else
        echo "Branch $CI_COMMIT_REF_NAME is not handled"
        exit 1
      fi
    - *setup_pipeline_vpn
    - *setup_pipeline_ssh_key
    - ssh $DEP_USER@$DEP_HOST 'ssh-keyscan -H {Хост сервера с GitLab} >> ~/.ssh/known_hosts'
    - ssh $DEP_USER@$DEP_HOST "cd ${DIR_TO_DEPLOY} && git remote set-url origin $REPOSITORY_URL"
    - ssh $DEP_USER@$DEP_HOST "cd ${DIR_TO_DEPLOY} && git fetch origin && git reset --hard origin/$CI_COMMIT_REF_NAME"
    - sudo pkill -SIGINT sstpc || echo "The VPN has been disabled"

В скрипте:

  1. Выбираем хост (тест/прод) в зависимости от ветки (stage/main).

  2. Запускаем блок с установкой VPN‑соединения.

  3. Запускаем блок с настройкой ssh.

  4. Добавляем удалённый хост GitLab в known_hosts на сервере.

  5. Переходим в директорию с проектом и устанавливаем URL для удалённого репозитория.

  6. Обновляем репозиторий изменениями из нужной ветки.

  7. Останавливаем VPN‑соединение.

Хост сервера с GitLab — заменить на свой сервер.

Полный код .gitlab-ci.yml:

stages:
  - deploy

variables:
  GIT_STRATEGY: none

.setup_pipeline_vpn: &setup_pipeline_vpn
  - sudo nohup timeout 60s sstpc --log-level 2 --log-stderr "$SSTP_HOST" user "$SSTP_USER" password "$SSTP_PASSWORD" noauth --cert-warn &
  - sleep 5
  - "if ! ip a | grep 'ppp0'; then echo 'Error: VPN connection is not established!'; exit 1; fi"
  - sudo ip route add "$DEP_HOST" dev ppp0

.setup_pipeline_ssh_key: &setup_pipeline_ssh_key
  - "which ssh-agent || ( dnf install -y openssh-clients )"
  - eval $(ssh-agent -s)
  - '[ -z "$DEP_KEY" ] && echo "ERROR: SSH key is missing!" && exit 1'
  - echo "$DEP_KEY" | tr -d '\r' | ssh-add -
  - mkdir -p ~/.ssh
  - chmod 700 ~/.ssh
  - echo "StrictHostKeyChecking no" > ~/.ssh/config
  - ssh-keyscan -H "$DEP_HOST" >> ~/.ssh/known_hosts

deploy-job:
  stage: deploy
  tags:
    - SharedRunner
  rules:
    - if: $CI_COMMIT_REF_NAME == "main"
    - if: $CI_COMMIT_REF_NAME == "stage"
  script:
    - |
      if [ "$CI_COMMIT_REF_NAME" == "stage" ]; then
        export DEP_HOST="$STAGE_HOST"
      elif [ "$CI_COMMIT_REF_NAME" == "main" ]; then
        export DEP_HOST="$MAIN_HOST"
      else
        echo "Branch $CI_COMMIT_REF_NAME is not handled"
        exit 1
      fi
    - *setup_pipeline_vpn
    - *setup_pipeline_ssh_key
    - ssh $DEP_USER@$DEP_HOST 'ssh-keyscan -H {Хост сервера с GitLab} >> ~/.ssh/known_hosts'
    - ssh $DEP_USER@$DEP_HOST "cd ${DIR_TO_DEPLOY} && git remote set-url origin $REPOSITORY_URL"
    - ssh $DEP_USER@$DEP_HOST "cd ${DIR_TO_DEPLOY} && git fetch origin && git reset --hard origin/$CI_COMMIT_REF_NAME"
    - sudo pkill -SIGINT sstpc || echo "The VPN has been disabled"

Запускаем пайплайн

Создадим ветку gitlab‑ci, закоммитим .gitlab-ci.yml, и создадим Merge request в ветку stage. Ждём, пока отработает пайплайн.

Посмотреть лог выполнения можно во вкладке Build→ Pipelines, или нажав на блок с пайплайном на странице Merge request:

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

Наблюдаем лог выполнения задачи:

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

Ради эксперимента, намеренно добавим в скрипт ошибку.

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

Ошибки и способы их решения

Значения переменных недоступны

При создании переменных, используемых в пайплайнах для незащищённых веток (stage, dev, test), нужно снимать флаг Protect variable. Это позволит использовать её во всех ветках, а не только в main.

При запуске Runner зависает на Initializing executor providers...

Ошибка может заключаться в недостаточных правах для пользователя gitlab‑runner.
Для решения нужно авторизоваться под root, и изменить владельца директории gitlab‑runner.

cd /home
chown -R gitlab-runner: gitlab-runner 

Итого

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

В будущем, скрипт можно улучить, добавив в него:

  1. Обновление изменений в подмодулях.

  2. Выполнение unit‑тестов.

  3. Подключение к VPN с использованием протоколов, помимо SSTP.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Используете ли вы GitLab CI/CD в компании?
61.54% Да, активно – пайплайны настроены, процесс отлажен8
15.38% Да, частично – только для некоторых проектов или тестов2
7.69% Планируем внедрить1
15.38% Нет, не используем2
Проголосовали 13 пользователей. Воздержался 1 пользователь.
Теги:
Хабы:
+4
Комментарии2

Публикации

Информация

Сайт
career.korusconsulting.ru
Дата регистрации
Дата основания
2000
Численность
1 001–5 000 человек
Представитель
Maria_Lenkina