Files
admin/.wiki/concepts/pilorama98-vds-deploy-runbook.md

11 KiB
Raw Blame History

title, type, tags, related, sources, updated
title type tags related sources updated
pilorama98.ru (pilonuxt) — VDS deploy runbook (стек 16, рестарт/пересборка) concept
pilorama98
pilonuxt
vds
deploy
docker
portainer
nuxt
runbook
cache
concepts/portainer-stack-management-vds.md
concepts/pilonuxt-restart-vs-redeploy-2026-08-16.md
concepts/admin-api-embed-pilonuxt.md
2026-08-26

pilorama98.ru (pilonuxt) — деплой на прод

⚙️ СЛУЖЕБНЫЙ РАНБУК — только для администратора проекта .admin. Применять/выполнять шаги может только .admin (оператор проекта admin). Другим проектам/агентам — читать по запросу, не выполнять. Из проекта не выносить: не копировать в другие вики, не пересказывать, не публиковать. Нужен деплой или прод-операция по этому ранбуку — поставить задачу админу (.admin) и написать письмо. Админ выполняет, остальные верифицируют.

Прод https://pilorama98.ru / www.pilorama98.ru = Nuxt-фронт из монорепы victor/pilorama98.ru (apps/web, Nuxt 4 / Nitro node-server preset). Хостится контейнером на VDS kzntsv, управляется Portainer-стеком. Легаси IIS (RUVDS) — rollback-путь, не используется.

Параметры

Что Значение
Хост VDS kzntsv 89.253.255.94
Стек Portainer Id 16, name pilonuxt, endpointId 1
Образ registry.kzntsv.site/pilonuxt:<git-sha7> — тег = короткий sha монорепы
Compose source-of-truth admin/host-stacks/vds-kzntsv/pilonuxt.compose.yml (в репо .admin)
Репо/код victor/pilorama98.ru @ master; apps/web/Dockerfile
БД mssql.kzntsv.site:1433 MoreThenCms (конфиг запечён в образ, см. ниже)
Версии на проде см. инвентарь ниже (текущий 2026-08-26: 8943333)

Артефакты

  • Образ: двухстадийный apps/web/Dockerfile (node:22-bookworm-slim; builder yarn workspace nuxt-app build.output; runner копирует @img нативы sharp). Сборка — workspace-сборка из КОРНЯ монорепы: -f apps/web/Dockerfile . (lockfile/.yarnrc.yml в корне). Тег = короткий git-sha коммита.
  • VERDACCIO_TOKEN — build-secret (--secret id=verdaccio_token,env=VERDACCIO_TOKEN), живой JWT, не ARG/ENV (не оседает в слоях). Источник: pass vds-kzntsv/full-envVERDACCIO_CI_TOKEN (books-ci, len ~312, 2 точки).
  • Registry auth: docker login registry.kzntsv.site -u vitya (пароль REGISTRY_PASS из pass vds-kzntsv/full-env).
  • Portainer auth: JWT POST /api/authvitya / PORTAINER_ADMIN_PASS из pass vds-kzntsv/full-env.
  • Конфиг запечён в apps/web/config/ внутри образа (MSSQL/SMTP/imgproxy ключи, siteId) — runtime env-override не настроен (решение 2026-06-14).

Две разные операции (не путать!)

Операция Когда нужна Как делается Что сбрасывает
Рестарт контейнера Контент/цены в БД изменились, код тот же Portainer POST /api/endpoints/1/docker/containers/pilonuxt/restart In-memory viewModel кэш content-api — свежие данные из MoreThenCms подтягиваются без пересборки
Пересборка (деploy) Изменился код (новый git-sha в монорепе) build → push → Portainer PUT стек 16 Код + всё вышеперечисленное

Симптом «данные в БД есть, на фронте нет» (цены/контент устарели) → рестарт, не деплой. Симптом «500 на странице со стеком @snolla/core / lodash _.find» → код/данные, не кэш — рестарт не лечит, нужна пересборка (см. concepts/pilonuxt-restart-vs-redeploy-2026-08-16.md).

Рестарт контейнера (кэш viewModel)

# Portainer docker-proxy (Python, urllib; либо .tmp/restart-stack16.ps1)
POST https://portainer.vds.kzntsv.site/api/auth   {"username":"vitya","password":"<PORTAINER_ADMIN_PASS из pass vds-kzntsv/full-env>"}
POST https://portainer.vds.kzntsv.site/api/endpoints/1/docker/containers/pilonuxt/restart   # body пустой, 204

Verify: GET .../containers/pilonuxt/jsonState.StartedAt обновился, образ тот же.

Пересборка + деплой (новый код)

# 1. Проверить коммит: локально master == origin/master, коммит — потомок
cd ~/projects/pilorama98.ru
git fetch origin master && git log --oneline -1 master

