Выкатка и откат
> Кратко. Веб собирают в отдельном рабочем дереве и подменяют каталог .output целиком, при остановленных службах. API переводят fast-forward на заранее известный коммит и перезапускают службы по одной. На каждом шаге есть проверка и автоматический возврат, а откат — тот же скрипт с ключом -Rollback.
Ниже — порядок, по которому устроены скрипты выкатки этого проекта (Windows, службы NSSM). Пути даны как пример раскладки, у вас они могут быть другими.
Раскладка на сервере
| Что | Пример |
|---|---|
Клон репозитория API (код, venv, .env) |
H:\deploy\tgid-server\itwin-api |
Веб: исходники, node_modules, .env, рабочая сборка .output |
H:\deploy\tgid-web |
| Рабочее дерево git для сборок веба | H:\deploy\tgid-web-build… |
| Журналы скриптов и служб | H:\deploy\logs |
| Состояние выкатки для отката (JSON с прежним коммитом) | H:\deploy\backups |
Службы:
| Служба | Что | Порт |
|---|---|---|
tgid-api-multi, tgid-api-multi-2 |
API на все города, по одному процессу uvicorn; nginx делит запросы между ними (least_conn) |
8010, 8017 |
tgid-worker-multi-1…N |
Воркеры Celery, -P solo, очереди всех городов |
— |
tgid-web, tgid-web-<город> |
Веб, по службе на город, общий каталог .output |
свой у каждого |
tgid-martin |
Тайлы сети | 3005 |
Два процесса API, а не --workers 2: на Windows второй процесс uvicorn при общем сокете стоит в accept()
до следующего соединения и не отвечает — иногда часами.
Веб
Почему не собирать в рабочем каталоге. Сервер Nuxt держит манифест сборки в памяти. Новая сборка в том же
.output удаляет старые файлы _nuxt/*.js, а работающие службы продолжают вставлять их в страницы. В итоге карта
не грузится, пока службы не перезапустят.
- Подготовка. В рабочем дереве сборки — нужный коммит:
git -C <дерево> checkout --detach <коммит>.node_modulesв дереве — ссылка (junction) наnode_modulesрабочего веба. - Сборка.
node build-tgid-web-with-env.mjs <путь к .env веба> <дерево>. Скрипт читает.envв окружение (уже заданные переменные не перекрывает), значения не печатает и запускаетnpm run build. Ключи подложек, адрес API и каталог слоёв GeoServer запекаются при сборке. - Проверка сборки. Запустите
.output/server/index.mjsиз дерева на свободном порту и откройте страницу. - Предпроверки скрипта выкатки (ничего не меняют):
- службы
tgid-web*есть, рабочая и новая сборки на месте; - дерево сборки на ожидаемом коммите;
- ни один JS-файл новой сборки не совпадает по имени с работающей. Имена солятся коммитом сборки, поэтому у вернувшихся пользователей не смешаются две сборки;
- резервной копии этой выкатки (
.output.bak-<дата>) ещё нет, то есть выкатку не запускали.
- службы
- Подмена. Остановить все службы
tgid-web*→ рабочий.outputпереименовать в.output.bak-<дата>→ новый.outputперенести на его место → запустить службы. Веб недоступен 20–40 с. - Проверка. Для каждого города запрашивается страница
/tgid/<город>/, из неё берётся entry-скрипт_nuxt/…. Обе ссылки должны ответить 200. Не поднялся хоть один город — службы снова останавливаются и возвращается прежняя сборка. - Откат (
-Rollback): та же подмена в обратную сторону. Неудачная сборка уходит в.output.rolled-back-<время>.
После выкатки пользователям достаточно обновить страницу (Ctrl+F5).
API
- Предпроверки (ничего не меняют):
git fetch; ожидаемый коммит есть;- текущий HEAD — его предок, то есть возможен fast-forward;
- в клоне нет изменённых файлов;
- обе службы API отвечают на
/health; - файла состояния этой выкатки ещё нет.
- Состояние. Прежний коммит записывается в JSON в каталоге резервных копий — по нему работает откат.
- Код.
git merge --ff-only <коммит>. Слияний и правок на сервере нет: только переход на проверенный коммит. - Импорт.
venv\Scripts\python.exe -c "import main"в каталоге API. Не импортируется —git reset --keepна прежний коммит, службы не трогались. - Миграции UsersDB, если в выпуске есть новая миграция в
migrations/versions:alembic upgrade headдо перезапуска. API сам UsersDB не мигрирует, а без новой колонки модель пользователя может уронить вход. Перед этим — резервная копия UsersDB. Схему баз городов API доводит сам при запуске (Несколько городов). - Перезапуск по одной. Сначала
tgid-api-multi-2, ждать ответа/healthдо 3 минут, затемtgid-api-multi. Пока одна служба перезапускается, nginx отправляет запросы во вторую — пользователи перерыва не замечают. - Не поднялась —
git reset --keepна прежний коммит и снова перезапуск обеих. - Откат (
-Rollback):git reset --keepна коммит из файла состояния, перезапуск, файл состояния переименовывается в….rolled-back.
Воркеры и движок
- Изменился
worker.pyили задачи Celery — воркеры перезапускают мягко. Командаshutdownидёт через брокер, воркер-P soloпринимает её между задачами: дорабатывает текущий расчёт и выходит. NSSM поднимает службу с новым кодом. Перезапуск средствами NSSM ждёт полторы секунды и обрывает идущий расчёт. - Код heatnet перезапуска не требует: каждый расчёт — новый процесс.
- Ядро Rust (
heatnet/_core.pyd) выкатывается отдельно: прежний файл переименовывается, а не перезаписывается, потому что идущий расчёт держит его открытым. Затем — пробные расчёты на копии сHEATNET_CORE=check. - Переменные служб (движок, флаги) хранятся в параметрах службы NSSM (
AppEnvironmentExtraвHKLM\SYSTEM\CurrentControlSet\Services\<служба>\Parameters). Меняйте их через реестр: PowerShell 5.1 искажает кавычки в аргументахnssm set.
Остановка и запуск служб
- Остановка:
nssm stop, затем ожидание до 90 с. Не остановилась — процесс службы завершается принудительно, ещё 30 с ожидания. - Запуск:
nssm start, ожидание до 60 с, одна повторная попытка. - Сообщение NSSM «Unexpected status SERVICE_STOP_PENDING» — не ошибка: скрипт ждёт сам.
Журналы выкатки
- Вывод скрипта —
logs\<имя скрипта>.log. При ошибке пришлите его целиком. - Службы пишут в
logs\<служба>.out.logиlogs\<служба>.err.log; NSSM ротирует их по 10 МБ. Ошибки запуска API — вtgid-api-multi*.err.log, веба — вtgid-web*.err.log.
Чего не делать
- Не собирать веб в каталоге, из которого работают службы.
- Не править код на сервере: выкатка — только fast-forward на коммит, который уже проверен.
- Не перезапускать обе службы API одновременно и не перезапускать воркеры средствами NSSM во время расчётов.
- Не передавать
python.exeаргументы с двойными кавычками внутри из PowerShell 5.1: кавычки срезаются, и команда ломается.
Если не получается
- Предпроверки не пройдены — скрипт ничего не менял. Причина в списке: нет коммита (не сделан push), не fast-forward (на сервере другой коммит), изменённые файлы в клоне.
- «Уже есть …json — выкатку запускали» — выкатку уже выполняли. Если её надо отменить — откат, повторить — после отката.
/healthне отвечает после перезапуска — смотритеtgid-api-multi*.err.log. Скрипт к этому моменту уже вернул прежний код./healthотвечает 200, а что-то не работает — проверьте полеstatus. Скрипты считают ответ 200 успехом, но/healthотвечает 200 и при"status": "degraded", то есть без базы или Redis — Состояние и журналы.
