5.4 KiB
Производительность и ресурсы контейнера
Что сделано для быстродействия, стабильности и минимального потребления ресурсов, и как это проверять.
Что включено
| Область | Решение |
|---|---|
| Остановка | SIGTERM/SIGINT → app.close() (дожидается запросов) → WAL checkpoint → close. Таймаут SHUTDOWN_TIMEOUT_MS (8 с), повторный сигнал — немедленный выход. В compose init: true, stop_grace_period: 15s. |
| SQLite | busy_timeout, cache_size, temp_store=MEMORY, mmap_size, лимит WAL; транзакции для записи доступа/аудита; версия схемы в user_version (миграции не гоняются на каждом старте); индексы под очистку и фильтры; фильтры/сортировки в SQL. |
| Очистка | Раз в час: истёкшие/отозванные сессии, коды авторизации, WebAuthn-челленджи (пачками по 1000, SESSION_RETENTION_DAYS). |
| Горячий путь | Кэш профиля пользователя (инвалидируется любой записью в users/доступ — блокировка действует сразу), кэш App Switcher, мемоизация JWKS. Лимитер argon2 (ARGON2_CONCURRENCY). Секреты OIDC-клиентов — SHA-256 (старые argon2 мигрируют при первом обмене). |
| HTTP | Hashed-ассеты immutable на год, index.html — no-cache; сжатие JSON на стороне Traefik; CORS по allowlist; /health (liveness, без БД) и /ready (БД + ключ OIDC); корректные 404 для /oauth, /.well-known, /api. |
| Образ | API бандлится tsup в один файл, в образ ставятся только нативные модули; статика только .br; healthcheck через wget; NODE_OPTIONS=--max-old-space-size=160; лимиты compose: 256 МиБ, 1 CPU, read_only, cap_drop: ALL. |
| Бэкап | docker exec auth-portal node dist/server.js backup или BACKUP_INTERVAL_HOURS; онлайн-копия, integrity_check, ротация. |
Дополнительно: подготовленные запросы (prepare один раз на соединение),
строгая CSP ('self', без inline-скриптов), опциональное сжатие в Node
(ENABLE_COMPRESS=true, если нет Traefik compress), базовый образ закреплён по
digest, design-preview-маршруты вырезаются из прод-образа, healthcheck Traefik
и depends_on: service_healthy, provenance + SBOM при публикации, Trivy
(информативно), общий composite-action CI, inputs в turbo.json.
Замеры (локально, Windows, Node 25; не Linux-контейнер)
| Что | Результат |
|---|---|
listActiveSessions по пользователю на 100k сессий |
~1.6 мс |
runHousekeeping, 90k устаревших сессий |
~220 мс |
appendAuditBatch, 10k событий одной транзакцией |
~610 мс |
listAudit(kind=logins) на 10k |
~5 мс |
| 20k prepared-запросов профиля | ~28 мс |
HTTP GET /health (autocannon, 50 соединений) |
~15 000 rps, p99 7 мс |
HTTP GET /api/v1/auth/me (JWT, кэш профиля) |
~8 300 rps, p99 11 мс |
web: файлов в dist/assets |
87 → 48 без preview-маршрутов; gzip JS 484 → 437 КБ |
| Рабочий набор процесса (Windows) | ~117 МиБ в покое, ~237 МиБ после потока 8 с при куче 160 МБ |
Рабочий набор Windows не равен RSS контейнера Linux. Реальный RSS под лимитом
256 МиБ мерит scripts/ci/smoke-image.sh и ночной nightly-perf — при
превышении увеличьте mem_limit и NODE_OPTIONS вместе.
Проверка
pnpm turbo test:coverage --filter=@authportal/api --filter=@authportal/db --filter=@authportal/shared
pnpm --filter web build && node scripts/ci/web-budget.mjs apps/web/dist/assets
bash scripts/ci/smoke-image.sh <image> # размер, RSS, /ready, docker stop, данные после рестарта
pnpm --filter @authportal/db test:perf # бюджеты производительности БД
node scripts/perf/load.mjs http://127.0.0.1:8080 [container] # autocannon (ночью: nightly-perf.yaml)
Бюджеты smoke-теста (env): MAX_IMAGE_MB=250, MAX_RSS_MB=200, MAX_START_S=15,
MAX_STOP_S=5. Подтяните их после первого замера на CI.
Эксплуатационные заметки
- В production
JWT_SECRETдолжен быть уникальным (≥ 16 символов), аADMIN_PASSWORDдля первого запуска — не короче 8 символов и неadmin. - Если нужен другой лимит памяти, меняйте
mem_limitиNODE_OPTIONSвместе (куча ≈ 60–65 % лимита). - Одна реплика: SQLite не поддерживает несколько писателей.