Как на хостовой Ubuntu установить автономный GitLab и GitLab Runner с Shell Executor с помощью Vagrant

1. Предварительные шаги

Выполним шаги 1, 2 и 3 из этого мануала: https://fkn.ktu10.com/?q=node/17437
Нужно клонировать три репозитория: ktu-articles, ktu-backend-pack, ktu-backend-test
Подтягивать зависимости командой "composer install" не обязательно.

2. Редактирование provision.sh — вставьте в файл следующее содержимое:

#!/bin/bash
# provision.sh — автоматическая установка и настройка окружения с GitLab, Docker и GitLab Runner

# Задаём переменные
GITLAB_URL="http://192.168.56.10"
PROJECT_DIR="/var/www/ktu-articles"
RUNNER_NAME="vagrant-runner"
RUNNER_TAGS="shell,vagrant,ci"
LOGFILE="/var/log/provision.log"

# Задаём подкоманды для изменения цвета - Цвета ANSI
info='\033[1;36m' # cyan
yellow='\033[1;33m'
reset='\033[0m'

# Не взаимодействовать с пользователем при установке (для предотвращения зависаний)
export DEBIAN_FRONTEND=noninteractive

# Прервать выполнение скрипта при любой ошибке команды
set -e

# Перенаправить весь stdout и stderr в файл лога
exec > >(tee -a "$LOGFILE") 2>&1

function add_user_to_group {
  local user=$1
  local group=$2
  if ! id "$user" > /dev/null 2>&1; then
    echo "⚠️ Пользователя $user не существует!"
    exit 1
  fi
  if getent group "$group" >/dev/null; then
    if id -nG "$user" | grep -qw "$group"; then
      echo "✅ Пользователь $user уже в группе $group"
    else
      usermod -aG "$group" "$user"
      # systemctl restart $user
      echo "✅ Пользователь $user добавлен в группу $group"
    fi
  else
    echo "⚠️ Группа $group не существует"
  fi
}

echo -e "${yellow}=== Начало provisioning $(date) ===${reset}"

echo -e "${info}Проверка и восстановление dpkg, если был прерван...${reset}"
dpkg --configure -a || true

echo -e "${info}Первоначальное обновление списка пакетов...${reset}"
apt-get update -y

echo -e "${info}Установка Make и необходимых утилит...${reset}"
apt-get install -y make ca-certificates curl gnupg lsb-release linux-headers-$(uname -r) 

# --- Docker ---
if [ ! -f /etc/apt/keyrings/docker.gpg ]; then
  echo -e "${info}Установка GPG-ключа Docker...${reset}"
  install -m 0755 -d /etc/apt/keyrings
  curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
    | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  chmod a+r /etc/apt/keyrings/docker.gpg
else
  echo "✅ GPG ключ Docker уже установлен"
fi

if [ ! -f /etc/apt/sources.list.d/docker.list ]; then
  echo -e "${info}Добавление репозитория Docker...${reset}"
  echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
    https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
    | tee /etc/apt/sources.list.d/docker.list > /dev/null
  apt-get update -y
else
  echo "✅ Репозиторий Docker уже добавлен"
fi

echo -e "${info}Установка Docker...${reset}"
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin

# пауза, чтобы ssh успел восстановиться
sleep 10

echo -e "${info}Проверка или добавление пользователя vagrant в docker группу...${reset}"
add_user_to_group vagrant docker

# --- GitLab ---
if ! command -v gitlab-ctl > /dev/null 2>&1; then
  echo -e "${info}Установка зависимостей GitLab...${reset}"
  apt-get install -y openssh-server tzdata postfix
  
  echo -e "${info}Добавление официального репозитория GitLab...${reset}"
  curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | bash
  
  echo -e "${info}Установка GitLab CE...${reset}"
  # EXTERNAL_URL="$GITLAB_URL" apt-get install -y gitlab-ce
  apt-get install -y gitlab-ce
else
  echo "✅ GitLab уже установлен"
fi

echo -e "${info}Оптимизация / настройка gitlab.rb с отключёнными службами...${reset}"
cat <<EOF > /etc/gitlab/gitlab.rb
external_url "$GITLAB_URL"
nginx['listen_port'] = 80
nginx['listen_https'] = false
# Отключаем Auto DevOps по умолчанию
# gitlab_rails['initial_auto_devops_enabled'] = false
puma['worker_processes'] = 1
puma['worker_timeout'] = 60
sidekiq['max_concurrency'] = 5
prometheus_monitoring['enable'] = false
alertmanager['enable'] = false
node_exporter['enable'] = false
redis_exporter['enable'] = false
postgres_exporter['enable'] = false
EOF

echo -e "${info}Применение конфигурации GitLab...${reset}"
RUBYOPT='-W0' gitlab-ctl reconfigure | tail -n 21
gitlab-ctl start

