# РАЗРАБОТКА И ВНЕДРЕНИЕ ОТКАЗОУСТОЙЧИВОЙ МУЛЬТИТЕНАНТНОЙ ИНФРАСТРУКТУРЫ ДЛЯ СИСТЕМЫ УПРАВЛЕНИЯ УНИВЕРСИТЕТСКИМ РАСПИСАНИЕМ «МАГИСТР»

**Диссертационное исследование и отчет о проделанной инженерной работе в качестве DevOps-архитектора проекта**

---

## ВВЕДЕНИЕ

Современные информационные системы для образовательных учреждений требуют строгого соблюдения требований к безопасности, изоляции данных различных организаций (университетов), гибкости масштабирования и высокой наблюдаемости (observability). В рамках разработки проекта **«Магистр»** — системы управления расписанием учебных занятий — передо мной встала задача проектирования и построения инфраструктуры, способной обслуживать десятки университетов в рамках единого прикладного контура при условии жесткой изоляции их баз данных.

В качестве DevOps-инженера проекта я спроектировал и реализовал архитектурное решение, объединяющее технологии аппаратной виртуализации, оркестрации контейнеров, автоматизации конфигурации, распределенного мониторинга и непрерывной интеграции. В данном документе подробно описаны теоретические предпосылки, практическая реализация и архитектурные решения, внедренные мной в продакшн-окружение проекта.

---

## РАЗДЕЛ 1. СИСТЕМНАЯ АРХИТЕКТУРА И КОНЦЕПЦИЯ МУЛЬТИТЕНАНТНОСТИ

### 1.1 Выбор паттерна изоляции данных
При проектировании мультитенантных (multi-tenant) систем классически выделяют три подхода к организации баз данных:
1. **Shared Database & Shared Schema**: Все клиенты используют общие таблицы, записи разделяются по полю `tenant_id`. Подход дешев в обслуживании, но несет колоссальные риски утечки данных из-за ошибок в SQL-запросах приложения и не позволяет разграничивать физический доступ к базам.
2. **Shared Database & Separate Schemas**: Одна СУБД, но разные логические схемы для каждого клиента. Обеспечивает умеренную изоляцию, но сохраняет единую точку отказа и общие аппаратные ресурсы СУБД.
3. **Database-per-Tenant (Выбранный подход)**: Каждый клиент (университет) владеет собственной физически изолированной базой данных, расположенной на выделенном сервере или виртуальной машине.

Для проекта «Магистр» мной был выбран и реализован паттерн **Database-per-Tenant**. Это обусловлено спецификой клиентов (высшие учебные заведения), которые согласно законодательству (например, ФЗ-152 «О персональных данных») обязаны хранить свои данные локально или требовать полной изоляции конфиденциальной информации. Кроме того, данный подход позволяет:
* Исключить влияние высокой нагрузки одного университета (например, в период составления сессий) на доступность системы для других вузов («синдром шумного соседа»).
* Выполнять индивидуальное резервное копирование и обслуживание БД каждого университета без остановки всей платформы.
* Переносить базы данных вузов на их собственные физические серверы в локальных сетях (on-premise), сохраняя при этом работу приложения в центральном облаке.

### 1.2 Архитектурная схема движения трафика
Ниже представлена разработанная мной схема прохождения сетевых запросов и агрегации телеметрии в системе:

```mermaid
graph TD
    Client[Пользовательский браузер] -->|HTTPS: tenant.zuev.company| Caddy[Caddy Reverse Proxy]
    Caddy -->|HTTP Round-Robin| Ingress[Traefik Ingress Controller]
    
    subgraph K3s Cluster
        Ingress -->|Route /*| Frontend[Frontend Pods x2 Apache]
        Ingress -->|Route /api/*| Backend[Backend Pods x1-x2 Spring Boot]
        Backend -->|K8s API: Get/Patch CM| K8sAPI[Kubernetes API Server]
        BackendConfig[ConfigMap: tenants-config] -.->|Mounted JSON| Backend
    end
    
    subgraph Proxmox VE Infrastructure
        Backend -->|JDBC Connection| VM1[(VM 1: DBMS MSU<br>PostgreSQL 16)]
        Backend -->|JDBC Connection| VM2[(VM 2: DBMS SWSU<br>PostgreSQL 16)]
        
        VM1 -.->|Metrics OTLP/gRPC| Collector1[OTel Collector VM1]
        VM2 -.->|Metrics OTLP/gRPC| Collector2[OTel Collector VM2]
    end

    subgraph Observability Hub
        Backend -->|Logs & Traces OTLP| SigNoz[SigNoz APM Server]
        Collector1 -->|Postgres Metrics| SigNoz
        Collector2 -->|Postgres Metrics| SigNoz
    end
    
    classDef cluster fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
    classDef vm fill:#efebe9,stroke:#4e342e,stroke-width:2px;
    classDef monitor fill:#efe8ff,stroke:#512da8,stroke-width:2px;
    class K3s Cluster cluster;
    class Proxmox VE Infrastructure vm;
    class Observability Hub monitor;
```

