From ff436f3f8d4e241d589f3dc38acc10d35ea05eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Thu, 9 Jul 2026 14:03:34 +0300 Subject: [PATCH] =?UTF-8?q?docs(runbook):=20=D0=B2=D1=8B=D0=BA=D0=B0=D1=82?= =?UTF-8?q?=20=E2=80=94=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=96=D0=98=D0=92=D0=9E=D0=99=20Managed-?= =?UTF-8?q?=D0=BA=D0=BB=D0=B0=D1=81=D1=82=D0=B5=D1=80=20(=D0=BD=D0=B5=20?= =?UTF-8?q?=D0=BC=D1=91=D1=80=D1=82=D0=B2=D0=B0=D1=8F=20VM-=D0=BA=D0=BE?= =?UTF-8?q?=D0=BF=D0=B8=D1=8F),=20=D0=BA=D0=B0=D1=82=D0=B8=D0=BC=20=D0=B8?= =?UTF-8?q?=D0=B7=20main,=20smoke=20=D0=BF=D0=BE=20lk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправлены опасные устаревшие шаги: §0/§6b/§8 велели миграции через sudo -u postgres на VM = мёртвая rollback-копия (после переезда 26.06 боевая = Managed PG). Теперь — crm_migrator на rw-endpoint кластера. Плюс: катим из main (=прод с 09.07), хирургический overlay, smoke по lk.liderra.ru, квирк 107 (optimize только www-data). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../2026-06-18-gitea-prod-deploy-pipeline.md | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/docs/superpowers/runbooks/2026-06-18-gitea-prod-deploy-pipeline.md b/docs/superpowers/runbooks/2026-06-18-gitea-prod-deploy-pipeline.md index 463b60f1..9b8ea97a 100644 --- a/docs/superpowers/runbooks/2026-06-18-gitea-prod-deploy-pipeline.md +++ b/docs/superpowers/runbooks/2026-06-18-gitea-prod-deploy-pipeline.md @@ -1,10 +1,16 @@ -# Рунбук: ручной выкат gitea → прод (liderra.ru) +# Рунбук: ручной выкат gitea → прод (lk.liderra.ru) -**Создан:** 18.06.2026. **Источник:** живой выкат июньской дельты 18.06.2026 (память `portal-security-audit-2026-06-17`). +**Создан:** 18.06.2026. **Обновлён:** 09.07.2026. **Источник:** живой выкат июньской дельты 18.06.2026 (память `portal-security-audit-2026-06-17`); правки 09.07 — по факту переезда на Managed PG (26.06) и сведения `main`=прод. -Постоянная процедура выката кода на боевой `liderra.ru`. Прод **не** git-чекаут — код кладётся overlay'ем. Сделан, чтобы следующая сессия не собирала пайплайн заново. +Постоянная процедура выката кода на боевой портал `lk.liderra.ru`. Прод **не** git-чекаут — код кладётся overlay'ем. Сделан, чтобы следующая сессия не собирала пайплайн заново. -> **Почему вручную, а не GitHub Actions.** Старый `.github/workflows/deploy.yml` **мёртв** — аккаунт GitHub `CoralMinister` suspended, Actions не запускаются. До восстановления аккаунта (или переезда CI на gitea) выкат — ручной по шагам ниже. Старый [deploy/redeploy.sh](../../../deploy/redeploy.sh) — это **серверная половина** (scp-эпоха); актуальная серверная часть — шаги 5–6 здесь (запуск от `www-data`, миграции через `postgres`). +> 🔴🔴🔴 **ГЛАВНОЕ ОБНОВЛЕНИЕ 09.07.2026 — читать до миграций:** +> - **Живая база = Managed PG кластер `c9q2cvtjpq3hgq6l0r96`** (`c-c9q2cvtjpq3hgq6l0r96.rw.mdb.yandexcloud.net:6432/liderra`). Миграции гнать **ТУДА** как `crm_migrator`. **`sudo -u postgres psql` на VM = СТАРАЯ мёртвая rollback-копия (127.0.0.1)** — миграции туда НЕ влияют на боевую базу! (Прежний §6b/§8 велел именно так — это было ошибкой после переезда 26.06, исправлено ниже.) +> - **Выкат — из ветки `main`** (с 09.07 `main` побайтово = боевой прод; см. память `project-main-eq-prod-reconcile-2026-07-09`). НЕ из feature/deploy-веток. Правку сначала свести в `main` (ff), потом катить из него. +> - **Портал переехал на `lk.liderra.ru`** (`liderra.ru` = лендинг). Smoke бить по `lk`. +> - Хирургический выкат (только изменённые файлы, `rsync --files-from`) безопаснее полного overlay'я, если не уверен, что ветка — надмножество боевого. Из `main` полный overlay безопасен (main=прод). + +> **Почему вручную, а не GitHub Actions.** Старый `.github/workflows/deploy.yml` **мёртв** — аккаунт GitHub `CoralMinister` suspended, Actions не запускаются. До восстановления аккаунта (или переезда CI на gitea) выкат — ручной по шагам ниже. Старый [deploy/redeploy.sh](../../../deploy/redeploy.sh) — это **серверная половина** (scp-эпоха); актуальная серверная часть — шаги 5–6 здесь (запуск от `www-data`, миграции — как `crm_migrator` на **живой Managed-кластер**, см. §6b). --- @@ -15,7 +21,8 @@ - **heredoc и `$$`.** В heredoc с **неэкранированным** разделителем bash раскрывает `$$` как PID. Для литерала писать `\$\$` либо использовать dollar-quoting `$tag$...$tag$`. - **Привилегии на проде:** - `composer` / `artisan` запускать `sudo -u www-data` — `vendor/` принадлежит `www-data:www-data` 775, ssh-юзер `ubuntu` **не** в группе → иначе `composer` падает `could not create vendor/X`. - - Миграции БД — через `sudo -u postgres psql -d liderra` — роль `crm_app_user` (`DB_USERNAME`) **не владелец** таблиц → `artisan migrate` падает `SQLSTATE 42501 must be owner of table deals`. + - `artisan optimize` (config:cache) — **только `sudo -u www-data`**: если запустить root'ом, кэш-файл станет root-owned и www-data его не прочитает → 500 на всём портале (квирк 107, инцидент 24.05). + - **Миграции БД — на ЖИВОЙ Managed-кластер как `crm_migrator`, НЕ на VM.** `crm_app_user` (`DB_USERNAME`) не владелец таблиц (`42501 must be owner`), а `crm_migrator` — владелец. Config на проде закэширован → env-override для `artisan migrate` НЕ работает → применять DDL напрямую `psql` к rw-endpoint кластера. `sudo -u postgres psql` на VM **не трогать** (мёртвая копия). См. §6b. --- @@ -36,10 +43,10 @@ ssh liderra-prod 'cd /var/www/liderra && sudo tar --exclude=app/node_modules --e Прод видит gitea по HTTP (с прода = HTTP 200). Самоподписанный TLS → `http.sslVerify=false`; креды в URL: ```bash -ssh liderra-prod 'rm -rf /tmp/liderra-build && git -c http.sslVerify=false clone https://liderra:Liderra_Admin_2026_Change@111.88.252.149.nip.io/liderra/portal.git /tmp/liderra-build' +ssh liderra-prod 'rm -rf /tmp/liderra-build && git -c http.sslVerify=false clone --depth 1 -b main https://liderra:Liderra_Admin_2026_Change@111.88.252.149.nip.io/liderra/portal.git /tmp/liderra-build' ``` -> Это собирает выкат из ветки `main` репо `liderra/portal` в gitea (origin = мёртвый github, не используется). Проверь, что в gitea запушено то, что собираешься катить. +> Явно `-b main` — с 09.07 `main` = боевой прод (канон). origin (github) мёртв, не используется. Перед выкатом свести свою правку в `main` (ff-push) и убедиться, что `gitea/main` содержит то, что катишь. --- @@ -69,6 +76,13 @@ ssh liderra-prod 'cd /var/www/liderra/app && sudo -u www-data php artisan down' ssh liderra-prod 'sudo rsync -rlt --no-perms --no-owner --no-group --exclude=.env --exclude=.env.* --exclude=storage --exclude=vendor --exclude=node_modules --exclude=.git /tmp/liderra-build/app/ /var/www/liderra/app/' ``` +> **Хирургический вариант (безопаснее при неполной уверенности).** Полный overlay безопасен ТОЛЬКО когда катишь из `main` (main=прод). Если катишь узкую правку — копируй **только изменённые файлы** списком, не всю папку: +> ```bash +> # список путей (относительно app/) в /tmp/deploy-files.txt на проде, затем: +> ssh liderra-prod 'sudo rsync -rlt --files-from=/tmp/deploy-files.txt /tmp/liderra-build/app/ /var/www/liderra/app/' +> ``` +> Так исключён клоббер чужих свежих правок (инцидент 08.07: полный overlay из устаревшей ветки снёс колонки «Сделок»). Для чисто-фронтовой правки достаточно заменить `public/build` (собранный бандл) + `chown www-data`. + --- ## 6. Серверная часть: зависимости, миграции, кэш (от правильных ролей) @@ -80,16 +94,26 @@ ssh liderra-prod 'cd /var/www/liderra/app && sudo -u www-data composer install - ssh liderra-prod 'cd /var/www/liderra/app && sudo -u www-data php artisan optimize' ``` -**6b. Миграции — через `postgres` superuser (НЕ `artisan migrate`).** `artisan migrate` от `crm_app_user` упрётся в `42501 must be owner`. Применять SQL миграции напрямую и **вручную** дописать строку в `migrations`: +**6b. Миграции — на ЖИВОЙ Managed-кластер как `crm_migrator` (НЕ `sudo -u postgres psql` на VM!).** + +⚠️ `sudo -u postgres psql -d liderra` на VM = **старая мёртвая копия** (127.0.0.1), миграции туда боевую базу НЕ меняют. Живая база — Managed-кластер. `artisan migrate` тоже не годится: config закэширован → env-override роли не сработает, а дефолтный `crm_app_user` не владелец (`42501`). Поэтому применяем DDL напрямую `psql` к rw-endpoint как `crm_migrator` (владелец таблиц), **одной транзакцией**, и вручную дописываем строку(и) в `migrations`: ```bash -# применить DDL миграции (пример; dollar-quoting вместо $$): -ssh liderra-prod 'sudo -u postgres psql -d liderra -c ""' -# отметить миграцию как применённую (batch — следующий номер): -ssh liderra-prod 'sudo -u postgres psql -d liderra -c "INSERT INTO migrations (migration, batch) VALUES (\x27<имя_файла_без_.php>\x27, )"' +# пароль crm_migrator — из Lockbox (ID секрета в памяти project-autopodbor-batch-prod-deploy-2026-07-09), НЕ хранить в рунбуке +ssh liderra-prod 'bash -s' <<'EOF' +YC=/home/ubuntu/yandex-cloud/bin/yc +PW=$($YC lockbox payload get --id --key postgresql_password) +cat > /tmp/mig.sql <<'SQL' +-- DDL миграции (additive безопаснее; ALTER TABLE ... ADD COLUMN nullable, CREATE INDEX, CREATE POLICY) +INSERT INTO migrations (migration, batch) VALUES + ('<имя_файла_без_.php>', (SELECT COALESCE(MAX(batch),0)+1 FROM migrations)); +SQL +PGPASSWORD="$PW" psql "host=c-c9q2cvtjpq3hgq6l0r96.rw.mdb.yandexcloud.net port=6432 dbname=liderra user=crm_migrator sslmode=require" -v ON_ERROR_STOP=1 -1 -f /tmp/mig.sql +rm -f /tmp/mig.sql +EOF ``` -> Повторный `artisan migrate` снова упрётся в привилегию — это ожидаемо, не «лечить» правами `crm_app_user`. Если миграция партиционированная — см. паттерн «pre-apply partitioned migrations via postgres superuser». +> Перед миграциями — убедиться, что у кластера есть свежий облачный бэкап (`yc managed-postgresql cluster list-backups --id c9q2cvtjpq3hgq6l0r96`, монитор `liderra-backup-monitor.sh` в помощь). Проверить состояние после: `SELECT` наличия колонки/индекса/политики + записи в `migrations`. **6c. Права на storage/cache (из redeploy.sh):** @@ -109,10 +133,10 @@ ssh liderra-prod 'sudo systemctl restart php8.3-fpm liderra-queue' Smoke (с прода или с dev — curl только наружу, не active scan): ```bash -ssh liderra-prod 'curl -skI https://liderra.ru/' +ssh liderra-prod 'curl -skI https://lk.liderra.ru/ | head -1; curl -skI https://liderra.ru/ | head -1' ``` -Ждём `HTTP/2 200` + корректные заголовки безопасности. Дополнительно: `audit:verify-chains`, `scheduler:check-heartbeats`, `incidents:watch-failures` — clean. +Ждём `HTTP/… 200` для **`lk.liderra.ru`** (портал) и `liderra.ru` (лендинг). Дополнительно: логи чисты (`storage/logs/laravel.log`), `queue:failed` пуст, крон-джобы clean. Для бэкенд-правки — рестарт `php8.3-fpm` обязателен (opcache), иначе крутится старый байткод. --- @@ -126,7 +150,7 @@ ssh liderra-prod 'cd /var/www/liderra && sudo tar -xzf /tmp/app-backup-<дата ssh liderra-prod 'cd /var/www/liderra/app && sudo -u www-data php artisan up' ``` -Миграции БД rollback'ятся отдельно (если применялись) — через `sudo -u postgres psql` обратным DDL + удаление строки из `migrations`. БД-откат планировать заранее: append-only audit-таблицы откатить нельзя. +Миграции БД rollback'ятся отдельно (если применялись) — **на живом Managed-кластере как `crm_migrator`** (тем же psql-способом из §6b) обратным DDL + `DELETE FROM migrations WHERE migration='<имя>'`. НЕ на VM-копии. Additive-миграции (ADD COLUMN nullable / CREATE INDEX / CREATE POLICY) откатываются мгновенно и данные не теряют. БД-откат планировать заранее: append-only audit-таблицы откатить нельзя; крайняя мера — PITR/облачный бэкап кластера. --- @@ -134,4 +158,7 @@ ssh liderra-prod 'cd /var/www/liderra/app && sudo -u www-data php artisan up' - [deploy/redeploy.sh](../../../deploy/redeploy.sh) — серверная половина scp-эпохи (исторический контекст шага 6). - Память `portal-security-audit-2026-06-17` — первый прогон этого пайплайна (18.06.2026, июньская дельта). +- Память `project-managed-pg-cluster-live-2026-06-26` — переезд на Managed PG (почему миграции теперь на кластер). +- Память `project-main-eq-prod-reconcile-2026-07-09` — `main`=прод (почему катим из main); `project-autopodbor-batch-prod-deploy-2026-07-09` — образец хирургического выката + миграций crm_migrator. +- Бэкапы: off-site git-репо (`liderra-git-backup.sh`, бакет `liderra-portal-git-backup`); свежесть БД-бэкапа стережёт `liderra-backup-monitor.sh`. - `ПИЛОТ.md` (корень) — снимок боевого состояния; волатильное перепроверять реальной командой по ssh.