Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Развёртывание и эксплуатация

Сценарий

Вы — администратор, который запускает gigastt на сервере, а не на ноутбуке. Маршрут этой главы: установить → ограничить → наблюдать → обновлять — один управляемый сервис (systemd или Docker), метрики в Prometheus/Grafana, алерты на важные режимы отказа и процедура обновления, не обрывающая живые сессии транскрибации.

Каждый рецепт заканчивается шагом «Проверить». Флаги сверены с gigastt serve --help; полный справочник флагов живёт в docs/cli.md и здесь не повторяется.

Предпосылки

  • gigastt установлен (бинарник, пакет или образ) — см. Начало работы.
  • Linux-хост с 4+ ГБ RAM: при --pool-size 2 по умолчанию с INT8-энкодером RSS составляет ~790 МиБ; оставьте запас под ОС и пики запросов.
  • Для пути с systemd: systemd 241 или новее (любой современный дистрибутив, включая Astra Linux, RED OS, ALT) и root-доступ.
  • Для пути с Docker: Docker 20.10+; NVIDIA Container Toolkit — только для CUDA-варианта.
  • Модель либо скачивается один раз (~850 МБ FP32, автоматически квантуется в INT8 при первом запуске), либо предустановлена из офлайн-бандла / deb с моделью.

Рецепт

Docker

Каждый тегированный релиз публикует мультиархитектурные образы в GHCR — предпочтительнее тянуть готовое, а не собирать:

docker pull ghcr.io/ekhodzitsky/gigastt:2.14.1        # CPU, linux/amd64 + linux/arm64
docker pull ghcr.io/ekhodzitsky/gigastt:2.14.1-cuda   # CUDA, linux/amd64

Закрепляйте конкретный тег для воспроизводимых развёртываний; :latest / :cuda — плавающие.

Запускайте с именованным томом, чтобы модель ~850 МБ (и автоматически сгенерированный INT8-энкодер) переживали замену контейнера:

docker run -d --name gigastt \
  -p 127.0.0.1:9876:9876 \
  -v gigastt-models:/home/gigastt/.gigastt/models \
  ghcr.io/ekhodzitsky/gigastt:2.14.1

Примечания:

  • Команда образа по умолчанию — serve --port 9876 --host 0.0.0.0 --bind-all (это нужно контейнерной сети); -p 127.0.0.1:9876:9876 оставляет доступ с хоста только на loopback. TLS-прокси ставится впереди точно так же, как при установке без контейнера.
  • Контейнер работает под непривилегированным пользователем gigastt; каталог моделей внутри — /home/gigastt/.gigastt/models, именно он монтируется томом.
  • В образ встроен HEALTHCHECK на /health, так что docker ps покажет healthy, как только порт начнёт отвечать. Во время первичного скачивания модели и INT8-квантования (~2 мин) /health отвечает 200 с model:"loading", а /ready503 {"status":"not_ready","reason":"initializing"} — пропускайте трафик по /ready, а не по /health.
  • Baked-образ (нулевой холодный старт, +~850 МБ): соберите локально с моделью внутри — docker build --build-arg GIGASTT_BAKE_MODEL=1 -t gigastt:baked .
  • CUDA: docker run --gpus all -p 127.0.0.1:9876:9876 ghcr.io/ekhodzitsky/gigastt:2.14.1-cuda (требуется NVIDIA Container Toolkit; при отсутствии GPU бинарник откатывается на CPU).

Проверить:

curl -s http://127.0.0.1:9876/ready
# {"status":"ready","pool_available":2,"pool_total":2}
curl -s http://127.0.0.1:9876/health
# {"status":"ok","model":"gigaam-v3-rnnt","variant":"rnnt","version":"2.14.1","punctuation":true,"itn":true}

Установка без сети (замкнутый контур)

Для хостов без доступа в интернет каждый релиз публикует самодостаточный tarball под каждую Linux-цель — бинарник + предквантованная INT8-модель rnnt + модель пунктуации + systemd-юнит + установщик — и два Debian-пакета (gigastt_<ver>_<arch>.deb + gigastt-model-int8_<ver>_all.deb). Полный состав бандла приведён в README-OFFLINE.md и здесь не повторяется.

