Данный проект настроен для автоматического развертывания отказоустойчивого приложения (Backend + Frontend) в кластере Docker Swarm.
infrastructure/: Скрипты для настройки и инициализации кластера (firewall, swarm init, registry, db).deploy/: Конфигурационные файлы Docker Compose для разных уровней стека.infrastructure.yml: База данных Postgres и Redis (размещаются на Manager ноде).services.yml: Прикладные сервисы Backend и Frontend (Frontend на Manager, Backend на Worker нодах).
services/: Исходный код бэкенда (FastAPI) и фронтенда (Next.js).update_services.py: Скрипт для сборки образов и обновления прикладных сервисов.
Отредактируйте файл infrastructure/inventory.json, указав IP-адреса ваших нод, пользователей, пути к SSH-ключам и параметры БД.
Запустите скрипт инициализации кластера:
python infrastructure/automate_deploy.pyЭтот скрипт настроит ноды, объединит их в Swarm и развернет базу данных с Redis на менеджер-ноде.
Для сборки и запуска бэкенда и фронтенда используйте:
python update_services.pyСкрипт соберет Docker-образы локально, отправит их в реестр на менеджер-ноде, запустит их в кластере (Frontend на менеджере, Backend на воркерах) и автоматически применит миграции базы данных.
Миграции запускаются автоматически в конце работы update_services.py. Скрипт находит один из запущенных контейнеров бэкенда и выполняет в нем команду alembic upgrade head.
Инструкции по локальному запуску всего стека (Backend, Frontend, DB, Redis) через Docker Compose или Makefile вынесены в отдельный файл:
LOCAL_DEVELOPMENT.md
- Разделение ролей: БД, Redis и Фронтенд работают на мастере (менеджере), Бэкенд распределяется по воркерам.
- Сетевая изоляция: Все компоненты общаются внутри overlay-сети
app_network. - Zero Downtime: Обновление сервисов происходит по стратегии
start-first.
Локальный Registry работает по протоколу HTTP (без SSL), поэтому Docker по умолчанию блокирует подключение к нему из соображений безопасности.
Вам НЕОБХОДИМО выполнить следующие действия:
-
На вашей локальной машине (Windows/Mac/Linux):
- Если вы используете Docker Desktop: зайдите в
Settings->Docker Engine. - Добавьте IP вашего менеджера в список
insecure-registries:{ "insecure-registries": ["85.198.86.165:5000"] } - Нажмите
Apply & Restart. - Без этого шага команда
docker pushзавершится ошибкой.
- Если вы используете Docker Desktop: зайдите в
-
На серверах (нодах):
- Скрипт
automate_deploy.pyпытается настроить это автоматически в файле/etc/docker/daemon.jsonи перезапустить Docker. - Если деплой все равно не видит образы, проверьте содержимое файла на нодах вручную.
- Скрипт
Пайплайн автоматизации (automate_deploy.py) спроектирован с учетом требований безопасности и отказоустойчивости. Ниже приведен разбор ключевых решений:
- Реализация: Реестр развернут как
docker service createс ограничением на запуск только на менеджер-ноде. - Почему так: В отличие от обычного контейнера (
docker run), Swarm-сервис автоматически перезапустится при сбое. Использование именованного томаregistry_dataгарантирует, что ваши образы не исчезнут при перезагрузке сервера. - Безопасность: Внедрена Basic Auth. Доступ к реестру (push/pull) возможен только по логину и паролю из
inventory.json.
- Реализация: Секреты создаются через
stdin(стандартный ввод) SSH-сессии. - Почему так: Мы отказались от команды
echo "pass" | docker secret create, так как она оставляет пароль в истории команд (.bash_history) и списке процессов (ps aux). Передача через stdin полностью исключает утечку секрета в логи системы.
- Реализация: В
docker-compose.ymlустановлены параметрыupdate_configсorder: start-first. - Почему так: Swarm сначала запускает новый контейнер и ждет, пока он пройдет проверку
healthcheck. Только после того, как новая версия подтвердит свою работоспособность, старый контейнер будет остановлен. Это гарантирует отсутствие простоя при обновлении кода.
- Реализация: Каждая сборка получает тег с временной меткой (например,
:20240522_193005). - Почему так: Если использовать только тег
:latest, Swarm может не заметить изменений в образе и не начать обновление. Уникальные теги гарантируют, что Swarm увидит новую версию и инициирует деплой.
- Реализация: Для приложения жестко заданы
limits(1GB RAM) иreservations(256MB RAM). - Почему так: Это защищает ноды от "прожорливых" процессов (Memory Leak). Лимиты не дают приложению уронить всю операционную систему, а резервирование гарантирует, что Swarm не попытается запустить контейнер там, где для него недостаточно ресурсов.
- Реализация: Используется образ
portainer-ce(вместо устаревшегоportainer), внутренняя сетьagent_networkс параметромinternal: true, а также удален флаг--tlsskipverify. - Почему так: Версия Community Edition содержит патчи безопасности для уязвимостей 2023-2024 гг. Изоляция сети агентов и удаление флага небезопасной проверки TLS гарантируют, что к Docker API кластера сможет обращаться только сам Portainer, защищая трафик от прослушивания и вмешательства.
После деплоя сервисы доступны по следующим адресам (при условии настройки DNS):
- Frontend:
https://tryout.site - Backend API:
https://tryout.site/api - Documentation:
https://tryout.site/api/docs - Grafana:
https://grafana.tryout.site(порт 3000 закрыт для прямого доступа) - pgAdmin:
https://pgadmin.tryout.site(порт 8080 закрыт для прямого доступа) - Portainer:
https://portainer.tryout.site(порт 9000 закрыт для прямого доступа)
Для работы поддоменов необходимо добавить A-записи у вашего регистратора:
| Поддомен | Тип | Значение (IP Менеджера) |
|---|---|---|
@ (или основной) |
A | 85.198.86.165 |
grafana |
A | 85.198.86.165 |
pgadmin |
A | 85.198.86.165 |
portainer |
A | 85.198.86.165 |
SSL-сертификаты (Let's Encrypt) будут выпущены автоматически через Traefik при первом обращении к доменам.
Если какой-то сервис (например, pgAdmin) не доступен:
-
Проверьте статус сервисов:
docker service ls
Убедитесь, что количество реплик совпадает (например,
1/1). -
Просмотрите логи проблемного сервиса:
# Для pgAdmin docker service logs fastapi_stack_pgadmin # Для Grafana docker service logs fastapi_stack_grafana
-
Проверьте секреты:
docker secret ls
При первом входе в Grafana (логин/пароль: admin/admin) необходимо подключить источники данных. Так как все сервисы находятся в одной Docker-сети app_network, используйте внутренние имена сервисов:
-
Prometheus (Метрики):
- URL:
http://prometheus:9090 - Access:
Server (default)
- URL:
-
Loki (Логи):
- URL:
http://loki:3100
- URL:
- URL:
https://pgadmin.tryout.site - Email:
admin@admin.com(изinventory.json) - Пароль:
admin_password_123(изinventory.json) - Подключение к БД внутри pgAdmin:
- Host:
db - Port:
5432 - Maintenance database:
app_db - Username:
postgres - Password: см.
db_passwordвinventory.json
- Host:
Для очистки всех нод от предыдущих деплоев, неиспользуемых контейнеров и образов используйте скрипт cleanup_nodes.py:
# Базовая очистка (удаление стеков, секретов, конфигов и сетей)
python infrastructure/cleanup_nodes.py
# Очистка с удалением всех данных (вольюмов)
python infrastructure/cleanup_nodes.py --volumes
# Полная очистка (удаление всего, включая выход из Docker Swarm и удаление образов)
python infrastructure/cleanup_nodes.py --fullВ docker-compose.yml настроены параметры для бесшовного обновления:
order: start-first: сначала запускается новый контейнер, затем останавливается старый.healthcheck: Swarm ждет, пока приложение станетhealthyперед тем, как переключить трафик и остановить старую версию.parallelism: 1: обновление происходит по одной реплике за раз.
В проекте настроен полноценный стек мониторинга:
- Grafana (порт 3000): Визуализация метрик и логов.
- Prometheus: Сбор метрик с сервисов и хостов.
- Loki: Централизованный сбор логов.
- Node-Exporter: Метрики операционной системы.
- cAdvisor: Метрики контейнеров.
- Portainer: Управление контейнерами (доступен по
https://portainer.tryout.site).
При каждом деплое через update_services.py автоматически запускается сканирование образов с помощью Trivy.
- Скрипт ищет уязвимости уровней
HIGHиCRITICAL. - Если уязвимости найдены, скрипт запросит подтверждение перед продолжением деплоя.
Все критические сервисы (DB, Redis, Backend, Frontend) снабжены проверками здоровья:
- Swarm автоматически перезапускает зависшие контейнеры.
- Обновление сервиса (Zero Downtime) не начнется, пока новый контейнер не станет
healthy.