Apps: "GitLab CE/EE", "GitLab Runner".
Задача: установить и предварительно настроить сервер "GitLab", предназначенный для хранения Git-репозиториев и управления простейшими процессами CI/CD, с последующей установкой и подключением агента исполнения "GitLab Runner".
Предварительная подготовка.
Настройки по умолчанию для компонентов "GitLab" таковы, что даже для работы вхолостую этому комбайну требуется не менее двух процессоров и двух гигабайт оперативной памяти. На практике нужно отталкиваться от 4GB для RAM, или наблюдение за утилизацией "свопа" станет обычным делом.
Объём выделяемого дискового пространства на первый взгляд определяется планируемым размером и приростом git-репозиториев, но на практике гораздо большую долю забирают служебные компоненты "GitLab", так что на старте есть смысл выделить не менее 50-100GB для файловой системы. Также следует иметь в виду, что операции чтения/записи множества мелких файлов, из которых состоят git-репозитории, весьма сильно нагружают устройства хранения, так что на практике HDD со скоростью вращения шпинделя менее 7200 RMP не обеспечат приемлемой отзывчивости сервиса.
Установка сервиса "GitLab CE (Community Edition)".
Перечень доступных для установки дистрибутивов, их различия и способы установки весьма хорошо раскрыты в официальной документации на сайте проекта. Далее просто последовательность простых шагов, как выжимка из полного руководства.
Прежде всего обеспечим наличие в системе утилит, необходимых для ручного подключения дополнительных APT-репозиториев:
# apt-get install apt-transport-https ca-certificates curl gnupg2 debian-archive-keyring
Для формирования ссылки на APT-репозиторий на сайте разработчиков есть специальный bash-скрипт:
# curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | bash
Для тех, кто очень не любит запускать напрямую загружаемые из интернета скрипты, далее приводится раскладка ручных операций по добавлению репозитория дистрибутивов "GitLab".
Скачаем и применим PGP-ключ, которым подписано содержимое APT-репозитория:
# curl -fsSL https://packages.gitlab.com/gitlab/gitlab-ce/gpgkey | apt-key add -
Создаём выделенный конфигурационный файл с описанием подключаемого APT-репозитория:
# echo -e "# Official APT-repository GitLab-CE\ndeb [arch=amd64] https://packages.gitlab.com/gitlab/gitlab-ce/$(. /etc/os-release; echo "$ID")/ $(lsb_release -cs) main\ndeb-src [arch=amd64] https://packages.gitlab.com/gitlab/gitlab-ce/$(. /etc/os-release; echo "$ID")/ $(lsb_release -cs) main" >> /etc/apt/sources.list.d/gitlab-ce.list
Обновляем сведения о доступном программном обеспечении и устанавливаем "GitLab":
# apt-get update && apt-get install gitlab-ce openssh-server
Перед установкой имеется возможность указать полный URL будущего сервера, и тогда он будет автоматически настроен оркестратором "Omnibus" на приём подключений по заданному адресу. Если указать в префиксе протокола "https", то инсталлятор сделает попытку запросить для него сертификаты "Let`s Encrypt".
Запускаем загрузку и развёртывание из APT-пакетов "GitLab":
# EXTERNAL_URL="https://gitlab.example.net" apt-get install gitlab-ce
Если не объявлять переменную окружения "EXTERNAL_URL", то соответствующие настройки можно будет произвести вручную, после загрузки и развёртывания "GitLab" из дистрибутива.
# vi /etc/gitlab/gitlab.rb
....
external_url 'https://gitlab.example.net'
....
external_url 'https://gitlab.example.net'
....
# gitlab-ctl reconfigure
При первом обращении через web-интерфейс "GitLab" попросит установить пароль для администратора сервиса - пользователя "root" (не имеющего никакого отношения к суперпользователю несущей операционной системы, разумеется).
Настройка HTTPS с "Let`s Encrypt" в "GitLab".
Как уже упоминалось выше, если при инсталляции "GitLab" в переменной "внешнего URL" указать префикс "https://", то будет включен режим работы на автоматически запрашиваемых SSL-сертификатах "Let`s Encrypt". Раньше для этого приходилось предпринимать специальные усилия.
Автоматически запрашиваемый сертификат также автоматически и обновляется, запускаясь внутренним планировщиком "GitLab" по расписанию, заданному следующими параметрами (в примере каждый седьмой день месяца, в 12:30):
....
letsencrypt['auto_renew_hour'] = "12"
letsencrypt['auto_renew_minute'] = "30"
letsencrypt['auto_renew_day_of_month'] = "*/7"
....
letsencrypt['auto_renew_hour'] = "12"
letsencrypt['auto_renew_minute'] = "30"
letsencrypt['auto_renew_day_of_month'] = "*/7"
....
Принудительно, не по расписанию, обновить "Let`s Encrypt" сертификаты в "GitLab" можно или запустив полную пересборку конфигурации, или точечной командой:
# gitlab-ctl renew-le-certs
В документации "GitLab" про настройку SSL весьма подробно написано.
Настройка HTTPS в "GitLab" со статичным SSL-сертификатом.
Если имеется выделенный SSL-сертификат, то можно применить в "GitLab" его, задав необходимые параметры в основном конфигурационном файле "Omnibus":
....
# Указываем URL, на котором GitLab принимает подключения
external_url "https://gitlab.example.net"
# Активируем принудительное перенаправление с HTTP на HTTPS
nginx['redirect_http_to_https'] = true
# Указываем месторасположение ключей SSL-сертификата
nginx['ssl_certificate'] = "/etc/ssl/gitlab/gitlab.example.net.crt"
nginx['ssl_certificate_key'] = "/etc/ssl/gitlab/gitlab.example.net.key"
# Отключаем ненужный "Let`s Encrypt"
letsencrypt['enable'] = false
....
# Указываем URL, на котором GitLab принимает подключения
external_url "https://gitlab.example.net"
# Активируем принудительное перенаправление с HTTP на HTTPS
nginx['redirect_http_to_https'] = true
# Указываем месторасположение ключей SSL-сертификата
nginx['ssl_certificate'] = "/etc/ssl/gitlab/gitlab.example.net.crt"
nginx['ssl_certificate_key'] = "/etc/ssl/gitlab/gitlab.example.net.key"
# Отключаем ненужный "Let`s Encrypt"
letsencrypt['enable'] = false
....
Применяем изменения:
# gitlab-ctl reconfigure
Нюансы настройки HTTPS в "GitLab" с "самоподписанным" SSL-сертификатом.
Если нет полностью валидного SSL-сертификата, проверяемого по всей цепочке подписей удостоверяющих центров в интернете - например, в случае запуска сервиса в сугубо локальной инфраструктуре - возможно применение "самоподписанного (self-signed)" сертификата.
В этом деле есть нюансы, так как "GitLab" не просто сайт, а комбайн из разномастных компонентов, которые связываются между собой по сети. Нужно добиться того, чтобы все участники схемы корректно принимали SSL-сертификат, подлинность которого обычным путём проверить невозможно.
Заготовим файл с "цифровым шумом", который потребуется в полуавтоматизированной процедуре далее и создадим директорию для сертификатов:
# openssl rand -out /root/.rnd -hex 256
# mkdir -p /etc/ssl/gitlab
# cd /etc/ssl/gitlab
# mkdir -p /etc/ssl/gitlab
# cd /etc/ssl/gitlab
Создаём закрытую и открытую часть простейшего сертификата ("самоподписанного") для сервера "GitLab":
# openssl req \
-subj "/C=RU/ST=State/L=City/O=Example, Inc./OU=GitLab/CN=gitlab.example.net" \
-newkey rsa:4096 -nodes -sha256 -keyout ./gitlab.example.net-ss.key \
-x509 -days 3650 -out ./gitlab.example.net-ss.crt
-subj "/C=RU/ST=State/L=City/O=Example, Inc./OU=GitLab/CN=gitlab.example.net" \
-newkey rsa:4096 -nodes -sha256 -keyout ./gitlab.example.net-ss.key \
-x509 -days 3650 -out ./gitlab.example.net-ss.crt
Копируем публичную часть сертификата в специальную директорию в конфигурации "GitLab", предназначенную для размещения сертификатов, которым он доверяет (таким образом мы добьёмся того, что большая часть его компонентов без возражений подключались к другим его же компонентам):
# cp /etc/ssl/gitlab/gitlab.example.net-ss.crt /etc/gitlab/trusted-certs/
К подкладыванию публичной части "самоподписанного (self-signed)" SSL-сертификата "GitLab" в перечень доверенных в дальнейшем придётся прибегнуть при настройке подключения почти всех автоматизированных сервисов, вроде "GitLab Runner"-ов и клиентов "Docker Registry".
Применяем изменения:
# gitlab-ctl reconfigure
Проверяем, правильным ли сертификатом отвечает "GitLab":
$ echo QUIT | openssl s_client -connect gitlab.example.net:443 | openssl x509 -noout -text | less
Первичная настройка web-интерфейса.
По умолчанию "GitLab" запускается в режиме доступа только для зарегистрированных в нём пользователей и в дополнительном усилении настроек безопасности нет острой необходимости, но ещё чуть "гайки подзатянуть" не помешает:
Admin -> Settings -> General:
Visibility and access controls:
Default branch protection: Fully protected, only maintainers can push (default)
Default project creation protection: Maintainers only
Default project visibility: Private
Default snippet visibility: Private
Default group visibility: Private
Restricted visibility levels: Internal, Public
Enabled Git access protocols: Both SSH and HTTPS
Account and limit:
Gravatar enabled: off
User OAuth applications: off
Sign-up restrictions:
Sign-up enabled: off
Visibility and access controls:
Default branch protection: Fully protected, only maintainers can push (default)
Default project creation protection: Maintainers only
Default project visibility: Private
Default snippet visibility: Private
Default group visibility: Private
Restricted visibility levels: Internal, Public
Enabled Git access protocols: Both SSH and HTTPS
Account and limit:
Gravatar enabled: off
User OAuth applications: off
Sign-up restrictions:
Sign-up enabled: off
Отключаем ненужные обычно сборы статистики:
Admin -> Settings -> Metrics and profiling:
Usage statistics:
Enable version check: off
Enable usage ping: off
Instance Statistics visibility: Only admins
Usage statistics:
Enable version check: off
Enable usage ping: off
Instance Statistics visibility: Only admins
Выключаем обычно ненужное совместное использование CI/CD-ресурсов:
Admin -> Settings -> CI/CD:
Default to Auto DevOps pipeline for all projects: off
Enable shared runners for new projects: off
Default to Auto DevOps pipeline for all projects: off
Enable shared runners for new projects: off
Устанавливаем день начала недели на европейский лад:
Admin -> Settings -> Preferences:
Localization:
Default first day of the week: Monday
Localization:
Default first day of the week: Monday
Изменения в стратегии хранения репозиториев.
В процессе развития "GitLab" его разработчики пришли к необходимости горизонтального масштабирования, включая не только организацию согласованной работы нескольких экземпляров web-приложений, но и обеспечение удобного доступа к распределённому хранилищу файлов git-репозиториев. Вначале для обращения к удалённым сетевым ресурсам использовалась NFS, но вскоре была разработана и внедрена специализированная подсистема "Gitalty", предоставляющая собой сервис RPC (Remote Procedure Call) для операций с произвольным набором git-репозиториев на инстансах "GitLab". Теперь это данность - насколько я понимаю, сейчас даже операции с локально хранящимся git-репозиторием возможны только через прослойку "Gitaly".
По мере развития "Gitaly" и проработки кластерного функционала "GitLab" естественным образом именование директорий git-репозиториев в файловой системе было изменено в пользу "hashed paths".
Было так:
/var/opt/gitlab/git-data/repositories
./userOne
./repoOne.git
....
./repoX.git
./userOne
./repoOne.git
....
./repoX.git
...а стало так:
/var/opt/gitlab/git-data/repositories/@hashed
./ef
./2d
./ef2d...e39d.git
....
./ef
./2d
./ef2d...e39d.git
....
Очевидно, что теперь без доступа к управляющему сервису или чтению "config"-ов всех репозиториев в месиве файлов не разобраться. Пока что режим именования структуры git-репозиториев посредством "хешей" можно отключить - проще будет искать нужное в резервных копиях:
Admin -> Settings -> Repository:
Repository storage:
Use hashed storage: off
Repository storage:
Use hashed storage: off
Сокрытие непрофильного web-функционала.
Документирование вещь безусловно нужная, но в правильном месте. Не в сервисе репозиториев кода, который может быть сегодня здесь, а завтра перенесён на другую технологическую платформу.
Тоже касается и функционала ведения заметок вне содержимого git-репозитория - для этого предназначены соответствующие сервисы организации групповой работы.
По идее в web-интерфейсе сервиса репозиториев программистам и делать нечего, а потому отключаем лишнее (на самом деле этот функционал будет доступен, если его явно включить в настройках конкретного проекта):
# vi /etc/gitlab/gitlab.rb
....
gitlab_rails['gitlab_default_projects_features_wiki'] = false
gitlab_rails['gitlab_default_projects_features_issues'] = false
....
gitlab_rails['gitlab_default_projects_features_wiki'] = false
gitlab_rails['gitlab_default_projects_features_issues'] = false
....
# gitlab-ctl reconfigure
Отключение избыточного ресурсоёмкого встроенного мониторинга.
Посредством встроенного сервиса мониторинга "Prometheus" собираются метрики состояния всех компонентов "GitLab". По умолчанию в дополнение ко всему запускается ещё и web-сервер "Grafana", предоставляющий возможность любоваться графиками, построенными по метрикам "Prometheus". По мне, так при исправной работе сервиса git-репозитория это совершенно ни к чему - просто отключаем лишнего потребителя системных ресурсов:
# vi /etc/gitlab/gitlab.rb
....
grafana['enable'] = false
....
grafana['enable'] = false
....
На самом деле для работы "GitLab" и мониторинг посредством "Prometheus" не требуется - его можно смело отключать и выигрывать за счёт этого около одного (!) гигабайта высвободившегося ОЗУ:
# vi /etc/gitlab/gitlab.rb
....
prometheus['enable'] = false
prometheus_monitoring['enable'] = false
....
prometheus['enable'] = false
prometheus_monitoring['enable'] = false
....
# gitlab-ctl reconfigure
В процессе работы "Grafana" и "Prometheus" быстро набирают гигабайты данных мониторинга, которые после таковых отключения уже не нужны - удаляем их из файловой системы:
# rm -rf /var/opt/gitlab/grafana/data/*
# rm -rf /var/opt/gitlab/prometheus/data/*
# rm -rf /var/opt/gitlab/prometheus/data/*
Настройка связки с почтовым сервисом.
Почему-то в web-интерфейсе "GitLab" нет раздела настроек связи с почтовым сервисом, повсеместно используемого, тем не менее. Сделать это можно только в основном конфигурационном файле "Omnibus":
# vi /etc/gitlab/gitlab.rb
....
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.example.net"
gitlab_rails['smtp_port'] = 25
gitlab_rails['smtp_user_name'] = "gitlab@example.net"
gitlab_rails['smtp_password'] = "***"
gitlab_rails['smtp_domain'] = "example.net"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
gitlab_rails['smtp_openssl_verify_mode'] = "peer"
....
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.example.net"
gitlab_rails['smtp_port'] = 25
gitlab_rails['smtp_user_name'] = "gitlab@example.net"
gitlab_rails['smtp_password'] = "***"
gitlab_rails['smtp_domain'] = "example.net"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
gitlab_rails['smtp_openssl_verify_mode'] = "peer"
....
# gitlab-ctl reconfigure
Очень удобно то, что прямо в специализированной консоли "GitLab" можно проверить верность внесённых настроек путём отправки тестового сообщения:
# gitlab-rails console
irb(main):001:0> Notify.test_email('user@example.net', 'Message Subject', 'Message Body').deliver_now
irb(main):001:0> quit
irb(main):001:0> Notify.test_email('user@example.net', 'Message Subject', 'Message Body').deliver_now
irb(main):001:0> quit
Установка агента исполнения "GitLab Runner".
Отмечу, что на управляющем сервере, несущем сервис "GitLab", агента исполнения его CI/CD-заданий устанавливать нежелательно. Прежде всего из-за непрогнозируемого потребления ресурсов - запросто может случиться так, что запущенное без явного указания места исполнения задание сборки большого проекта на длительное время отберёт на себя все доступные ресурсы, создав затруднения при работе с основным функционалом.
Кроме того, через "агента" при должном старании (и некомпетентности администратора сервера) можно получить доступ к настройкам самого "GitLab", что нехорошо.
Благо, в отличии от "Jenkins" реализация механизма работы с агентом "GitLab" позволяет явно и полностью перевести исполнение задачи на определённый "GitLab Runner", так что серверу управления в данном случае можно оставить задачи хранения репозиториев и распределения ролей, а его "агентам" на удалённых сетевых узлах - исполнение "pipelines".
Подключаем APT-репозиторий от разработчиков:
# apt-get install apt-transport-https ca-certificates curl gnupg2 debian-archive-keyring
# curl -fsSL https://packages.gitlab.com/runner/gitlab-ci-multi-runner/gpgkey | apt-key add -
# echo -e "# Official APT-repository GitLab-Runner\ndeb [arch=amd64] https://packages.gitlab.com/runner/gitlab-ci-multi-runner/$(. /etc/os-release; echo "$ID")/ $(lsb_release -cs) main" >> /etc/apt/sources.list.d/gitlab-ci-multi-runner.list
# curl -fsSL https://packages.gitlab.com/runner/gitlab-ci-multi-runner/gpgkey | apt-key add -
# echo -e "# Official APT-repository GitLab-Runner\ndeb [arch=amd64] https://packages.gitlab.com/runner/gitlab-ci-multi-runner/$(. /etc/os-release; echo "$ID")/ $(lsb_release -cs) main" >> /etc/apt/sources.list.d/gitlab-ci-multi-runner.list
Устанавливаем "GitLab-Runner":
# apt-get update && apt-get install gitlab-ci-multi-runner
По умолчанию после установки сервис "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
Разумеется, при необходимости режим запуска "GitLab Runner" можно будет изменить, но для типового набора задач конфигурация по умолчанию вполне работоспособна. SUDO для пользователя "gitlab-runner" тоже удобный вариант.
Регистрация CI/CD-агента в "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"-а запускаем процедуру его регистрации в сервисе управления "GitLab" (набор ключей в параметре "tag-list" служит для выделения этой "ноды" в дальнейших операциях автоматизации):
# gitlab-runner register \
--non-interactive \
--url "https://gitlab.example.net/" \
--registration-token "Lz7...5JF" \
--builds-dir ".builds" \
--executor "shell" \
--shell "bash" \
--description "node0.example.net" \
--tag-list "shell,node0,master" \
--run-untagged \
--locked="true"
--non-interactive \
--url "https://gitlab.example.net/" \
--registration-token "Lz7...5JF" \
--builds-dir ".builds" \
--executor "shell" \
--shell "bash" \
--description "node0.example.net" \
--tag-list "shell,node0,master" \
--run-untagged \
--locked="true"
Соотвественно, для "runner"-а другой "ноды" наименование и перечень "тегов" будут соответствующие только ей:
# gitlab-runner register \
....
--description "node1.example.net" \
--tag-list "shell,node1,testing" \
....
....
--description "node1.example.net" \
--tag-list "shell,node1,testing" \
....
Обращаю внимание на то, что в процессе отработки "задач", описываемых впоследствии в ".gitlab-ci.yml" именно набор ключевых слов в параметре "tag-list" послужит определяющим, какому из "runner"-ов отправить задание.
В случае, если "GitLab" работает через "сампоподписанный (self-signed)" SSL-сертификат, придётся явно указать его публичную часть как доверенную при запуске "GitLab Runner":
# mkdir -p /etc/ssl/gitlab
# cp ./gitlab.example.net-ss.crt /etc/ssl/gitlab
# cp ./gitlab.example.net-ss.crt /etc/ssl/gitlab
# gitlab-runner register \
....
--url "https://gitlab.local/" \
--tls-ca-file=/etc/ssl/gitlab/gitlab.example.net-ss.crt \
....
....
--url "https://gitlab.local/" \
--tls-ca-file=/etc/ssl/gitlab/gitlab.example.net-ss.crt \
....
Регистрацию агента на "GitLab"-сервере можно отозвать:
# gitlab-runner unregister --name node0.example.net
Разумеется, есть команда проверки текущего состояния "runner"-а:
# gitlab-runner verify
Возвращаемся к разделу настроек автоматизации проекта в web-интерфейсе "GitLab" и видим, что в перечне "runner"-ов появился тот, что мы только что добавляли:
Group -> Repository -> CI/CD Setting -> Runners:
Runners activated for this project:
node0.example.net
....
Runners activated for this project:
node0.example.net
....
Список и параметры всех зарегистрированных в "GitLab" CI/CD-агентов можно посмотреть на специальной странице администрирования "https://gitlab.example.net/admin/runners".