Redis timeout 200ms давал ложные срабатывания под пиковой нагрузкой на общем хосте — подняли до 800ms и добавили cpu/mem лимиты сервисам в compose, чтобы соседи не выедали CPU у Redis. Добавили метрику и WARN на случай, когда демаскирование не находит соответствие ни по id, ни по отпечатку маски (раньше тихо превращалось в повторное маскирование без единого следа в логах). Кластерные узлы (node-a/node-b) получили обе NER-модели (WikiNEuRal для имён, ruBERT для адресов) — раньше конфиг ссылался на несуществующие свойства и вторая ступень молча не работала. lb (nginx) и volume для prometheus/grafana данных зафиксированы в compose. Плюс код-ревью фиксы: утечка нативных ONNX-ресурсов при ошибке загрузки модели (BLOCKER), неверный HTTP-статус при сбое обработки, generic Exception в LlmClient заменён на конкретные, лишние same-package импорты убраны.
114 lines
4.9 KiB
YAML
114 lines
4.9 KiB
YAML
# `docker compose up` поднимает кластер целиком: redis, node-a, node-b за
|
||
# nginx-балансировщиком на 8080, плюс Prometheus на 9090 и Grafana на 3000.
|
||
#
|
||
# Маскирование детерминировано и работает на любом узле, а вот обратный шаг
|
||
# требует общего состояния — отсюда общий Redis между node-a и node-b.
|
||
#
|
||
# Лимиты CPU/RAM: сервер — 4 vCPU. Без лимитов node-a/node-b/prometheus/grafana
|
||
# под нагрузкой отжимали CPU у Redis, тот не укладывался в таймаут команд, узлы
|
||
# теряли общее состояние и демаскирование съезжало на резервный путь. Лимиты —
|
||
# это потолок (docker compose без swarm не умеет в гарантированные reservations),
|
||
# но они не дают соседям выесть Redis подчистую.
|
||
services:
|
||
|
||
prometheus:
|
||
image: prom/prometheus:v2.54.1
|
||
command:
|
||
- --config.file=/etc/prometheus/prometheus.yml
|
||
- --storage.tsdb.retention.time=6h
|
||
- --web.enable-lifecycle
|
||
ports:
|
||
- "9090:9090"
|
||
volumes:
|
||
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||
- prometheus-data:/prometheus
|
||
cpus: 0.5
|
||
mem_limit: 512m
|
||
|
||
grafana:
|
||
image: grafana/grafana:11.2.0
|
||
ports:
|
||
- "3000:3000"
|
||
environment:
|
||
# Демонстрационный стенд: вход без пароля, чтобы жюри не искало учётные данные.
|
||
# Для контура с реальными данными это надо снять.
|
||
GF_AUTH_ANONYMOUS_ENABLED: "true"
|
||
GF_AUTH_ANONYMOUS_ORG_ROLE: Admin
|
||
GF_AUTH_DISABLE_LOGIN_FORM: "true"
|
||
GF_USERS_DEFAULT_THEME: light
|
||
# Сразу открывать дашборд модуля, а не пустую главную.
|
||
GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH: /var/lib/grafana/dashboards/pd-guard.json
|
||
volumes:
|
||
- ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
|
||
- ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro
|
||
- grafana-data:/var/lib/grafana
|
||
cpus: 0.3
|
||
mem_limit: 512m
|
||
|
||
redis:
|
||
image: redis:7-alpine
|
||
command: ["redis-server", "--save", "", "--appendonly", "no", "--maxmemory", "1gb", "--maxmemory-policy", "allkeys-lru"]
|
||
healthcheck:
|
||
test: ["CMD", "redis-cli", "ping"]
|
||
interval: 5s
|
||
timeout: 2s
|
||
retries: 5
|
||
cpus: 1.0
|
||
mem_limit: 1200m
|
||
|
||
node-a: &node
|
||
image: pd-guard-spring:jvm
|
||
cpus: 1.0
|
||
mem_limit: 2200m
|
||
environment:
|
||
PDGUARD_STORE_BACKEND: redis
|
||
SPRING_DATA_REDIS_HOST: redis
|
||
PDGUARD_MAX_CONCURRENT: "2000"
|
||
PDGUARD_WARMUP_ITERATIONS: "2000"
|
||
# Вторая ступень распознавания — две модели под разные задачи (см.
|
||
# NameCascade.java): WikiNEuRal размечает имена, ruBERT — составляющие
|
||
# адреса. Модели в репозиторий не входят: ./tools/fetch-ner-model.sh.
|
||
# Пока движок не задан, соответствующая часть ступени выключена и
|
||
# сервис работает на одних правилах.
|
||
PDGUARD_NER_NAME_ENGINE: wikineural
|
||
PDGUARD_NER_NAME_MODEL: /deployments/models/wikineural-ner
|
||
PDGUARD_NER_ADDRESS_ENGINE: rubert
|
||
PDGUARD_NER_ADDRESS_MODEL: /deployments/models/rubert-ner
|
||
# ВРЕМЕННО для разового разбора формата тестовых payload'ов — пишет сырые ПД
|
||
# в логи узла. LOGGING_LEVEL_* не подходит: relaxed binding из env приводит
|
||
# имя логгера к нижнему регистру и не совпадает с ru.pdguard.core.Pipeline
|
||
# (заглавная P), поэтому уровень задан через -D, где регистр сохраняется.
|
||
# Выключить (убрать переменную) перед официальным нагрузочным прогоном.
|
||
JAVA_OPTS: "-Dspring.config.additional-location=optional:file:/deployments/config/ -Dlogging.level.ru.pdguard.core.Pipeline=DEBUG"
|
||
volumes:
|
||
- ./config:/deployments/config:ro
|
||
- ./models:/deployments/models:ro
|
||
depends_on:
|
||
redis:
|
||
condition: service_healthy
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
|
||
interval: 10s
|
||
timeout: 2s
|
||
retries: 3
|
||
|
||
node-b:
|
||
<<: *node
|
||
|
||
lb:
|
||
image: nginx:1.27-alpine
|
||
cpus: 0.3
|
||
mem_limit: 128m
|
||
ports:
|
||
- "8080:80"
|
||
volumes:
|
||
- ./nginx/lb.conf:/etc/nginx/nginx.conf:ro
|
||
depends_on:
|
||
node-a:
|
||
condition: service_healthy
|
||
node-b:
|
||
condition: service_healthy
|
||
|
||
volumes:
|
||
prometheus-data:
|
||
grafana-data: |