Установка GitLab на Oracle Linux 8
Docker Oracle Linux GitLab git GitLab Runner GitLab Pages CI/CD
Решил поднять свой личный GitLab - в тот момент актуальной версией была 17. Для этого заказал VPS на 2 ядра и 2 гига оперативки под управлением OL8. Помимо базовой настройки я расскажу еще и о Pages с пространством имен в URL.
Подготовка
Oracle Linux 8 выбран несколько случайно. Вроде как существует специализированный пакет, но оказалось, что он для 9-ки. А шаблона OL9 у хостера не было… Ничего страшного, оно 8-й вроде как ресурсов поменьше жрет.
Поехали (здесь и далее работаем под root, поэтому sudo или там решетки к командам не добавляю). Меняем собственный пароль (root'а в данном случае), обновляем систему и для удобства устанавливаем Midnight Commander:
passwd dnf update dnf install mc
Kdump включен, отключил:
systemctl disable kdump grubby --remove-args="crashkernel" --update=ALL
Создаем файл подкачки – я что-то разозлился и создал на 4 гига, а надо было наверное на 3. Но не суть:
fallocate -l 4G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile
В /etc/fstab добавляем:
/swapfile none swap defaults 0 0
В /etc/sysctl.conf добавляем:
vm.swappiness=10
В firewalld открываем порты 80 и 443:
firewall-cmd --permanent --add-service=http firewall-cmd --permanent --add-service=https
Желательно защитить SSH fail2ban'ом и/или настроить доступ по ключам. Позволю себе не заострять на этом внимание в данной статье.
Перезагружаемся. Если система загрузилась, значит мы почти готовы к установке – осталось настроить DNS-записи. Далее будут фигурировать git.example.com
и pages.example.com
, так что минимально это должны быть A-записи (и AAAA, если поддерживается IPv6), указывающие на сервер.
Установка GitLab
Я устанавливал прямо на хост из пакетов. Добавляем репозитории волшебным скриптом:
curl -s https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash
Устанавливаем GitLab с помощью dnf
. Предлагаю сразу задать пароль root (в смысле для GitLab) и внешний URL:
GITLAB_ROOT_PASSWORD="generate_secure_pwd" EXTERNAL_URL="https://git.example.com" dnf install -y gitlab-ce
Если пароль не указать (например, чтобы он не остался в истории команд), то временный сохранится в файле /etc/gitlab/initial_root_password. В случае URL с https установщик настроит Let's Encrypt для получения сертификатов SSL. Должен сказать, PostgreSQL и прочие Redis'ы входят в сам пакет (я думал это будут внешние зависимости), а процесс установки достаточно длительный. В частности он надолго застревает на:
execute[clear the gitlab-rails cache] action run
Надеюсь, в конечном итоге установщик справится. Заходим на свежеустановленный git.example.com:
Наверное и впрямь регистрацию лучше будет отключить, а если не задали пароль администратора сразу, то установить.
Есть официальная инструкция по оптимизации потребления памяти. Честно говоря, я выполнил ее не всю – побоялся копировать довольно внушительный кусок настроек для Gitaly (сервиса хранилища) и некую MALLOC_CONF
в gitlab_rails['env']
. Тем не менее, внес несколько изменений в /etc/gitlab/gitlab.rb.
Отключил кластеризованный режим Пумы (веб-сервера Ruby):
puma['worker_processes'] = 0
Понизил конкуренцию ключевого второстепенного персонажа – демона фоновых процессов Sidekiq:
sidekiq['concurrency'] = 8
И отключил Прометей (мониторинг):
prometheus_monitoring['enable'] = false
Переконфигурировал GitLab (эта команда впоследствии будет часто требоваться):
gitlab-ctl reconfigure
Кстати мониторинг надо отключать не только через файл конфигурации, но и через админ-панель: Admin Area (это такая едва заметная кнопка внизу у root'а, в более поздних версиях просто Admin) – Settings – Metrics and profiling, развернуть Metrics – Prometheus и отключить. Я бы заодно и сбор статистики отключил (Usage statistics).
Настройка после установки
По этому поводу существует весьма внушительный раздел документации, а еще рекомендации по защите. Давайте я очень-очень коротко расскажу о сделанных мной изменениях.
Admin area – Settings
General
- Visibility and access controls:
- Enabled Git access protocols – Only HTTP(s)
- Account and limit:
- Отключил Gravatar
- Import and export settings:
- Отметил источники импорта проектов (GitHub, Bitbucket)
- Sign-up restrictions:
- Sign-up enabled – по идее отключили сразу
- Email confirmation settings – Hard
- Minimum password length (number of characters) – подсыпать
- Sign-in restrictions:
- Отключил Allow password authentication for Git over HTTP(S) – буду пользоваться токенами
- Diagrams.net – не пользуюсь, отключил
CI/CD
- Continuous Integration and Deployment:
- Поскольку пока не нужен, отключил Default to Auto DevOps pipeline for all projects и Enable instance runners for new projects.
- На всякий случай прописал example.com (мой домен) в Auto DevOps domain.
Отключил Container register совсем через gitlab.rb (и затем переконфигурировал):
registry['enable'] = false
Reporting
Настроил Spam and Anti-bot Protection. Правда поддерживается только reCaptcha v2, но пусть будет.
Network
Включить лимиты? По факту ничего не стал менять.
Appearance
Не настраиваем
Preferences
- What's New – отключил
- Localization – настроил
Admin area – Overview – Users
На странице пользователей создал себя, в том числе выпустил ключ для доступа к репозиториям (права read_repository, write_repository).
Отправка почты
https://docs.gitlab.com/omnibus/settings/smtp/
Настраиваем через gitlab.rb:
gitlab_rails['smtp_enable'] = true gitlab_rails['smtp_address'] = "smtp.mail.ru" gitlab_rails['smtp_port'] = 465 gitlab_rails['smtp_authentication'] = "login" gitlab_rails['smtp_tls'] = true gitlab_rails['smtp_enable_starttls_auto'] = false gitlab_rails['smtp_openssl_verify_mode'] = 'peer' gitlab_rails['gitlab_email_from'] = 'noreply@example.com' gitlab_rails['gitlab_email_reply_to'] = 'noreply@example.com'
Я пользуюсь сервисом от mail.ru, поэтому указан его smtp_address
. Соответственно никакие sendmail с postfix'ом не требуются. Как видите, здесь не прописаны логин и пароль – зашифруем их. Для этого сначала создадим smtp.yaml в открытом виде:
user_name: 'noreply@example.com' password: 'secure_smtp_password'
А далее выполним команды:
cat smtp.yaml | gitlab-rake gitlab:smtp:secret:write rm smtp.yaml gitlab-ctl reconfigure
Проверить отправку почты можно через консоль. Для ее запуска:
gitlab-rails console
В ней:
Notify.test_email('your@email.com', 'GitLab test', 'Hello! This is message body.').deliver_now
Импорт репозиториев из BitBucket
https://docs.gitlab.com/integration/bitbucket/
Заходим в BitBucket, настройки рабочего пространства. Там по идее и будут OAuth consumers. Добавляем оного с правами доступа:
- Account: Email, Read
- Projects: Read
- Repositories: Read
- Pull Requests: Read
- Issues: Read
- Wiki: Read and Write
Нас интересуют ключ и секрет созданного «потребителя». С этими данными добавляем провайдер в gitlab.rb:
gitlab_rails['omniauth_providers'] = [ { name: "bitbucket", app_id: "<bitbucket_app_key>", app_secret: "<bitbucket_app_secret>", url: "https://bitbucket.org/" } ]
После традиционного переконфигурирования дальше вроде все просто – Новый проект, импорт, Bitbucket Cloud, выбрать репозиторий.
Импорт репозиториев из GitHub
Намного проще Битбакета, достаточно указать свой «классический» персональный токен.
Let's Encrypt
Желательно указать свой e-mail в gitlab.rb:
letsencrypt['contact_emails'] = ['admin@example.com']
Ну и вообще убедиться, что он включен – letsencrypt['enable'] = true
.
GitLab Pages
Эту страсть я изначально решил настроить в контексте хранения «локальной» копии документации к GitLab, однако в итоге эта самая документация у меня просто крутится в контейнере Docker. Тем не менее, в рамках настройки Pages мы в немалой степени продвигаемся по линии DevOps, а там может и какой-нибудь статический сайт можно будет развернуть.
Настройка
Для начала добавляем альтернативный домен (в gitlab.rb), чтобы Let's Encrypt выпустил сертификат:
letsencrypt['alt_names'] = ['pages.example.com']
Важно сразу же переконфигурировать, чтобы отработала проверка (.well-known и т.д.)
Далее:
pages_external_url 'https://pages.example.com' gitlab_pages['enable'] = true gitlab_pages['namespace_in_path'] = true pages_nginx['redirect_http_to_https'] = true
Внезапно установка URL действительно без =
. Я решил включить пространства имен в URL вместо доменов, получается, 4-го уровня (не хватало еще с ними возиться).
Принудительно указываем путь к «общему» сертификату:
pages_nginx['ssl_certificate'] = "/etc/gitlab/ssl/git.example.com.crt" pages_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/git.example.com.key"
Очередная реконфигурация.
Это, видимо, больше относится к варианту пространства имен в доменах, но все же: под root, Admin Area, Settings – Preferences – Pages. Обнулить лимит, указать e-mail и согласиться с условиями использования Let's Encrypt.
Бегун
Для всякого рода CI/CD, к коим относится и развертывание страниц, требуется gitlab-runner
. Опять-таки устанавливаем его через репозитории:
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | bash dnf install gitlab-runner
Ну и куда же теперь без портового рабочего…
yum-config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
Да, внезапно именно yum-config-manager
, а не к примеру dnf-config-manager.
dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Включаем:
systemctl enable --now docker
По желанию – проверяем:
docker run hello-world
Возвращаемся в GitLab. Под root, в Admin Area, CI/CD – Runners создаем его:
- Tags:
pages
- Description – по желанию («Публикация GitLab Pages»)
Раз мы указали метку, то теперь нужно будет вручную добавлять ее в файлы .gitlab-ci.yml. С этой точки зрения можно было бы оставить бегуна и без меток. Так или иначе, мы должны получить токен для регистрации, которая выполняется в командной строке:
gitlab-runner register --url https://git.example.com --token glrt-xxxx-yyyyyyyyyyyyyyy
Будет задано несколько вопросов, в частности:
- URL – передали в параметре
- Имя – по умолчанию
- Исполнитель (executor):
docker
- Образ по умолчанию:
alpine:latest
(илиdocker:latest
, или любой другой не слишком большой)
Исполнитель, казалось бы, shell
, ан нет – образы (image) таковой не поддерживает. Именно для этого и накатил докер. Собственно образ по умолчанию по идее не критичен, поскольку конкретный будет определен в пайплайне.
Во избежание возможных ошибок предлагаю увеличить максимальный размер артефактов: Admin Area, Settings – CI/CD – Continuous Integration and Deployment, Maximum artifacts size (MB).
Проверка
В GitLab есть несколько шаблонов проектов Pages, в принципе для наших целей достаточен Pages/Plain HTML. Хотя в него уже входит .gitlab-ci.yml, GitLab тупит и приходится настраивать развертывание заново (Deploy – Pages).
На первом шаге указываем образ (busybox
) и директорию для публикации (public
, по умолчанию). Второй шаг (Installation steps) пропускаем. Build steps берем из шаблона: echo "The site will be deployed to $CI_PAGES_URL"
. На четвертом шаге непосредственно в черновик (Draft: .gitlab-ci.yml) добавляем метку:
tags: - pages
Коммит. Смотрим Build – Jobs, если все в порядке, то через некоторое время система опубликует сайт. Правда под каким-то очень странным «уникальным» URL, мне кажется лучше снять галку Use unique domain в Deploy – Pages, вкладке Domains & settings.
Pages как «кастомный» сайт
Выполненная ранее настройка проработает ровно до того момента, когда наступит время обновлять сертификат Let's Encrypt. Дело в том, что обновляется он с использованием проверки по http (http-01 challenge), а для этого должна быть доступна /.well-known/acme-challenge/. В конфигурации Nginx для Pages эта локация не настроена.
Таким образом, делаем «финт ушами». Копируем /var/opt/gitlab/nginx/conf/gitlab-pages.conf в (например) /etc/nginx/reverse-proxy/pages.conf, немного чистим комментарии и добавляем блоки location /.well-known/acme-challenge/
. Я еще заодно логи отключил для http и добавил прослушивание по IPv6. Должно получиться примерно следующее:
################################### ## configuration ## ################################### ## Redirects all HTTP traffic to the HTTPS host server { listen *:80; listen [::]:80; ## Experimental - Handle requests having namespace in path server_name ~^pages\.example\.com$; server_tokens off; ## Don't show the nginx version number, a security best practice location /.well-known/acme-challenge/ { root /var/opt/gitlab/nginx/www/; } location / { return 301 https://$http_host:$request_uri; } access_log off; error_log off; } server { listen *:443 ssl http2; listen [::]:443 ssl http2; ## Experimental - Handle requests having namespace in path server_name ~^pages\.example\.com$; server_tokens off; ## Don't show the nginx version number, a security best practice ## Disable symlink traversal disable_symlinks on; ## Strong SSL Security ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/ ssl_certificate /etc/gitlab/ssl/git.example.com.crt; ssl_certificate_key /etc/gitlab/ssl/git.example.com.key; # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; ssl_session_timeout 1d; ## Real IP Module Config ## http://nginx.org/en/docs/http/ngx_http_realip_module.html ## HSTS Config ## https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ add_header Strict-Transport-Security "max-age=63072000 "; ## Individual nginx logs for this GitLab vhost access_log /var/log/gitlab/nginx/gitlab_pages_access.log gitlab_access; error_log /var/log/gitlab/nginx/gitlab_pages_error.log error; # Define custom error pages error_page 403 /403.html; error_page 404 /404.html; # Pass everything to pages daemon when namespace in host location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-Ssl on; # Prevent NGINX from caching pages in response to the pages `Cache-Control` # header. # # Browsers already respect this directive and Pages can handle the request # volume without help from NGINX. # # If this changes in the future, ensure `proxy_cache_key` is set to a value # like `$scheme$host$request_uri`, as the default value does not take the # Pages hostname into account, leading to incorrect responses being served. # # See https://gitlab.com/gitlab-org/gitlab-pages/issues/73 proxy_cache off; proxy_http_version 1.1; proxy_pass http://localhost:8090; } location /.well-known/acme-challenge/ { root /var/opt/gitlab/nginx/www/; } }
Отключаем Nginx для страниц в gitlab.rb:
pages_nginx['enable'] = false
В нем же добавляем подключение дополнительных файлов конфигураций Nginx:
nginx['custom_nginx_config'] = "include /etc/nginx/reverse-proxy/*.conf;"
Фактически благодаря этому трюку мы можем размещать какие-то свои ресурсы помимо GitLab. Переконфигурирование, возможно, с первого раза не сработает из-за «сертификации». В самом крайнем случае можно перезапустить GitLab или даже сервер целиком.
Копия документации
Поскольку у нас теперь есть Docker, копию документации к GitLab можно развернуть в виде контейнера. Набросаем /root/gldocs/compose.yaml (напоминаю, что я бессовестным образом работаю под root, но Вы так не делайте):
services: gitlab_docs: image: registry.gitlab.com/gitlab-org/gitlab-docs/archives:17.4 restart: unless-stopped ports: - "127.0.0.1:4000:4000"
Примечание. Начиная с версии 17.8 путь к образам другой: registry.gitlab.com/gitlab-org/technical-writing/docs-gitlab-com/archives.
Запускаем:
docker compose up -d
В gitlab.rb добавляем домен для сертификации:
letsencrypt['alt_names'] = ['pages.example.com', 'gldocs.example.com']
И добавляем примерно такую конфигурацию обратного прокси /etc/nginx/reverse-proxy/gldocs.conf:
server { listen *:80; listen [::]:80; server_name gldocs.example.com; server_tokens off; location /.well-known/acme-challenge/ { root /var/opt/gitlab/nginx/www/; } location / { return 301 https://$http_host:$request_uri; } access_log off; error_log /var/log/gitlab/nginx/gldocs_error.log; } server { listen *:443 ssl http2; listen [::]:443 ssl http2; server_name gldocs.example.com; server_tokens off; ## Don't show the nginx version number, a security best practice ssl_certificate /etc/gitlab/ssl/git.example.com.crt; ssl_certificate_key /etc/gitlab/ssl/git.example.com.key; # Let's Encrypt SSL options ssl_session_cache shared:SSL:10m; ssl_session_timeout 1440m; ssl_session_tickets off; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"; access_log off; error_log /var/log/gitlab/nginx/gldocs_error.log; location /.well-known/acme-challenge/ { root /var/opt/gitlab/nginx/www/; } location / { proxy_cache off; proxy_pass http://localhost:4000; } }
Реконфигурация. Возможно, не будет работать редирект на актуальную версию, тогда придется открывать вручную адрес типа gldocs.example.com/17.4/. А еще может задалбывать согласие на использование печенек. Тем не менее для страховки пусть живет.
Резервное копирование
Это тоже весьма обширная тема, но если совсем коротко, то для резервного копирования предусмотрен скрипт:
gitlab-backup create
В архив не попадает содержимое объектного хранилища (S3), если вдруг оно настроено, ну и конфигурацию надо отдельно сохранять. В любом случае это:
- /etc/gitlab/gitlab-secrets.json
- /etc/gitlab/gitlab.rb
И, в моем конкретном случае:
- /var/opt/gitlab/gitlab-rails/shared/encrypted_settings/smtp.yaml.enc
- /etc/gitlab-runner
- /etc/nginx/reverse-proxy
В gitlab.rb раскомментировал строку (и конечно же переконфигурировал GitLab):
gitlab_rails['backup_keep_time'] = 604800
Тем самым бэкапы будут храниться 7 дней.
В crontab -e
root'а добавил задание:
0 2 * * * /opt/gitlab/bin/gitlab-backup create CRON=1
Добавил некий специальный параметр, указывающий, что резервирование происходит в рамках регулярной процедуры. Кстати tar
внезапно в Oracle Linux может быть не установлен, если это так, то:
dnf install tar
В свою очередь, чтобы сохранить полученные архивы в облако, я пользуюсь rclone
. Он есть в EPEL:
dnf install epel-release dnf install rclone rclone config
Создаю подключение к mega.nz (в программе называется просто Mega) под названием meganz
. Хотя конечно вопрос с шифрованием доступов остается открытым: для SMTP зашифровали, а для rclone – нет. Зная путь к архивам (/var/opt/gitlab/backups), добавляем примерно такое задание на 4 часа утра (уж за два-то часа наверное личный GitLab успеет сохраниться):
0 4 * * * /usr/bin/rclone sync /var/opt/gitlab/backups meganz:/gitlab/backups
Возникает вопрос, как в случае чего все восстанавливать. Все не так просто – версии GitLab должны строго совпадать (что, кстати, может быть затруднительным в случае установки из репозиториев), а целевая инстанция должна быть сконфигурированной (т.е.: установить, восстановить как минимум gitlab.rb и gitlab-secrets.json, переконфигурировать) и запущенной.
Перед восстановлением предлагается остановить процессы, работающие с БД:
gitlab-ctl stop puma gitlab-ctl stop sidekiq
Разместить архив в новой /var/opt/gitlab/backups и наконец восстановить уже (в параметре не указывается _gitlab_backup.tar):
gitlab-backup restore BACKUP=1728082883_2024_10_04_17.4.0
Если я правильно понял, после восстановления нужно еще раз выполнить реконфигурацию (наверное в любом случае не повредит):
gitlab-ctl reconfigure
Запускаем все обратно и проверяем:
gitlab-ctl start gitlab-rake gitlab:check SANITIZE=true gitlab-rake gitlab:doctor:secrets
Наконец, рекомендуется обновить статистические данные БД:
gitlab-psql -c 'SET statement_timeout = 0; ANALYZE VERBOSE;'
Искренне желаю вам удачи, чтобы ничего восстанавливать не пришлось.
Заключение
Если честно, промучился я со всем этим примерно год (пока сервер был оплачен), а потом взял и перенес все на более мощный VPS 4/4, да еще и полностью в Docker настроил и Traefik'ом заполировал. Возможно я зря не выполнил инструкцию по экономии памяти полностью, но на 2 гигах оперативки было тяжело. Еще я пока не настраивал реестр контейнеров, как настрою – постараюсь написать отдельную статью.
Но в целом собственный GitLab определенно стоит своих денег за VPS и времени на настройку.
Категория: Программирование, веб | Опубликовано 04.12.2024 | Редакция от 04.07.2025