# 2. Build (из корня монорепы, build-secret). Предусловия:
#    - apps/web/src/generated/ populated (после yarn install) — иначе ensure-schema лезет в mssql
#    - .yarnrc.yml c npmAlwaysAuth: true (канон в репо)
VERDACCIO_TOKEN=$(pass show vds-kzntsv/full-env | grep '^VERDACCIO_CI_TOKEN=' | cut -d= -f2-)
docker build -f apps/web/Dockerfile --secret id=verdaccio_token,env=VERDACCIO_TOKEN \
  -t registry.kzntsv.site/pilonuxt:<sha7> .

# 3. Push
echo '<REGISTRY_PASS из pass vds-kzntsv/full-env>' | docker login registry.kzntsv.site -u vitya --password-stdin
docker push registry.kzntsv.site/pilonuxt:<sha7>

# 4. Portainer PUT стека 16 (замена тега, env preserve, pullImage)
#    GET /api/stacks/16/file → regexp замена pilonuxt:<old7> → pilonuxt:<new7>
#    PUT /api/stacks/16?endpointId=1  {stackFileContent, env: <preserve>, prune:false, pullImage:true}

⚠️ PUT сбрасывает env-массив, если не передать env в payload — у стека 16 env пуст (Env: []), передавать literal [] (плейсхолдер-трюк при PS: -replace '"__ENV__"','[]'). ⚠️ Кодировка: тело PUT слать UTF-8-байтами (PowerShell 5.1 коррапит кириллицу, gotcha 9 concepts/portainer-stack-management-vds.md). ⚠️ Синхронизировать source-of-truth host-stacks/vds-kzntsv/pilonuxt.compose.yml (тег) после PUT.

Verify

# контейнер на новом образе, Up
GET https://portainer.vds.kzntsv.site/api/endpoints/1/docker/containers/pilonuxt/json
# → Config.Image == registry.kzntsv.site/pilonuxt:<sha7>, State.running

# страницы (SSR, live): категория бруска + карточки товаров
curl -ksS https://pilorama98.ru/catalog/brus-estestvennoj-vlazhnosti | grep -c '19 500'
curl -ksS https://pilorama98.ru/shop/products/brusok-estestvennoj-vlazhnosti-40-50-6000 | grep -c '19 500'
# viewModel API (content-api, свежие данные из БД):
#   GET https://pilorama98.ru/snolla/stores/F42F4113-EC3E-4644-8D0E-70D3F430219D/products/viewModel?slug=<slug>
#   → volume_price (каноническое поле). ⚠️ voulme_price — legacy-опечатка, НЕ использовать (фикс 38c26c7 + 8943333).
# консоль контейнера: docker logs pilonuxt — без краш-лупа

БД напрямую (если надо сверить запечённое): docker exec mssql через Portainer (/containers/mssql/exec, sqlcmd -U snolla -P <pass из pass mssql-vds/...>), SELECT ... FROM Products WHERE Slug IN (...), Content — JSON-массив [{name,value}] (array-canon 0.11.0).

Rollback

Тот же PUT стека 16 с прежним тегом (<new7><old7>), pullImage:true. Старые теги висят в registry (инвентарь ниже). RTO ~30-60с (пересоздание контейнера).

Инвентарь образов (откат-цепочка)

  • 8943333текущий (2026-08-26: fix catalog voulme_price→volume_price)
  • 64c2539 — предыдущий (admin-api 0.11.0 array-canon)
  • 1128ddc — core 0.26.7 (фикс 500 _.find)
  • f876e70 — заглушки каталога → фото (3 шт.)
  • 24a13da — «Брус антисептированный» фото
  • 40806b1 — services-[slug] настоящий SSR 404
  • 340c0e7 — выпил «Строительство каркасных домов»
  • 8b7e8c4 — SMTP-фикс (noreply@snolla.com)

Gotchas

  1. Рестарт ≠ деплой. Письма прогера «подтяни цены» = рестарт (кэш), письма «задеплой коммит X» = пересборка. Не путать (см. инцидент 2026-08-16 и 2026-08-26).
  2. voulme_price — legacy-опечатка (в БД застыла на старых значениях). Канон — volume_price (loader пишет сюда с фикса 38c26c7). Категории рендерят volume_price с фикса 8943333 (2026-08-26). Если где-то снова всплывёт voulme_price — это баг кода.
  3. PUT стека сбрасывает env — всегда передавать env в payload (у 16 — []).
  4. src/generated/ обязателен в build-контексте — иначе билд лезет в mssql (ensure-schema), а в чистом git clone COPY упадёт.
  5. VERDACCIO_TOKEN — build-secret, не ARG (слой-чистота). Токен — живой JWT, протухает — при 401 на verdaccio перевыпустить (books-ci, см. verdaccio-token-lifecycle).
  6. Kэш viewModel в памяти контейнера — рестарт сбрасывает, пересборка не нужна при смене данных. Проверено 2026-08-16 («цена за м³ подтянулась») и 2026-08-26 (категории voulme_price — это был баг кода, не кэш).
  7. Конфиг запечён в образ — правки config/ требуют пересборки; runtime env-override не настроен (решение 2026-06-14).

Связи