Деплой, мониторинг и нагрузочное тестирование¶
Деплой¶
Exchange Engine¶
Расположение файлов: /exchange/deployment/ (23 файла)
Docker-образы¶
Используется multi-stage build: golang:1.25.10-alpine → alpine:3.20. Dockerfile находится в корне репозитория.
Build args:
| Аргумент | Описание |
|---|---|
APP_NAME |
Имя собираемого бинарника |
VERSION |
Версия образа (используется в метаданных) |
Тегирование: формат YYYYMMDD-COMMITHASH, например 20260306-0f1b10b
Собираемые образы (12 штук)¶
| Группа | Образы |
|---|---|
| Основные | admin, api, engine |
| Topup Scan | topupscan-btc, topupscan-evm, topupscan-tron, topupscan-processor, topupscan-aml-poller, topupscan-elliptic-check, topupscan-refund-processor |
| TX Processor | txprocessor-btc, txprocessor-evm, txprocessor-tron |
| Hot Manager | hotmanager-btc, hotmanager-evm, hotmanager-tron |
Build-инструменты¶
- Skaffold — конфигурация в
skaffold.yaml, используется для локальной разработки и сборки - Makefile — основные цели:
make all # сборка + lint + тесты
make build # только сборка образов
make lint # golangci-lint
make test-go # запуск Go-тестов
Secrets¶
Секреты управляются через Sealed Secrets — зашифрованные манифесты хранятся в /deployment/sealed-secrets/.
CI/CD¶
GitHub Actions (.github/workflows/tests.yml) выполняет при каждом PR и пуше:
- Go unit-тесты
golangci-lint— статический анализ кодаbuf— lint и breaking-change проверки для Protobuf-схем
Kubernetes-ресурсы¶
Метрики: каждый pod экспортирует Prometheus-метрики на порту 8081 по пути /metrics.
Univex Monorepo¶
Расположение: у каждого микросервиса свой подкаталог deployments/; актуальные stage-манифесты — в /stage-deployments/.
Docker-образы¶
Используется multi-stage build: golang:1.25.10 → минимальный runtime-образ. Dockerfile расположен внутри каждого микросервиса.
Kubernetes namespace: stage
Kubernetes-ресурсы¶
| Параметр | Requests | Limits |
|---|---|---|
| CPU | 100–200m | 300m–1000m |
| Memory | 256–512Mi | 512Mi–2Gi |
Диапазон зависит от нагрузки конкретного микросервиса.
Миграции БД¶
Для каждой базы данных запускается отдельный Kubernetes Job (migrator-job.yaml) с настройкой backoffLimit: 3. Миграция выполняется до старта основного Deployment.
Temporal Workers¶
Workers Temporal вынесены в отдельные Deployment-ресурсы и масштабируются независимо от HTTP/gRPC-сервисов.
Service mesh и hardening¶
Stage namespace stage включён в Istio mesh. Целевое состояние:
- namespace и workload
PeerAuthenticationработают вSTRICTmTLS; DestinationRule stage-mesh-mtlsиспользуетISTIO_MUTUALдля*.stage.svc.cluster.local;- privileged destinations закрыты
AuthorizationPolicyallowlist-ами по service account и порту; - application pods запускаются non-root, с
allowPrivilegeEscalation: false,RuntimeDefaultseccomp, dropped capabilities иautomountServiceAccountToken: false.
Быстрая проверка:
Важно
PeerAuthentication STRICT шифрует и аутентифицирует mesh-трафик, но сам по себе не ограничивает, какой workload может обращаться к destination. Для privileged services обязательно проверять наличие AuthorizationPolicy.
Базы данных stage¶
Docker Compose файл: /univex/ops/docker-compose-prod.yml
На DB-хосте nsc-db-1 развёрнуто 10 сервисных PostgreSQL баз данных, каждая с выделенным PgBouncer в режиме transaction pooling (max 100 connections на пул). PgBouncer слушает только tailnet-IP 100.123.97.86, требует TLS и доступен для k8s nodes на портах 6100-6110.
| База данных | PgBouncer-порт |
|---|---|
univex-id |
6100 |
exchange |
6101 |
sms |
6102 |
email |
6103 |
sumsub |
6104 |
p2p |
6105 |
bot |
6107 |
casbin |
6108 |
exchange_int |
6109 |
ledger |
6110 |
Temporal использует отдельный PostgreSQL контейнер prod_temporal_psql на
100.123.97.86:6120 с direct PostgreSQL TLS и без PgBouncer.
Temporal¶
Temporal server развёрнут как Helm release temporal в namespace temporal.
Он использует dedicated PostgreSQL на DB-хосте и имеет три реплики каждого
основного компонента: frontend, history, matching, worker и web.
Операторские endpoints доступны только через Tailscale:
| Endpoint | Назначение |
|---|---|
univex-temporal:7233 |
Temporal frontend gRPC через Tailscale |
https://univex-temporal-ui.tailb72375.ts.net |
Temporal Web UI через Tailscale |
Внутри кластера новый frontend доступен как
temporal-frontend.temporal.svc.cluster.local:7233.
App deployments используют k8s Temporal напрямую. Legacy Temporal dev-server в
tmux на nsc-db-1 выведен из эксплуатации и не должен запускаться для stage.
Ingress-паттерн¶
Все HTTP/WebSocket-сервисы используют единый шаблон ingress:
- Ingress class:
nginx - TLS: cert-manager с cluster-issuer
letsencrypt(автоматическое получение сертификатов Let's Encrypt) - CORS: настраивается через аннотацию
configuration-snippetсmore_set_headers
WebSocket выносится в отдельный ingress с увеличенными таймаутами:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
Текущие публичные stage-домены:
| Домен | Назначение |
|---|---|
univex.nostress.dev |
in-cluster product frontend |
univex-admin.nostress.dev |
in-cluster admin frontend |
api.univex.nostress.dev |
public gateway API |
api.exchange.nostress.dev |
exchange REST API |
ws.exchange.nostress.dev |
exchange WebSocket API |
admapi.univex.nostress.dev |
admin gateway API |
docs.univex.nostress.dev |
docs UI |
hawk.univex.nostress.dev |
temporary Hawk placeholder |
Frontend images are built in univex-web and deployed by
stage-deployments/frontend-domains.yaml in the backend repo. They run as
non-root containers with explicit resource requests/limits and cannot call
stage services directly through the mesh.
Мониторинг (Grafana + Prometheus)¶
Расположение всех манифестов: /univex/monitoring/
Prometheus¶
| Файл | Назначение |
|---|---|
prometheus-config.yaml |
Основная конфигурация: scrape jobs, targets |
prometheus-deployment.yaml |
Kubernetes Deployment |
prometheus-ingress.yaml |
Внешний доступ к UI |
prometheus-rbac.yaml |
ServiceAccount + ClusterRole для service discovery |
Scrape interval: 15 секунд
Prometheus автоматически собирает метрики со всех микросервисов:
- Univex — порт метрик
8000 - Exchange Engine — порт метрик
8081
Grafana¶
| Файл | Назначение |
|---|---|
grafana-deployment.yaml |
Dev-окружение |
grafana-prod-deployment.yaml |
Продакшн |
grafana-ingress.yaml / grafana-prod-ingress.yaml |
Ingress для dev / prod |
grafana-datasource.yaml |
Datasource: Prometheus |
grafana-dashboard-provisioning.yaml |
Автоматическая загрузка дашбордов |
Дашборды (16 штук)¶
Все дашборды хранятся как Kubernetes ConfigMap и подгружаются через provisioning при старте Grafana.
| Дашборд | Файл | Ключевые метрики |
|---|---|---|
| Overview | grafana-dashboard-overview.yaml |
Все сервисы: RPS, SLA, ошибки |
| Gateway | grafana-dashboard-gateway.yaml |
univex_grpc, ledger_rpc, p2p_rpc |
| Admin Gateway | grafana-dashboard-admin-gateway.yaml |
Admin gateway запросы |
| UnivexID | grafana-dashboard-univex-id.yaml |
id_service RPC-метрики |
| Ledger | grafana-dashboard-ledger.yaml |
ledger_rpc: запросы/мин, SLA%, ошибки |
| P2P | grafana-dashboard-p2p.yaml |
p2p_rpc: запросы, длительность, ошибки |
| Exchange | grafana-dashboard-exchange.yaml |
exchange_rpc, grpc_server |
| Exchange Integration | grafana-dashboard-exchange-integration.yaml |
exchange_integration_rpc |
| Trades Aggregator | grafana-dashboard-trades-aggregator.yaml |
trades_rpc |
grafana-dashboard-email.yaml |
email_rpc |
|
| SMS Gate | grafana-dashboard-sms-gate.yaml |
sms_rpc |
| Sumsub | grafana-dashboard-sumsub-integration.yaml |
sumsub_rpc |
| Business | grafana-dashboard-business.yaml |
Бизнес-метрики Univex |
| Exchange Business | grafana-business-dashboard.yaml |
Exchange бизнес-метрики |
Отслеживаемые метрики¶
На каждом дашборде отображаются:
- RPC requests/min — количество запросов в минуту
- Success rate (SLA%) — доля успешных ответов
- Error rate — доля ошибочных ответов (4xx, 5xx, gRPC errors)
- Latency percentiles — p50, p95, p99
Нагрузочное тестирование¶
Exchange Engine¶
ghz (gRPC benchmarking)¶
Файлы конфигураций: /exchange/deployment/loadtest-*.yaml (5 конфигураций)
Docker-образ: ghcr.io/bojand/ghz:latest
Тестируемый RPC: EngineService/AddOrder
| Сценарий | Описание | Конкурентность | Длительность |
|---|---|---|---|
| S3 local | Локальный стакан | 5 | 60s |
| S3 remote | Удалённый стакан | 5 | 60s |
| S5 BID | Только BID-ордера | 10 | 60s |
| S5 ASK | Только ASK-ордера | 10 | 60s |
| S5 prefill | Prefill 30 000 ордеров + смешанная нагрузка | 20 | 60s |
cmd/bench (кастомный Go-инструмент)¶
Утилита находится в cmd/bench и позволяет гибко настраивать нагрузку:
| Флаг | Описание |
|---|---|
-addr |
Адрес gRPC-сервера (TCP) |
-unix |
Путь к Unix-сокету (альтернатива TCP) |
-account-id |
UUID торгового аккаунта |
-c |
Конкурентность (количество горутин) |
-d |
Длительность теста |
-ticker |
Торговая пара (например BTCUSDT) |
-seed |
Сид для воспроизводимой генерации ордеров |
Результат сравнения транспортов:
| Транспорт | RPS | Latency |
|---|---|---|
| TCP | базовый | базовый |
| Unix socket | +24% RPS | −19% latency |
Unix-сокет даёт существенный прирост производительности при локальном деплое (engine + клиент на одном узле).
Univex — Yandex.Tank¶
Phantom (GET-запросы)¶
Файл: /univex/load-testing/tank-pod.yaml
Target: gateway.<namespace>.svc.cluster.local:80
Instances: 3000
Timeout: 6s
Профиль нагрузки: line(1, 500, 60s) → const(500, 240s)
Итого: ~20 минут
Эндпоинтов: 29 GET-маршрутов
BFG (мутирующие операции)¶
Файл: /univex/load-testing/tank-bfg.yaml
Python-скрипт: load_script.py
Тестируемые сценарии:
- Payment methods — CRUD-операции
- Blacklist — управление списком
- Advertisements — создание и удаление объявлений
Autostop-условия¶
Тест автоматически останавливается при:
| Условие | Порог | Окно |
|---|---|---|
| HTTP 5xx | > 10% | 30 сек |
| Сетевые ошибки | > 25% | 30 сек |
| Средняя задержка | > 5 сек | 30 сек |
Запуск¶
# Phantom
kubectl apply -f tank-pod.yaml -n stage
# BFG
kubectl create configmap tank-bfg-script \
--from-file=load_script.py -n stage
kubectl create configmap tank-bfg-ammo \
--from-file=ammo.txt -n stage
kubectl apply -f tank-bfg.yaml -n stage
Результаты нагрузочного тестирования (4 марта 2026)¶
| Метрика | Значение |
|---|---|
| Всего запросов | 541 148 |
| Целевой RPS | 500 |
| Ошибок | 0 |
| Avg latency | 11.6 мс |
| p95 latency | 33 мс |
| p99 latency | 47 мс |
| Оценка DAU | ~1.2–1.4 млн пользователей |