Files
admin/.wiki/concepts/tandemmebel-vds-deploy-runbook.md
vitya 632c843fd5 deploy(tandemmebel): 8df10ee LIVE — drop FB/Twitter/Google+ share buttons
In-place template bump на прод (stack 20): 0cd9351 → 8df10ee. fix(web) remove
FB/Twitter/Google+ share-кнопки (extremist-icon compliance РФ), keep VK+OK.
Template-only (apps/web/views/social_buttons.liquid, 1 file 12 delet), snolla pin
0.42.1 НЕ менялся.

Верификация перед сборкой поймала расхождение: tandemmebel-записка утверждала
live=ed96b18/0.42.0, фактически крутился 0cd9351/0.42.1 (in-place bump 2026-07-12).
gitea compare 0cd9351...8df10ee = total_commits:1 → фикс один коммит поверх 0.42.1
(правильная база), yarn.lock идентичен → регрессии sitemap нет.

Completeness-gate (staging :5020): sitemap NEW==PROD 184=184 identical, 0
регрессий. Live-smoke С VDS 4 share-block страницы (project-post ×2,
/furniture/bedrooms, /furniture/kitchens/classic) — vk+ok на месте, fb/tw/gp=0.
TLS-серт CN=tandemmebel.ru не дёрнут (in-place swap). Tandemmebel-сессия подтвердила.

- .tasks/STATUS.md: _Updated 2026-07-13 строка (deploy-результат + discrepancy)
- .wiki/concepts/tandemmebel-vds-deploy-runbook.md: секция In-place bump 8df10ee
  (предсборочная верификация, build→staging→gate→swap, rollback, hygiene-заметка
  про устаревшие STAGING-комменты в Portainer stack file)
- host-stacks/vds-kzntsv/tandemmebel.compose.yml: image 8df10ee + LIVE-комменты
  + история bump'ов (source-of-truth синхронен с Portainer)

Rollback: PUT стека 20 → 0cd9351 (0.42.1, {% order %} fix) / ed96b18 (0.42.0) /
b02ca18 в registry; ИЛИ revert DNS→80.64.31.36.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-13 22:50:49 +03:00

