docs(log): шаги 0.5/0.2 — разбор инцидента с досрочным деплоем Dockerfile
Прод восстановлен (5426c70): app healthy, uid=1001(app), health 200,
миграция 0079 применена, trgm-индексы на месте
This commit is contained in:
@@ -87,3 +87,31 @@
|
|||||||
- Как проверялось: миграция применена на scratch-контейнере postgres:16-alpine с 100k синтетических задач — все 7 операторов чисто. EXPLAIN ANALYZE поиска `ilike '%омент%'` (совпадение внутри слова — инвариант): полный проход 77.5 мс (Seq Scan) → 4.8 мс (Bitmap Index Scan по trgm + BitmapAnd с org-индексом), ускорение ~16×; с LIMIT 50 — 10.8 мс → 9.7 мс. Отдельно проверено: `CREATE EXTENSION pg_trgm` выполняется не-суперпользователем-владельцем БД (trusted extension, PG 13+) — на проде миграция пройдёт под appuser.
|
- Как проверялось: миграция применена на scratch-контейнере postgres:16-alpine с 100k синтетических задач — все 7 операторов чисто. EXPLAIN ANALYZE поиска `ilike '%омент%'` (совпадение внутри слова — инвариант): полный проход 77.5 мс (Seq Scan) → 4.8 мс (Bitmap Index Scan по trgm + BitmapAnd с org-индексом), ускорение ~16×; с LIMIT 50 — 10.8 мс → 9.7 мс. Отдельно проверено: `CREATE EXTENSION pg_trgm` выполняется не-суперпользователем-владельцем БД (trusted extension, PG 13+) — на проде миграция пройдёт под appuser.
|
||||||
- Влияние на поиск/UX: семантика НЕ меняется (тот же ilike, тот же API) — только скорость.
|
- Влияние на поиск/UX: семантика НЕ меняется (тот же ilike, тот же API) — только скорость.
|
||||||
- Подводные камни: на проде построение GIN-индексов без CONCURRENTLY кратковременно блокирует запись в tasks — при текущих объёмах (десятки тысяч задач) это секунды, приемлемо.
|
- Подводные камни: на проде построение GIN-индексов без CONCURRENTLY кратковременно блокирует запись в tasks — при текущих объёмах (десятки тысяч задач) это секунды, приемлемо.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [0.5] Чистый production-образ (без devDependencies)
|
||||||
|
|
||||||
|
- Статус: ✅ done (коммиты `a54ec77` (Dockerfile), `9fc6ad1`, `5426c70` (фиксы запуска))
|
||||||
|
- Зачем: в прод-образ не должны попадать typescript/vite/vitest — поверхность атаки и размер.
|
||||||
|
- Что изменено:
|
||||||
|
- `Dockerfile` — production-стадия: `npm ci --omit=dev` вместо копирования node_modules из builder.
|
||||||
|
- `package.json` — vitest/supertest/@types/* (16 пакетов) перенесены в devDependencies (именно `vitest` в dependencies тянул vite/esbuild/tsx в прод-образ).
|
||||||
|
- `server/vite.ts` — vite и vite.config импортируются динамически (dev-only), vite.config — по вычисляемому URL.
|
||||||
|
- Как проверялось: локальная сборка образа; бандл стартует без devDeps (проверка до подключения БД); `grep` — 0 статических импортов vite в dist/index.js.
|
||||||
|
- Влияние на поиск/UX: нет.
|
||||||
|
- **Инцидент и подводные камни (важно для будущих агентов):**
|
||||||
|
1. Правки Dockerfile ушли в main досрочно (попали в коммит 0.11 через `git add -A`) — автодеплой поднял их до завершения smoke-теста. Правило: коммитить явным списком файлов, не `git add -A`.
|
||||||
|
2. Первое падение: `ERR_MODULE_NOT_FOUND @vitejs/plugin-react` — `server/vite.ts` статически импортировал vite/vite.config; раньше спасало наличие всех devDeps в образе.
|
||||||
|
3. Второе падение: esbuild без `--splitting` ИНЛАЙНИТ статический `import("../vite.config")` в бандл и поднимает его импорты на верхний уровень — динамический импорт должен быть по вычисляемому пути (`new URL(...)`, esbuild не анализирует).
|
||||||
|
4. Локальная проверка `node dist/index.js` НЕ ловит такие ошибки, если локальный node_modules содержит devDeps — резолвится молча. Проверять grep'ом по бандлу или в чистом окружении.
|
||||||
|
5. Даунтайм продакшена ~25 минут (краш-луп до фикса `5426c70`).
|
||||||
|
|
||||||
|
## [0.2] Контейнер не от root
|
||||||
|
|
||||||
|
- Статус: ✅ done (коммит `a54ec77`)
|
||||||
|
- Зачем: компрометация приложения не даёт root в контейнере.
|
||||||
|
- Что изменено: `Dockerfile` — `adduser -D app` (uid 1001), `chown -R app:app /app`, `USER app`, `HOME=/tmp`, `npm_config_cache=/tmp/.npm`, `PYTHONDONTWRITEBYTECODE=1`; `docker/Dockerfile.documents` — аналогично (там уже был `npm ci --production`).
|
||||||
|
- Как проверялось: `docker run --entrypoint id` → uid=1001(app); `/app/data` и `/tmp` доступны на запись от app; document-worker на проде поднялся и healthy сразу.
|
||||||
|
- Влияние на поиск/UX: нет.
|
||||||
|
- Подводные камни: named volume `crm_crm_data` на проде был root-owned — на сервере заранее выполнен `chown -R 1001:1001` на `crm_crm_data` и `crm_crm_uploads` (иначе app не смог бы писать в /app/data). При развёртывании на новых серверах — учитывать.
|
||||||
|
|||||||
Reference in New Issue
Block a user