Files
ddadmin 4ad904efd0
Deploy MkDocs to VPS / deploy (push) Successful in 13s
docs(site): дополнены инструкции восстановления публикации
- Зачем:
  - runbook требовал незафиксированного контекста для первого деплоя и восстановления сайта на чистой VPS.
- Что:
  - добавлены prerequisites, установка пакетов, первый workflow, UFW и проверенный атомарный откат.
  - спецификация и PRD обновлены по факту завершённого переноса на VPS.
  - локальные команды Snap uv заменены на persistent Python venv.
- Проверка:
  - mkdocs build --strict выполнен через persistent venv пользователя gitea-runner.
  - последовательность rollback проверена на временном дереве releases и symlink.
2026-08-05 03:14:52 -04:00
..

Эксплуатация публикации de.dementev.space

Этот runbook реализует спецификацию 2026-08-04-gitea-vps-site-publishing.md. Команды рассчитаны на Ubuntu 26.04 и Gitea 1.27.

Зафиксированные параметры

  • Gitea Runner: 2.3.0, Linux amd64.
  • SHA256: 1e9fb1bea022fdf40993ecbc1a13e87db1bfd3d7f42666e37f15f71d840d53b3.
  • Runner: repository-scoped, метка de-roadmap-host:host.
  • Пользователь сервиса: gitea-runner, без sudo и Docker.
  • Корень публикации: /srv/de-roadmap.
  • Хранение: текущий релиз и две предыдущие версии.
  • Окружение сборки: /var/lib/gitea-runner/venvs/site.
  • Публичный IPv4 VPS: 167.224.64.252.

Предварительные условия

  • Есть пользователь с sudo и рабочий SSH-доступ к VPS.
  • Есть доступ к управлению DNS-зоной dementev.space.
  • В репозитории ddmitry/de-roadmap включены Gitea Actions.

Подготовка VPS

Установить Git, HTTP-сервер, Certbot, UFW и Python venv:

sudo apt-get update
sudo apt-get install \
  ca-certificates \
  certbot \
  curl \
  git \
  jq \
  nginx \
  python3-certbot-nginx \
  python3-venv \
  ufw

Получить административный checkout, из которого устанавливаются tracked-файлы:

git clone https://git.dementev.space/ddmitry/de-roadmap.git
cd de-roadmap

Создать пользователя и каталоги:

sudo useradd \
  --system \
  --home-dir /var/lib/gitea-runner \
  --create-home \
  --shell /usr/sbin/nologin \
  gitea-runner
sudo install -d -o gitea-runner -g gitea-runner -m 0755 \
  /var/lib/gitea-runner/workspaces \
  /srv/de-roadmap \
  /srv/de-roadmap/releases
sudo install -d -o root -g root -m 0755 /etc/gitea-runner

Скачать runner во временный каталог, сверить checksum и установить root-owned бинарник в /usr/local/bin/gitea-runner:

runner_tmp_dir=$(mktemp -d /tmp/de-roadmap-runner.XXXXXX)
curl -fsSLo "${runner_tmp_dir}/gitea-runner" \
  https://dl.gitea.com/gitea-runner/2.3.0/gitea-runner-2.3.0-linux-amd64
printf '%s  %s\n' \
  '1e9fb1bea022fdf40993ecbc1a13e87db1bfd3d7f42666e37f15f71d840d53b3' \
  "${runner_tmp_dir}/gitea-runner" \
  | sha256sum --check
sudo install -o root -g root -m 0755 \
  "${runner_tmp_dir}/gitea-runner" /usr/local/bin/gitea-runner
rm "${runner_tmp_dir}/gitea-runner"
rmdir "$runner_tmp_dir"

Из корня репозитория установить конфигурацию и unit:

sudo install -o root -g root -m 0644 \
  project/ops/gitea-vps-site/gitea-runner.yaml \
  /etc/gitea-runner/config.yaml
sudo install -o root -g root -m 0644 \
  project/ops/gitea-vps-site/gitea-runner.service \
  /etc/systemd/system/gitea-runner.service
sudo systemd-analyze verify /etc/systemd/system/gitea-runner.service

Регистрация runner

Repository registration token получают в Gitea: ddmitry/de-roadmap → Settings → Actions → Runners. Токен не сохраняют в Git или shell history. Временный файл с токеном создают с владельцем gitea-runner:gitea-runner и режимом 0600. После регистрации файл /var/lib/gitea-runner/.runner должен принадлежать тому же пользователю и иметь режим 0600.

Token-file должен содержать ровно 40 символов без завершающего перевода строки. При извлечении JSON-ответа через jq использовать jq --join-output '.token', а не jq --raw-output, который добавляет newline.

sudo -u gitea-runner \
  /usr/local/bin/gitea-runner \
  --config /etc/gitea-runner/config.yaml \
  register \
  --no-interactive \
  --instance https://git.dementev.space \
  --token-file /var/lib/gitea-runner/.registration-token \
  --name de-roadmap-vps
sudo rm /var/lib/gitea-runner/.registration-token
sudo chmod 0600 /var/lib/gitea-runner/.runner
sudo systemctl daemon-reload
sudo systemctl enable --now gitea-runner
systemctl is-enabled gitea-runner
systemctl is-active gitea-runner

