4-й сайт тиража v3 (on-snolla, kupimknigi, labtools.ru, labtools.pro — 4/6 GREEN). Stack 19 image 0610432→a106622, env 8/8 preserved, mem_limit 512m. Staging :5081 parity-gate GREEN: theme lato.css 200 9994B md5-identical (v3 aws-sdk v3 S3-direct read из vds MinIO 2025), sitemap 7 детей == live, order-parity press-forms 12 изделий, / 4-байт diff = og:url/og:image request-host (benign). Live smoke GREEN, TLS CN=labtools.pro не тронут. Rollback = labtools-pro:0610432 (0.42.1, жив в registry). Compose source-of-truth + runbook v3 секция + handoff обновлены. Co-Authored-By: Claude <noreply@anthropic.com>
15 KiB
labtools.pro → VDS deploy runbook
Вынос labtools.pro (snolla-приложение, @snollajs/snolla 0.28.7, server-side Liquid, каталожный сайт)
с ../entities/ruvds-iis-host (catch-all CMS) в отдельный docker-контейнер на ../entities/vds-kzntsv
(89.253.255.94), за traefik. Модель — snolla-app: читает БД-контент MoreThenCms (mssql.kzntsv.site)
- ассеты из MinIO (
minio.kzntsv.site) через imgproxy. Зеркало emspb-vds-deploy-runbook — тот же паттерн; отличие: каталог/products/*+Culture=en(англоязычный близнец labtools.ru) + Cache-Control отсутствует (см. ниже).
Артефакты
- Код:
victor/labtools.pro@7bd9fae(apps/web, ре-ревью PASS все 9 дименшнов, ноль хаков, snolla 0.28.7 final).deploy/Dockerfile(multi-stage node:22-slim, non-root, healthcheck/robots.txt). - Образ: ⚠️
registry.kzntsv.site/labtools-pro:7bd9fae(+:latest) — имяlabtools-pro, НЕlabtools(тот у labtools.ru!). Собран НА VDS (обход traefik-499). digestsha256:23d0bc597c8930a1bf443c418a4af68d63b692903963c0e1f9f25baa4a1dd236. - Стек Portainer:
labtools-pro(Id 19, endpoint 1). Source-of-truth compose:admin/host-stacks/vds-kzntsv/labtools-pro.compose.yml. - siteId:
663F9410-A6CC-4651-9A5C-62844A313957, activeTheme389AD745-E3EE-4F23-BE16-DF38440A0941(theme store MinIOthemes/389ad745e3ee4f23be16df38440a0941/). Non-secret — вproduction.json, НЕ env. - siteUrl:
https://www.labtools.pro— вproduction.json(R3: sitemap/robots/canonical отдают www).
Сборка образа (на VDS)
git -C ~/projects/labtools.pro archive --format=tar 7bd9fae \
| ssh vitya@89.253.255.94 'rm -rf ~/build/labtools-pro && mkdir -p ~/build/labtools-pro && tar -x -C ~/build/labtools-pro'
ssh vitya@89.253.255.94 "cd ~/build/labtools-pro && VERDACCIO_TOKEN='<token>' docker build -f deploy/Dockerfile \
--build-arg VERDACCIO_TOKEN -t registry.kzntsv.site/labtools-pro:7bd9fae -t registry.kzntsv.site/labtools-pro:latest . \
&& docker push registry.kzntsv.site/labtools-pro:7bd9fae && docker push registry.kzntsv.site/labtools-pro:latest"
- VERDACCIO_TOKEN —
pass vds-kzntsv/full-env VERDACCIO_CI_TOKEN, build-time only (не в финальный образ; docker linter warn-only). config/default.json(dev-секреты) исключён.dockerignore— guard проверен на архиве: default.json ABSENT (только production.json + custom-environment-variables.json + default.example.json).
Runtime env-контракт (8 секретов — Portainer stack-env, НЕ в образ/git)
Значения идентичны labtools/emspb (все snolla-тенанты = одна MoreThenCms + общий MinIO/imgproxy/smtp; tenant-разница = siteId, не секрет). При создании стека env-массив переиспользован verbatim из labtools stack (Id 17) через Portainer API. 8 env: DB_USER DB_PASSWORD IMGPROXY_KEY IMGPROXY_SALT S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY SMTP_USER SMTP_PASSWORD. Маппинг env→config: apps/web/config/custom-environment-variables.json.
PORT — не переопределять (образ 5000; EXPOSE 5000; healthcheck /robots.txt). traefik service-port = 5000.
Создание стека (Portainer API, X-API-Key)
K=$(pass show vds-kzntsv/full-env | sed -n 's/^PORTAINER_API_KEY=//p'); BASE=https://portainer.vds.kzntsv.site
ENV=$(curl -ksS -H "X-API-Key: $K" $BASE/api/stacks/17 | jq '.Env') # verbatim из labtools stack 17
COMPOSE=$(cat ~/build/labtools-pro.compose.yml)
PAYLOAD=$(jq -n --arg name labtools-pro --arg compose "$COMPOSE" --argjson env "$ENV" '{name:$name,stackFileContent:$compose,env:$env,fromAppTemplate:false}')
curl -ksS -X POST "$BASE/api/stacks/create/standalone/string?endpointId=1" -H "X-API-Key: $K" -H 'Content-Type: application/json' --data "$PAYLOAD" # → Id 19
Предусловие pull: registry.kzntsv.site зарегистрирован в Portainer как Custom registry Id 1 (иначе no basic auth).
Runtime egress
mssql.kzntsv.site:1433 (БД-контент) · minio.kzntsv.site:443 (ассеты) · imgproxy.kzntsv.site:443 · smtp.yandex.ru:465 (формы).
Staging smoke (2026-07-02, GREEN) — labtools-pro.vds.kzntsv.site vs бой www.labtools.pro (RUVDS IIS 80.64.31.36)
Гнать с самого VDS (воркстейшн ловит LAN-DNS-перехват на *.labtools.pro; с VDS бой резолвится публично → RUVDS).
- Status-паритет: nav (
/ /about /contacts) + 3 каталог-секции (/products/laboratory-presses,/products/press-forms,/products/milling-accessories) + продукты (plg-20,press-forms/ring) — все 200/200. - Редиректы (301 parity, target идентичен modulo host): каталог#1
/products/laboratory-ball-mills→…/laboratory-ball-mills/lshm-750;/Contacts→lowercase/contacts;/contacts/→trailing/contacts. - Sitemap — структура index (snolla) vs плоский (legacy), покрытие идентично: бой отдаёт плоский
/sitemap.xmlс 25 URL; snolla отдаёт sitemap-INDEX из 7 дочерних (sitemap-pages-13,sitemap-static-pages-12,sitemap-sections-13, 4×sitemap-catalog-<GUID>-products-1= 1+2+2+12=17), все 200, в сумме ровно 25 URL, набор == бой (comm — ноль расхождений).<loc>host =www.labtools.pro(R3). НЕ дефект — index-структура штатна для snolla (как у pilorama98.ru). - theme-ассеты из MinIO md5-IDENTICAL бою (проверены css/js/png/svg под
/themes/389ad745e3ee4f23be16df38440a0941/): 7/7 MD5-OK, http 200. - ⚠️ Cache-Control ОТСУТСТВУЕТ на theme-ассетах — КОРРЕКТНО (labtools.pro
CachingOptions=[]empty-config; движок 0.28.7 не ставит заголовок = бой). НЕ флагать как дефект (в отличие от emspb/labtools где былmax-age=86400). - Контент-страницы рендерят реальное тело (12–22 KB), дельта vs бой +155…+222 B — стабильна, объясняется длиной staging-host в canonical/og (на 11 симв. длиннее www; на cutover уравняется в byte-parity).
- Контейнер healthy, MSSQL (tedious) + S3 (aws-sdk) подключены,
listening at http://localhost:5000, лог чист (только deprecation-warnings tedious/aws-sdk-v2).
Косметика (не дефект, отдано workshop)
Стартовый баннер в логе — labtools.ru (snolla) listening… — строка-константа унаследована из labtools.ru-референса при скаффолде. Идентификация сайта = siteId в production.json (663F9410…), на рендер/контент не влияет.
Cutover (live DNS) — ✅ ВЫПОЛНЕН 2026-07-02
Порядок был (как emspb):
- (оператор) reg.ru: A-записи
labtools.pro+www.labtools.pro→89.253.255.94(с RUVDS80.64.31.36). - (ops) verify флипа перед traefik: предыдущие проверки ловили старый TTL-кэш (публичные резолверы отдавали 80.64.31.36); авторитетный
ns1.reg.ruчерезResolve-DnsNameподтвердилlabtools.pro+www→89.253.255.94(TTL 3600), 8.8.8.8 догнал. Именно.pro(НЕ.ru). Урок: при "поменял DNS" проверять авторитетный NS напрямую, а не только кэширующие резолверы. Cм. memory [operator-dns-flip-verify-domain-before-cutover]. - (ops) ТОЛЬКО ПОСЛЕ flip: стек
labtools-pro(Id 19) traefik-rule →Host(\labtools.pro`) || Host(`www.labtools.pro`)` (Portainer PUT, env сохранён 8/8, pullImage=false). LE HTTP-01 выпустил cert мгновенно (ssl_verify=0 с первого хита). - Live-smoke GREEN: оба хоста 200 (nav+каталог+продукты); редиректы (ball-mills→lshm-750, /Contacts→lowercase, /contacts/→trailing) parity; sitemap index 7 детей host=www; контент реальный (12728 B,
lang="en"). staging-хостlabtools-pro.vds.kzntsv.siteубран из rule (→ 404). Внешняя проверка с RUVDS-хоста (Королёв, public DNS):labtools.pro+www→ 89.253.255.94 HTTP 200, cert доверенный. - RUVDS labtools.pro — rollback-путь, жив (отвечает 301, TLS ок), НЕ тронут.
⚠️ Порядок критичен: Host-правило добавлять ТОЛЬКО ПОСЛЕ flip DNS (иначе traefik проактивно тянет LE-cert HTTP-01, challenge упадёт на RUVDS → сожжём rate-limit).
Rollback
- DNS: вернуть A-записи
labtools.pro/www→80.64.31.36(RUVDS IIS живой, нетронут). Откат = смена DNS. - Стек: Portainer → stack
labtools-pro(Id 19) → remove (или откат тега образа). Образ в registry остаётся. - RUVDS IIS labtools.pro не выводить до явного решения оператора (директива 2026-07-01). (Ср.: emspb.ru биндинги на RUVDS сняты 2026-07-02 после подтверждённого cutover — см. ../entities/ruvds-iis-host.)
0.42.1 in-place bump — ✅ ВЫПОЛНЕН 2026-07-05
Тираж snolla 0.42.1. labtools.pro обновлён на живом стеке 19 in-place (домен уже на VDS, DNS не трогали). Рецепт — snolla-live-prod-inplace-image-bump. Оператор дал отмашку на боевой apply.
- Образ:
registry.kzntsv.site/labtools-pro:0610432(snolla 0.28.7→0.42.1, digest3d543fc…). Собран на VDS. - Acceptance С VDS (throwaway-staging из env стека 19): 25 sitemap page-locs все 200 (+robots+sitemap.xml = 27 прогерских), content-not-lost 25/25 (0 потерь).
- Order-парити ВСЕ 3 секции MATCH == прод (фикс liquid 0.10.2): presses=
plg-20,plg-12, milling=milling-jars,grinding-media, press-forms=12 изделий (round-xrf…round-collapsible). NB: первичный ручной счёт дал 13 (задвоил тайл), прог-сверка = 12, парити не задет. - Swap: Portainer PUT стека 19 (env 8/8 сохранён) → healthy → live-smoke GREEN (nav+presses+press-forms 200, order вживую), TLS CN=labtools.pro не тронут.
- Rollback: тег
labtools-pro:7bd9fae(0.28.7, в registry) / стек 19 PUT назад / DNS→RUVDS. - Compose обновлён на
0610432. Таска[labtools-pro-deploy-snolla-0-42-1]🟢. Notify=victor/labtools.pro.
0.43.2 v3 cutover (aws-sdk v2→v3) — ✅ ВЫПОЛНЕН 2026-07-30
4-й сайт тиража v3 (после on-snolla, kupimknigi, labtools.ru — все GREEN). MinIO split Track B3-v3.
Паттерн отработан x3; labtools.pro = English-twin (Culture=en) labtools.ru, отдельный репо victor/labtools.pro.
Оператор дал GO на боевой apply после staging parity-gate.
- Bump:
@snollajs/snolla 0.42.1→0.43.2вapps/web/package.json(commit12f8992) +resolutions-блок в rootpackage.json(verbatim labtools.ru: @aws-sdk/client-s3 3.1005.0 + @smithy chain 5.3.11) + регенyarn.lock(commita106622, single @smithy/signature-v4@5.3.11 deduped). Pushed victor/labtools.pro master6c7ea88..a106622. - Endpoint fix
6c7ea88(s3.endpoint→minio.vds.kzntsv.site) был в master с 2026-07-29, но в бегущем образе0610432(v2) его не было — v3 cutover заодно перевёл S3-direct read на vds minio. - Build на VDS:
git archive a106622→~/build/labtools-pro→docker build -f deploy/Dockerfile --build-arg VERDACCIO_TOKEN→registry.kzntsv.site/labtools-pro:a106622(+latest), digestsha256:2152642c4b17…, 476MB, EXIT=0. - Staging :5081 parity-gate GREEN (throwaway контейнер из env живого стека 19, 8 секретов):
/nav catalog = 4-байт diff =og:url+og:imagerequest-host (localhost:508113 chars vswww.labtools.pro15 chars × 2 = 4B). Benign request-context — post-cutover (оба за traefik Host=www.labtools.pro) byte-identical. (Ср. kupimknigi 3-байт form-url diff.)- Redirects (
/contacts/→/contacts,/products/laboratory-ball-mills→/lshm-750) IDENT (301). /robots.txt/sitemap.xmlIDENT (7 sitemap детей == live, locs identical, host=www).- theme
lato.css200 9994B md5-identical — v3 aws-sdk v3 S3-direct read из vds MinIO 2025 WORKS (ключевая v3-верификация). - Order-parity
/products/press-forms= 12 изделий identical order (multi-position…vacuum-ring). - Self-consistency: всё 200/3xx, 0 × 4xx/5xx.
- Portainer PUT stack 19 LIVE: node in-memory
put-stack(НЕ PS Invoke-RestMethod — корраптит кириллицу compose; скриптscripts/tmp/put-stack-labtools-pro.mjs), env 8/8 preserved,prune:false pullImage:true mem_limit 512m. PUT 200. Контейнерa106622healthy сразу. - Live smoke GREEN (via traefik):
/200 12736B,/about+/contacts200,/robots.txt+/sitemap.xml200 (7 детей все 200), 3 каталог-секции 200, продукты (press-forms/ring17255B,laboratory-presses/plg-2022077B) 200, themelato.css200 9994B (v3 S3-direct), redirects parity,labtools.proapex 200. Лог чист (tedious deprecation + cosmeticlabtools.ru listeningbanner — known benign). TLS CN=labtools.pro не тронут (LE notBefore Jul 2 / notAfter Sep 30 2026). - Rollback:
labtools-pro:0610432(0.42.1, жив в registry) — Portainer PUT назад / compose revert. - Compose (admin source-of-truth) синхронно закоммичен
0610432→a106622. 4/6 v3 сайтов GREEN. Остались: emspb (стек 18), tandemmebel (стек 20 — был BLOCKED на sharp-staging визуальном дефекте, проверить перед бампом).