Files
magistr/docs/INFRASTRUCTURE.md
2026-07-13 03:28:18 +03:00

7.0 KiB
Raw Blame History

🏭 Инфраструктура

Docker Compose (локальная разработка)

Сервисы

services:
  backend:     # Spring Boot (Java 17), порт 8080
  frontend:    # Apache httpd:alpine, порт 80
  db:          # PostgreSQL alpine3.23, порт 5432

Сеть

Все сервисы работают в Docker-сети proxy (external). Перед первым запуском:

docker network create proxy

Переменные окружения

Файл .env в корне проекта:

POSTGRES_USER=myuser
POSTGRES_PASSWORD=replace-with-local-password
POSTGRES_DB=app_db
JWT_SECRET=replace-with-random-jwt-secret-minimum-32-bytes
JWT_ACCESS_TOKEN_TTL=15m
JWT_REFRESH_TOKEN_TTL=7d

POSTGRES_PASSWORD обязателен для docker compose up: пароль не хранится в compose.yaml. JWT_SECRET должен быть случайным секретом длиной минимум 32 байта. В продакшене секреты задаются через Kubernetes Secret, а не через коммитимые файлы.

Встроенного JWT fallback в приложении нет. Отсутствующее, короткое или шаблонное значение останавливает запуск; профиль production также требует Secure refresh-cookie.

Локальный backend/tenants.json тоже не коммитится. Для ручного запуска backend вне Docker можно взять backend/tenants.example.json, создать рядом tenants.json и подставить локальный пароль.

Dockerfile (Backend)

Backend собирается через multi-stage сборку Maven:

  1. Этап сборки: maven:3.9-eclipse-temurin-17mvn package
  2. Этап запуска: eclipse-temurin:17-jre-alpinejava -jar app.jar

Dockerfile (Frontend)

FROM httpd:alpine
COPY . /usr/local/apache2/htdocs/
RUN chown -R www-data:www-data /usr/local/apache2/htdocs/

Kubernetes (продакшн)

Расположение конфигурации

Файлы Kubernetes манифестов: ../k8s/

Ключевые ресурсы

Ресурс Тип Описание
backend Deployment Spring Boot приложение
frontend Deployment Apache httpd
tenants-secret Внешний Secret JSON-список tenant-подключений; значения отсутствуют в манифестах

JWT настройки

app-config задаёт TTL access/refresh-токенов и признак Secure-cookie:

  • JWT_ACCESS_TOKEN_TTL=15m
  • JWT_REFRESH_TOKEN_TTL=7d
  • JWT_REFRESH_COOKIE_SECURE=true

app-secret задаёт JWT_SECRET. Этот Secret не создаётся файлами ../k8s/: его заранее предоставляет внешний secret manager или оператор. Deployment явно включает профиль production, поэтому небезопасная cookie-конфигурация останавливает startup.

Secrets для приложения и тенантов

Обязательные внешние объекты:

Secret Назначение
app-secret JWT и fallback credentials backend
tenants-secret Полный tenants.json с credentials tenant-БД
otel-postgres-secret Credentials PostgreSQL receivers для OTel Collector
gitea-registry Доступ Kubernetes к registry

Secret tenants-secret монтируется в pod backend по пути /config/tenants.json.

При добавлении тенанта через API:

  1. DatabaseController обновляет in-memory DataSource
  2. KubernetesTenantSecretUpdater обновляет Secret через Kubernetes API
  3. TenantConfigWatcher на остальных pod подхватывает смонтированное изменение (каждые 30 сек)

Kubernetes-клиент доверяет только service-account CA и проверяет hostname API server. RBAC ограничен объектом tenants-secret. Скрипт ../k8s/deploy.sh проверяет наличие объектов и ключей до rollout, не читая и не печатая их значения.

Создание, ротация, отзыв скомпрометированных значений и очистка истории описаны в SECURITY_RUNBOOK.md. Эти действия требуют полномочий оператора и не выполняются автоматически.

Обновление backend

kubectl rollout restart deployment backend -n magistr

Caddy (реверс-прокси)

Расположение: ../caddy-proxy/ для локальной разработки, в продакшене - отдельный сервис

В продакшене Caddy обрабатывает входящий трафик для *.zuev.company:

  • Автоматическое получение TLS-сертификатов (Let's Encrypt)
  • Маршрутизация /api/* → backend:8080
  • Маршрутизация статики → frontend:80

CI/CD (Gitea Actions)

Пайплайн сборки Docker-образов

Расположение: .gitea/workflows/docker-build.yaml

Основные шаги:

  1. Checkout кода
  2. Login в Docker Registry
  3. Build + Push образов (backend, frontend)
  4. Генерация меток через docker/metadata-action

Мониторинг (SigNoz + OpenTelemetry)

Архитектура мониторинга

graph LR
    Backend["Spring Boot"] -->|OTLP gRPC| Collector["OTel Collector"]
    Frontend["JS (otel.js)"] -->|OTLP HTTP| Collector
    Collector --> SigNoz["SigNoz"]
    
    Collector -->|"Метрики PostgreSQL"| PgExporter["pg_exporter"]

Интеграция Backend

Backend отправляет через OpenTelemetry:

  • Логи — через Logback + OTLP exporter
  • Трейсы — автоинструментация Spring Boot
  • Метрики — JVM метрики, HTTP метрики

Tenant ID добавляется в:

  • MDC (логи): MDC.put("tenant.id", tenant)
  • Span атрибуты: Span.current().setAttribute("tenant.id", tenant)

Интеграция Frontend

Файл admin/js/otel.js — клиентская телеметрия:

  • Метрики производительности страниц
  • Трейсы пользовательских действий

PostgreSQL receivers collector получают endpoint и credentials только из otel-postgres-secret; ConfigMap collector содержит лишь ${env:...} ссылки.

Дашборды SigNoz

  • JVM Dashboard (Heap, GC, Threads)
  • PostgreSQL Dashboard (Connections, Queries)
  • HTTP Dashboard (Requests, Latency, Errors)