---

## РАЗДЕЛ 2. АППАРАТНАЯ ВИРТУАЛИЗАЦИЯ НА БАЗЕ PROMVOX VE

Для размещения баз данных клиентов и вспомогательных инфраструктурных служб мной был развернут гипервизор **Proxmox Virtual Environment (PVE)** на выделенных серверных мощностях.

### 2.1 Конфигурация виртуализации и изоляции ресурсов
Каждая база данных университета разворачивается в выделенной виртуальной машине (KVM), что гарантирует жесткую изоляцию на уровне ядра ОС и гипервизора.
* **Сетевой уровень**: В Proxmox настроена виртуальная коммутация (Linux Bridge `vmbr0`). Сетевые интерфейсы виртуальных машин баз данных вынесены в приватную подсеть, изолированную от внешней сети. Доступ к ним имеет только подсеть кластера Kubernetes и управляющая машина администратора.
* **Дисковая подсистема**: Использовано локальное хранилище на базе ZFS с зеркалированием (RAID-10 на SSD-накопителях). Это обеспечивает:
  * Высокую скорость операций ввода-вывода (IOPS), критически важную для СУБД при транзакционных нагрузках.
  * Механизм мгновенных снимков (snapshots) для безопасного проведения обновлений.
  * Аппаратное сжатие данных (LZ4) без потери производительности, снижающее износ накопителей.
* **Резервное копирование**: Интегрирован **Proxmox Backup Server (PBS)**. Каждую ночь выполняются инкрементальные бэкапы виртуальных машин с дедупликацией на уровне блоков данных, что минимизирует нагрузку на сеть и диски, позволяя восстановить любую БД на любой момент времени за последние 30 дней.

---

## РАЗДЕЛ 3. ОРКЕСТРАЦИЯ КОНТЕЙНЕРНОЙ ИНФРАСТРУКТУРЫ В KUBERNETES (K3s)

В качестве платформы оркестрации приложений был выбран **K3s** — сертифицированный дистрибутив Kubernetes от Rancher, оптимизированный под средние и малые нагрузки, обладающий минимальными накладными расходами на RAM/CPU для Control Plane, но сохраняющий полную совместимость с upstream-спецификациями Kubernetes API.

### 3.1 Архитектура манифестов и структура ресурсов
Мной была разработана декларативная структура ресурсов, развернутая в изолированном пространстве имен `magistr` (манифест `namespace.yaml`):

* **Frontend (`frontend.yaml`)**:
  * Реализован в виде `Deployment` с 2 репликами для обеспечения высокой доступности (HA) и возможности бесшовного обновления rolling-update.
  * В качестве базового образа контейнера применен легковесный веб-сервер Apache HTTPd; версия и manifest digest закреплены в Dockerfile.
  * Для балансировки и внутреннего доступа настроен `Service` типа ClusterIP, слушающий порт 80.
  
* **Backend (`backend.yaml`)**:
  * `Deployment` с 1-2 репликами (детали балансировки конфигурации описаны ниже).
  * Для сборки образов используется multi-stage сборка Maven (JDK 17); build/runtime-образы одновременно закреплены точным tag и manifest digest.
  * Интегрирован Java-агент OpenTelemetry для автоматического инструментирования трассировки и логов.
  * Для связи с Ingress настроен ClusterIP-сервис на порту 8080.
  
* **Ingress (`ingress.yaml`)**:
  * В роли Ingress-контроллера выступает встроенный **Traefik** (`kube-system/traefik`).
  * Он доступен извне через Klipper Service LoadBalancer (DaemonSet), который прокидывает порты 80/443 (hostPort) на внешние IP адреса нод кластера (`192.168.1.104`, `192.168.1.105`, `192.168.1.106`).
  * Мной настроены строгие правила маршрутизации по доменным именам (например, `magistr.zuev.company` и `n8n.zuev.company`). Запросы к API (`/api`) проксируются на бэкенд, а к корню (`/`) — на фронтенд.

