feat: add production deployment baseline

This commit is contained in:
2026-08-28 08:56:12 +03:00
parent b91bc98611
commit bf0b6ff070
11 changed files with 133 additions and 8 deletions

View File

@@ -2,6 +2,8 @@
## Не выпущено ## Не выпущено
- Добавлен production deployment baseline для Search API и OpenSearch с
постоянными хранилищами и healthcheck.
- Добавлено восстановление незавершённой разметки из `localStorage` после перезагрузки страницы. - Добавлено восстановление незавершённой разметки из `localStorage` после перезагрузки страницы.
- Добавлена внутренняя лаборатория проверки поисковой выдачи: просмотр документов, - Добавлена внутренняя лаборатория проверки поисковой выдачи: просмотр документов,
оценка релевантности 03, комментарии и SQLite-экспорт подписанных снимков. оценка релевантности 03, комментарии и SQLite-экспорт подписанных снимков.

View File

@@ -10,13 +10,13 @@ Telegram-бот — только часть рабочего окружения
## Текущее состояние ## Текущее состояние
Сейчас реализованы Telegram-бот-секретарь версии `0.2.2` и backend версии Сейчас реализованы Telegram-бот-секретарь версии `0.2.2` и backend версии
`0.8.1`: добавлено сохранение черновика разметки в браузере. `0.8.2`: добавлен production deployment baseline для Search API и OpenSearch.
размеченном наборе запросов. размеченном наборе запросов.
| Компонент | Версия | Состояние | | Компонент | Версия | Состояние |
|---|---:|---| |---|---:|---|
| Telegram-бот | `0.2.2` | на Synology работает `0.2.1`; обновление после слияния | | Telegram-бот | `0.2.2` | на Synology работает `0.2.1`; обновление после слияния |
| Backend | `0.8.1` | добавлено сохранение черновика разметки | | Backend | `0.8.2` | добавлен production deployment baseline |
| Frontend | — | ещё не создан | | Frontend | — | ещё не создан |
| Сбор и обработка правовых данных | `0.8.0` | добавлены relevance set и оценка выдачи | | Сбор и обработка правовых данных | `0.8.0` | добавлены relevance set и оценка выдачи |
| RAG и база знаний | — | ещё не созданы | | RAG и база знаний | — | ещё не созданы |
@@ -59,4 +59,4 @@ python3 -m unittest discover -s tools/telegram-bot -v
--- ---
Акылдаш · Telegram-бот v0.2.2 · Backend v0.8.1 · Frontend — не создан Акылдаш · Telegram-бот v0.2.2 · Backend v0.8.2 · Frontend — не создан

View File

@@ -1,6 +1,6 @@
# Backend Акылдаш # Backend Акылдаш
Версия: `0.8.1` Версия: `0.8.2`
Первая backend-область проекта — загрузка правовых документов из официального Первая backend-область проекта — загрузка правовых документов из официального
Open Data API ЦБД Минюста Кыргызской Республики. Код расположен в Open Data API ЦБД Минюста Кыргызской Республики. Код расположен в
@@ -240,4 +240,4 @@ PYTHONPATH=backend python3 -m search.query "ЖЧК ачуу тартиби" --la
--- ---
Акылдаш · Backend v0.8.1 · Frontend — не создан Акылдаш · Backend v0.8.2 · Frontend — не создан

View File

@@ -21,7 +21,7 @@ from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import Callable, Iterable from typing import Callable, Iterable
APP_VERSION = "0.8.1" APP_VERSION = "0.8.2"
API_BASE_URL = "https://cbd.minjust.gov.kg/api/v1/OpenData/" API_BASE_URL = "https://cbd.minjust.gov.kg/api/v1/OpenData/"
LANGUAGES = {"Rus": "ru", "Kyr": "ky"} LANGUAGES = {"Rus": "ru", "Kyr": "ky"}
IMAGE_LANGUAGES = {"Russian": "ru", "Kyrgyz": "ky"} IMAGE_LANGUAGES = {"Russian": "ru", "Kyrgyz": "ky"}

View File

@@ -23,7 +23,7 @@ from pathlib import Path
from typing import Callable from typing import Callable
from urllib.parse import urlsplit from urllib.parse import urlsplit
APP_VERSION = "0.8.1" APP_VERSION = "0.8.2"
SCHEMA_VERSION = "1" SCHEMA_VERSION = "1"
NORMALIZER_VERSION = "1.0.0" NORMALIZER_VERSION = "1.0.0"
LANGUAGES = ("ru", "ky") LANGUAGES = ("ru", "ky")

View File