На машине с сетью скачайте файлы и проверьте их до переноса в контур (зачем и от каких угроз: docs/verifying-releases.md):

gh release download v2.14.1 -R ekhodzitsky/gigastt \
    -p 'gigastt-2.14.1-offline-x86_64-unknown-linux-gnu.tar.gz' \
    -p 'gigastt-2.14.1-offline-x86_64-unknown-linux-gnu.tar.gz.sha256' \
    -p 'gigastt-2.14.1-offline-x86_64-unknown-linux-gnu.tar.gz.minisig'
sha256sum -c gigastt-2.14.1-offline-x86_64-unknown-linux-gnu.tar.gz.sha256
minisign -Vm gigastt-2.14.1-offline-x86_64-unknown-linux-gnu.tar.gz -p gigastt.pub
gh attestation verify gigastt-2.14.1-offline-x86_64-unknown-linux-gnu.tar.gz \
    --repo ekhodzitsky/gigastt

На целевом хосте:

tar xf gigastt-2.14.1-offline-x86_64-unknown-linux-gnu.tar.gz
cd gigastt-2.14.1-offline
sudo ./install.sh    # verifies SHA256SUMS.txt, then installs binary + models + unit
sudo systemctl enable --now gigastt

Альтернатива для Debian-семейства:

sudo dpkg -i gigastt_2.14.1_amd64.deb gigastt-model-int8_2.14.1_all.deb
sudo systemctl enable --now gigastt

Модель уже в INT8 — ни скачивания, ни квантования, ни сети. Установленный юнит выставляет GIGASTT_OFFLINE=1 через /etc/gigastt/gigastt.env, поэтому любой путь кода, который попытался бы скачать модель (включение --vad, диаризация, альтернативная голова распознавания), падает быстро с ошибкой, называющей нужный файл, вместо зависания на connect timeout. Чтобы добавить опциональные модели позже, выполните gigastt download на машине с сетью и скопируйте файлы в /usr/share/gigastt/models/.

Типовые ошибки:

  • install.sh прерывается с sha256sum: WARNING: 1 computed checksum did NOT match — tarball повреждён при переносе в замкнутый контур. Проверьте внешний .sha256, скопируйте заново и повторите; частичной установки не происходит.
  • Ошибка офлайн-режима, называющая отсутствующий файл (например, модель VAD после включения --vad) — этой модели нет в бандле; скачайте её на машине с сетью и скопируйте.

Проверить:

systemctl is-active gigastt
# active
curl -s http://127.0.0.1:9876/health
# {"status":"ok",...} — served immediately, the model is pre-installed

systemd-сервис

Усиленный юнит лежит в packaging/systemd/ и устанавливается как deb-пакетом, так и офлайн-бандлом. Ключевые свойства (сам юнит короткий и с комментариями — полный список читайте в нём):

  • Запуск под непривилегированным пользователем gigastt; модели в /usr/share/gigastt/models доступны ему только на чтение.
  • Прослушивание только loopback (127.0.0.1:9876); наружу API выставляется через reverse proxy.
  • Restart=on-failure, RestartSec=5 — падение перезапускается, чистый systemctl stop — нет.
  • Набор харденинга, совместимый с systemd 241 (ProtectSystem=strict, NoNewPrivileges, PrivateTmp, …), поэтому юнит работает без изменений на Astra Linux, RED OS и ALT.
  • Переопределения живут в /etc/gigastt/gigastt.env (переменные GIGASTT_*, RUST_LOG) и подхватываются через EnvironmentFile.

Логи идут в журнал:

journalctl -u gigastt -f          # follow
journalctl -u gigastt -n 100      # recent

Уровень логирования меняется правкой /etc/gigastt/gigastt.env (RUST_LOG=gigastt=debug), затем sudo systemctl restart gigastt.

Флаги меняйте через drop-in — никогда не правьте поставляемый юнит (обновление пакета его перезапишет). ExecStart нужно сначала очистить, потом задать заново:

