magos/ARCHITECTURE.md
2026-04-10 20:23:09 +00:00

1232 lines
48 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Архитектура Magos
## Обзор системы
**Magos** - это система управления WAF (Web Application Firewall) контейнерами на базе Nginx/Angie, которая:
- Автоматически разворачивает и управляет Docker-контейнерами с Nginx
- Настраивает балансировщик Angie для распределения трафика
- Интегрируется с API для получения конфигурации
- Поддерживает graceful shutdown при масштабировании и миграции
- Обеспечивает изоляцию клиентов через отдельные контейнеры
### Ключевые особенности:
- **Многоклиентовая архитектура** - каждый клиент имеет свои контейнеры
- **Динамическое управление портами** - автоматическое выделение портов из пулов
- **Кэширование данных** - минимизация обращений к API
- **Failover БД** - автоматическое переключение между primary/secondary
- **Graceful draining** - безопасное удаление контейнеров без потери трафика
---
## Компоненты системы
### 1. Основные файлы программы
```
main.go # Точка входа, основной цикл обработки
config.go # Управление конфигурацией
db.go # Работа с PostgreSQL
api.go # Интеграция с ServicePipe API
types.go # Структуры данных
utils.go # Утилиты и вспомогательные функции
```
### 2. Управление контейнерами
```
containers_part1.go # Создание и настройка контейнеров
containers_part2.go # Управление жизненным циклом контейнеров
drain.go # Graceful shutdown и миграция
```
### 3. Генерация конфигураций
```
nginx.go # Генерация конфигов Nginx и Angie
ports.go # Управление портами
```
---
## Архитектура данных
### База данных PostgreSQL
#### Таблица `instances_new`
Связь hostname → instance (waf_instance)
```sql
hostname | instance
------------|------------
waf-host-01 | instance-a
waf-host-01 | instance-b
waf-host-02 | instance-c
```
#### Таблица `client_info`
Основная информация о клиентах
```sql
Поля:
- client_title # Уникальный идентификатор клиента
- containers_count # Количество контейнеров (1-20)
- ptaf_config # Дополнительная конфигурация PTAF
- fluent_bit_port # Порт для FluentBit логирования
- waf_instance # Instance для привязки к хосту
```
**Важно:** `waf_instance` определяет на каком хосте должен работать клиент
#### Таблица `apps_settings`
Настройки каждого L7 ресурса (домена)
```sql
Основные поля:
- l7resourceid # ID ресурса из ServicePipe API
- client_title # Связь с client_info
- waf_enabled # Включен ли WAF
- mode # auto/manual/disabled
Порты:
- custom_input_http_ports[] # HTTP порты для прослушивания (Angie)
- custom_input_https_ports[] # HTTPS порты для прослушивания (Angie)
- custom_output_http_ports[] # HTTP порты для проксирования к origin
- custom_output_https_ports[] # HTTPS порты для проксирования к origin
Кастомизация Nginx:
- upstream_nginx_custom # Кастомные upstream директивы
- server_nginx_custom # Кастомный server блок
- location_nginx_custom # Кастомные location директивы
- server_directives_nginx_custom # Дополнительные server директивы
Кастомизация Angie:
- upstream_angie_custom # Кастомные upstream директивы
- server_angie_custom # Кастомный server блок
- location_angie_custom # Кастомные location директивы
- server_directives_angie_custom # Дополнительные server директивы
- custom_angie_ssl # Полная замена SSL директив
Специальные настройки:
- sni # 0 = keepalive on, 1 = keepalive off
- ssl_enabled # true/false (по умолчанию true)
```
#### Таблица `sp_info` (кэш API данных)
Кэширует данные из ServicePipe API
```sql
Поля:
- sid # l7resourceid
- domain_name # Основной домен
- aliases # JSON массив алиасов
- origins # JSON массив origin серверов
- updated_at # Timestamp обновления
```
**TTL кэша:** 10 минут
#### Таблица `manual_info`
Ручное управление (альтернатива API)
```sql
Поля:
- sid # l7resourceid
- domain_name # Основной домен
- aliases # JSON массив алиасов
- origins # JSON массив origin серверов
```
**Примечание:** Данные из `manual_info` не имеют TTL и всегда актуальны
#### Таблица `ips`
Настройки IP сетей
```sql
Поля:
- id (always = 1)
- antibot_networks[] # Массив CIDR для antibot
- waf_networks[] # Массив CIDR для WAF (фильтруются из origins)
```
---
## Процесс работы
### 1. Инициализация
```go
main() initConfig() connectToDB()
```
**Шаги:**
1. Подключение к БД (primary → secondary fallback)
2. Загрузка IP сетей из таблицы `ips`
3. Получение hostname текущего сервера
4. Определение instances для этого хоста
### 2. Основной цикл обработки
```
┌─────────────────────────────────────────┐
│ Для каждого instance на этом хосте │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Получить всех клиентов для instance │
│ (getClientInfoByInstance) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Для каждого клиента: │
│ 1. Получить apps_settings │
│ 2. Собрать данные о ресурсах
│ 3. Выделить порты │
│ 4. Сгенерировать конфиги │
│ 5. Управление контейнерами │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Глобальная проверка drain-контейнеров │
│ (processAllDrainingContainersGlobally) │
└─────────────────────────────────────────┘
```
### 3. Сбор данных о ресурсах
Для каждого ресурса определяется режим работы через поле `mode`:
#### Режим `auto` (по умолчанию)
```
1. Проверка кэша sp_info (TTL 10 мин)
├─ Данные свежие → использовать
└─ Данные устарели → API запрос
2. API запросы (с retry × 3):
├─ GET /l7/resource/{id}/global → domain_name
├─ GET /l7/alias/global?l7ResourceId={id} → aliases
└─ GET /l7/origin/global?l7ResourceId={id} → origins
3. Фильтрация origins (удалить WAF IP)
4. Сохранение в sp_info
```
#### Режим `manual`
```
1. Чтение из таблицы manual_info
2. Фильтрация origins (удалить WAF IP)
3. API и кэш игнорируются
```
#### Режим `disabled`
```
Ресурс полностью пропускается
```
---
## Управление портами
### Диапазоны портов
```go
const (
HTTPPortStart = 18000 // HTTP: 18000-20999 (3000 портов)
HTTPPortEnd = 20999
HTTPSPortStart = 21000 // HTTPS: 21000-23999 (3000 портов)
HTTPSPortEnd = 23999
)
```
### Алгоритм выделения портов
```
┌────────────────────────────────────────┐
│ 1. Получить занятые порты (ss -tuln) │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 2. Загрузить сохраненные порты │
│ из .ports.json (если есть) │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 3. Исключить порты клиента │
│ (для переиспользования) │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 4. Для каждого ресурса: │
│ - Определить нужные порты из │
│ custom_input_http_ports[] или │
│ custom_input_https_ports[] │
│ - Выделить свободные Docker порты │
│ - Повторить для каждого контейнера │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 5. Сохранить в .ports.json │
└────────────────────────────────────────┘
```
### Структура портов
```go
type PortMapping struct {
HTTPPorts map[int]int // map[customPort]dockerPort
HTTPSPorts map[int]int // map[customPort]dockerPort
}
```
**Пример:**
```json
{
"container_num": 1,
"resources": [
{
"l7resourceid": 12345,
"http_ports": {
"80": 18000,
"8080": 18001
},
"https_ports": {
"443": 21000,
"8443": 21001
}
}
]
}
```
---
## Graceful Shutdown и миграция
### Типы drain операций
#### 1. Масштабирование вниз (Scale Down)
**Timeout:** 90 секунд
```
Триггер: containers_count уменьшен
Процесс:
1. Контейнер помечается маркером /tmp/ptaf-drain-{name}
2. Ожидание 90 секунд
3. Удаление контейнера
```
#### 2. Миграция между хостами
**Timeout:** 3600 секунд (1 час)
```
Триггер: waf_instance изменен
Процесс:
1. Старый хост помечает контейнеры маркером (isMigration=true)
2. Новый хост создает контейнеры
3. Через 1 час старый хост удаляет контейнеры
4. Проверка актуальности перед удалением
```
### Структура маркеров
```bash
# Контейнеры
/tmp/ptaf-drain-ptaf_client-name_001
# Angie конфиги
/tmp/ptaf-angie-drain-client-name
```
**Формат маркера:**
```
{timestamp}:{migration_flag}
1738845600:1 # migration
1738845600:0 # scale down
```
### Процесс проверки drain
```
processDrainingContainers()
Для каждого контейнера с маркером:
├─ Прошло времени < timeout?
│ └─ Логировать оставшееся время
└─ Прошло времени >= timeout?
├─ Migration?
│ ├─ Проверить актуальность (checkHostBelongsToInstance)
│ ├─ Клиент вернулся? → отменить удаление
│ └─ Клиент ушел? → удалить контейнер
└─ Scale down?
└─ Удалить контейнер
```
---
## Генерация конфигураций
### Структура файлов
```
/home/install/ptaf/{client-title}/
├── {client-title}-ptaf-agent001/
│ ├── docker-compose.yml
│ └── .ports.json
├── {client-title}-ptaf-agent002/
│ ├── docker-compose.yml
│ └── .ports.json
└── ...
/home/install/conf/ptaf-nginx/{client-title}/
├── ptaf-agent001/
│ ├── nginx.conf
│ ├── mime.types
│ └── conf.d/
│ ├── {client-title}_12345.conf
│ ├── {client-title}_67890.conf
│ └── ...
└── ...
/var/log/ptaf_nginx/{client-title}/
└── ptaf-agent001/
├── access.log
└── error.log
/etc/angie/http.d/
└── angie-ptaf-{client-title}.conf
```
### 1. Генерация nginx.conf
**Функция:** `generateNginxConf()`
**Содержимое:**
```nginx
env POD_IP={extracted_or_127.0.0.1};
user nginx;
worker_processes {config.WorkerProcesses};
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections {config.WorkerConnections};
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
log_format main '$remote_addr - $remote_user [$time_local] ...';
access_log /var/log/nginx/access.log main;
# Performance
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
# PTAF config (если указан)
{ptafConfig}
# Include resource configs
include /etc/nginx/conf.d/*.conf;
}
```
**Особенности:**
- POD_IP извлекается из существующего конфига (для сохранения при обновлении)
- ptafConfig берется из `client_info.ptaf_config`
### 2. Генерация конфигов ресурсов (Nginx)
**Функция:** `generateNginxResourceConfig()`
#### Структура конфига ресурса:
```nginx
# Upstream блоки для каждого output порта
upstream {clientTitle}_{l7ResourceID}_http_{outputPort} {
least_conn;
{для каждого origin:}
server {origin.IP}:{outputPort} weight={origin.Weight} max_fails=3 fail_timeout=30s;
# Custom upstream или дефолтные keepalive (если sni != 1)
keepalive 60;
keepalive_timeout 70s;
}
upstream {clientTitle}_{l7ResourceID}_https_{outputPort} {
least_conn;
{для каждого origin:}
server {origin.IP}:{outputPort} weight={origin.Weight} max_fails=3 fail_timeout=30s;
# Custom upstream или дефолтные keepalive (если sni != 1)
keepalive 60;
keepalive_timeout 70s;
}
# Server блоки для каждого input порта
server {
listen {dockerPort};
server_name {serverName} {aliases с www.};
# Custom server directives (если есть)
{server_directives_nginx_custom}
# Custom location или дефолтный
location / {
proxy_pass http://{upstream_name};
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... стандартные proxy заголовки
}
}
server {
listen {dockerPort} ssl;
server_name {serverName} {aliases с www.};
# SSL сертификаты
ssl_certificate /opt/ptaf/ssl/{sanitizedServerName}.crt;
ssl_certificate_key /opt/ptaf/ssl/{sanitizedServerName}.key;
# Custom server directives (если есть)
{server_directives_nginx_custom}
# Custom location или дефолтный
location / {
proxy_pass https://{upstream_name};
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... стандартные proxy заголовки
}
}
# Custom SERVER_BLOCK блоки (если есть)
server {
listen {customPort};
server_name {customServerName} {customAliases};
{customContent}
}
```
#### Логика кастомизации:
1. **Custom Upstream** (`upstream_nginx_custom`):
- Если заполнено → заменяет keepalive директивы
- Если пусто → используются дефолтные (keepalive 60 + keepalive_timeout 70s)
- Исключение: если `sni = 1`, keepalive отключен
2. **Custom Server** (`server_nginx_custom`):
- Полная замена всего server блока
- Используется вместо генерации стандартных блоков
3. **Custom Location** (`location_nginx_custom`):
- Заменяет дефолтный location /
- proxy_pass корректируется автоматически
4. **Custom Server Directives** (`server_directives_nginx_custom`):
- Добавляются внутрь server блока
- Поддержка [SERVER_BLOCK:PORT:DOMAIN] синтаксиса
5. **SERVER_BLOCK директивы**:
```
[SERVER_BLOCK:8080:example.com:alias1.com:alias2.com]
location / {
proxy_pass http://backend;
}
[/SERVER_BLOCK]
```
- Создают отдельные server блоки
- Домены из SERVER_BLOCK исключаются из основных блоков
### 3. Генерация конфига Angie
**Функция:** `generateAngieConfig()`
#### Структура конфига Angie:
```nginx
# Upstream блоки для контейнеров
upstream angie_{clientTitle}_{l7ResourceID}_http_{customPort} {
least_conn;
{для каждого контейнера:}
server 127.0.0.1:{dockerPort} weight=1 max_fails=3 fail_timeout=30s;
# Custom upstream или дефолтные keepalive (если sni != 1)
keepalive 60;
keepalive_timeout 70s;
}
upstream angie_{clientTitle}_{l7ResourceID}_https_{customPort} {
least_conn;
{для каждого контейнера:}
server 127.0.0.1:{dockerPort} weight=1 max_fails=3 fail_timeout=30s;
# Custom upstream или дефолтные keepalive (если sni != 1)
keepalive 60;
keepalive_timeout 70s;
}
# Server блоки
server {
listen {customPort};
server_name {serverName} {aliases с www.};
# Custom server directives (если есть)
{server_directives_angie_custom}
# Custom location или дефолтный
location / {
proxy_pass http://angie_{upstream_name};
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... стандартные proxy заголовки
}
}
server {
listen {customPort} ssl;
server_name {serverName} {aliases с www.};
# SSL сертификаты
ssl_certificate /etc/ssl/certs/{sanitizedServerName}.crt;
ssl_certificate_key /etc/ssl/private/{sanitizedServerName}.key;
# Custom SSL или дефолтный
{если custom_angie_ssl заполнен:}
{custom_angie_ssl}
{иначе:}
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Custom server directives (если есть)
{server_directives_angie_custom}
# Custom location или дефолтный
location / {
proxy_pass http://angie_{upstream_name};
proxy_http_version 1.1;
proxy_set_header Connection "";
# ... стандартные proxy заголовки
}
}
# Custom SERVER_BLOCK блоки (если есть)
server {
listen {customPort};
server_name {customServerName} {customAliases};
{customContent}
}
```
#### Особенности Angie конфига:
1. **Балансировка между контейнерами:**
- Upstream указывает на локальные Docker порты (127.0.0.1:18xxx)
- Количество серверов = containers_count
2. **SSL настройки:**
- `custom_angie_ssl` **полностью заменяет** дефолтные директивы
- Если не заполнено → используются безопасные дефолты
3. **Санитизация доменов:**
- В Angie: `_` заменяется на `-`
- В Nginx: домены без изменений
- Причина: совместимость и единообразие
4. **mTLS поддержка:**
- Автоматическая замена путей `/etc/ssl/` → `/opt/ptaf/conf/main/`
- Применяется в custom директивах
### 4. Генерация docker-compose.yml
**Функция:** `generateDockerCompose()`
```yaml
version: '3.8'
services:
ptaf_{clientTitle}_{containerNum:03d}:
image: {config.DockerImage}
container_name: ptaf_{clientTitle}_{containerNum:03d}
restart: unless-stopped
ports:
# Для каждого ресурса и каждого custom порта:
- "{dockerPort}:{customPort}" # HTTP
- "{dockerPort}:{customPort}" # HTTPS
volumes:
- /home/install/conf/ptaf-nginx/{clientTitle}/ptaf-agent{containerNum:03d}:/etc/nginx:ro
- /var/log/ptaf_nginx/{clientTitle}/ptaf-agent{containerNum:03d}:/var/log/nginx
- /home/install/ssl:/opt/ptaf/ssl:ro
- /home/install/mtls:/opt/ptaf/conf/main:ro
networks:
- ptaf-network
logging:
driver: "fluentd"
options:
fluentd-address: "127.0.0.1:{fluentBitPort}"
tag: "ptaf.{clientTitle}.agent{containerNum:03d}"
fluentd-async: "true"
fluentd-max-retries: "3"
fluentd-retry-wait: "1s"
networks:
ptaf-network:
driver: bridge
```
**Важно:**
- Порты сортируются для детерминированного вывода
- Volume с конфигами монтируется read-only
- SSL сертификаты общие для всех контейнеров
- FluentBit для централизованного логирования
---
## API интеграция
### ServicePipe API
**Base URL:** `https://api.servicepipe.ru/api/v1/`
#### Endpoints:
1. **Получение ресурса:**
```http
GET /l7/resource/{l7ResourceID}/global
Authorization: Bearer {token}
Response:
{
"data": {
"result": {
"l7ResourceId": 12345,
"l7ResourceName": "example.com",
...
}
}
}
```
2. **Получение aliases:**
```http
GET /l7/alias/global?limit=1000&l7ResourceId={l7ResourceID}
Authorization: Bearer {token}
Response:
{
"data": {
"result": {
"items": [
{
"id": 1,
"domain": "www.example.com",
"l7ResourceId": 12345,
...
}
]
}
}
}
```
3. **Получение origins:**
```http
GET /l7/origin/global?limit=1000&l7ResourceId={l7ResourceID}
Authorization: Bearer {token}
Response:
{
"data": {
"result": {
"items": [
{
"id": 1,
"ip": "192.168.1.100",
"weight": 100,
"mode": "active",
...
}
]
}
}
}
```
### Retry механизм
```go
maxRetries := 3
для attempt := 1 до maxRetries:
попытка запроса
если успех:
return результат
если ошибка:
waitTime = attempt² секунд // 1s, 4s, 9s
sleep(waitTime)
return ошибка
```
### Кэширование
**Стратегия:**
1. Проверка `sp_info` (TTL 10 минут)
2. Если данные свежие → использовать
3. Если устарели или отсутствуют → API запрос + сохранение
**Преимущества:**
- Снижение нагрузки на API
- Устойчивость к временным сбоям API
- Быстрая работа при повторных запусках
---
## Схема взаимодействия
### Полный flow обработки клиента
```
┌─────────────────────────────────────────────────────────────┐
│ НАЧАЛО ОБРАБОТКИ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 1. Получение данных из БД │
│ - client_info (containers_count, waf_instance) │
│ - apps_settings (все ресурсы для client_title) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. Для каждого ресурса (apps_settings): │
│ ┌──────────────────────────────────────────┐ │
│ │ mode = disabled? → SKIP │ │
│ │ mode = manual? → manual_info │ │
│ │ mode = auto? → sp_info cache или API │ │
│ └──────────────────────────────────────────┘ │
│ Результат: ResourceData { │
│ L7ResourceID, ServerName, Aliases, Origins │
│ } │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. Выделение портов (PortAllocator) │
│ - Сканирование занятых портов (ss -tuln) │
│ - Попытка загрузки .ports.json │
│ - Выделение свободных портов из пулов │
│ - Сохранение в .ports.json │
│ │
│ Результат: map[l7ResourceID][]PortMapping │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. Генерация конфигов для каждого контейнера │
│ Для containerNum := 1 до containers_count: │
│ ├─ nginx.conf (основной конфиг) │
│ ├─ mime.types │
│ └─ conf.d/*.conf (для каждого ресурса) │
│ │
│ Отслеживание изменений: containerChanges[containerNum] │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. Генерация docker-compose.yml для каждого контейнера │
│ - Маппинг портов │
│ - Volumes │
│ - Logging (FluentBit) │
│ │
│ Отслеживание изменений: composeChanges[containerNum] │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 6. Управление контейнерами │
│ Для containerNum := 1 до containers_count: │
│ ┌──────────────────────────────────────────┐ │
│ │ Контейнер существует? │ │
│ │ ├─ НЕТ → docker-compose up -d │ │
│ │ └─ ДА: │ │
│ │ ├─ composeChanges? → recreate │ │
│ │ ├─ !running? → start │ │
│ │ └─ containerChanges? │ │
│ │ ├─ nginx -t (test) │ │
│ │ └─ nginx -s reload │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 7. Генерация Angie конфига │
│ /etc/angie/http.d/angie-ptaf-{clientTitle}.conf │
│ - Upstream для всех контейнеров │
│ - Server блоки для всех портов │
│ - SSL настройки │
│ │
│ Если изменился → angie -t && angie -s reload │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 8. Обработка лишних контейнеров (drain) │
│ Для containerNum := containers_count+1 до MAX: │
│ ├─ Контейнер существует? │
│ │ ├─ Уже помечен для drain? │
│ │ │ └─ Проверить timeout → удалить │
│ │ └─ Не помечен → пометить для drain │
│ └─ Контейнер не существует → очистить маркер │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ ЗАВЕРШЕНИЕ ОБРАБОТКИ │
└─────────────────────────────────────────────────────────────┘
```
### Схема миграции клиента
```
СТАРЫЙ ХОСТ (waf-host-01) НОВЫЙ ХОСТ (waf-host-02)
│ │
│ waf_instance изменен в БД │
│ client_title: waf-02 (было: waf-01) │
↓ ↓
┌───────────────────┐ ┌───────────────────┐
│ Проверка │ │ Проверка │
│ принадлежности: │ │ принадлежности: │
НЕ принадлежит │ │ Принадлежит │
└───────────────────┘ └───────────────────┘
↓ ↓
┌───────────────────┐ ┌───────────────────┐
│ Пометка всех │ │ Создание новых │
│ контейнеров для │ │ контейнеров │
│ drain (migration) │ │ ├─ Выделить порты │
│ timeout: 1 час │ │ ├─ Генерация │
└───────────────────┘ │ │ конфигов │
↓ │ └─ docker-compose │
┌───────────────────┐ │ up -d │
│ Пометка Angie │ └───────────────────┘
│ конфига для │ ↓
│ удаления │ ┌───────────────────┐
└───────────────────┘ │ Генерация Angie │
↓ │ конфига │
┌───────────────────┐ │ ├─ angie -t │
│ Ожидание 1 час... │ │ └─ angie -s │
│ │ │ reload │
│ [трафик все еще │ └───────────────────┘
│ обрабатывается] │ ↓
└───────────────────┘ ┌───────────────────┐
↓ │ Новые контейнеры │
┌───────────────────┐ │ принимают трафик │
│ Через 1 час: │ └───────────────────┘
│ - Проверка │
│ актуальности │
│ - Клиент вернулся?│
│ → ОТМЕНА
│ - Клиент ушел? │
│ → УДАЛЕНИЕ │
└───────────────────┘
┌───────────────────┐
│ Удаление: │
│ ├─ docker-compose │
│ │ down │
│ ├─ rm -rf dirs │
│ └─ rm angie.conf │
└───────────────────┘
```
### Схема масштабирования
```
containers_count: 3 → 2 (уменьшение)
КОНТЕЙНЕР 1 КОНТЕЙНЕР 2 КОНТЕЙНЕР 3
│ │ │
│ │ │
↓ ↓ ↓
┌────────┐ ┌────────┐ ┌────────┐
│ Обновл.│ │ Обновл.│ │ Помечен│
│ конфиг │ │ конфиг │ │ drain │
│ и порты│ │ и порты│ │timeout:│
└────────┘ └────────┘ │ 90 сек │
│ │ └────────┘
│ │ │
↓ ↓ ↓
┌────────┐ ┌────────┐ [Ожидание]
│ reload │ │ reload │ [активных]
│ nginx │ │ nginx │ [соединений]
└────────┘ └────────┘ ↓
│ │ ┌────────┐
│ │ │ Через │
↓ ↓ │ 90 сек │
[Обрабатывают] [Обрабатывают] └────────┘
трафик трафик ↓
┌────────┐
│ docker-│
│ compose│
│ down │
└────────┘
┌────────┐
│ Удален │
└────────┘
```
---
## Константы и лимиты
### Производительность
```go
DefaultWorkerProcesses = 4 // Nginx worker processes
DefaultWorkerConnections = 64000 // Соединений на worker
```
### Ограничения
```go
MaxContainersPerClient = 20 // Максимум контейнеров на клиента
```
### Timeouts
```go
ContainerDrainTimeout = 90 // Секунд для scale down
MigrationDrainTimeout = 3600 // Секунд для миграции (1 час)
APITimeout = 120 // Секунд для API запросов
CacheTTL = 600 // Секунд (10 минут) для sp_info
```
### Retry
```go
MaxAPIRetries = 3 // Попыток для API запросов
RetryWaitFormula = attempt² // 1s, 4s, 9s
```
---
## Безопасность
### Фильтрация IP адресов
**WAF Networks** - исключаются из origins:
```
109.238.89.0/24
89.20.63.0/24
```
Причина: предотвращение routing loops (WAF не должен проксировать на себя)
### Antibot Networks
```
185.66.85.0/24
185.66.86.0/24
185.35.5.0/24
185.35.6.0/24
212.67.26.0/24
+
сети заказчиков у которых свой АнтиДДОС
```
Используется для идентификации antibot трафика
### SSL/TLS
**Дефолтные настройки (Angie):**
```nginx
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
```
**Кастомизация через `custom_angie_ssl`** - полная замена дефолтов
### mTLS
**Автоматическая замена путей:**
```
/etc/ssl/certs/ → /opt/ptaf/conf/main/
/etc/ssl/private/ → /opt/ptaf/conf/main/
```
Применяется во всех custom директивах, содержащих `proxy_ssl_certificate`
---
## Мониторинг и логирование
### Логирование контейнеров
**FluentBit интеграция:**
```yaml
logging:
driver: "fluentd"
options:
fluentd-address: "127.0.0.1:{fluentBitPort}"
tag: "ptaf.{clientTitle}.agent{containerNum:03d}"
fluentd-async: "true"
fluentd-max-retries: "3"
fluentd-retry-wait: "1s"
```
### Структура логов
```
/var/log/ptaf_nginx/{client-title}/ptaf-agent001/
├── access.log # HTTP access logs
└── error.log # Nginx error logs
```
### Логирование программы
**Уровни:**
- `` - Успешная операция
- `` - Информация (без изменений)
- `` - Предупреждение
- `` - Действие/переход
- `└─` - Детали операции
**Примеры:**
```
✓ nginx.conf изменен: /path/to/nginx.conf
○ nginx.conf не изменился: /path/to/nginx.conf
⚠ Данные в кеше устарели (возраст: 15.3 мин)
→ Запуск нового контейнера client-ptaf-agent001
└─ Используются кастомные SSL директивы для Angie
```
---
## Оптимизации
### 1. Кэширование API данных
- TTL: 10 минут
- Снижение нагрузки на API
- Устойчивость к сбоям
### 2. Переиспользование портов
- Сохранение в `.ports.json`
- Проверка актуальности (список ресурсов, порядок)
- Минимизация изменений docker-compose
### 3. Детерминированная генерация
- Хеширование содержимого (SHA256)
- Запись только при изменениях
- Сортировка портов для стабильного вывода
### 4. Graceful reload
- `nginx -t` перед reload
- Минимизация перезапусков контейнеров
- Изолированный reload (по контейнерам)
### 5. Batch обработка
- Все клиенты instance обрабатываются за один запуск
- Одно подключение к БД
- Один reload Angie для всех изменений
---
## Troubleshooting
### Проблема: Контейнер не запускается
**Проверить:**
1. `docker logs ptaf_{client}_{num}`
2. Наличие конфигов в `/home/install/conf/ptaf-nginx/`
3. Занятость портов: `ss -tuln | grep {port}`
4. Права на `/var/log/ptaf_nginx/` (должны быть 0777)
### Проблема: Nginx не перезагружается
**Проверить:**
1. `docker exec ptaf_{client}_{num} nginx -t`
2. Синтаксис в `conf.d/*.conf`
3. Логи: `/var/log/ptaf_nginx/{client}/ptaf-agent{num}/error.log`
### Проблема: Порты конфликтуют
**Действия:**
1. Удалить `.ports.json`
2. Перезапустить программу (порты будут перевыделены)
3. Проверить диапазоны: 18000-20999 (HTTP), 21000-23999 (HTTPS)
### Проблема: Клиент не мигрирует
**Проверить:**
1. `waf_instance` в `client_info`
2. Записи в `instances_new`
3. Наличие маркеров `/tmp/ptaf-drain-*`
4. Логи программы на обоих хостах
### Проблема: API данные не обновляются
**Действия:**
1. Проверить `sp_info.updated_at`
2. Если > 10 минут → должен быть API запрос
3. Проверить `mode` в `apps_settings`:
- `disabled` → пропускается
- `manual` → использует `manual_info`
- `auto` → кэш или API
---
## Расширение системы
### Добавление нового клиента
```sql
-- 1. Добавить в client_info
INSERT INTO client_info (client_title, containers_count, waf_instance)
VALUES ('new-client', 2, 'instance-a');
-- 2. Добавить ресурсы в apps_settings
INSERT INTO apps_settings (l7resourceid, client_title, waf_enabled, mode)
VALUES (12345, 'new-client', true, 'auto');
-- 3. (Опционально) Для ручного режима добавить в manual_info
INSERT INTO manual_info (sid, domain_name, aliases, origins)
VALUES (12345, 'example.com', '["www.example.com"]', '[{"ip":"192.168.1.100","weight":100,"mode":"active"}]');
```
### Добавление нового instance
```sql
INSERT INTO instances_new (hostname, instance)
VALUES ('waf-host-03', 'instance-d');
```
### Кастомизация портов
```sql
UPDATE apps_settings
SET
custom_input_http_ports = ARRAY[80, 8080],
custom_input_https_ports = ARRAY[443, 8443],
custom_output_http_ports = ARRAY[80],
custom_output_https_ports = ARRAY[443]
WHERE l7resourceid = 12345;
```
### Переопределение SSL
```sql
UPDATE apps_settings
SET custom_angie_ssl = 'ssl_protocols TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_stapling on;
ssl_stapling_verify on;'
WHERE l7resourceid = 12345;
```