Инфраструктура проекта 1anketa.uz
Введение
Данное руководство описывает минимально достаточную, безопасную и управляемую инфраструктуру на «обычном» облачном хостинге без использования managed-LB и без кластеризации БД/кеша. Основные цели: отделить данные от фронт-части, вынести статику в S3-совместимое хранилище, навести порядок в сетевой сегментации и фаервол-правилах, не усложняя схему сверх необходимого.
Ключевые допущения: – Один публичный IP на pfSense (NAT/фильтрация). – Внутренняя подсеть 172.19.10.0/24 общая для новых ВМ. – Один Web-сервер (приложение и TLS на нём). – Отдельные ВМ для MySQL, Redis, MongoDB. – S3 провайдера (AWS-совместимый) только для статики. – Мониторинг и резервное копирование подключаются позже.
Текущий ландшафт и требования
- Сервер приложения (Web Service).
- База данных (MySQL).
- Кеш (Redis).
- Документы/медиа вынести в S3; старый сервер должен выгрузить накопленные файлы в тот же бакет.
- Минимум открытых портов из Интернета: всё за pfSense.
- VPN и базовая сетебезопасность уже настроены на уровне облака; используем их как данность.
Целевая минимальная схема
Интернет
|
[ ПУБЛИЧНЫЙ IP ]
|
+-------------------+
| pfSense (NAT) |
| Firewall / VPN |
+-------------------+
|
| (LAN: 172.19.10.1/24)
|
172.19.10.0/24 (внутренняя подсеть)
┌──────────────────────┬───────────────────────┬───────────────────────┐
│ │ │ │
+----------------+ +----------------+ +----------------+ +----------------+
| WEB #1 | | MySQL | | Redis | | MongoDB |
| 172.19.10.25 | | 172.19.10.22 | | 172.19.10.23 | | 172.19.10.24 |
| HTTPS/HTTP | | 3306/TCP | | 6379/TCP | | 27017/TCP |
+----------------+ +----------------+ +----------------+ +----------------+
|
| Исходящий 443/TCP (узкие правила egress)
V
+----------------------+
| S3 (провайдера) |
| бакет: приватный |
| versioning: ON |
| object lock: OFF |
+----------------------+
Миграция статики: [Старый сервер] --443--> S3 (тот же бакет) → WEB #1 использует S3 на чтение/запись
Объяснение компонентов (почему так)
-
pfSense Роль единственной точки публикации (NAT 443/80 на WEB #1) и фильтрации. Входящее — только к Web-серверу. Исходящее — по белому списку (в первую очередь: S3 endpoint провайдера и системные репозитории).
-
Web #1 Единственный публично доступный сервис за pfSense. TLS на стороне приложения (сертификат выдают разработчики). Доступ к БД/кешу/NoSQL — только по внутренним адресам. Работа со статикой — через S3 SDK/клиент; локальные медиа не накапливаем.
-
Data-узлы (MySQL/Redis/MongoDB) Изолированы в той же подсети, но с доступом только от WEB #1 по соответствующим портам. Из Интернета не публикуются. Реплики/кластеризация на этом этапе не требуются.
-
S3 (облачный провайдер, AWS-совместим) Бакет приватный. Версионность включена (защита от случайных перезаписей при миграции и эксплуатации). Object Lock выключен (иначе усложнит удаление/исправление артефактов). Возможна точечная политика публичного чтения для определённого префикса при необходимости.
Адресация и DNS
Адреса: – pfSense LAN: 172.19.10.1/24 – MySQL: 172.19.10.22/24 – Redis: 172.19.10.23/24 – MongoDB: 172.19.10.24/24 – WEB #1: 172.19.10.25/24
DNS: – Публичный A/AAAA → публичный IP pfSense. – Внутренние имена (любой вариант: локальный DNS на pfSense или /etc/hosts):
Публикация и NAT (pfSense)
Port Forward: – TCP 443 → 172.19.10.25:443 (WEB #1) – TCP 80 → 172.19.10.25:80 (только если нужен HTTP-01/редирект; иначе можно закрыть)
WAN Rules: – Разрешить входящие 443 (и 80 при необходимости) только к соответствующим правилам NAT. – Остальное — блокировать.
LAN Egress (минимально необходимые): – WEB #1 → S3 endpoint провайдера (443/TCP). – WEB #1 → системные репозитории/обновления (443/TCP). – Data-узлам (MySQL/Redis/MongoDB) — как минимум NTP/DNS/обновления; остальной исходящий трафик по умолчанию запрещён.
Внутрисетевые правила (основная логика)
Allow: – WEB #1 → MySQL (3306/TCP). – WEB #1 → Redis (6379/TCP). – WEB #1 → MongoDB (27017/TCP).
Deny: – Любые подключения к MySQL/Redis/MongoDB не с WEB #1. – Входящие из Интернета к Data-узлам.
SSH: – Доступ к ВМ — только через ваш существующий VPN/бастион; пароли запрещены, только ключи.
Хранилище статики (S3) — настройки
Создание бакета: – Приватный. – Версионность: включена. – Object Lock (блокировка): выключена.
Доступы: – Для приложения (WEB #1) — отдельный ключ/секрет с минимальными правами (Get/Put/Delete/List в пределах бакета или конкретного префикса). – Для старого сервера — отдельный временный ключ только на миграцию.
Использование в приложении: – Сервер-сайд: прямые операции через SDK. – При необходимости клиентских загрузок — presigned URLs; включить CORS на бакете для нужных методов и источников.
Опционально (позже): – Публичное чтение только для конкретного префикса (например, uploads/public/*) отдельной bucket-политикой.
Миграция статики со старого сервера
- Подключить старый сервер к тому же S3 (endpoint провайдера, ключ/секрет).
- Выполнить первичный sync (aws-cli или rclone) из локальной директории статики в бакет.
- Перевести приложение на использование S3 в «read-through» режиме.
- Выполнить финальный дельта-sync в момент переключения.
- Отключить локальные записи на старом сервере, убедиться, что новая статика пишет/читается из S3.
Мини-чеклист запуска
– pfSense: Port Forward 443/80 → WEB #1; egress на S3 и репозитории. – ВМ: статические IP; службы слушают только на 172.19.10.0/24; хост-фаервол по желанию (доп. к pfSense). – WEB #1: переменные окружения S3 (endpoint, region, bucket, ключи); приложение работает с S3. – Data-узлы: убедиться, что доступны только с WEB #1. – DNS: публичный домен указывает на публичный IP pfSense; внутренние имена разрешаются. – Тест: загрузка/чтение файла через приложение → S3; доступ к БД/кешу/NoSQL — ок; лишние пути извне закрыты.
Дальнейшее развитие (когда придёт время)
– Добавить балансировщик (Nginx/HAProxy) и второй Web-инстанс; хранить сессии в Redis. – Включить мониторинг (Zabbix или Prometheus). – Настроить резервное копирование БД/конфигов и lifecycle-политику для S3-версий. – Рассмотреть private/VPC endpoint для S3 у провайдера (если доступен), чтобы убрать исходящий трафик в Интернет для статики.
Дополнительные детали и пояснения
CORS для бакета (значения в диалоговом окне)
Если фронт на вашем домене:
Allowed Origins: https://app.example.com
Allowed Methods: GET, POST, PUT (при необходимости добавьте DELETE, HEAD)
Allowed Headers: *
Expose Headers: ETag, Content-Length, Content-Type, x-amz-request-id, x-amz-version-id
Max age seconds: 3000
Если нужно быстро “на всё” (временное решение):
Allowed Origins: *
Allowed Methods: GET, POST, PUT
Allowed Headers: *
Expose Headers: ETag, Content-Length, Content-Type
Max age seconds: 3000
Комментарий: выставить CORS через UI достаточно; aws-cli не обязателен. Если позже понадобится публичный доступ к части файлов, откроем публичное чтение точечно на префикс политикой бакета.
Правила для pfSense (WEB1 = 172.19.10.25)
Вход с Интернета (NAT + auto-rules на WAN):
- Проброс 443/TCP → 172.19.10.25:443.
- Проброс 80/TCP → 172.19.10.25:80 (только если нужен HTTP-01/редирект; можно выключить после выпуска сертификата).
Исходящий трафик (LAN egress):
- Разрешить 172.19.10.25 → S3-эндпоинт провайдера: 443/TCP.
- Разрешить обновления ОС (172.19.10.25 и узлам БД/кеша/NoSQL) → репозитории: 443/TCP.
- Остальное исходящее — по умолчанию запрет (опционально, если хотите «белый список»).
Внутрисетевые правила (LAN → LAN):
- Разрешить: 172.19.10.25 → 172.19.10.20:3306 (MySQL).
- Разрешить: 172.19.10.25 → 172.19.10.30:6379 (Redis).
- Разрешить: 172.19.10.25 → 172.19.10.40:27017 (MongoDB).
- Запретить: любые другие источники → [ MySQL, Redis, MongoDB ] (все порты).
- SSH/админка — только через уже настроенный VPN/бастион.
Подсказки:
- Создайте Aliases: WEB1, MYSQL, REDIS, MONGO; S3_ENDPOINTS (FQDN), REPOS (сети/домены репозиториев).
- Порядок правил важен: разрешающие — выше общего “Block”.
Рекомендуемые размеры ВМ (реалистично под ваш текущий профиль)
Дам два профиля: “Бюджетный старт” (укладывается примерно в ваши 24 ГБ суммарно) и “Умеренный запас”.
Бюджетный старт (сумма ≈ 24 ГБ RAM)
-
pfSense: 2 vCPU, 2 ГБ RAM, 20–40 ГБ SSD.
-
WEB1: 4 vCPU, 6–8 ГБ RAM, 80–160 ГБ SSD.
-
MySQL: 4 vCPU, 8 ГБ RAM, 150–300 ГБ NVMe (отдельный диск под /var/lib/mysql).
- innodb_buffer_pool_size ≈ 4–5 ГБ (50–60% RAM узла).
-
Redis: 2 vCPU, 2–4 ГБ RAM, 20–50 ГБ SSD.
- maxmemory выставить под рабочий объём; AOF everysec.
-
MongoDB: 4 vCPU, 6–8 ГБ RAM, 150–300 ГБ NVMe.
- WiredTiger cache по умолчанию ~50% RAM; при 6–8 ГБ RAM это ок. При необходимости ограничить
storage.wiredTiger.engineConfig.cacheSizeGB.
- WiredTiger cache по умолчанию ~50% RAM; при 6–8 ГБ RAM это ок. При необходимости ограничить
Умеренный запас (если увидите, что узкие места мешают)
- WEB1: 6–8 vCPU, 8–12 ГБ RAM.
- MySQL: 6–8 vCPU, 12–16 ГБ RAM (buffer pool 7–10 ГБ).
- Redis: 2–4 vCPU, 4–6 ГБ RAM (под больший кеш/сессии).
- MongoDB: 6–8 vCPU, 10–12 ГБ RAM (cache ~5–6 ГБ).
Примечания по тюнингу:
- MySQL: включить slow query log, периодически смотреть индексы;
innodb_flush_log_at_trx_commit=1(или 2, если нужна прибавка скорости с малым риском). - Redis:
protected-mode on, bind только на 172.19.10.0/24, пароль/ACL;saveи AOF — по требованиям к персистентности. - MongoDB: auth включить; bind только на внутр. IP; WiredTiger по умолчанию обычно оптимально; при нехватке RAM урезать cacheSizeGB.
- WEB1: держать статику в S3; локально — только временные файлы/кеш; следить за ulimit (
nofile ≥ 65535) иnet.core.somaxconn ≥ 1024.