### 3.2 Реализация динамической мультитенантности через K8s API
Одним из наиболее сложных этапов проектирования стала организация бесшовного добавления новых университетов без перезапуска бэкенда и изменения исходного кода приложения.

1. **Хранение конфигурации**:
   Список подключений к базам данных университетов хранится в JSON-формате (`tenants.json`) внутри Kubernetes `ConfigMap` с именем `tenants-config`.

2. **Проблема Read-Only монтирования**:
   По умолчанию Kubernetes монтирует ConfigMap как файловую систему в режиме Read-Only. Это исключало возможность для Java-приложения перезаписывать `tenants.json` при запросах от администратора на добавление нового тенанта.
   
3. **Решение с использованием `initContainer` и `emptyDir`**:
   Я спроектировал схему с временным перезаписываемым томом (`emptyDir`):
   * В спецификацию пода backend добавлен `initContainer` на базе образа `busybox`.
   * При старте пода `initContainer` монтирует ConfigMap `tenants-config` и копирует файл `tenants.json` во временный раздел `emptyDir` (путь `/config`).
   * Основной контейнер `backend` монтирует этот же `emptyDir` в режиме Read-Write.

4. **K8s RBAC и динамическое сохранение**:
   Чтобы предоставить бэкенду возможность сохранять изменения конфигурации обратно в кластер, мной были написаны манифесты авторизации (`rbac.yaml`):
   * Создан `ServiceAccount` `backend-sa` и назначен поду бэкенда.
   * Описана Kubernetes `Role` `backend-configmap-role`, дающая права только на операции `get` и `patch` для ресурса `configmaps` с конкретным именем `tenants-config` (принцип наименьших привилегий).
   * Создан `RoleBinding` `backend-configmap-binding`, связывающий сервис-аккаунт с этой ролью.
   
   В коде бэкенда класс `ConfigMapUpdater` через Kubernetes Java Client отправляет PATCH-запрос к API-серверу при добавлении нового тенанта, обновляя ConfigMap в самом кластере.

5. **Репликация и синхронизация подов**:
   Для обеспечения консистентности данных в условиях работы нескольких реплик бэкенда:
   * Класс `TenantConfigWatcher` запускает фоновую задачу, которая каждые 30 секунд сверяет MD5-хеш локального файла `tenants.json` с версией из ConfigMap.
   * При обнаружении расхождения файл перезаписывается, DataSource-инстансы пересоздаются в памяти приложения на лету, исключая рассинхронизацию подов.
   * *Примечание*: В ходе тестирования для минимизации задержек и исключения эффекта "мигания" данных на время отладки количество реплик backend было ограничено до 1, с последующим переходом на полноценный StatefulSet/Shared Storage при росте количества нод.

### 3.3 Повышение отказоустойчивости бэкенда (Resiliency)
При запуске бэкенда в облаке возникала проблема: если база данных одного из университетов становилась недоступной (например, из-за сетевого сбоя или регламентных работ), пул подключений HikariCP падал с ошибкой при инициализации бинов, отправляя под бэкенда в циклическую перезагрузку (`CrashLoopBackOff`), что делало недоступной систему для *всех* остальных вузов.

Для предотвращения этого каскадного сбоя я внедрил комплекс защитных механизмов:
* **H2 Fallback**: В сборку (`pom.xml`) добавлена СУБД H2 in-memory. Если при старте ConfigMap пуст, Spring Boot инициализирует пустой JPA-контекст на базе H2, что позволяет приложению успешно пройти фазу запуска.
* **HikariCP Resiliency**: В конфигурации источников данных задано свойство `setInitializationFailTimeout(-1)`. Это заставляет HikariCP игнорировать отсутствие связи с целевой БД при старте приложения, продолжая попытки подключения в фоновом режиме.
* **Явное указание диалекта Hibernate**: Принудительно задан `org.hibernate.dialect.PostgreSQLDialect` в `TenantDataSourceConfig.java`. Это избавило Hibernate от необходимости выполнять тестовый запрос к БД для автоматического определения диалекта на ранних этапах инициализации контекста.
* **Автоматическое создание таблиц (`DataInitializer`)**: Для новых БД вузов полностью автоматизирован процесс наката структуры. При обнаружении новой базы Java-код проверяет наличие таблицы `users` и, в случае ее отсутствия, самостоятельно применяет SQL-скрипт инициализации (`init.sql`), включая создание дефолтных справочников и учетной записи суперадминистратора.