Временный файл с токеном удаляют сразу после успешной регистрации. В Gitea на странице Settings → Actions → Runners runner de-roadmap-vps должен перейти в состояние online и показывать метку de-roadmap-host.

Nginx и первичная публикация

Из корня репозитория установить bootstrap-конфигурацию nginx, создать ссылку, отключить стандартный сайт Ubuntu и только затем перечитать проверенную конфигурацию:

sudo install -o root -g root -m 0644 \
  project/ops/gitea-vps-site/nginx.conf \
  /etc/nginx/sites-available/de-roadmap
sudo ln -s \
  /etc/nginx/sites-available/de-roadmap \
  /etc/nginx/sites-enabled/de-roadmap
sudo unlink /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

Первый server block в nginx.conf возвращает 404 для неизвестных HTTP Host, не раскрывает версию nginx и отклоняет TLS handshake для IP или неизвестного SNI. Поэтому сертификат de.dementev.space не выдаётся при обращении к VPS по IP. Если проверка конфигурации не прошла, отключить новый virtual host, восстановить стандартный сайт и перечитать проверенную конфигурацию:

sudo unlink /etc/nginx/sites-enabled/de-roadmap
sudo ln -s \
  /etc/nginx/sites-available/default \
  /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

Файл project/ops/gitea-vps-site/nginx.conf предназначен только для запуска до выпуска сертификата. После выпуска сертификата Certbot изменяет установленный virtual host. Повторная установка bootstrap-файла поверх рабочего конфига удалит TLS-директивы.

Окружение сборки

Скрипт .gitea/scripts/build-site.sh создаёт persistent venv при первом запуске и переиспользует его в следующих сборках. pip install выполняется каждый раз, чтобы применить изменения .gitea/requirements-site.txt, но уже установленные версии пакетов не переустанавливаются.

Первый деплой

В Gitea открыть Actions → Deploy MkDocs to VPS, выбрать ветку main и нажать Run workflow. Job deploy должен завершиться успешно. Проверить опубликованный release и локальную выдачу nginx до переключения DNS:

readlink -f /srv/de-roadmap/current
curl --fail --header 'Host: de.dementev.space' http://127.0.0.1/

Затем разрешить SSH, HTTP и HTTPS в UFW. Если SSH работает не на стандартном порту 22, сначала разрешить фактический порт вместо профиля OpenSSH:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status verbose

Ожидается политика deny (incoming) и разрешения только для SSH, 80/tcp и 443/tcp.

DNS и TLS

  1. Уменьшить TTL записи de.dementev.space.

  2. Направить A на VPS; удалить или корректно направить AAAA.

  3. Убедиться, что сайт доступен извне по HTTP:

    curl --fail --head http://de.dementev.space/
    
  4. Выпустить сертификат и включить перенаправление HTTP на HTTPS:

    sudo certbot --nginx \
      --non-interactive \
      --agree-tos \
      --email me@dementev.space \
      --redirect \
      -d de.dementev.space
    
  5. В созданном Certbot HTTP-блоке для de.dementev.space добавить server_tokens off;, затем проверить и перечитать конфигурацию:

    sudoedit /etc/nginx/sites-available/de-roadmap
    sudo nginx -t
    sudo systemctl reload nginx
    
  6. Проверить перенаправление, HTTPS, сертификат и автоматическое продление:

    curl --head http://de.dementev.space/
    curl --fail --head https://de.dementev.space/
    ! curl --insecure --head https://167.224.64.252/
    sudo certbot certificates
    systemctl is-enabled certbot.timer
    systemctl is-active certbot.timer
    

    Ожидаются 301 Moved Permanently, затем 200 OK, отказ TLS по IP, действующий сертификат и состояния таймера enabled и active.

  7. Вернуть обычный DNS TTL.

Проверка и откат

Активная версия определяется ссылкой /srv/de-roadmap/current. Для ручного отката сначала выбрать точный release id из сохранённых каталогов, затем создать временную ссылку и атомарно заменить current:

find /srv/de-roadmap/releases \
  -mindepth 1 \
  -maxdepth 1 \
  -type d \
  -printf '%f\n' \
  | sort

rollback_release='<COMMIT_SHA>-<RUN_ID>'
rollback_link='/srv/de-roadmap/.current.rollback'
[[ "$rollback_release" =~ ^[0-9a-f]{40}-[0-9]+$ ]]
sudo test -d "/srv/de-roadmap/releases/${rollback_release}"
sudo test ! -e "$rollback_link"
sudo -u gitea-runner \
  ln -s "releases/${rollback_release}" "$rollback_link"
sudo -u gitea-runner \
  mv -Tf "$rollback_link" /srv/de-roadmap/current
readlink -f /srv/de-roadmap/current
curl --fail --head https://de.dementev.space/

Откат не удаляет более новые releases. Перед их ручным удалением всегда проверять результат readlink -f /srv/de-roadmap/current.

Диагностика:

sudo systemctl status gitea-runner
sudo journalctl -u gitea-runner
sudo nginx -t
readlink -f /srv/de-roadmap/current
curl --fail --head https://de.dementev.space/