# Отключаем Auto DevOps гарантированно
echo -e "${info}Отключение Auto DevOps...${reset}"
gitlab-rails runner "ApplicationSetting.current.update(auto_devops_enabled: false)" \
  || echo "⚠️ Не удалось отключить Auto DevOps"

echo -e "${info}Ожидание запуска GitLab...${reset}"
timeout 300 bash -c '\
  until curl -s http://localhost/users/sign_in | grep -q GitLab; do \
    echo "Ждём загрузки GitLab..."; \
    sleep 5; \
  done'

echo -e "${info}Установка пароля root-пользователя...${reset}"
# root-пароль 'mytest12' задан только для локальной разработки. Не используйте в продакшене!
gitlab-rails runner "
  u = User.find_by_username('root');
  u.password = 'mytest12';
  u.password_confirmation = 'mytest12';
  u.save!
" || echo "⚠️ Не удалось установить пароль root!"

echo "✅ Установка или настройка GitLab завершена. Доступен по адресу: ${GITLAB_URL}"
echo -e "${info}логин: root${reset}"
echo -e "${info}пароль: mytest12${reset}"

# --- PHP + Composer ---
echo -e "${info}Установка PHP 8.2...${reset}"
add-apt-repository ppa:ondrej/php -y
apt-get update -y
apt-get install -y php8.2-cli php8.2-curl php8.2-dom php8.2-xml unzip zip
update-alternatives --set php /usr/bin/php8.2
 
echo -e "${info}Установка Composer глобально...${reset}"
if ! command -v composer >/dev/null 2>&1; then
  curl -sS https://getcomposer.org/installer -o composer-setup.php
  php composer-setup.php --install-dir=/usr/local/bin --filename=composer
  rm composer-setup.php
  if [ -f /usr/local/bin/composer ]; then
    chmod +x /usr/local/bin/composer
  else
    echo "⚠️ Ошибка: файл /usr/local/bin/composer не найден после установки!"
    exit 1
  fi
else
  echo "✅ Composer уже установлен"
fi

echo "✔ Composer версия: $(composer --version)"
sleep 20

# --- Установка зависимостей проекта (~12 минут) ---
echo -e "${info}Установка зависимостей проекта с помощью Composer...${reset}"
if [ -d "$PROJECT_DIR" ]; then
  su - vagrant -c "\
    cd $PROJECT_DIR && \
    COMPOSER_PROCESS_TIMEOUT=900 composer install --no-interaction --prefer-dist"
else
  echo "⚠️ Проектная директория $PROJECT_DIR не найдена"
  exit 1
fi

# --- GitLab Runner ---
if ! command -v gitlab-runner > /dev/null; then
  echo -e "${info}Установка GitLab Runner...${reset}"
  curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | bash
  apt-get install -y gitlab-runner
else
  echo "✅ GitLab Runner уже установлен"
fi

echo -e "${info}Проверка или добавление пользователя gitlab-runner в docker группу...${reset}"
add_user_to_group gitlab-runner docker

echo -e "${info}Изменение владельца Composer и проверка его доступности для gitlab-runner...${reset}"
if [ -f /usr/local/bin/composer ]; then
  chown gitlab-runner:gitlab-runner /usr/local/bin/composer
  sudo -u gitlab-runner /usr/local/bin/composer --version || {
    echo "⚠️ Composer недоступен от имени gitlab-runner"
    exit 1
  }
else
  echo "⚠️ /usr/local/bin/composer отсутствует, пропускаем chown и проверку доступа"
fi

echo -e "${info}Удаление всех зарегистрированных раннеров, если таковые имеются...${reset}"
gitlab-runner unregister --all-runners || true

echo -e "${info}Получение токена для GitLab Runner...${reset}"
REG_TOKEN=$(gitlab-rails runner -e production \
  "puts Gitlab::CurrentSettings.current_application_settings.runners_registration_token" \
  2>/dev/null || echo "")
if [ -z "$REG_TOKEN" ]; then
  echo "⚠️  Не удалось получить токен!"
  exit 1
else
  echo "✅ Registration token: $REG_TOKEN"
fi

echo -e "${info}Токен получен. Регистрируем GitLab Runner...${reset}"
gitlab-runner register --non-interactive \
  --url "$GITLAB_URL/" \
  --registration-token "$REG_TOKEN" \
  --executor shell \
  --description "$RUNNER_NAME" \
  --tag-list "$RUNNER_TAGS" \
  --run-untagged=true \
  --locked=false

echo -e "${info}Запуск сервиса GitLab Runner...${reset}"
systemctl enable gitlab-runner
systemctl restart gitlab-runner

echo -e "${yellow}=== Provisioning завершён $(date) ===${reset}"