---

## РАЗДЕЛ 4. АВТОМАТИЗАЦИЯ КОНФИГУРАЦИИ СУБД С ПОМОЩЬЮ ANSIBLE

Для развертывания и администрирования баз данных университетов на удаленных виртуальных машинах Proxmox мной был спроектирован и реализован комплексный плейбук **Ansible** с модульной структурой ролей.

### 4.1 Структура и функционал Ansible-ролей
Вся конфигурация целевого сервера разбита на 6 последовательных этапов (плейбук `site.yml`):

1. **`common` (Базовая подготовка и Hardening ОС)**:
   * Создание системного пользователя `deploy` с беспарольным доступом к `sudo` (через валидацию файла `/etc/sudoers.d/deploy` утилитой `visudo`).
   * Установка системных утилит (`curl`, `git`, `htop`, `chrony` для синхронизации времени по NTP).
   * **SSH Hardening**: Изменение стандартного SSH-порта на `2222`, отключение авторизации по паролям, запрет входа для пользователя `root`, ограничение таймаута сессий.
   * Адаптация под различные дистрибутивы ОС: написаны отдельные сценарии для семейств Debian/Ubuntu/Astra Linux (пакетный менеджер `apt`), RedHat/РЕД ОС (`dnf`) и ALT Linux (кастомная обертка для `apt-get`).

2. **`docker` (Среда контейнеризации)**:
   * Автоматическое добавление официальных GPG-ключей и репозиториев Docker CE.
   * Установка Docker Engine, CLI и Compose плагина.
   * Включение системной службы `docker` и добавление пользователя `deploy` в группу `docker` для беспарольного запуска контейнеров.

3. **`firewall` (Сетевая безопасность)**:
   * Настройка межсетевого экрана UFW (для Debian-based систем) или firewalld (для RedHat и ALT Linux).
   * Полная блокировка входящего трафика по умолчанию.
   * Разрешение входящих пакетов только на порт SSH (`2222`) и порт PostgreSQL (`5432`). Реализована поддержка ограничения доступа к порту `5432` только для IP-адресов нод Kubernetes-кластера (`firewall_postgresql_allowed_ips`).

4. **`postgresql` (Контейнеризированная СУБД)**:
   * Генерация файлов конфигурации `docker-compose.yml` и `.env` на основе шаблонов Jinja2.
   * Развертывание PostgreSQL 16 на базе легковесного Alpine-образа.
   * Физическое монтирование каталога данных БД с хост-системы (`/opt/magistr/postgres/data`), предотвращающее потерю данных при пересоздании контейнера.
   * Ограничение системных ресурсов контейнера (limits: memory: 512M) и тюнинг параметров PostgreSQL (`shared_buffers=256MB`, оптимизация пула соединений, логирование медленных запросов `log_min_duration_statement=1000`).

5. **`backup` (Резервное копирование на уровне БД)**:
   * Установка на хост автоматического bash-скрипта бэкапа.
   * Настройка задачи `cron` (запуск ежедневно в 03:00).
   * Скрипт осуществляет горячий дамп базы через `docker exec pg_dump`, архивирует его с помощью `gzip` и сохраняет в локальный каталог `/opt/magistr/backups/`.
   * Реализован алгоритм автоматической ротации: удаление архивных файлов старше заданного количества дней (по умолчанию 14 дней).

6. **`monitoring` (Сбор телеметрии хоста и БД)**:
   * Развертывание контейнера OpenTelemetry Collector на целевом сервере БД.
   * Конфигурирование коллектора на сбор метрик производительности PostgreSQL и ОС (процессор, память, диски) и их отправку на единый сервер SigNoz по OTLP/HTTP.

### 4.2 Управление секретами: Ansible Vault
Для исключения попадания паролей администраторов БД в систему контроля версий (Git) все чувствительные переменные (например, `vault_postgresql_password`) вынесены в файл `group_vars/databases/vault.yml` и зашифрованы алгоритмом AES-256 с помощью **Ansible Vault**.

Для бесшовной интеграции в CI/CD пароль расшифрования считывается из локального файла `.vault_pass` на управляющей машине, доступ к которому ограничен правами `chmod 600`.

