Привет. Это закономерное продолжение предыдущей статьи про Gitea, когда я взял сервак и тупо вкатил её туда без особых заморочек.
Обстоятельства изменились и теперь я хочу большего. Обо всём этом далее — как обычно, в виде описания процесса и готового решения. Рекомендую почитать, потому что это может дать пищу для размышлений и идей, а также понимание происходящего.
Под хостом в тексте понимается машина, на которой производятся операции. Это может быть твой ПК или сервер.
Содержание
Исходные данные
- Виртуальный сервер на Ubuntu 22.04 с «белым» IP
- На сервере установлены:
- бинарник Gitea последней версии
nginx
последней версии как реверс-прокси- MariaDB 10.6 как СУБД
- Docker не используется
- На сервере прописан ssh-ключ юзера хоста
Задача
- Добиться полной переносимости Gitea и воспроизводимости окружения засчёт Docker для деплоя как нового инстанса, так и его резервной копии
- Перенести основной инстанс Gitea на другой виртуальный сервер с «белым» IP уже в Docker-окружении
- Внедрить OpenGist в общее Docker-окружение
- Настроить домены и SSL
- Наладить резервное копирование на сервере / с сервера, развернуть резервные инстансы в домашней сети
Что будет использовано: git
, ssh
, scp
, nginx
, certbot
, docker
, docker-compose-plugin
, любой текстовый редактор
Общее описание
Начал я со сборки compose.yml
для докера по кускам из документации gitea rootless. Подготовил небольшой файл .env
— из него будут подсасываться всякие секретики при деплое. Сложные пайплайны и волты здесь не нужны, файлика вполне хватит.
В качестве СУБД выбрана MariaDB как здоровая альтернатива гигачаду-Postgres и чимсу-SQLite. Версия для гити выбрана та, на которой крутилась исходная гити с момента первоначального разворота — уже довольно старая 10.6. Для гиста я выбрал ту же версию: его я разворачиваю впервые и бэкапа нет, так что можно было бы взять и посвежее, но зато так не придётся качать два разных докер-образа.
Главная идея в том, что я должен получить связку из двух сервисов, которые должны уметь стартовать из бэкапов. Так что сразу вписываю Opengist в тот же компоуз-файл и готовлю сервисам по директории. В этих директориях будут конфиги и директории с файлами, и могут быть файлики с дампами БД. При наличии дампов Маша их автоматически выжмет из .sql.gz и импортирует, предварительно создав нужных юзеров засчёт реквизитов из переменных окружения.
Но т. к. файлов дампов может и не быть (читай, инициализируем сервисы с нуля), то нам нужно предусмотреть файл compose.override.yml
с дополнительными volume
для обеих СУБД. Если прописать их в основной композ, а файлов не будет, то докер создаст директории с теми же именами и владельцем root
. Мелочь, но неприятно.
Мой кейс как раз с бэкапами гити (это я решал в первую очередь, а для гиста просто применил тот же конфиг), так что иницализацию чистых сервисов я пока особо не рассматривал — просто подстелил соломку.
На прошлом сервере гити работала на уровне системы, поэтому там не было особой возни с ssh. Но когда гити в контейнере, то документация даёт такой хитрый схематоз: для хостового юзера git
нужно создать скрипт-оболочку, который будет прокидывать команды в контейнер гити, а авторизовываться юзер будет по ключам из контейнера гити. Этот скрипт я ещё слегка доработал, дальше увидишь как. Мелочь, но приятно.
И последнее, что стоит упомянуть, это конфиги самих сервисов. Ну, они будут просто лежать в своих директориях. Останется только скорректировать настройки и реквизиты под новую среду, если потребуется.
Теперь сладенькое.
Порядок действий
Склонируем репозиторий и подготовим файлы:
git clone https://git.axenov.dev/anthony/gitea-opengist.git
cd gitea-opengist
cp .env.example .env
Резервирование старой среды
Зайдём на сервер Gitea и потушим её. Выполним команды, учитывая корректные пути:
cp compose.override.yml.example compose.override.yml
cd ./gitea
scp user@example.com:/etc/gitea/app.ini ./
scp -r user@example.com:/var/lib/gitea ./data
ssh user@example.com \
mysqldump -u$DB_USER -p$DB_PASSWORD $DB_NAME \
| gzip -9 - \
> ./dump.sql.gz
где:
user@example.com
— пользовательuser
на сервереexample.com
, где развёрнута Gitea (можно обращаться просто по тому имени, которое задано в~/.ssh/config
);$DB_USER
,$DB_PASSWORD
и$DB_NAME
— переменные, которые должны содержать соответствующие параметры для подключения к БД и снятия её дампа.
Для сервиса Opengist, при необходимости, повтори аналогичные шаги. Я этого не делал, но отличий будет минимум.
Снова запускать Gitea на сервере особого смысла нет. Теперь ты работаешь с тем, что выгрузил.
Файл compose.override.yml
нужен будет один раз, после успеха его можно будет удалить.
Подготовка к запуску
Когда я настраивал прошлый сервер, я выполнял такую команду:
adduser \
--system \
--shell /bin/bash \
--gecos 'Gitea user' \
--group \
--disabled-password \
--home /home/git \
git
Если сейчас такого юзера нет, то надо выполнить эту команду. Обрати внимание на --disabled-password
— да, пользователь будет входить без пароля, но по ключам. Об этом ниже.
UID/GID юзера git
должны быть 1000
/1000
, если не — исправляем:
usermod -u 1000 git
usermod -g 1000 git
Директории {gitea/opengist}/data
должны принадлежать юзеру UID=1000
и группе GID=1000
. Если не, то выполняй:
chown git: -R {gitea/opengist}/data
Но вообще я бы рекомендовал сделать это для всего репозитория ./gitea-opengist
, от греха.
Права на всю файловую структуру должны сохраниться со старого сервера.
В файле .env
подготовь все необходимые настройки и реквизиты. Некоторые из них обязательны, а некоторые — наеборот.
В конфиге gitea/app.ini
скорректируй настройки, чтобы они хоть немного соответствовали новому окружению. Как минимум, это реквизиты подключения к БД, порты и адреса самой гити.
Конфиг opengist/opengist.yml
может отсутствовать, т. е. сервис может быть настроен только переменными окружения в compose.override.yml
, а главная из них для подключения к БД уже прописана в основном композе. Так что это на твоё усмотрение.
Максимальная комплектация файлов может выглядеть так (но всё зависит от твоих обстоятельств и необходимостей):
./gitea-opengist
├── gitea
│ ├── app.ini
│ ├── data
│ ├── dump.sql.gz
│ └── gitea.sh
├── opengist
│ ├── data
│ ├── dump.sql.gz
│ └── opengist.yml
├── .env
├── .env.example
├── compose.override.yml
├── compose.override.yml.example
└── compose.yml
Запуск
Пробуем запустить стек:
docker compose up -d --build
Смотрим логи контейнеров:
docker logs -f gitea
docker logs -f gitea-db
docker logs -f opengist
docker logs -f opengist-db
Логи контейнеров *-db
первичны, т.к. там можно будет найти записи об импорте дампов (при наличии) и готовности Маши к подключениям после.
Логи контейнеров сервисов тоже нужно посмотреть, потому что они должны успешно подключиться к своим БД. Если что-то пойдёт не так, они об этом сообщат. Скорее всего, если проблемы и будут, то на уровне реквизитов или прав на ФС.
Дальше проверяем веб-морды.
Заходим на localhost:8080 — это гити. Если дампа не было, то она предложит установку. Идеальный результат таков: ты видишь список репозиториев, логинишься и навигируешься как ни в чём не бывало, в URL для клонирования корректные адреса и порты.
Заходим на localhost:8081 — это Opengist. Он тупо будет работать сразу: он намного проще гити, ему какая-то особая процедура установки не требуется и проблем, скорее всего, не встретишь. Но если дампа не было, то нужно сразу зарегистрироваться — первый юзер будет верховным админом. Идеальный результат должен быть похож на гити.
Другой способ — через curl -I ...
Gitea + ssh
Вот ещё мякотка. Я в начале упомянул про хитрость и ты уже видел gitea/gitea.sh
. Это как раз та оболочка, в которую будет входить юзер git
по ssh.
На хосте назначаем ему этот скрипт как оболочку:
usermod -s /home/user/gitea/gitea/gitea.sh git
где путь /home/user/gitea/gitea/gitea.sh
должен быть корректным абсолютным адресом файла в твоей ФС.
Обрати внимание на if
внутри скрипта. Если gitea
и/или gitea-db
будут лежать, при git push
(и не только) в stderr
будет выводиться сообщение о недоступности хранилища:
$ git push
============================================
Gitea is currently offline. Try again later.
============================================
fatal: Не удалось прочитать из внешнего репозитория.
Удостоверьтесь, что у вас есть необходимые права доступа
и репозиторий существует.
Чтобы юзер git
мог пройти авторизацию, нужны ключи, которые существуют только в гити, а хостовый sshd
должен их как-то получить. Так что создаём конфиг /etc/ssh/sshd_config.d/gitea.conf
и пишем в него это:
Match User git
AuthorizedKeysCommandUser git
AuthorizedKeysCommand /usr/bin/docker exec -i gitea /usr/local/bin/gitea keys -e git -u %u -t %t -k %k
Чтобы всё заработало, перечитываем конфиги:
systemctl reload sshd
Через это мы обезопасиваем… обезопаши… обезопасим беспарольный вход.
Opengist + ssh
Если ты не знал, каждый гист есть репозиторий, даже в гитхабе. Разница только в гуйне.
Но я, как и многие другие, привык с гистами работать как с гуёвым заметочником. Поэтому я не задавался целью наладить ssh в Opengist.
Так что оставлю этот блок на будущее, может быть позже разберусь ради любопытства и опишу эти настройки.
Настройка доменов
Первично надо в DNS своих (под)доменов настроить A/AAAA-записи на новый сервер. Пока идёт обновление, займёмся реверс-прокси.
Для примера мы разместим всё на доменах git.example.com
и gist.example.com
. У тебя будет что-то своё.
Устанавливаем nginx
и готовим конфиги:
/etc/nginx/sites-available/gitea.conf
# /etc/nginx/sites-available/gitea.conf
server {
listen 80;
listen [::]:80;
server_name git.example.com
access_log /var/log/nginx/gitea-access.log;
error_log /var/log/nginx/gitea-error.log;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
# /etc/nginx/sites-available/opengist.conf
server {
listen 80;
listen [::]:80;
server_name gist.example.com
access_log /var/log/nginx/opengist-access.log;
error_log /var/log/nginx/opengist-error.log;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:8081;
}
}
Несложно догадаться, что nginx будет слушать порт 80 хоста и просто прокидывать все запросы на http-серверы, которые, в свою очередь, прокинуты на хост в порта 8080/8081 из контейнеров с дефолтных портов сервисов.
Чтобы это заработало, нужно сделать симлинки конфигов в /etc/nginx/sites-enabled
(тем «включив» конфиги) и перезапустить веб-сервер:
ln -s /etc/nginx/sites-available/gitea.conf \
/etc/nginx/sites-enabled/gitea.conf
ln -s /etc/nginx/sites-available/opengist.conf \
/etc/nginx/sites-enabled/opengist.conf
unlink /etc/nginx/sites-enabled/default
systemctl restart nginx
Это необходимый и достаточный минимум для старта.
За это время наверняка обновились глобальные DNS-записи. Это можно проверить так:
nslookup git.example.com
nslookup gist.example.com
В ответе должно быть эдакое:
$ nslookup git.example.com
Server: 127.0.0.53
Address: 127.0.0.53#53
Non-authoritative answer:
Name: git.example.com
Address: %твой новый IP-адрес%
Проще всего проверить через curl -i http://git.example.com
Для гиста всё проверяем также. В ответах должны быть успехи и html соответствующих сервисов. Их легко отличить от минималистичных дефолтных заглушек nginx.
Потерпи немношк, скоро финиш.
Настраиваем SSL
Устанавливаем свет-наш-ясно-солнышко certbot
и плагин к нему python3-certbot-nginx
.
Настройка SSL на доменах сводится к короткой процедуре:
certbot --nginx
Указываем свой email, (не)соглашаемся на рассылку. Будет показан список доменов, который считан согласно конфигов nginx
. Выбираем любой один, немного ждём, видим успех, проверяем в браузере и радуемся. Повторяем для второго домена.
Автопродление сертификатов будет происходить автоматически по крону.
Резервирование новой среды
Для этого можно использовать те же команды, которые использовались в начале. Но теперь у нас СУБД внутри контейнеров, поэтому команду снятия дампа мы не выполняем на хосте, а отправляем в контейнер:
ssh user@example.com \
"docker exec gitea-db mysqldump -u$DB_USER -p$DB_PASSWORD $DB_NAME" \
| gzip -9 - > ./dump.sql.gz
Должно работать. Аналогично для гиста.
Директории можно просто качать по scp как в начале. Но я покажу лайфхак, похожий на выкачку дампа:
ssh user@example.com \
"tar -zOc /home/git/gitea/gitea/data" \
> backup.tar.gz
Это сохранит .tar.gz архив с директорией сразу на твою тачку. Как это применить придумай сам.
Пфух, всё, вроде ничего не забыл. Работа в целом на уровне типичного вебмастера нулевых, только ещё с докером. Просто текста получилось многовато.
Изучай, пробуй, предлагай доработки.
Вероятно, как обычно это бывает, я буду сюда возвращаться и потихоньку корректировать статью и реп.
Использованные материалы
- https://axenov.dev/install-gitea/
- https://opengist.io/docs/
- https://docs.gitea.com/installation/install-with-docker-rootless
- https://docs.gitea.com/administration/backup-and-restore
- https://github.com/MariaDB/mariadb-docker/blob/master/README.md
- https://forum.gitea.com/t/move-gitea-to-a-new-docker-host/4197