fix: устойчивость Redis-кластера под нагрузкой и код-ревью замечания
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 импорты убраны.
This commit is contained in:
+58
-46
@@ -1,41 +1,16 @@
|
||||
# Основной вариант — JVM: прогретая, она вдвое экономнее native по процессору и
|
||||
# втрое быстрее по задержке. Холодное окно закрывает прогрев на старте.
|
||||
# docker compose up pd-guard
|
||||
# `docker compose up` поднимает кластер целиком: redis, node-a, node-b за
|
||||
# nginx-балансировщиком на 8080, плюс Prometheus на 9090 и Grafana на 3000.
|
||||
#
|
||||
# Наблюдение поднимается вместе со всем стеком: `docker compose up` даёт сервис,
|
||||
# Prometheus на 9090 и Grafana на 3000 с уже заведённым дашбордом.
|
||||
# Маскирование детерминировано и работает на любом узле, а вот обратный шаг
|
||||
# требует общего состояния — отсюда общий Redis между node-a и node-b.
|
||||
#
|
||||
# Несколько узлов: маскирование детерминировано и работает на любом узле, а вот
|
||||
# обратный шаг требует общего состояния — иначе запрос попадёт не на тот узел.
|
||||
# docker compose --profile cluster up
|
||||
# Лимиты CPU/RAM: сервер — 4 vCPU. Без лимитов node-a/node-b/prometheus/grafana
|
||||
# под нагрузкой отжимали CPU у Redis, тот не укладывался в таймаут команд, узлы
|
||||
# теряли общее состояние и демаскирование съезжало на резервный путь. Лимиты —
|
||||
# это потолок (docker compose без swarm не умеет в гарантированные reservations),
|
||||
# но они не дают соседям выесть Redis подчистую.
|
||||
services:
|
||||
|
||||
pd-guard:
|
||||
image: pd-guard-spring:jvm
|
||||
build:
|
||||
context: .
|
||||
dockerfile: src/main/docker/Dockerfile
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
PDGUARD_MAX_CONCURRENT: "2000"
|
||||
PDGUARD_STORE_TTL_MINUTES: "30"
|
||||
# Прогоны на старте, чтобы первые запросы не шли по непрогретому коду.
|
||||
PDGUARD_WARMUP_ITERATIONS: "2000"
|
||||
# Вторая ступень распознавания. Модель в репозиторий не входит:
|
||||
# ./tools/fetch-ner-model.sh. Пока её нет, ступень сама выключится и
|
||||
# сервис работает на одних правилах.
|
||||
PDGUARD_NER_ENGINE: rubert
|
||||
PDGUARD_NER_MODEL: /deployments/models/rubert-ner
|
||||
volumes:
|
||||
- ./config:/deployments/config:ro
|
||||
- ./models:/deployments/models:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
|
||||
interval: 10s
|
||||
timeout: 2s
|
||||
retries: 3
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:v2.54.1
|
||||
command:
|
||||
@@ -46,8 +21,9 @@ services:
|
||||
- "9090:9090"
|
||||
volumes:
|
||||
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||
depends_on:
|
||||
- pd-guard
|
||||
- prometheus-data:/prometheus
|
||||
cpus: 0.5
|
||||
mem_limit: 512m
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:11.2.0
|
||||
@@ -65,11 +41,11 @@ services:
|
||||
volumes:
|
||||
- ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
|
||||
- ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro
|
||||
depends_on:
|
||||
- prometheus
|
||||
- grafana-data:/var/lib/grafana
|
||||
cpus: 0.3
|
||||
mem_limit: 512m
|
||||
|
||||
redis:
|
||||
profiles: ["cluster"]
|
||||
image: redis:7-alpine
|
||||
command: ["redis-server", "--save", "", "--appendonly", "no", "--maxmemory", "1gb", "--maxmemory-policy", "allkeys-lru"]
|
||||
healthcheck:
|
||||
@@ -77,26 +53,62 @@ services:
|
||||
interval: 5s
|
||||
timeout: 2s
|
||||
retries: 5
|
||||
cpus: 1.0
|
||||
mem_limit: 1200m
|
||||
|
||||
node-a: &node
|
||||
profiles: ["cluster"]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: src/main/docker/Dockerfile
|
||||
ports:
|
||||
- "8081:8080"
|
||||
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:
|
||||
- "8082:8080"
|
||||
- "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:
|
||||
Reference in New Issue
Block a user