---

## РАЗДЕЛ 5. НЕПРЕРЫВНАЯ ИНТЕГРАЦИЯ И ДОСТАВКА (CI/CD)

В рамках оптимизации внутренних процессов разработки и деплоя мной была развернута и настроена инфраструктура непрерывной интеграции на базе **Gitea** и **Gitea Actions**.

### 5.1 Автоматизация сборки (CI)
В репозитории проекта создан workflow-манифест `.gitea/workflows/docker-build.yaml`. При каждом пуше изменений в ветку `main` запускается конвейер:
1. **Checks**: Backend/frontend-тесты, Compose validation, тест immutable rollback и статическая проверка закрепления артефактов.
2. **Build & Push**: Параллельная сборка и публикация backend/frontend с SHA-tag или release tag; mutable `latest` не создаётся.
3. **Attestations**: BuildKit добавляет к обоим образам SBOM и максимальную provenance-attestation.
4. **Security scan**: Закреплённый digest Trivy блокирует доставку при исправимых уязвимостях `HIGH`/`CRITICAL`.
5. **Deploy**: Только прошедшие gates registry digests передаются в production rollout. Все сторонние Actions закреплены полными commit SHA.

### 5.2 Доставка в кластер (CD)
Для авторизации нод K3s в приватном реестре Gitea мной был создан секрет `gitea-registry` типа `docker-registry` в пространстве имен `magistr`. Этот секрет ассоциирован со спецификациями деплоев через директиву `imagePullSecrets`.

Непосредственно Gitea Actions Runner работает как демон `act_runner` внутри изолированного LXC контейнера (CTID 107). В пайплайне шаг развертывания (`deploy-to-k8s`) динамически генерирует `kubeconfig` из секрета, проверяет checksum закреплённой версии `kubectl` и выполняет атомарное обновление обоих образов без использования тяжеловесных GitOps операторов:

```bash
bash scripts/deploy-images.sh \
  gitea.zuev.company/zuev/magistr-backend@sha256:<digest> \
  gitea.zuev.company/zuev/magistr-frontend@sha256:<digest>
```

Скрипт принимает только полные `image@sha256:...`, ожидает готовность обоих Deployment и при
ошибке возвращает предыдущую пару digest. Поэтому содержимое релиза не зависит от повторного
разрешения mutable tag.

---

## РАЗДЕЛ 6. ВХОДНОЙ ПРОКСИ-СЕРВЕР НА БАЗЕ CADDY PROXY

Для маршрутизации внешнего трафика и защиты соединений на входе в инфраструктуру развернут веб-сервер **Caddy**.

### 6.1 Преимущества Caddy и управление SSL/TLS
В отличие от классического Nginx, Caddy был выбран благодаря:
* Встроенной интеграции с удостоверяющими центрами Let's Encrypt и ZeroSSL. При добавлении нового поддомена вуза (например, `swsu.zuev.company`) Caddy автоматически запрашивает, валидирует через DNS/HTTP-вызовы и устанавливает TLS-сертификат, а также следит за его продлением.
* Лаконичному синтаксису конфигурации (`Caddyfile`).
* Полноценной поддержке протокола HTTP/3 «из коробки».

### 6.2 Конфигурация балансировки
Внешний Caddy-сервер развернут в изолированном LXC контейнере (CTID 101) и обрабатывает запросы ко всем поддоменам проекта. Он перенаправляет их на ноды кластера K3s, выполняя роль внешнего балансировщика, а также проксирует телеметрию с фронтенда напрямую в сборщик OTel:

```caddyfile
(otel_proxy) {
  handle_path /otel/* { reverse_proxy 192.168.1.100:4318 }
}

(k8s_nodes) {
  handle {
    reverse_proxy 192.168.1.104:80 192.168.1.105:80 192.168.1.106:80 {
      lb_policy round_robin
      lb_try_duration 5s
      lb_try_interval 250ms
    }
  }
}

magistr.zuev.company { import otel_proxy; import k8s_nodes }
n8n.zuev.company { import otel_proxy; import k8s_nodes }
```
Сетевые запросы к приложениям распределяются по 3 физическим нодам K3s. Благодаря сниппетам, добавление нового тенанта в инфраструктуре Caddy требует всего двух строчек конфигурации.

---

## РАЗДЕЛ 7. СКВОЗНОЙ МОНИТОРИНГ И ОБСЕРВАБИЛИТИ (SigNoz + OpenTelemetry)

