6-й сайт тиража v3 (после on-snolla, kupimknigi, labtools.ru, labtools.pro, emspb). 6/6 v3 сайтов GREEN — тираж v3 ЗАВЕРШЁН. - Bump: @snollajs/snolla 0.42.1→0.43.2 (apps/web) + resolutions verbatim labtools.ru (@aws-sdk/client-s3 3.1005.0 + @smithy chain 5.3.11 — optional determinism) + реген yarn.lock (single @smithy/signature-v4@5.3.11 deduped). Commit 9d51c09 в victor/tandemmebel.ru. Заодно нёс на live s3.endpoint fix f4ddce6 (→ minio.vds.kzntsv.site), был в master с 2026-07-29. - Build на VDS: registry.kzntsv.site/tandemmebel:9d51c09, digest d14982c24dc7, 476MB (v2 583MB), EXIT=0. - Staging :5020 parity-gate GREEN: robots md5 identical, sitemap 184=184 locs IDENTICAL, self-consistency 183×200 + 1×404 (/articles parity), Gotham-Pro.css 200 4436B (v3 S3-direct). - Portainer PUT stack 20 (node in-memory, кириллица compose), env 8/8, pullImage:true, mem_limit 512m. PUT 200. - Live smoke GREEN: / 200 45742B, /projects 200, sitemap 184, theme 200 4436B. TLS CN не тронут (LE Oct 10). - Rollback = 8df10ee (v2, жив в registry). Co-Authored-By: Claude <noreply@anthropic.com>
161 lines
18 KiB
Markdown
161 lines
18 KiB
Markdown
---
|
||
title: tandemmebel.ru snolla-app — VDS deploy runbook (cutover + 0.42.1 bumps + 0.43.2 v3 cutover)
|
||
type: concept
|
||
tags: [tandemmebel, snolla, vds, deploy, docker, portainer, traefik, runbook, migration, blog-portfolio, v3, aws-sdk]
|
||
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]], [[minio-split-cutover-runbook]]
|
||
updated: 2026-07-30
|
||
---
|
||
|
||
# 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`.)
|
||
|
||
## 0.43.2 v3 cutover (aws-sdk v2→v3) — ✅ ВЫПОЛНЕН 2026-07-30
|
||
6-й сайт тиража v3 (после on-snolla, kupimknigi, labtools.ru, labtools.pro, emspb — 5 GREEN). In-place bump стека 20 (DNS на VDS с 2026-07-12, не трогали). Рецепт — [[minio-split-cutover-runbook]] §B3-v3. **6/6 v3 сайтов GREEN = тираж завершён.**
|
||
- **Bump:** `@snollajs/snolla 0.42.1→0.43.2` в `apps/web/package.json` (commit `9d51c09`) + `resolutions`-блок в root `package.json` (verbatim labtools.ru: `@aws-sdk/client-s3 3.1005.0` + `@smithy` chain 5.3.11 — optional determinism, live verify доказал 5.6.12 работает vs vds MinIO 2025) + реген `yarn.lock` (single `@smithy/signature-v4@5.3.11` deduped, no dual-instance). Pushed victor/tandemmebel.ru master `f4ddce6..9d51c09`. Endpoint fix `f4ddce6` (s3.endpoint→minio.vds.kzntsv.site) был в master с 2026-07-29, но в бегущем образе `8df10ee` (v2) его не было — v3 cutover заодно перевёл S3-direct read на vds minio.
|
||
- **Build на VDS:** `git archive 9d51c09` → `~/build/tandemmebel` → `docker build -f deploy/Dockerfile --build-arg VERDACCIO_TOKEN` → `registry.kzntsv.site/tandemmebel:9d51c09`(+latest), digest `sha256:d14982c24dc7c61a334c8973e6f1c48147c7d3cbcc772b28af6c629431947b16`, 476MB (v2 8df10ee был 583MB — v3 leaner), EXIT=0. `yarn install --immutable` в Docker прошёл (yarn.lock matches).
|
||
- **Staging :5020 parity-gate GREEN** (throwaway контейнер из env живого стека 20, 8 секретов, `--network proxy`):
|
||
- robots.txt md5 `a01e4c60…` byte-identical (87 B).
|
||
- sitemap index→5 sub-sitemaps → **184 locs == live 184 IDENTICAL** (0 prod-only / 0 new-only — regression нет).
|
||
- **self-consistency 183×200 + 1×404** (`/articles` — prod=404 parity benign, не регрессия). 0×301/5xx.
|
||
- `/` 200 staging 45738B vs prod 45181B (+557B = og:url/og:image request-host context — benign, byte-identical post-cutover).
|
||
- theme `Gotham-Pro.css` 200 4436B (v3 aws-sdk v3 S3-direct read из vds MinIO 2025 — латентный прод-баг 0B→4436B починен ещё cutover'ом 2026-07-12, сохранён).
|
||
- **Portainer PUT stack 20 LIVE:** node in-memory `put-stack` (НЕ PS — кириллица compose; Node 22 global fetch, GET `/stacks/20` для Env + `/stacks/20/file` для StackFileContent), env 8/8 preserved, `prune:false pullImage:true mem_limit 512m`. PUT 200. Контейнер `9d51c09` healthy ~9s.
|
||
- **Live smoke GREEN (via traefik):** `/` 200 45742B, `/projects` 200 90758B, `/sitemap.xml` 200 (184 locs == parity), robots md5 `a01e4c60…`, `/articles` 404 parity, theme `Gotham-Pro.css` 200 4436B (v3 S3-direct). TLS CN=tandemmebel.ru, expire Oct 10 2026 (не тронут — in-place swap). Лог чист (tedious deprecation + `tandemmebel.ru (snolla) listening`).
|
||
- **Rollback:** `tandemmebel:8df10ee` (0.42.1 v2, template-only share-buttons, жив в registry) — Portainer PUT назад / compose revert. `0cd9351` (0.42.1) + `ed96b18` (0.42.0) — deeper rollback. DNS→RUVDS 80.64.31.36 — крайний.
|
||
- Compose (admin source-of-truth) синхронно закоммичен `8df10ee`→`9d51c09`. **6/6 v3 сайтов GREEN — тираж v3 завершён.** |