# sudo systemctl edit gigastt
[Service]
ExecStart=
ExecStart=/usr/bin/gigastt serve --model-dir /usr/share/gigastt/models --punct-model-dir /usr/share/gigastt/models/punct --metrics

systemctl restart gigastt шлёт SIGTERM; сервер дренирует живые WebSocket/SSE-сессии — каждый клиент получает кадр Final + Close(1001 Going Away) — в течение --shutdown-drain-secs (по умолчанию 10 с), что с запасом укладывается в стандартный стоп-таймаут systemd 90 с. Как использовать это при обновлении версий: Обновление и откат ниже.

Проверить:

systemctl status gigastt --no-pager
curl -s http://127.0.0.1:9876/health

Наблюдаемость

Метрики включаются опционально и отдаются на отдельном слушателе — никогда на порту API, поэтому они находятся вне CORS-allowlist и per-IP rate-лимитера:

gigastt serve --metrics                                  # http://127.0.0.1:9090/metrics
gigastt serve --metrics --metrics-listen 127.0.0.1:9100  # custom port

Держите слушатель на loopback, если только ваш Prometheus не на другом хосте — и даже тогда привязывайте его к доверенному интерфейсу, никогда к публичному.

Минимальная проводка Prometheus (prometheus.yml):

scrape_configs:
  - job_name: gigastt
    static_configs:
      - targets: ["127.0.0.1:9090"]

rule_files:
  - /etc/prometheus/rules/gigastt-alerts.yml   # copy of docs/observability/alerts.yml

Метрики, которые важны (все с префиксом gigastt_):

МетрикаЗначение
gigastt_http_requests_totalЗапросы по path/method/status — доля 5xx, 503
gigastt_http_request_duration_secondsГистограмма HTTP-латентности (p50/p95/p99)
gigastt_pool_available / gigastt_pool_waitersСвободные триплеты инференса против ожидающих вызовов — сигнал насыщения
gigastt_pool_timeouts_totalТаймауты checkout → клиенты получили 503 + Retry-After
gigastt_inference_timeouts_totalЗапуски, прерванные --inference-timeout-secs
gigastt_inference_duration_secondsГистограмма латентности инференса
gigastt_ws_active_connectionsЖивые WebSocket-сессии
gigastt_rate_limit_rejections_totalОтветы 429 от per-IP лимитера
gigastt_batch_pool_available / gigastt_batch_pool_waitersТе же метрики пула для раздела --batch-pool-size

Готовые артефакты — импортируйте, не изобретайте:

  • docs/observability/alerts.yml — правила Prometheus: 5xx выше 5%, gigastt_pool_available == 0 в течение 1 мин, p95 выше 10 с, устойчивые таймауты пула, падение health-пробы (blackbox exporter).
  • docs/observability/dashboard.json — дашборд Grafana (Dashboards → Import): частота запросов, латентность, 5xx, доступность пула, активные WebSocket, длительность инференса, отказы rate-лимитера.

Что алертить на практике: насыщение пула (gigastt_pool_available == 0 устойчиво — клиенты получают 503), долю 5xx и RAM на уровне узла (gigastt не экспортирует метрику собственного RSS; используйте node_exporter или cAdvisor).

Логи: env-фильтр tracing через RUST_LOG (по умолчанию gigastt=info; gigastt=debug для разбора). Логи содержат метаданные запросов — длительности, число слов — но никогда текст транскриптов (docs/privacy.md).

Проверить:

curl -s http://127.0.0.1:9876/ready > /dev/null   # samples the pool gauges once
curl -s http://127.0.0.1:9090/metrics | grep '^gigastt_pool_available'
# gigastt_pool_available 2

Безопасность по умолчанию