Для контроля здоровья системы и оперативного выявления аномалий мной была спроектирована и внедрена централизованная система мониторинга на базе APM-платформы **SigNoz** (с хранилищем **ClickHouse**) и стандартов **OpenTelemetry (OTel)**. 

Платформа SigNoz вынесена в отдельный LXC контейнер (CTID 100) по адресу `192.168.1.100` и развернута через `docker-compose`. Данные ClickHouse и SQLite метаданные персистентно хранятся в Docker volumes хоста для обеспечения сохранности при рестартах.

### 7.1 Сбор телеметрии Backend
Java-приложение бэкенда запускается с подключением агента OpenTelemetry (`opentelemetry-javaagent.jar`). Это позволяет без изменения кода собирать:
* **Трейсы (Traces)**: Сквозное прохождение HTTP-запросов через контроллеры, сервисы и JDBC-драйвер к базам данных.
* **Метрики (Metrics)**: Метрики JVM (утилизация Heap, активность сборщика мусора GC, состояние потоков), метрики HTTP-запросов (интенсивность, латентность, коды ответов).
* **Логи (Logs)**: Системные логи Logback экспортируются по протоколу OTLP напрямую в SigNoz.

### 7.2 Идентификация тенантов в мониторинге
Для глубокого анализа производительности СУБД конкретных университетов критически важно разделять телеметрию по тенантам. Мной была реализована сквозная маркировка:
* В бэкенде через Java-интерцептор `TenantInterceptor` при каждом запросе идентификатор тенанта помещается в контекст логирования SLF4J MDC (`MDC.put("tenant.id", tenant)`) и одновременно записывается в атрибуты текущего OpenTelemetry спана (`Span.current().setAttribute("tenant.id", tenant)`).
* Благодаря переменной окружения `OTEL_INSTRUMENTATION_LOGBACK_APPENDER_EXPERIMENTAL_CAPTURE_MDC_ATTRIBUTES=tenant.id`, OTel-агент автоматически парсит MDC и прикрепляет его к логам, отправляемым в SigNoz. Это позволяет администраторам фильтровать ошибки и строить графики нагрузки в разрезе каждого университета.

### 7.3 Мониторинг баз данных на хостах
Для сбора детальной статистики с изолированных серверов баз данных:
1. На хостах БД силами Ansible разворачивается OpenTelemetry Collector.
2. В конфигурации коллектора (`otel-collector-config.yml.j2`) описывается ресивер `postgresql`, который подключается к локальной БД и считывает системные метрики (размер таблиц, cache hit ratio, количество транзакций, активные сессии, блокировки).
3. К собираемым метрикам жестко прикрепляются ресурсные атрибуты хоста: `service.name=magistr-db-<tenant_domain>` и `university.name=<University Name>`.
4. Метрики экспортируются в центральный коллектор SigNoz. В результате в интерфейсе SigNoz доступны кастомные дашборды JVM, PostgreSQL и HTTP, позволяющие оперативно локализовать проблемы с производительностью баз данных конкретных вузов.

---

## ЗАКЛЮЧЕНИЕ

Разработанная и внедренная мной DevOps-архитектура для проекта «Магистр» успешно решила задачи изоляции данных, отказоустойчивости и автоматизации администрирования. 

### Ключевые результаты проделанной работы:
1. **Изоляция данных**: Реализована физическая изоляция БД университетов на выделенных серверах (VM в Proxmox VE), соответствующая требованиям безопасности и законодательства.
2. **Динамическое масштабирование**: Внедрен механизм динамического добавления тенантов без простоя системы (zero-downtime) за счет связки Spring Boot, K8s RBAC и ConfigMapWatcher.
3. **Отказоустойчивость**: Минимизированы риски каскадных сбоев бэкенда за счет применения in-memory fallback баз данных и оптимизации параметров подключения HikariCP.
4. **Автоматизация**: Создан универсальный Ansible-плейбук для быстрого ввода в эксплуатацию новых серверов БД (время развертывания «под ключ» сокращено до нескольких минут).
5. **Наблюдаемость**: Реализован сквозной мониторинг логов, метрик и трассировок с детализацией по тенантам на базе стека OpenTelemetry + SigNoz.

Созданная инфраструктура обладает высокой степенью гибкости и готова к дальнейшему горизонтальному масштабированию по мере подключения новых высших учебных заведений к проекту «Магистр».