@@ -17,7 +17,7 @@ from typing import Iterator
from search.catalog import authority_codes, source_code from search.catalog import authority_codes, source_code
APP_VERSION = "0.8.1" APP_VERSION = "0.8.2"
LANGUAGES = {"ru", "ky"} LANGUAGES = {"ru", "ky"}
DEFAULT_MAPPING = Path(__file__).with_name("minjust-fragments-index.json") DEFAULT_MAPPING = Path(__file__).with_name("minjust-fragments-index.json")

View File

@@ -0,0 +1,8 @@
# Absolute path on the production host containing minjust-normalized/.
DATA_ROOT=/volume1/docker/akyldash/data
BACKEND_PORT=8080
SEARCH_INDEX=akyldash-fragments-current
OPENSEARCH_MEM_LIMIT=4g
OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g
# Generate with: openssl rand -hex 32
REVIEW_SECRET=replace-with-a-random-secret

View File

@@ -0,0 +1,11 @@
FROM python:3.12-slim
WORKDIR /app
COPY backend /app/backend
ENV PYTHONPATH=/app/backend
EXPOSE 8080
CMD ["python", "-m", "search.api", "--host", "0.0.0.0", "--port", "8080", "--url", "http://opensearch:9200", "--index", "akyldash-fragments-current", "--data", "/app/data/minjust-normalized", "--reviews-db", "/app/data/search-reviews.sqlite3"]
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s CMD ["python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/review', timeout=3)"]

View File

@@ -0,0 +1,3 @@
FROM opensearchproject/opensearch:3.7.0
RUN /usr/share/opensearch/bin/opensearch-plugin install --batch analysis-icu

View File

@@ -0,0 +1,37 @@
# Production deployment
This compose project runs the Search API and its private OpenSearch node. It
binds the API only to `127.0.0.1`; publish it through an authenticated reverse
proxy or VPN. OpenSearch is not published outside the compose network.
The current compose intentionally disables the OpenSearch security plugin to
match the existing API client. Keep both services on a private host/network
until authenticated OpenSearch support is implemented.
## First deployment
1. Copy this directory to the host with the repository source.
2. Copy `.env.example` to `.env`, set an absolute `DATA_ROOT`, and replace
`REVIEW_SECRET` with a random value. Do not commit `.env`.
3. Put the normalized dataset under `$DATA_ROOT/minjust-normalized/`.
4. Check the rendered configuration:
```bash
docker compose --env-file .env -f compose.yaml config
```
5. Start the services:
```bash
docker compose --env-file .env -f compose.yaml up -d --build
docker compose --env-file .env -f compose.yaml ps
curl -fsS http://127.0.0.1:${BACKEND_PORT:-8080}/review >/dev/null
```
6. Load the versioned index and switch its alias only after the import and
validation succeed. Back up `DATA_ROOT` and the `opensearch-data` volume
before the first import.
This is a deployment baseline, not a public internet exposure recipe. TLS,
authentication, backups, monitoring, and a production OpenSearch security
configuration must be provided by the host reverse proxy/operations setup.

View File

@@ -0,0 +1,64 @@
services:
opensearch:
build:
context: ../..
dockerfile: deploy/production/Dockerfile.opensearch
restart: unless-stopped
environment:
discovery.type: single-node
bootstrap.memory_lock: "true"
DISABLE_SECURITY_PLUGIN: "true"
OPENSEARCH_JAVA_OPTS: ${OPENSEARCH_JAVA_OPTS:--Xms2g -Xmx2g}
mem_limit: ${OPENSEARCH_MEM_LIMIT:-4g}
expose:
- "9200"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- opensearch-data:/usr/share/opensearch/data
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:9200/_cluster/health || exit 1"]
interval: 30s
timeout: 10s
retries: 10
backend:
build:
context: ../..
dockerfile: deploy/production/Dockerfile.backend
restart: unless-stopped
environment:
REVIEW_SECRET: ${REVIEW_SECRET:?set REVIEW_SECRET in .env}
command:
- python
- -m
- search.api
- --host
- 0.0.0.0
- --port
- "8080"
- --url
- http://opensearch:9200
- --index
- ${SEARCH_INDEX:-akyldash-fragments-current}
- --data
- /app/data/minjust-normalized
- --reviews-db
- /app/data/search-reviews.sqlite3
- --review-secret
- ${REVIEW_SECRET}
ports:
- "127.0.0.1:${BACKEND_PORT:-8080}:8080"
depends_on:
opensearch:
condition: service_healthy
volumes:
- ${DATA_ROOT:?set DATA_ROOT in .env}:/app/data
volumes:
opensearch-data: