runbook: pilorama98.ru (pilonuxt) VDS deploy — рестарт vs пересборка, стек 16
This commit is contained in:
160
.wiki/concepts/pilorama98-vds-deploy-runbook.md
Normal file
160
.wiki/concepts/pilorama98-vds-deploy-runbook.md
Normal file
@@ -0,0 +1,160 @@
|
||||
---
|
||||
title: "pilorama98.ru (pilonuxt) — VDS deploy runbook (стек 16, рестарт/пересборка)"
|
||||
type: concept
|
||||
tags: [pilorama98, pilonuxt, vds, deploy, docker, portainer, nuxt, runbook, cache]
|
||||
related: [concepts/portainer-stack-management-vds.md, concepts/pilonuxt-restart-vs-redeploy-2026-08-16.md, concepts/admin-api-embed-pilonuxt.md]
|
||||
sources: []
|
||||
updated: 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-env`
|
||||
→ `VERDACCIO_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/auth` — `vitya` / `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)
|
||||
|
||||
```python
|
||||
# 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/json` → `State.StartedAt` обновился, образ тот же.
|
||||
|
||||
## Пересборка + деплой (новый код)
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# контейнер на новом образе, 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).
|
||||
|
||||
## Связи
|
||||
|
||||
- [[concepts/pilonuxt-restart-vs-redeploy-2026-08-16.md]] — рестарт vs пересборка, инцидент 2026-08-16
|
||||
- [[concepts/portainer-stack-management-vds.md]] — общий Portainer-канон (JWT, gotchas 9/10)
|
||||
- [[concepts/admin-api-embed-pilonuxt.md]] — admin-api embed, array-canon
|
||||
- Репо-вики: `apps/web/.wiki/concepts/docker-deploy.md` (сборка образа, деплой)
|
||||
- Ранбук stostayer: [[concepts/stostayer-web-deploy-runbook.md]] — похожий канал (другой хост/стек)
|
||||
Reference in New Issue
Block a user