Значения по умолчанию — это уже усиленная конфигурация, поэтому рецепт в основном о том, как её не ослабить:

  • Привязка к loopback. serve отказывается слушать не-loopback адреса, пока не задан --bind-all / GIGASTT_ALLOW_BIND_ANY=1. Удалённый доступ = TLS-терминирующий reverse proxy на том же хосте (конфиги Caddy/nginx: docs/deployment.md).
  • Origin-allowlist. Loopback-источники разрешены всегда; любой другой Origin нужно перечислить через --allow-origin (повторяемый флаг, точное совпадение). --cors-allow-any — только для разработки. Неразрешённые источники получают 403.
  • Лимиты запросов. --body-limit-bytes (по умолчанию 50 МиБ), --ws-frame-max-bytes (512 КиБ), --idle-timeout-secs (300), --max-session-secs (3600), --inference-timeout-secs (600), --pool-checkout-timeout-secs (30) — при насыщении пула 503 + Retry-After.
  • Rate-лимитинг (опционально): --rate-limit-per-minute N с --rate-limit-burst429 + Retry-After. За прокси он работает по-клиентски, только если прокси перезаписывает X-Forwarded-For и задан --trust-proxy — копируйте сниппеты прокси из docs/deployment.md дословно.
  • Целостность модели. Скачивания проверяются по SHA-256 и атомарно переименовываются (.partial → финальный); повреждённый файл никогда не попадает в каталог моделей.
  • Верификация релизов. У каждого артефакта релиза есть .sha256-сайдкар + SHA256SUMS.txt, подпись minisign, CycloneDX SBOM и SLSA-провенанс сборки. Проверяйте перед установкой — docs/verifying-releases.md. Минимальный ритуал:
minisign -Vm gigastt-2.14.1-x86_64-unknown-linux-gnu.tar.gz -p gigastt.pub
gh attestation verify gigastt-2.14.1-x86_64-unknown-linux-gnu.tar.gz \
    --repo ekhodzitsky/gigastt
  • Приватность. Нет телеметрии, нет исходящих соединений после разового скачивания модели, транскрипты не логируются (docs/privacy.md).

Проверить:

ss -ltnp | grep 9876
# tcp LISTEN 0 ... 127.0.0.1:9876 ...   (loopback only)
curl -s -o /dev/null -w '%{http_code}\n' \
    -H 'Origin: https://attacker.example' http://127.0.0.1:9876/v1/models
# 403

Горячая перезагрузка модели без рестарта

Когда вы заменили файлы модели на диске (новый INT8-энкодер, другая голова, обновлённая punct-модель), движок можно пересобрать на месте, не останавливая serve:

# Только с loopback — не-loopback клиенты получают 403 loopback_only
# даже при --bind-all.
curl -s -X POST http://127.0.0.1:9876/v1/admin/reload
# {"reloaded":true,"variant":"rnnt","encoder":"int8"}

Сервер пересобирает engine по boot-рецепту (каталог моделей, размер пула, punct / ITN / VAD / hotwords), греет новый engine и атомарно подменяет его. Запросы в полёте дорабатывают на старом. Ошибка сборки оставляет прежнюю модель (503 reload_failed). Параллельные reload → 409 reload_in_progress. Полный контракт: docs/api.md — Admin reload.

Проверка:

curl -s -X POST http://127.0.0.1:9876/v1/admin/reload | tee /tmp/reload.json
python3 -c "import json; d=json.load(open('/tmp/reload.json')); assert d['reloaded'] is True"
# From a non-loopback bind (only if you deliberately opened one): expect 403.

Обновление и откат

Закрепляйте то, что разворачиваете (тег образа, версию deb), чтобы обновление было осознанным и обратимым шагом. Каталог моделей — это состояние: он переживает обновления, движок сам определяет установленную голову распознавания, и никакого молчаливого перекачивания при смене бинарника не происходит. В скриптах установки предпочтите резолв latest-тега:

