Доброго времени суток, коллеги.
На протяжении уже полутора лет работаю в области, которую сейчас принято называть DevOps, поэтому решил начать делиться некоторыми знаниями и наработками. Наработки будут на базе моих личных фриланс проектов, тк про основное место деятельности лучше не упоминать — бумажки там всякие подписаны и пр)
О чем будем говорить
Итак, о чем будет эта статья — она будет о том, как организовать свой собственный современный Git сервер внутри команды/компании. Почем так важно иметь свой сервер, почему бы например не воспользоваться GitLab облаком, Github, Bitbucket и иже с ними?
Во первых — у проекта могут быть свои требования безопасности и приватности, скажем -не пользоваться сторонними сервисами
Во вторых — на бесплатных тарифах ест куча ограничений, тот же GitHub только недавно ввел бесплатные приватные репозитории, однако все подобные решения идут с ограничениями так или иначе (как минимум на число участников)
В третьих — если вы даже покупаете платный тариф, цена там идет за пользователя — то есть резкое расширения команды ударит по вашему кошельку, в то же время стоимость какой-нибудь vds у Hetzner или аналогичных товарищей в месяц будет дешевле самого простого тарифа у облачных репозиториев.
В четвертых — ставя свой обособленное решение вы получаете возможность управлять им, управлять пользователями (что с бесплатными тарифами например не сделать), настраивать резервное копирование и многое другое — жизненно важный для Вас сервис находится под Вашим контролем.
Я думаю этих причин уже достаточно! Да, я согласен — есть моменты где стоит пойти и купить услугу или хватит бесплатного тарифа, но есть и ситуации описанные выше.
Итак, эта статья описывает простую установку и настройку Gitlab в связке с корпоративной Active Directory для авторизации и так же является предисловием для другой статьи ( по эксплуатации).
Кому это нужно
У многих системных администраторов наверняка зреет вопрос — «если я НЕ работаю в этой области, нужна ли мне эта хрень»? Я отвечу — да, нужна. Как минимум Вам стоит познакомиться с Git и существующими облачными решениями. Если вы в своей работе пишите какие-то скрипты, утилиты ( а вы должны, вы же администратор а не эникей — ваша работа в том числе автоматизировать свою деятельность а не просто кнопки жать), формируете конфиги для сервисов — то есть так или иначе работаете с чем-то в текстовом виде что стоит версионировать, Вам стоит начать использовать Git для себя а лучше в своей компании! Тут то вам и может пригодиться собственный Git сервер. И на мой взгляд, free opensource версия Gitlab тут подойдет как нельзя кстати!
Если Вы разработчик или DevOps инженер — вы и так прекрасно знаете что это и зачем…
Предисловие
Gitlab это сервис, реализующий для нас систему контроля версий на базе популярного (стандарт де факто) движка и протокола git.
Подробнее что это такое и зачем- читайте в Википедии:
Мы используем версию Gitlab CE — Community Edition, она же бесплатная и свободно распространяемая. Ее можно поставить на свой хостинг и пользоваться. Что мы и используем. Подробнее о Gitlab CE: https://about.gitlab.com/pricing/self-managed/feature-comparison/
Инфраструктура
Проект, в котором мне пришлось «поднимать» свой Gitlab сервер, с моей же подачи использует Proxmox VE в качестве системы виртуализации поверх нескольких железных серверов. Вся серверная инфраструктура компании так или иначе представлена набором виртуальных машин. Под Gitlab была использована обычная виртуальная машина на базе Ubuntu x64.
Что касается Proxmox — Я использую опять же comunity edition версию, однако устанавливаю ее не из «коробочного дистибутива», а поверх debian (увы, поверх других дистрибутивов установка не поддерживается).
Что из себя представляет vm под GitLab:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
root@git:~# uname -r 4.4.0-138-generic root@git:~# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.5 LTS Release: 16.04 Codename: xenial root@git:~# free -h total used free shared buff/cache available Память: 7,8G 1,6G 4,8G 86M 1,4G 5,8G Подкачка: 974M 364K 974M root@git:~# cat /proc/cpuinfo | grep name model name : Common KVM processor model name : Common KVM processor |
Да, Ubuntu не самая свежая как и ядро, однако статья написана на много позже развертывания системы… На тот момент 18.04 еще не вышла.
Рекомендуемые системные требования: https://docs.gitlab.com/ee/install/requirements.html#cpu
Установка
Опустим детали установки и настройки VM и гостевой ОС — тут все взрослые, думаю справитесь…
Установка самого gitlab:
1 2 3 4 5 6 7 8 9 10 |
apt-get update apt-get install ca-certificates curl openssh-server cd /tmp curl -LO https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh bash /tmp/script.deb.sh apt-get install gitlab-ce vim /etc/gitlab/gitlab.rb # правим параметр external_url gitlab-ctl reconfigure |
Небольшой тюнинг ОС — Настройка ssh сервера чтобы без ключа не ходили! («…а то ходют тут всякие…» (с) )
1 2 3 |
sed -i 's/PermitRootLogin yes/PermitRootLogin no/g' /etc/ssh/sshd_config sed -i 's/PermitRootLogin without-password/PermitRootLogin no/g' /etc/ssh/sshd_config service ssh reload |
Все, Gitlab установлен!
Настройка
Авторизация через AD — делаем для gitlab учетку в AD и начинаем интеграцию:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
vim /etc/gitlab/gitlab.rb gitlab_rails['ldap_enabled'] = true gitlab_rails['ldap_servers'] = YAML.load <<-'EOS' main: label: 'LDAP' host: 'FQDN ВАШЕГО КОНТРОЛЛЕРА ДОМЕНА' port: 389 uid: 'sAMAccountName' method: 'plain' bind_dn: 'CN=GITUSER,OU=ServiceUsers,OU=*OU где искать пользователей*,DC=*имя-*,DC=*-вашего-,DC=*-домена*' password: 'PASSWORD' timeout: 10 active_directory: true allow_username_or_email_login: false block_auto_created_users: false base: 'OU=*OU где искать пользователей*,DC=*имя-*,DC=*-вашего-,DC=*-домена*' user_filter: '(memberof=CN=gitlab_users,OU=AccessGroups,OU=*OU где искать пользователей*,DC=*имя-*,DC=*-вашего-,DC=*-домена*)' EOS |
Небольшое отступление с комментариями:
- Для gitlab был создан пользователь GITUSER с паролем PASSWORD в структуре OU домена «OU=ServiceUsers» (‘CN=GITUSER,OU=ServiceUsers,OU=*OU где искать пользователей*,DC=*имя-*,DC=*-вашего-,DC=*-домена*’)
- Пользователь этот был лишен всяческих доменных привилегий путем удаления из группы «пользователи домена» ( для этого создаете любую группу пустышку, добавляете пользователя в нее, делаете эту группу для него первичной и удаляете все лишние из настроек пользователя)
- Использован фильтр ( чтобы не все доменные пользователи могли логиниться) — по членству в группе «gitlab_users» из OU AccessGroups (таковые были созданы). В итоге те, кто входит в эту группу- могут и в Gitlab (user_filter: ‘(memberof=CN=gitlab_users,OU=AccessGroups,OU=*OU где искать пользователей*,DC=*имя-*,DC=*-вашего-,DC=*-домена*)’)
После добавления этого нужно сделать 3 действия:
-
gitlab-ctl reconfigure — реконфигурируем гитлаб. должно пройти без ошибок
-
gitlab-rake gitlab:ldap:check — проверим работу ldap подключения. Должно пройти без ошибок и показать только те учетки, которые могут авторизоваться (входят в соответствующую группу в AD).
-
перезагрузить виртуалку. Ну или перезапустить сервис gitlab (gitlab-ctl restart). у меня без этого в веб интерфейсе не появлялась вкладка. после перезапуска сервиса нужно подождать немного, пока не переподнимется сервер приложений а то nginx отдает 502
Потом выключаем возможность самостоятельной регистрации в сервисе (sign-up)
1 |
Admin Area > Settings > Sign-up Restrictions - убрать галочку с Sign-Up enabled |
Первичная настройка завершена- дальше только настройка логики через веб интерфейс!
П.С. Особо хочу отметить — CE версия не умеет матчить в базу группы из AD. Поэтому кто там есть кто (юзер, админ и пр) — придется вручную определять внутри самого gitlab. При первом логине AD пользователя, создается локальный матчинг в базе пользователей Gitlab — подтягивается логи, фио, имейл, пароль и пр. После этого через админку самого gitlab юзера можно запихивать в группу.
Резервное копирование
Оговорюсь сразу — исторически сложилось так, что местом хранения резервных копий в этом проекте является Windows File share ( Виндовая файлопомойка — говоря по русски). Отказываться от этого решения никто не хотел, моей задачей было его поддержать. В Вашем случае коллеги, можно выбрать что угодно иное!
Резервное копирование устроено просто:
-
На файлсервере, выполняющем роль хранилища бекапов сделана файлшара, специально под git сервер
-
На самом сервере установлен veeam agent for linux для бекапа самой виртуалки «на горячую» — на случай краха сервера
-
Плюс сделан скрипт который бекапит конфиги и данные gitlab на случай если вируталка останется живой но пострадают данные (например в результате обновления)
Файл шара:
-
Основная папка — «E:\git»
-
Папка для бекапов виртуалки — «E:\git\git git-backup» (называется так потому что veeam формирует имя каталога из имени сервера — git + имя задачи — git-backup)
-
Папка для бекапов скриптом — «E:\git\data-backup» — там две подпапки — «cfg» и «gitlab_backups». Первое это конфиги, второе данные самого gitlab
Хранение бекапов:
-
Конфиги в папке cfg хранятся в виде архива «etc_gitlab-backup-(дата бекапа).tar.gz» (например etc_gitlab-backup-11-04-18.tar)
-
Данные в папке «gitlab_backups» хранятся в виде архива «(id бекапа)_(дата)_(версия сервера)_gitlab_backup.tar» — без сжатия ( например 1541342986_2018_11_04_11.4.4_gitlab_backup.tar)
-
Бекапы виртуалок хранятся в «git git-backup» в виде файлов «git-backup_(дата)(id бекапа).vbk» (например git-backup_2018-11-04T165941.vbk)
Настройка бекапов
В gitlab уже встроен функционал для создания резервных копий: https://docs.gitlab.com/ee/raketasks/backup_restore.html
По сути, используемый мною ( и написанный) скрипт является не более чем оберткой, вызывающей команду:
1 |
/usr/bin/gitlab-rake gitlab:backup:create STRATEGY=copy |
плюс выполняющий дополнительные действия.
Конфигурация самого gitlab движка для создания бекапов так, как надо нам (файл /etc/gitlab/gitlab.rb):
1 2 3 4 5 6 7 8 9 |
gitlab_rails['backup_upload_connection'] = { :provider => 'Local', :local_root => '/home/backup' } # The directory inside the mounted folder to copy backups to # Use '.' to store them in the root directory gitlab_rails['backup_upload_remote_directory'] = 'gitlab_backups' gitlab_rails['backup_archive_permissions'] = 0644 |
Скрипт лежит по пути:
1 2 |
root@git:~# ls -l /home/scripts/git_backup.sh -rwxr-xr-x 1 root root 3961 ноя 4 17:51 /home/scripts/git_backup.sh |
и запускается cron задачей (уж простите но логины, пароли и прочее я тут подтер)
1 2 3 4 |
root@git:~# crontab -l # резервное копирование данных гитлаба 0 1 * * * /home/scripts/git_backup.sh gitlab /etc/gitlab /home/backup //win-fs01.*имя сервера*/git/data-backup (backup USER) (backup password) &>/var/log/git-backup.log |
Лог бекапа выглядит таким образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
root@git:~# cat /var/log/git-backup.log Starting backup of [/etc/gitlab] folder to [//win-fs01.*имя сервера*/git/data-backup] * [Mount smb share] - Done! * [Test write access] - Done! * [Backup config files] - Done! * [Test backup archive] - Done! Dumping database ... Dumping PostgreSQL database gitlabhq_production ... [DONE] done Dumping repositories ... done Dumping uploads ... done Dumping builds ... done Dumping artifacts ... done Dumping pages ... done Dumping lfs objects ... done Dumping container registry images ... [DISABLED] Creating backup archive: 1541357698_2018_11_04_11.4.4_gitlab_backup.tar ... done Uploading backup archive to remote storage gitlab_backups ... done Deleting tmp directories ... done done done done done done done done Deleting old backups ... skipping * [Backup GitLab data via internal backup tool] - Done! * [Check and delete old backups] - Done! * [Unmount smb share] - Done! : backup job [gitlab] done successfully. |
Текст скрипта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
#!/bin/bash # ### Description: Script for static files backup. ### This version is Ubuntu server 16.04 compatible. ### Version: 0.1 ### Written by: Kirill Kazarin, Russia, SPB, 06-2018 - kazarin.ka@yandex.ru ### Usage: bash *scriptname* task_name backup_source_dir mount_folder smb_share_destination smb_user smb_password ### Example: bash file_backup.sh wiki /home/data /home/backup //192.168.100.1/backup_test kirill Qwerty.123 TASK_NAME="$1" SOURCE_DIR="$2" MOUNT_DIR="$3" SMB_SHARE="$4" SMB_USER="$5" SMB_PASSWD="$6" LOG_FILE="backup.log" BACKUP_DATE=$(date +"%m-%d-%y") BACKUP_FILE="$MOUNT_DIR"/cfg/etc_"$TASK_NAME"-backup-"$BACKUP_DATE".tar.gz BACKUP_AGE=7 # delete files older that this value ## check result of operation operation_result() { # arguments: # $1 - return code # $2 - action name if [ ! "$1" -eq 0 ]; then printf " * There was a problem during [%s] \nSee [%s] for more information!\n" "$2" "$LOG_FILE" printf "%s: backup job %s failed.\n" "$BACKUP_DATE" "$TASK_NAME" >> "$LOG_FILE" exit 1 fi printf " * [%s] - Done!\n" "$2" | tee -a "$LOG_FILE" } ## Check that script running with root privileges if [ "$EUID" -ne 0 ]; then printf "Use root account for backup! \n" exit 1 fi ## Check that user don't forget about all arguments if [ "$#" -ne 6 ]; then printf "Illegal number of parameters \nParameters: task_name, source_directory, mount_point, smb_share_addr, smb_user, smb_password!\n" exit 1 else printf "Starting backup of [%s] folder to [%s]\n" "$SOURCE_DIR" "$SMB_SHARE" | tee -a "$LOG_FILE" fi ## Check if a source (backup) directory does not exist if [ ! -d "$SOURCE_DIR" ] then printf "Directory [%s] DOES NOT exists!\n" "$SOURCE_DIR" | tee -a "$LOG_FILE" exit 1 fi ## Checking that cifs-utils package installed DEP="$(which mount.cifs | wc -l)" if [ "$DEP" -ne 1 ]; then printf " * cifs-utils package not installed!\n" | tee -a "$LOG_FILE" exit 1 fi ## Check that smb share already mounted. If not - mount it! if [[ $(findmnt -M "$MOUNT_DIR") ]]; then printf " * [%s] already mounted, continue...\n" "$MOUNT_DIR" | tee -a "$LOG_FILE" else ## Mount smb share for backup mount -t cifs "$SMB_SHARE" "$MOUNT_DIR" --verbose -o username="$SMB_USER",password="$SMB_PASSWD",uid=git,file_mode=0660,dir_mode=0775&>> "$LOG_FILE" operation_result $? "Mount smb share" fi ## Check that we can write into this folder and delete this file echo "Test_1234567890_string." &>> "$LOG_FILE" > "$MOUNT_DIR"/test.txt \ && rm "$MOUNT_DIR"/test.txt &>> "$LOG_FILE" operation_result $? "Test write access" ## start backup process tar -czf "$BACKUP_FILE" "$SOURCE_DIR" &>> "$LOG_FILE" operation_result $? "Backup config files" ## check archive after backup tar tzf "$BACKUP_FILE" &> /dev/nul if [ ! "$?" -eq 0 ]; then printf " * [Test backup archive] - failed!\n" | tee -a "$LOG_FILE" ## Unmount smb share umount "$MOUNT_DIR" &>> "$LOG_FILE" operation_result $? "Unmount smb share" exit 1 else printf " * [Test backup archive] - Done!\n" | tee -a "$LOG_FILE" fi ## Create gitlab backup /usr/bin/gitlab-rake gitlab:backup:create STRATEGY=copy | tee -a "$LOG_FILE" operation_result $? "Backup GitLab data via internal backup tool" ## Delete old backups find "$MOUNT_DIR" -mtime +"$BACKUP_AGE" -type f -delete &>> "$LOG_FILE" operation_result $? "Check and delete old backups" ## Unmount smb share umount "$MOUNT_DIR" &>> "$LOG_FILE" operation_result $? "Unmount smb share" ## writes results in log printf "%s: backup job [$TASK_NAME] done successfully.\n" | tee -a "$LOG_FILE" exit 0 |
Как видно из листинга, скрипт делает ряд шагов:
- Проверяет все что нужно ему для работы ( права, установленное ПО, набор аргументов)
- Проверяет не смонтирована ли уже файл-шара, если нет то монтирует ее иначе шаг пропускается
- Тестируем возможность записать что-то на файл-шару
- Делает своими средствами бекап конфигурации GitLab
- Использует механизм Gitlab для резервного копирования
- Удаляет старые бекапы ( по умолчанию все что старше 7 дней)
- Размонтирует файл шару
Параллельно скрипт ведет полный лог действий.
Надеюсь статья была Вам полезна, коллеги!