146 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: tandemmebel.ru snolla-app — VDS deploy runbook (cutover + in-place 0.42.1 bump)
type: concept
tags: [tandemmebel, snolla, vds, deploy, docker, portainer, traefik, runbook, migration, blog-portfolio]
related: [[../entities/vds-kzntsv]], [[../entities/ruvds-iis-host]], [[portainer-stack-management-vds]], [[minio-imgproxy-on-vds]], [[snolla-live-prod-inplace-image-bump]], [[labtools-vds-deploy-runbook]], [[labtools.pro-vds-deploy-runbook]], [[emspb-vds-deploy-runbook]]
updated: 2026-07-12
---
# tandemmebel.ru → VDS deploy runbook
Вынос `tandemmebel.ru` (snolla-приложение, `@snollajs/snolla` **0.42.1**, server-side Liquid, **блог-портфолио БЕЗ e-commerce каталога**)
с [[../entities/ruvds-iis-host]] (catch-all CMS, 80.64.31.36) в отдельный docker-контейнер на [[../entities/vds-kzntsv]]
(89.253.255.94), за traefik. Модель — snolla-app: читает БД-контент MoreThenCms (`mssql.kzntsv.site`)
+ ассеты из MinIO (`minio.kzntsv.site`). In-process sharp (resize+data-driven watermark), delivery=serve-bytes,
variant-cache бакет в MinIO. imgproxy ИЗ ПУТИ tandem УБРАН (сырой `/imgproxy` → 404 норма).
Паттерн = [[emspb-vds-deploy-runbook]] / [[labtools.pro-vds-deploy-runbook]]. Отличие: **блог-портфолио** (не каталожный →
order-парити секций НЕ применимо — см. 0.42.1 bump ниже). Culture `ru-RU`.
## Артефакты
- **Код:** `victor/tandemmebel.ru` @ `0cd9351` (apps/web, snolla **0.42.1** / core 0.24.1 / liquid 0.10.2 / data 0.14.1).
`deploy/Dockerfile` (multi-stage node:22-slim, `yarn install --immutable`, non-root uid `node`, healthcheck `/robots.txt`).
- **Образ:** `registry.kzntsv.site/tandemmebel:0cd9351` (+`:latest`). Имя **`tandemmebel`** (= имя стека/контейнера).
Собран НА VDS (обход traefik-499). digest `sha256:f29c187fe6114d6ab3926132f4b3eb09df9b606d448cd8fd873780687cb46b8f`.
- **Стек Portainer:** `tandemmebel` (**Id 20**, endpoint 1). Source-of-truth compose: `admin/host-stacks/vds-kzntsv/tandemmebel.compose.yml`.
- **siteId:** `78080707-F6E0-4330-BA30-7922354C2CEF` (non-secret, в `production.json`НЕ env).
- **siteUrl:** `https://www.tandemmebel.ru``production.json`).
- **Runtime env (8 секретов):** `DB_USER DB_PASSWORD IMGPROXY_KEY IMGPROXY_SALT S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY SMTP_USER SMTP_PASSWORD` — Portainer stack env (переиспользованы verbatim из labtools стека 17). НЕ в образе, НЕ в git.
- **mem_limit:** `512m` (guardrail; см. [[portainer-stack-management-vds]] § Convention).
## Cutover 2026-07-12 (первый вынос на VDS — DNS-gated)
Последовательность (LE-критичность соблюдена) — см. `NEXT_SESSION.md`:
1. **Verify авторит. NS**`nslookup tandemmebel.ru ns1/ns2.reg.ru` = оба `89.253.255.94` (apex + www), не только резолвер.
2. **GET стек 20** — образ `tandemmebel:ed96b18` (0.42.0, staging cutover-подготовлен), env 8/8, rule staging `Host(tandemmebel.vds.kzntsv.site)`.
3. **PUT стек 20**`Host(tandemmebel.ru) || Host(www.tandemmebel.ru)`, env-preserving, `prune:false, pullImage:false`. Python urllib UTF-8 (НЕ PS Invoke-RestMethod — gotcha кириллицы, см. [[portainer-stack-management-vds]]).
4. **Poll** — контейнер healthy, LE-серт issued on first hit: CN=tandemmebel.ru, SAN оба, issuer YR2, until 2026-10-10.
5. **Live-smoke С VDS** — GREEN.
6. Compose source-of-truth + борд обновлены.
**Латентный прод-баг починен cutover'ом:** `Gotham-Pro.css` был 0B на RUVDS → теперь 200/4436B.
## In-place bump 2026-07-12: 0.42.0 → 0.42.1 (order-tag Drop-field fix)
По рецепту [[snolla-live-prod-inplace-image-bump]] (поверх cutover'а того же дня).
### Консюмер-бамп (решён оператором, без dev-source)
Тот же блокер-паттерн 2026-07-04 (заявленная snolla-версия ≠ консюмер-пин) — на этот раз решён самим оператором:
- `apps/web/package.json:12``0.42.0``0.42.1`.
- `yarn install` в корне монорепы → обновил корневой `yarn.lock` (snolla 0.42.1 / core 0.24.1 / liquid 0.10.2 / data 0.14.1). Dockerfile `yarn install --immutable` — lock обязан совпадать с пином.
- commit `0cd9351` + push origin (git.kzntsv.site/victor/tandemmebel), подтверждён `ls-remote`.
- Тег образа = sha монорепы `0cd9351`.
### Build → staging → gate → swap
1. **Build на VDS** (archive `0cd9351``~/build/tandemmebel``docker build -f deploy/Dockerfile --build-arg VERDACCIO_TOKEN -t .../tandemmebel:0cd9351 -t .../tandemmebel:latest . && push`). Guard: `config/default.json` ABSENT в архиве (.dockerignore), пин = 0.42.1.
2. **Throwaway-staging :5020** из env живого контейнера (`docker inspect tandemmebel``.staging.env`, фильтр `^(HOSTNAME|PATH|NODE_VERSION|YARN_VERSION|HOME)=`). `docker run -d --name tandemmebel-staging --env-file .staging.env -p 127.0.0.1:5020:5000 .../tandemmebel:0cd9351` → healthy. robots.txt+`/`+sitemap 200.
3. **Completeness-gate С VDS** — sitemapindex разворот (`expand()`), два чека:
- **NEW==PROD locs: 184 = 184 IDENTICAL** (вкл. 0.42.x sitemap-реструктуризацию — parity сохранился).
- **self-consistency + content-not-lost** (locs идентичны → один проход): 183×2xx + 1×404.
- Единственный 404 `/articles`**идентичен прод-оракулу** (404 и на live `https://www.tandemmebel.ru/articles`) → benign (пустая секция-без-индекса, не регрессия). Tandemmebel НЕ каталожный → order-парити **не применимо** (0.42.1 order-fix инертен на этой теме — как archive-роуты на 0.40→0.42).
- **GREEN = 0 регрессий в 404/5xx.**
4. **Боевой swap** — operator-gated, node `put-stack.js` (env-preserving: 8/8 name+value, `prune:false, pullImage:true`):
`META=st20.json node put-stack.js 20 1 ed96b18 0cd9351` → PUT 200. Контейнер 0cd9351+healthy за ~8s.
5. **Live-smoke**`https://www.tandemmebel.ru/{robots.txt,/,/projects,/sitemap.xml}` = 200. TLS-серт CN=tandemmebel.ru **не дёрнут** (in-place swap серт не трогает). `docker rm -f tandemmebel-staging`.
### Rollback
- Тег `ed96b18` (0.42.0) в registry (+ `b02ca18`, 0.16.2-staging) → PUT стека 20 назад.
- ИЛИ revert DNS → 80.64.31.36 (RUVDS IIS жив, не тронут — rollback всех мигрированных).
## In-place bump 2026-07-13: 0.42.1 → 0.42.1 (sha 0cd9351 → 8df10ee, template-only)
Ops-handoff от tandemmebel-сессии (inbox-запрос): убрать FB/Twitter/Google+ share-кнопки
(экстремистистская символика РФ), оставить ВК+Одноклассники. Коммит `8df10ee` в `victor/tandemmebel.ru`
master, меняет только `apps/web/views/social_buttons.liquid` (1 file, 12 deletions), snolla pin 0.42.1
НЕ менялся → in-place bump на той же 0.42.1 (не консюмер-бамп).
### Предсборочная верификация (поймала расхождение)
Записка от tandemmebel-сессии утверждала: «сейчас живой на стеке 20 — `ed96b18`/0.42.0». Фактически
на проде крутился **`0cd9351`/0.42.1** (in-place bump 0.42.0→0.42.1 был 2026-07-12). Без проверки это
не повлияло бы (см. ниже), но вслепую строить нельзя. Проверки С VDS + gitea API (токен `gitea/admin-token`):
- `docker inspect tandemmebel``registry.kzntsv.site/tandemmebel:0cd9351`, healthy, started 2026-07-12.
- gitea `compare/0cd9351...8df10ee``total_commits:1`: фикс ровно один коммит поверх 0.42.1 (правильная база).
- `git show 8df10ee:apps/web/package.json``@snollajs/snolla: 0.42.1` (pin не менялся); yarn.lock snolla 0.42.1 /
core 0.24.1 / liquid 0.10.2 / data 0.14.1 — идентично live 0cd9351.
- `git show 8df10ee --stat` → только `apps/web/views/social_buttons.liquid` (modified).
- 8df10ee full sha `8df10eedcba9979ac91dce2025b2ff827d60e808`, master HEAD = 8df10ee.
Если бы 8df10ee был на базе 0.42.0 (172 locs) → completeness-gate поймал бы регрессию 184→172. Он на 0.42.1 →
parity 184=184.
### Build → staging → gate → swap
1. **Build на VDS** из чистого архива `8df10ee` (gitea API `/archive/<fullsha>.tar.gz`, token- auth `oauth2:`-
НЕ работает, `Authorization: token`-header на API endpoints работает; git clone по HTTPS фейлится даже
с oauth2:token — gitea admin-token = API-only, не git-transport). Extract `--strip-components=1` в
`~/build/tandemmebel-8df10ee`. `docker build -f deploy/Dockerfile --build-arg VERDACCIO_TOKEN=<books-ci JWT>
-t registry.kzntsv.site/tandemmebel:8df10ee -t .../tandemmebel:latest .` → digest
`sha256:30a7e5ab82f2bf371042f1b5fa0cd7ac5ceda71379d1da59e148dee8166bd560`, layer-cache hit (package.json+
yarn.lock идентичны 0cd9351), push OK.
2. **Throwaway-staging :5020** из env живого контейнера (`docker inspect tandemmebel``.staging.env`,
фильтр `^(HOSTNAME|PATH|NODE_VERSION|YARN_VERSION|HOME|PORT|NODE_ENV)=`). `docker run -d --name
tandemmebel-staging --env-file .staging.env -p 127.0.0.1:5020:5000 .../tandemmebel:8df10ee` → healthy 8s.
3. **Completeness-gate С VDS** — sitemapindex разворот. **Важно:** staging sitemapindex содержит АБСОЛЮТНЫЕ
prod-URLs (siteUrl=prod в production.json) → sub-sitemaps надо фетчить со staging ПО ПУТЯМ
(`http://127.0.0.1:5020/sitemap-pages-1.xml`), а не следовать абсолютным URL (иначе сравнишь PROD==PROD).
- NEW==PROD locs: **184 = 184 IDENTICAL** (0 prod-only, 0 new-only).
- self-consistency: 183×200 + 1×404 (`/articles` benign parity staging==prod).
- share-block: staging vk+ok / fb-tw-gp=0 vs prod-before (0cd915 live ещё имел) vk+ok+fb+tw+gp —
фикс убирает ровно лишнее, остальное не трогает.
- GREEN = 0 регрессий.
4. **Боевой swap** — Portainer JWT auth (`POST /api/auth` vitya/Pryakhin9-VDS-2026; API-key `ptr_*` даёт 401,
workaround = JWT). GET `/api/stacks/20` (env 8/8) + `/api/stacks/20/file` (compose). Python urllib UTF-8
(НЕ PS Invoke-RestMethod — gotcha кириллицы #9). Замена `tandemmebel:0cd9351``8df10ee` в StackFileContent
(ровно 1 замена, traefik rule line не тронута). PUT `/api/stacks/20?endpointId=1` `{stackFileContent,
env: <8 preserved>, prune:false, pullImage:true}` → HTTP 200. Контейнер 8df10ee+healthy ~8s.
5. **Live-smoke С VDS** — robots/`/`/sitemap 200, /articles 404 parity, sitemap 184 locs, 4 share-block
страницы (project-post ×2 `/projects/2011/...`, `/furniture/bedrooms`, `/furniture/kitchens/classic`) все
200 → **vk+ok на месте, fb/tw/gp=0**. TLS-серт CN=tandemmebel.ru (LE YR2, until 2026-10-10) **не дёрнут**.
`docker rm -f tandemmebel-staging`.
### Rollback (8df10ee)
- **Образный (предпочт):** PUT стека 20 назад на `0cd9351` (0.42.1, {% order %} fix) — template-only фикс,
откатится чисто. Тег в registry.
- `ed96b18` (0.42.0) / `b02ca18` — в registry (ed96b18 стёрт с VDS при disk-cleanup 2026-07-13, `pullImage:true`
дотянет из registry).
- DNS: revert reg.ru → 80.64.31.36 (RUVDS IIS жив, не тронут).
### Hygiene-заметка
Portainer stack 20 file несёт устаревшие STAGING-комменты (строки 16-17 «Домен: STAGING», 53
«STAGING-RULE. Cutover: после flip DNS сменить на Host(...)») — но сама `traefik...rule` строка (55) уже LIVE
(`Host(tandemmebel.ru) || Host(www.tandemmebel.ru)`, совпадает с label'ом живого контейнера). Косметика,
функционально нейтральна. При деплое 8df10ee обновлён только image-line коммент; шапочные STAGING-комменты
не трогались (минимальное изменение). Source-of-truth compose `host-stacks/vds-kzntsv/tandemmebel.compose.yml`
актуализирован полностью (8df10ee + LIVE-комменты + история bump'ов). При следующем full-sync можно выровнять
Portainer file под git-source.
## Гочи (специфичные)
- **Блокер-паттерн консюмер-пина** — snolla-репо релизит версию, но консюмер-пин в tandemmebel-репо отстаёт. На 0.42.0 (2026-07-04) ждали dev-source; на 0.42.1 (2026-07-12) оператор сделал сам. Всегда byte-verify пин+yarn.lock+`ls-remote` перед build.
- **`yarn install --immutable`** в Dockerfile — корневой `yarn.lock` обязан быть снапшотом целевого пина, иначе build падает на checksum-mismatch. Бамп = package.json + `yarn install` (обновляет lock) + commit обоих.
- **0.42.x sitemap-реструктуризация** — parity сверять по **page-locs** (развёрнутый sitemapindex), НЕ по именам под-sitemap'ов. 184 тут (было 172 на 0.42.0 — выросло, но NEW==PROD).
- **`/articles` 404 benign** — loc из sitemap отдаёт 404, если прод-оракул идентичен → не дефект (см. рецепт § Completeness-gate).
- Staging-порт `5020` — проверять `docker ps | grep 127.0.0.1:50` перед bind (stale throwaway от прошлого тиража).
- Smoke гнать **С VDS** (воркстейшн ловит LAN-DNS-перехват прод-доменов — см. memory `workstation-lan-dns-serves-local-cms-copy`).
- build-secret (VERDACCIO_TOKEN в ARG/ENV build-стадии) — известный follow-up, НЕ блокер (runtime-стадия отдельная, токена в финальном образе нет).
## Тираж snolla 0.42.1 — статус
Все 5 snolla-сайтов на VDS:
- labtools.ru (17), emspb.ru (18), labtools.pro (19) — 0.42.1 in-place bump 2026-07-05.
- kupimknigi (21), **tandemmebel (20)** — 0.42.0 cutover, tandemmebel in-place bump до 0.42.1 2026-07-12. (kupimknigi остался на 0.42.0 по решению оператора — см. `NEXT_SESSION.md`.)