Содержание
Скрипты и конфиги у нас долго жили на файловой шаре, в папках вида final_v2_точно_последний. Облачный GitLab не подходил: код не должен уходить за периметр. Поднял свой на виртуалке, со входом по доменным учёткам.
Дальше — установка GitLab CE внутри закрытой сети. DNS и сертификат от внутреннего CA, вход через Active Directory, почта через свой релей, бэкапы и обновления. Сквозной пример — ВМ gitlab.corp.local в домене corp.local.
Что понадобится для установки GitLab в корпоративной сети
Требования и подготовка
Виртуалка на любом гипервизоре. У меня это Hyper-V, но роли он не играет. По ресурсам для команды на пару сотен человек мне хватает 4 vCPU и 8 ГБ памяти. На 4 ГБ GitLab живёт в swap и открывает страницы по полминуты.
Диски я делаю два. Первый под систему, второй под /var/opt/gitlab: там репозитории, база, артефакты CI и бэкапы. Второй диск ставлю на LVM, расти он будет всегда.
ОС — Debian 12 или Ubuntu 24.04. Свежие релизы ОС пакеты GitLab поддерживают не сразу. Перед установкой сверяюсь со списком поддерживаемых систем в документации.
Из корпоративной инфраструктуры понадобятся:
- A-запись в DNS;
- сертификат от внутреннего CA, у меня это AD CS;
- SMTP-релей, который принимает почту с IP GitLab без авторизации;
- сервисная учётка в AD для чтения каталога и группа, чьим членам разрешён вход;
- синхронизация времени с контроллером домена, иначе LDAP и сертификаты начнут чудить.
A-запись заводим прямо на контроллере домена через PowerShell:
Add-DnsServerResourceRecordA -ZoneName "corp.local" -Name "gitlab" -IPv4Address "10.10.20.15"
Установка и подготовка среды
Установка пакетов / компонентов
Если прямого выхода в интернет нет, apt ходит через корпоративный прокси:
# /etc/apt/apt.conf.d/95proxy
Acquire::http::Proxy "http://proxy.corp.local:3128";
Acquire::https::Proxy "http://proxy.corp.local:3128";
Ставим зависимости. Postfix не нужен, почту GitLab будет отдавать релею сам:
apt update
apt install -y curl openssh-server ca-certificates perl
Подключаем репозиторий GitLab. Скрипт сначала скачиваю и читаю, потом запускаю:
curl -fsSL -o gitlab-repo.sh https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh
less gitlab-repo.sh
bash gitlab-repo.sh
Сам GitLab ставлю сначала на http. С https в EXTERNAL_URL установщик полезет за сертификатом Let's Encrypt. Из закрытой сети он его не получит, и установка закончится ошибкой.
EXTERNAL_URL="http://gitlab.corp.local" apt install gitlab-ce
Если выхода наружу нет совсем, даже через прокси, пакет качаю на машине с интернетом. Нужный .deb лежит на packages.gitlab.com. Переношу его на ВМ и ставлю локально:
EXTERNAL_URL="http://gitlab.corp.local" apt install ./gitlab-ce_<версия>_amd64.deb
Пароль root лежит в /etc/gitlab/initial_root_password. Через сутки файл удалится сам, поэтому пароль меняю сразу после первого входа.
Теперь сертификат. Ключ и запрос генерирую на самой ВМ:
install -d -m 755 /etc/gitlab/ssl
cd /etc/gitlab/ssl
openssl req -new -newkey rsa:2048 -nodes \
-keyout gitlab.corp.local.key -out gitlab.corp.local.csr \
-subj "/CN=gitlab.corp.local" -addext "subjectAltName=DNS:gitlab.corp.local"
chmod 600 gitlab.corp.local.key
Запрос подписываю на CA по шаблону веб-сервера:
certreq -submit -attrib "CertificateTemplate:WebServer" gitlab.corp.local.csr gitlab.corp.local.cer
Готовый сертификат кладу рядом с ключом как gitlab.corp.local.crt. Если CA двухуровневый, в этот файл дописываю и промежуточный сертификат. nginx должен отдавать всю цепочку, иначе часть клиентов ругнётся.
Корневой сертификат CA нужен и самому GitLab. Через него он проверяет LDAPS и почтовый релей. Внутри GitLab свой набор доверенных CA, системный он не читает:
cp corp-root-ca.crt /etc/gitlab/trusted-certs/
Базовая настройка GitLab
Основные параметры
Вся настройка живёт в одном файле, /etc/gitlab/gitlab.rb. После любой правки нужен gitlab-ctl reconfigure, без него изменения не применятся. Из того, что важно в корпоративной сети:
external_url— адрес, по которому GitLab строит все ссылки, включая адреса для clone;letsencrypt['enable']— в закрытой сети выключаем явно;nginx['ssl_certificate']иnginx['ssl_certificate_key']— пути к сертификату и ключу;gitlab_rails['smtp_*']— отправка почты через релей;gitlab_rails['ldap_servers']— подключение к AD;gitlab_rails['backup_keep_time']— сколько секунд хранить локальные бэкапы;puma['worker_processes']иsidekiq['concurrency']— сколько процессов и потоков держать в памяти.
По умолчанию GitLab рассчитан на машину побольше. На 8 ГБ я урезаю число worker у Puma и потоков у Sidekiq. Для пары сотен пользователей разницы в скорости не видно, а памяти заметно больше.
Основная конфигурация
Пример конфигурации
Мой рабочий gitlab.rb для такой ВМ, без закомментированных строк по умолчанию:
# /etc/gitlab/gitlab.rb
external_url 'https://gitlab.corp.local'
letsencrypt['enable'] = false
nginx['redirect_http_to_https'] = true
nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.corp.local.crt"
nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.corp.local.key"
gitlab_rails['time_zone'] = 'Europe/Moscow'
gitlab_rails['gitlab_email_from'] = 'gitlab@corp.local'
gitlab_rails['gitlab_email_display_name'] = 'GitLab'
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "mail.corp.local"
gitlab_rails['smtp_port'] = 25
gitlab_rails['smtp_domain'] = "corp.local"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['ldap_enabled'] = true
gitlab_rails['ldap_servers'] = {
'main' => {
'label' => 'AD',
'host' => 'dc01.corp.local',
'port' => 636,
'uid' => 'sAMAccountName',
'encryption' => 'simple_tls',
'verify_certificates' => true,
'bind_dn' => 'CN=svc-gitlab,OU=Service Accounts,DC=corp,DC=local',
'active_directory' => true,
'base' => 'OU=Users,DC=corp,DC=local',
'user_filter' => '(memberOf:1.2.840.113556.1.4.1941:=CN=GitLab-Users,OU=Groups,DC=corp,DC=local)',
'lowercase_usernames' => true
}
}
gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '10.10.20.5/32']
gitlab_rails['backup_keep_time'] = 604800
puma['worker_processes'] = 2
sidekiq['concurrency'] = 10
Пароля сервисной учётки в файле нет, и это не опечатка. gitlab.rb читают все, кто когда-либо заходил на сервер под root. Пароль я кладу в зашифрованный секрет:
gitlab-rake gitlab:ldap:secret:edit EDITOR=nano
Внутри пишется YAML с ключом main и полем password. Имя совпадает с именем сервера в ldap_servers.
Про фильтр отдельно. Обычный memberOf видит только прямых членов группы. Длинный OID в фильтре заставляет AD проверять и вложенные группы. Без него человек из подгруппы войти не сможет.
monitoring_whitelist открывает эндпоинты /-/health и /-/readiness для сервера Zabbix. По умолчанию они отвечают только с localhost.
Применяем:
gitlab-ctl reconfigure
Сетевой доступ ограничиваю корпоративными подсетями. SSH добавляю первым, чтобы не отрезать себя от ВМ:
apt install ufw
ufw allow from 10.0.0.0/8 to any port 22 proto tcp
ufw allow from 10.0.0.0/8 to any port 80 proto tcp
ufw allow from 10.0.0.0/8 to any port 443 proto tcp
ufw enable
Порт 22 здесь общий. Через него ходят и админы, и git по SSH от имени пользователя git.
Проверка работоспособности
Проверка и тестирование
Сначала сервисы и встроенная самопроверка:
gitlab-ctl status
gitlab-rake gitlab:check SANITIZE=true
gitlab-ctl status должен показать все компоненты в состоянии run. gitlab:check проходит по конфигурации, правам и репозиториям. Всё зелёное — идём дальше.
Потом AD:
gitlab-rake gitlab:ldap:check
Команда подключается под сервисной учёткой и выводит первых найденных пользователей. Пустой список — фильтр или base не совпадают с вашей структурой OU.
Почту проверяю тестовым письмом прямо из GitLab:
gitlab-rails runner "Notify.test_email('admin@corp.local', 'GitLab test', 'Работает').deliver_now"
И health-эндпоинт, в него же потом смотрит Zabbix:
curl -sk https://127.0.0.1/-/health
В ответ приходит GitLab OK. Финальный тест делаю с рабочей станции в домене. Вхожу доменной учёткой, создаю проект, клонирую его по HTTPS и по SSH.
Логи и диагностика
Все логи лежат в /var/log/gitlab, по каталогу на компонент. Смотреть их удобнее через gitlab-ctl:
gitlab-ctl tail # все компоненты сразу
gitlab-ctl tail nginx # только nginx
gitlab-ctl tail gitlab-rails # приложение, LDAP, почта
Если падает gitlab-ctl reconfigure, в выводе будет имя упавшего рецепта. По нему обычно сразу понятно, какой параметр в gitlab.rb не понравился.
Сводку по версиям и окружению выдаёт gitlab-rake gitlab:env:info. Её же я прикладываю, когда спрашиваю совета на форуме или в issue.
Безопасность и резервное копирование
С безопасностью в первую очередь делаю четыре вещи в Admin Area:
- выключаю самостоятельную регистрацию, вход только через AD;
- ставлю видимость проектов по умолчанию Private и запрещаю Public;
- включаю обязательную двухфакторку для администраторов;
- завожу себе личную админскую учётку, а пароль root убираю в хранилище паролей.
Видимость Internal в GitLab означает «все, кто залогинился». В корпоративной сети это вся компания. Для кода с паролями и конфигами это слишком широко.
GitLab регулярно выпускает security-релизы, и часть из них критичные. Я подписан на их рассылку и не откладываю такие обновления надолго.
Теперь резервное копирование. Штатный gitlab-backup сохраняет базу, репозитории, загрузки и артефакты. Запускаю его по таймеру каждую ночь:
# /etc/systemd/system/gitlab-backup.service
[Unit]
Description=GitLab backup
[Service]
Type=oneshot
ExecStart=/opt/gitlab/bin/gitlab-backup create STRATEGY=copy CRON=1
# /etc/systemd/system/gitlab-backup.timer
[Unit]
Description=Nightly GitLab backup
[Timer]
OnCalendar=*-*-* 01:30:00
Persistent=true
[Install]
WantedBy=timers.target
STRATEGY=copy снимает копию файлов перед упаковкой. Без неё бэкап живого GitLab иногда падает на файлах, которые изменились во время архивации. Готовые архивы лежат в /var/opt/gitlab/backups, оттуда их забирает сервер резервного копирования.
Главное, чего в этом архиве нет, — конфигов. gitlab.rb и gitlab-secrets.json копирую отдельно, после каждой правки и каждого обновления:
tar czf /root/gitlab-config-$(date +%F).tgz -C / etc/gitlab
chmod 600 /root/gitlab-config-*.tgz
Без gitlab-secrets.json восстановленный GitLab не расшифрует переменные CI, токены и двухфакторку. Храню его отдельно от основного бэкапа. Если оба архива утекут вместе, секреты утекут тоже.
Восстановление работает только на той же версии GitLab. Версия есть в имени каждого архива, так что это не проблема. Раз в квартал я поднимаю тестовую ВМ и восстанавливаю на неё свежий бэкап. Бэкап, который ни разу не восстанавливали, — это надежда, а не бэкап.
Типичные ошибки
Let's Encrypt в закрытой сети. gitlab-ctl reconfigure падает на рецепте letsencrypt. С https в external_url GitLab пытается получить сертификат сам. Лечится строкой letsencrypt['enable'] = false и своим сертификатом.
Whoops, GitLab is taking too much time to respond. Ошибка 502 в первые минуты после старта — это нормально, компоненты ещё поднимаются. Если она не уходит, смотрю gitlab-ctl status и free -h. Чаще всего GitLab не хватает памяти, и OOM killer прибил Puma.
Правка без reconfigure. Поменяли gitlab.rb, перезапустили gitlab-ctl restart, а изменений нет. restart только перезапускает сервисы со старой конфигурацией. Применяет новую только gitlab-ctl reconfigure.
Invalid credentials при входе через AD. Пароль верный, а войти нельзя. Проверьте gitlab:ldap:check: если пользователя нет в выводе, дело в base или user_filter. Если команда падает на подключении, проверьте bind_dn и пароль сервисной учётки.
certificate verify failed. LDAPS или почта падают с SSLError. Корневого сертификата CA нет в /etc/gitlab/trusted-certs, или после его добавления не было reconfigure.
SSL certificate problem: unable to get local issuer certificate. Так ругается Git for Windows на клиенте. У него свой набор сертификатов, и про доменный CA он не знает. Лечится командой git config --global http.sslBackend schannel, тогда git берёт сертификаты из хранилища Windows.
Потерялся пароль root. Файл с начальным паролем удалился через сутки, а пароль никто не записал. Сбрасывается из консоли: gitlab-rake "gitlab:password:reset[root]".
Кончилось место. /var/opt/gitlab забит бэкапами и артефактами CI. Отдельный диск, backup_keep_time и срок хранения артефактов в настройках CI решают это заранее.
Практические рекомендации
Обновление и обслуживание
Главное правило обновлений GitLab: прыгать через версии нельзя. Есть обязательные промежуточные остановки. Маршрут от текущей версии до целевой показывает Upgrade Path tool из документации GitLab.
Чтобы случайный apt upgrade не обновил GitLab без спроса, фиксирую пакет:
apt-mark hold gitlab-ce
Обновление по маршруту выглядит так:
apt-cache madison gitlab-ce | head
apt-mark unhold gitlab-ce
apt install gitlab-ce=<версия>
apt-mark hold gitlab-ce
Перед каждым шагом делаю снапшот ВМ и свежий gitlab-backup. Пакет при обновлении сам сохраняет базу, но только базу. Снапшот удобен для быстрого отката, но бэкапом он не считается.
После шага открываю Admin Area → Monitoring → Background migrations. Следующее обновление запускаю, только когда там всё завершено. Иначе очередная версия может отказаться стартовать.
Ещё несколько привычек:
- В Zabbix мониторю
/-/health, свободное место на/var/opt/gitlabи срок действия сертификата. Сертификат от внутреннего CA истекает так же внезапно, как любой другой. - Срок хранения артефактов CI ставлю явно. По умолчанию они копятся, пока не кончится диск.
gitlab.rbдержу в git, без секретов. Через год ответ на вопрос «почему здесь так» найдётся в истории коммитов.- Runner для CI ставлю на отдельную ВМ, не на сервер GitLab. Иначе первый тяжёлый pipeline уронит интерфейс для всех.
Шара с папками final_v2 так никуда и не делась. Писать туда давно перестали, а удалить её никто не решается.