3. Запуск виртуальной машины

Из папки проекта (my-vagrant) выполним команду:
если Vagrant-проект был создан сейчас и ранее никогда не запускался:

vagrant up

после выполнения команды свободное место на диске уменьшится на ~11ГБ
время выполнения команды может занять около одного часа
если Vagrant-проект существовал ранее и сейчас только внесли изменения в Vagrantfile и provision.sh:

vagrant up --provision

если предыдущая ВМ была удалена, то после выполнения команды свободное место на диске уменьшится на ~8ГБ

Если всё прошло успешно, из браузера из хостовой системы GitLab будет доступен по адресу:
http://192.168.56.10 (не сразу, может потребоваться несколько минут)
Логин "root" пароль "mytest12". Не используйте этот пароль в продакшене!

4. Настройка GitLab

Сначала убедимся, что в папке микросервиса ktu-articles нет файла .gitlab-ci.yml
Добавим второй remote для репозитория микросервиса ktu-articles, чтобы оставить обе платформы — и Bitbucket и GitLab:
Из папки репозитория ktu-articles:

git remote add gitlab http://192.168.56.10/root/ktu-articles.git
git push -u gitlab master

Подробнее здесь (6-ой шаг): https://fkn.ktu10.com/?q=node/17437
После этого наш проект должен быть доступен по адресу:
http://192.168.56.10/root/ktu-articles

5. Создание .gitlab-ci.yml в микросервисе ktu-articles

Если нужно, чтобы GitLab запускал CI/CD (например, пайплайны сборки или тестов), создайте в корне микросервиса файл .gitlab-ci.yml командой:

touch .gitlab-ci.yml

и вставьте в файл следующее содержимое:

stages:
  - test

# Кэш для composer и vendor — ускоряет install
cache:
  key:
    files:
      - composer.lock
  paths:
    - vendor/
    - ~/.composer/cache/

variables:
  GIT_STRATEGY: none  # Отключает клонирование проекта
  PROJECT_DIR: "/var/www/ktu-articles"
  COMPOSE_FILE: "docker/docker-compose.yml"
  COMPOSER_FLAGS: "--prefer-dist --no-interaction --no-progress --optimize-autoloader"
  SERVICE_NAME: "ktu-articles_php"

functional_tests:
  stage: test
  tags:
    - shell
# rules:
#   - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
#     when: always
#   - when: never
  before_script:
    - |
      echo "Работаем с хостовой директории проекта: $PROJECT_DIR"
      cd "$PROJECT_DIR"
    - |
      echo "Проверяем, запущены ли контейнеры"
      if ! docker compose -f "$COMPOSE_FILE" ps | grep -q "$SERVICE_NAME"; then
        echo "Контейнеры не запущены, поднимаем...";
        docker compose -f "$COMPOSE_FILE" up -d --wait;
      else
        echo "✅ Контейнеры уже работают, пропускаем запуск.";
      fi
    - |
      if ! docker exec "$SERVICE_NAME" test -f /var/www/project/vendor/autoload.php; then
        echo "Устанавливаем зависимости через Composer"
        docker exec "$SERVICE_NAME" composer install $COMPOSER_FLAGS
      else
        echo "✅ Зависимости уже установлены. Пропускаем composer install."
      fi

  script:
    - echo "Запускаем функциональные тесты"
    - docker exec ktu-articles_php vendor/bin/codecept run functional

6. Проверка работоспособности пайплайнов

Из папки репозитория ktu-articles добавим все изменения, закоммитим и запушим:

git add . && git commit . -m "Add .gitlab-ci.yml" && git push gitlab master

Посмотреть пайплайны можно на странице:
http://192.168.56.10/root/ktu-articles/-/pipelines
Выполнение первого пайплайна может занять до 20 минут.

7. Если нужно, чтобы GitLab запускал пайплайны только при Merge-request

Создадим новую ветку (название может быть любым), в которой будет вестись разработка:

git checkout -b test/pipeline

Раскомментируем директиву "rules" в файле .gitlab-ci.yml (замените символы # на знаки пробела)
Сохраним файл, добавим изменения и сделаем коммит:

git add .gitlab-ci.yml
git commit -m "Отредактирован файл .gitlab-ci.yml"

Установим адрес удалённого репозитория на локальный GitLab:

git remote set-url origin http://192.168.56.10/root/ktu-articles.git

Отправим новую ветку в GitLab:

git push gitlab test/pipeline

Логин "root" пароль "mytest12". Не используйте этот пароль в продакшене!

Как добавить изменения из своей ветки в основную, объединение слияние веток:
https://fkn.ktu10.com/?q=node/11807

Посмотреть мердж-реквесты можно на странице:
http://192.168.56.10/root/ktu-articles/-/merge_requests