TAG=$(gh api repos/ekhodzitsky/gigastt/releases/latest -q .tag_name)   # e.g. v2.14.1
VER=${TAG#v}

Docker (обновление до выбранного пина — здесь 2.14.1):

docker pull ghcr.io/ekhodzitsky/gigastt:2.14.1
docker stop --time 15 gigastt && docker rm gigastt
docker run -d --name gigastt \
  -p 127.0.0.1:9876:9876 \
  -v gigastt-models:/home/gigastt/.gigastt/models \
  ghcr.io/ekhodzitsky/gigastt:2.14.1

docker stop шлёт SIGTERM; --time 15 даёт окну дренажа (--shutdown-drain-secs, по умолчанию 10 с) завершиться до SIGKILL — стандартные 10 с Docker соревнуются с дренажом. Клиенты получают Final + Close(1001) и переподключаются; короткие REST-загрузки в полёте, возможно, придётся повторить.

systemd / deb:

sudo dpkg -i gigastt_2.14.1_amd64.deb
sudo systemctl restart gigastt
journalctl -u gigastt -f    # expect a clean drain, no "Drain window expired"

В Kubernetes то же правило действует со стороны оркестратора: terminationGracePeriodSecondsshutdown_drain_secs + 5 (полный манифест: docs/deployment.md).

Откат. Разверните предыдущий тег или пакет — набор моделей на диске не менялся, поэтому старый бинарник стартует на тех же файлах:

docker run -d --name gigastt \
  -p 127.0.0.1:9876:9876 \
  -v gigastt-models:/home/gigastt/.gigastt/models \
  ghcr.io/ekhodzitsky/gigastt:2.14.0
# or: sudo dpkg -i gigastt_2.14.0_amd64.deb && sudo systemctl restart gigastt

Если регрессия дренажа ломает ваших WebSocket-клиентов после обновления, аварийный выход — --shutdown-drain-secs 0 (прижимается к 1 с); полная таблица симптомов — docs/runbook.md.

Проверить (после каждого обновления или отката):

curl -s http://127.0.0.1:9876/health
# "version" is the release you deployed; "model"/"variant" are unchanged
curl -s http://127.0.0.1:9876/ready

Проверка результата

Сквозной смоук после любого рецепта выше:

systemctl is-active gigastt || docker ps --filter name=gigastt --format '{{.Status}}'
curl -s http://127.0.0.1:9876/health     # status ok, expected version
curl -s http://127.0.0.1:9876/ready      # ready, pool_available >= 1
curl -s http://127.0.0.1:9090/metrics | grep '^gigastt_pool_available'

Затем транскрибируйте один короткий файл через тот API, который реально выставлен (REST-рецепты: CLI и пакетная обработка; проверка через CLI: Начало работы).

Частые ошибки

  • OOM — контейнер или сервис убит. RSS растёт вместе с --pool-size: INT8-энкодер — ~400 МиБ на триплет, ~790 МиБ при пуле 2 по умолчанию; FP32-энкодер примерно в 4 раза больше (никогда не передавайте --skip-quantize в production). На машине с 4 ГБ держите --pool-size в пределах 1–2; --pool-min-size 1 позволяет серверу подняться на деградированном пуле вместо падения при нехватке памяти. Если Kubernetes сообщает OOMKilled, уменьшите пул или поднимите лимит пода — подробности в docs/runbook.md.
  • 503 timeout под нагрузкой. Все триплеты заняты, и вызывающий дождался конца --pool-checkout-timeout-secs (30 с): REST получает 503 + Retry-After, WebSocket — ошибку с retry_after_ms. Это противодавление, а не баг — поднимите --pool-size, отделите пакетную работу через --batch-pool-size и следите за gigastt_pool_waiters / gigastt_pool_timeouts_total.
  • /metrics недоступен с хоста Prometheus. Так задумано: слушатель по умолчанию — 127.0.0.1:9090. Направьте скрейпер на сам хост gigastt или осознанно перепривяжите порт через --metrics-listen на доверенном интерфейсе — никогда на публичном. Скрейпер, по-прежнему смотрящий на :9876/metrics, получит 404: метрики убраны с порта API.
  • Флапающие readiness-пробы при первом запуске. Первый запуск скачивает ~850 МБ и квантует (~2 мин). Всё это время /health возвращает 200 с model:"loading", но /ready возвращает 503 initializing. Если ваш балансировщик маршрутизирует по /health, ранние клиенты получат 503 — пробуйте /ready или предустановите / запеките модель, чтобы окно исчезло.
  • Rate-лимитер штрафует всех за прокси. Без --trust-proxy — и без прокси, перезаписывающего, а не дописывающего X-Forwarded-For — все клиенты делят один бакет, ключованный адресом прокси. Симптомы и точная конфигурация прокси: docs/deployment.md.

Ссылки